diff options
| author | eltrac <hi@eltr.ac> | 2026-10-29 12:42:50 +0100 |
|---|---|---|
| committer | Eltrac <codeberg@eltr.ac> | 2026-10-29 12:42:50 +0100 |
| commit | 573e5f513387ebd34ad9df4b7fe4dc83d7323c8d (patch) | |
| tree | 70e60433aab15f65f19a90a44aa15b7ca3242273 /test | |
| parent | 77ab58d1ed5686b3c1c1685a6cc0bdd68603ab93 (diff) | |
all: use JF2 as a single source of truth, compile microformats/JSON-LD/<meta> to JF2 (#80)
closes #75
This is a large squash commit. For details, see https://codeberg.org/eltrac/weepinbell/pulls/80
Diffstat (limited to 'test')
| -rw-r--r-- | test/resources/webmention-jf2-test.edn | 17 | ||||
| -rw-r--r-- | test/resources/webmention-test.edn | 17 | ||||
| -rw-r--r-- | test/weepinbell/jf2_test.clj | 279 | ||||
| -rw-r--r-- | test/weepinbell/microformats_test.clj | 122 | ||||
| -rw-r--r-- | test/weepinbell/migration_test.clj | 39 | ||||
| -rw-r--r-- | test/weepinbell/retriever_test.clj | 93 | ||||
| -rw-r--r-- | test/weepinbell/storage_test.clj | 4 | ||||
| -rw-r--r-- | test/weepinbell/wrapper_test.clj | 100 |
8 files changed, 510 insertions, 161 deletions
diff --git a/test/resources/webmention-jf2-test.edn b/test/resources/webmention-jf2-test.edn new file mode 100644 index 0000000..5ec6a0d --- /dev/null +++ b/test/resources/webmention-jf2-test.edn @@ -0,0 +1,17 @@ +{:webmention {:source "https://alice.example/posts/hello", :target "https://example.com/blog/first-post", :id "0a1b2c3d4e5f60718293a4b5c6d7e8f9", :modified 1700000000000}}
+{:webmention {:source "https://bob.example/notes/re-first-post", :target "https://example.com/blog/first-post", :id "1b2c3d4e5f60718293a4b5c6d7e8f90a", :modified 1700000100000}, :jf2 {:type "entry", :name "Re: first post", :url nil, :published "2023-11-14T22:13:20+00:00", :author {:type "card", :name "Bob", :url "https://bob.example"}, :content {:html "Really enjoyed <em>this</em>.", :text "Really enjoyed this."}, :wm-property :mention}}
+{:webmention {:source "https://carol.example/likes/first-post", :target "https://example.com/blog/first-post", :id "2c3d4e5f60718293a4b5c6d7e8f90a1b", :modified 1700000200000}, :extra {:proxied-photo "http://localhost:3000/avatars/9f8e7d6c5b4a39281706f5e4d3c2b1a0.png"}, :jf2 {:type "entry", :name nil, :url nil, :published nil, :author {:type "card", :name "Carol", :url "https://carol.example", :photo "https://carol.example/avatar.png"}, :like-of ["https://example.com/blog/first-post"], :wm-property :like-of}}
+{:webmention {:source "https://dan.example/reposts", :target "https://example.com/blog/first-post", :id "3d4e5f60718293a4b5c6d7e8f90a1b2c", :modified 1700000300000}, :jf2 {:type "entry", :name nil, :url nil, :published nil, :author {:type "card", :name "Dan", :url "https://dan.example"}, :repost-of ["https://example.com/blog/first-post"], :wm-property :repost-of}}
+{:webmention {:source "https://erin.example/replies/42", :target "https://example.com/blog/second-post", :id "4e5f60718293a4b5c6d7e8f90a1b2c3d", :modified 1700000400000}, :extra {:proxied-photo "http://localhost:3000/avatars/0123456789abcdef0123456789abcdef.jpg"}, :jf2 {:type "entry", :name nil, :url nil, :published nil, :author {:type "card", :name "Erin", :url "https://erin.example", :photo "https://erin.example/media/erin.jpg"}, :content {:text "I disagree, but interesting take."}, :in-reply-to ["https://example.com/blog/second-post"], :wm-property :in-reply-to}}
+{:webmention {:source "https://frank.example/events/attend", :target "https://example.com/events/meetup-2024", :id "5f60718293a4b5c6d7e8f90a1b2c3d4e", :modified 1700000500000}, :jf2 {:type "entry", :name "I will be there", :url nil, :published nil, :author {:type "card", :name "Frank", :url nil}, :rsvp ["yes"], :wm-property :rsvp}}
+{:webmention {:source "https://grace.example/blog/a-link", :target "https://example.com/blog/post-a", :id "60718293a4b5c6d7e8f90a1b2c3d4e5f", :modified 1700000600000}, :jf2 {:type "entry", :name "A link worth sharing", :url nil, :published nil, :author {:type "card", :name "Grace", :url "https://grace.example"}, :wm-property :mention}}
+{:webmention {:source "https://heidi.example/blog/another-link", :target "https://example.com/blog/post-b", :id "718293a4b5c6d7e8f90a1b2c3d4e5f60", :modified 1700000700000}, :jf2 {:type "entry", :name "Also worth sharing", :url nil, :published nil, :author {:type "card", :name "Heidi", :url "https://heidi.example"}, :wm-property :mention}}
+{:webmention {:source "https://ivan.example/about", :target "https://example.com/blog/post-a", :id "8293a4b5c6d7e8f90a1b2c3d4e5f6071", :modified 1700000800000}, :extra {:proxied-photo "http://localhost:3000/avatars/fedcba9876543210fedcba9876543210.webp"}, :jf2 {:type "entry", :name nil, :url nil, :published nil, :author {:type "card", :name "Ivan", :url "https://ivan.example", :photo "https://ivan.example/photo.webp"}, :wm-property :mention}}
+{:webmention {:source "https://judy.example/empty", :target "https://example.com/blog/post-b", :id "93a4b5c6d7e8f90a1b2c3d4e5f607182", :modified 1700000900000}, :jf2 {:type "entry", :name nil, :url nil, :published nil, :author {:type "card", :name nil, :url nil}, :wm-property :mention}}
+{:webmention {:source "https://karl.example/notes/nested", :target "https://example.com/blog/post-c", :id "a4b5c6d7e8f90a1b2c3d4e5f60718293", :modified 1700001000000}, :jf2 {:type "entry", :name "Nested children", :url nil, :published nil, :author {:type "card", :name "Karl", :url nil}, :wm-property :mention}}
+{:webmention {:source "https://laura.example/feed", :target "https://example.com/blog/post-c", :id "b5c6d7e8f90a1b2c3d4e5f60718293a4", :modified 1700001100000}, :jf2 {:type "entry", :name nil, :url nil, :published nil, :author {:type "card", :name "Laura", :url nil}, :like-of ["https://example.com/blog/post-c/"], :wm-property :like-of}}
+{:webmention {:source "https://mallory.example/posts/trailing", :target "https://example.com/blog/post-d/?ref=reader", :id "c6d7e8f90a1b2c3d4e5f60718293a4b5", :modified 1700001200000}, :jf2 {:type "entry", :name nil, :url nil, :published nil, :author {:type "card", :name nil, :url nil}, :content {:text "Citation with trailing slash weblog."}, :in-reply-to ["https://example.com/blog/post-d"], :wm-property :in-reply-to}}
+{:webmention {:source "https://niaj.example/mentions/no-mf", :target "https://example.com/blog/post-d/", :id "d7e8f90a1b2c3d4e5f60718293a4b5c6", :modified 1700001300000}, :extra {:proxied-photo "http://localhost:3000/avatars/mnopqrstuvwxyz0123456789abcdef01.png"}}
+{:webmention {:source "https://olivia.example/notes/older-id", :target "https://example.com/blog/first-post", :id "e8f90a1b2c3d4e5f60718293a4b5c6d7", :modified 1699999999000}, :jf2 {:type "entry", :name nil, :url nil, :published nil, :author {:type "card", :name "Olivia", :url "https://olivia.example"}, :like-of ["https://example.com/blog/first-post"], :wm-property :like-of}}
+{:webmention {:source "https://peggy.example/legacy-hash", :target "https://example.org/notes/legacy", :id "deadbeef", :modified 1699999998000}, :jf2 {:type "entry", :name nil, :url nil, :published nil, :author {:type "card", :name nil, :url nil}, :repost-of ["https://example.org/notes/legacy"], :wm-property :repost-of}}
+{:webmention {:source "https://quinn.example/posts/long-id", :target "https://example.org/notes/legacy", :id "1234567890abcdef1234567890abcdef", :modified 1699999997000}}
diff --git a/test/resources/webmention-test.edn b/test/resources/webmention-test.edn new file mode 100644 index 0000000..e655daf --- /dev/null +++ b/test/resources/webmention-test.edn @@ -0,0 +1,17 @@ +{:webmention {:source "https://alice.example/posts/hello" :target "https://example.com/blog/first-post" :id "0a1b2c3d4e5f60718293a4b5c6d7e8f9" :modified 1700000000000}} +{:webmention {:source "https://bob.example/notes/re-first-post" :target "https://example.com/blog/first-post" :id "1b2c3d4e5f60718293a4b5c6d7e8f90a" :modified 1700000100000} :microformats [{:type ["h-entry"] :properties {:name ["Re: first post"] :content [{:value "Really enjoyed this." :html "Really enjoyed <em>this</em>."}] :author [{:type ["h-card"] :properties {:name ["Bob"] :url ["https://bob.example"]}}] :published ["2023-11-14T22:13:20+00:00"]}}]} +{:webmention {:source "https://carol.example/likes/first-post" :target "https://example.com/blog/first-post" :id "2c3d4e5f60718293a4b5c6d7e8f90a1b" :modified 1700000200000} :microformats [{:type ["h-entry"] :properties {:like-of ["https://example.com/blog/first-post"] :author [{:type ["h-card"] :properties {:name ["Carol"] :photo ["https://carol.example/avatar.png"] :url ["https://carol.example"]}}]}}] :extra {:proxied-photo "http://localhost:3000/avatars/9f8e7d6c5b4a39281706f5e4d3c2b1a0.png"}} +{:webmention {:source "https://dan.example/reposts" :target "https://example.com/blog/first-post" :id "3d4e5f60718293a4b5c6d7e8f90a1b2c" :modified 1700000300000} :microformats [{:type ["h-entry"] :properties {:repost-of ["https://example.com/blog/first-post"] :author [{:type ["h-card"] :properties {:name ["Dan"] :url ["https://dan.example"]}}]}}]} +{:webmention {:source "https://erin.example/replies/42" :target "https://example.com/blog/second-post" :id "4e5f60718293a4b5c6d7e8f90a1b2c3d" :modified 1700000400000} :microformats [{:type ["h-entry"] :properties {:in-reply-to ["https://example.com/blog/second-post"] :content [{:value "I disagree, but interesting take."}] :author [{:type ["h-card"] :properties {:name ["Erin"] :photo [{:value "https://erin.example/media/erin.jpg" :alt "Erin"} ] :url ["https://erin.example"]}}]}}] :extra {:proxied-photo "http://localhost:3000/avatars/0123456789abcdef0123456789abcdef.jpg"}} +{:webmention {:source "https://frank.example/events/attend" :target "https://example.com/events/meetup-2024" :id "5f60718293a4b5c6d7e8f90a1b2c3d4e" :modified 1700000500000} :microformats [{:type ["h-entry"] :properties {:rsvp ["yes"] :name ["I will be there"] :author [{:type ["h-card"] :properties {:name ["Frank"]}}]}}]} +{:webmention {:source "https://grace.example/blog/a-link" :target "https://example.com/blog/post-a" :id "60718293a4b5c6d7e8f90a1b2c3d4e5f" :modified 1700000600000} :microformats [{:type ["h-entry"] :properties {:name ["A link worth sharing"] :author [{:type ["h-card"] :properties {:name ["Grace"] :url ["https://grace.example"]}}]}}]} +{:webmention {:source "https://heidi.example/blog/another-link" :target "https://example.com/blog/post-b" :id "718293a4b5c6d7e8f90a1b2c3d4e5f60" :modified 1700000700000} :microformats [{:type ["h-entry"] :properties {:name ["Also worth sharing"] :author [{:type ["h-card"] :properties {:name ["Heidi"] :url ["https://heidi.example"]}}]}}]} +{:webmention {:source "https://ivan.example/about" :target "https://example.com/blog/post-a" :id "8293a4b5c6d7e8f90a1b2c3d4e5f6071" :modified 1700000800000} :microformats [{:type ["h-card"] :properties {:name ["Ivan"] :url ["https://ivan.example"] :photo ["https://ivan.example/photo.webp"]}}] :extra {:proxied-photo "http://localhost:3000/avatars/fedcba9876543210fedcba9876543210.webp"}} +{:webmention {:source "https://judy.example/empty" :target "https://example.com/blog/post-b" :id "93a4b5c6d7e8f90a1b2c3d4e5f607182" :modified 1700000900000} :microformats []} +{:webmention {:source "https://karl.example/notes/nested" :target "https://example.com/blog/post-c" :id "a4b5c6d7e8f90a1b2c3d4e5f60718293" :modified 1700001000000} :microformats [{:type ["h-entry"] :properties {:name ["Nested children"] :author [{:type ["h-card"] :properties {:name ["Karl"]}}]} :children [{:type ["h-cite"] :properties {:name ["Cited work"] :url ["https://somewhere.example/paper"]}}]}]} +{:webmention {:source "https://laura.example/feed" :target "https://example.com/blog/post-c" :id "b5c6d7e8f90a1b2c3d4e5f60718293a4" :modified 1700001100000} :microformats [{:type ["h-feed"] :properties {:name ["Laura's feed"]} :children [{:type ["h-entry"] :properties {:like-of ["https://example.com/blog/post-c/"] :author [{:type "h-card" :properties {:name ["Laura"]}}]}}]}]} +{:webmention {:source "https://mallory.example/posts/trailing" :target "https://example.com/blog/post-d/?ref=reader" :id "c6d7e8f90a1b2c3d4e5f60718293a4b5" :modified 1700001200000} :microformats [{:type ["h-entry"] :properties {:in-reply-to ["https://example.com/blog/post-d"] :content [{:value "Citation with trailing slash weblog."}]}}]} +{:webmention {:source "https://niaj.example/mentions/no-mf" :target "https://example.com/blog/post-d/" :id "d7e8f90a1b2c3d4e5f60718293a4b5c6" :modified 1700001300000} :extra {:proxied-photo "http://localhost:3000/avatars/mnopqrstuvwxyz0123456789abcdef01.png"}} +{:webmention {:source "https://olivia.example/notes/older-id" :target "https://example.com/blog/first-post" :id "e8f90a1b2c3d4e5f60718293a4b5c6d7" :modified 1699999999000} :microformats [{:type ["h-entry"] :properties {:like-of ["https://example.com/blog/first-post"] :author [{:type ["h-card"] :properties {:name ["Olivia"] :url ["https://olivia.example"]}}]} :children []}]} +{:webmention {:source "https://peggy.example/legacy-hash" :target "https://example.org/notes/legacy" :id "deadbeef" :modified 1699999998000} :microformats [{:type ["h-entry"] :properties {:repost-of ["https://example.org/notes/legacy"]}}]} +{:webmention {:source "https://quinn.example/posts/long-id" :target "https://example.org/notes/legacy" :id "1234567890abcdef1234567890abcdef" :modified 1699999997000}} diff --git a/test/weepinbell/jf2_test.clj b/test/weepinbell/jf2_test.clj new file mode 100644 index 0000000..acaaaca --- /dev/null +++ b/test/weepinbell/jf2_test.clj @@ -0,0 +1,279 @@ +(ns weepinbell.jf2-test + (:require [clojure.test :refer [deftest testing is]] + [hickory.core :as hickory] + [weepinbell.metadata.microformats :refer [parse-microformats]] + [weepinbell.metadata.json-ld :refer [parse-json-ld]] + [weepinbell.metadata.html-head :refer [parse-meta]] + [weepinbell.metadata.jf2 :refer [parse supplement-merge]])) + +(def to-hick (comp hickory/as-hickory hickory/parse)) + +;; microformats -> JF2 + +(def test-data + [{:in {:items [{:type ["h-entry"], :properties {:author [{:type ["h-card"], :properties {:name ["A. Developer"], :url ["https://example.com/"]}, :value "A. Developer"}], :name ["Hello World"], :summary ["Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus imperdiet ultrices pulvinar."], :url ["https://example.com/2015/10/21"], :published ["2015-10-21T12:00:00-0700"], :content [{:html "<p>Donec dapibus enim lacus, <i>a vehicula magna bibendum non</i>. Phasellus id lacinia felis, vitae pellentesque enim. Sed at quam dui. Suspendisse accumsan, est id pulvinar consequat, urna ex tincidunt enim, nec sodales lectus nulla et augue. Cras venenatis vehicula molestie. Donec sagittis elit orci, sit amet egestas ex pharetra in.</p>", :value "Donec dapibus enim lacus, a vehicula magna bibendum non. Phasellus id lacinia felis, vitae pellentesque enim. Sed at quam dui. Suspendisse accumsan, est id pulvinar consequat, urna ex tincidunt enim, nec sodales lectus nulla et augue. Cras venenatis vehicula molestie. Donec sagittis elit orci, sit amet egestas ex pharetra in."}]}}]} + :out {:type "entry", :author {:type "card", :url "https://example.com", :name "A. Developer"}, :url "https://example.com/2015/10/21", :published "2015-10-21T12:00:00-0700", :name "Hello World", :summary "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus imperdiet ultrices pulvinar.", :content {:html "<p>Donec dapibus enim lacus, <i>a vehicula magna bibendum non</i>. Phasellus id lacinia felis, vitae pellentesque enim. Sed at quam dui. Suspendisse accumsan, est id pulvinar consequat, urna ex tincidunt enim, nec sodales lectus nulla et augue. Cras venenatis vehicula molestie. Donec sagittis elit orci, sit amet egestas ex pharetra in.</p>", :text "Donec dapibus enim lacus, a vehicula magna bibendum non. Phasellus id lacinia felis, vitae pellentesque enim. Sed at quam dui. Suspendisse accumsan, est id pulvinar consequat, urna ex tincidunt enim, nec sodales lectus nulla et augue. Cras venenatis vehicula molestie. Donec sagittis elit orci, sit amet egestas ex pharetra in."}, :wm-property :mention}}]) + +(deftest jf2-parse-microformats-test + (testing "Parsing example microformats to J2F, from https://jf2.spec.indieweb.org/" + (doseq [data test-data] + (is (= (:out data) + (parse-microformats (:in data))))))) + +;; JSON-LD -> JF2 + +(def test-jsonld " + <script type=\"application/ld+json\">{\n\t\"@context\": \"https://schema.org\",\n\t\"@type\": \"Article\",\n\t\"publisher\": {\n\t\t\"@type\": \"Organization\",\n\t\t\"name\": \"freeCodeCamp.org\",\n\t\t\"url\": \"https://www.freecodecamp.org/news/\",\n\t\t\"logo\": {\n\t\t\t\"@type\": \"ImageObject\",\n\t\t\t\"url\": \"https://cdn.freecodecamp.org/platform/universal/fcc_primary.svg\",\n\t\t\t\"width\": 2100,\n\t\t\t\"height\": 240\n\t\t}\n\t},\n\t\"image\": {\n\t\t\"@type\": \"ImageObject\",\n\t\t\"url\": \"https://www.freecodecamp.org/news/content/images/2020/03/open-graph.jpg\",\n\t\t\"width\": 600,\n\t\t\"height\": 400\n\t},\n\t\"url\": \"https://www.freecodecamp.org/news/what-is-open-graph-and-how-can-i-use-it-for-my-website/\",\n\t\"mainEntityOfPage\": {\n\t\t\"@type\": \"WebPage\",\n\t\t\"@id\": \"https://www.freecodecamp.org/news/\"\n\t},\n\t\"datePublished\": \"2020-03-26T14:51:33.000Z\",\n\t\"dateModified\": \"2020-03-26T14:51:33.000Z\",\n\t\"keywords\": \"Digital Marketing , HTML, marketing, open graph, social media, tech , Web Development\",\n\t\"description\": \"It can take a lot of time to build content and maintain a website. How can we make sure our content stands out when getting shared on social feeds around the internet?\\n\\nWhat is Open Graph?\\nWhy do I need it?\\nWhat happens if I don’t have it?\\nStarting w...\",\n\t\"headline\": \"What is Open Graph and how can I use it for my website?\",\n\t\"author\": {\n\t\t\"@type\": \"Person\",\n\t\t\"name\": \"Colby Fayock\",\n\t\t\"url\": \"https://www.freecodecamp.org/news/author/colbyfayock/\",\n\t\t\"sameAs\": [\n\t\t\t\"http://colbyfayock.com/\",\n\t\t\t\"https://x.com/colbyfayock\"\n\t\t],\n\t\t\"image\": {\n\t\t\t\"@type\": \"ImageObject\",\n\t\t\t\"url\": \"https://cdn.hashnode.com/res/hashnode/image/upload/v1601829804572/1eKdHWWIz.jpeg\",\n\t\t\t\"width\": 600,\n\t\t\t\"height\": 600\n\t\t}\n\t}\n}</script> + ") + +(def jsonld->jf2 + {:type "entry", :name "What is Open Graph and how can I use it for my website?", :url "https://www.freecodecamp.org/news/what-is-open-graph-and-how-can-i-use-it-for-my-website/", :author {:type "card", :name "Colby Fayock", :url "https://www.freecodecamp.org/news/author/colbyfayock/", :photo "https://cdn.hashnode.com/res/hashnode/image/upload/v1601829804572/1eKdHWWIz.jpeg"}, :published "2020-03-26T14:51:33.000Z", :content "", :wm-property :mention, :summary "It can take a lot of time to build content and maintain a website. How can we make sure our content stands out when getting shared on social feeds around the internet?\n\nWhat is Open Graph?\nWhy do I need it?\nWhat happens if I don’t have it?\nStarting w..."}) + +(def test-jsonld-html + (str "<html><head>" test-jsonld "</head></html>")) + +(deftest jf2-parse-jsonld-test + (testing "parse JSON-LD from a document" + (is (= (-> test-jsonld-html to-hick parse-json-ld) + jsonld->jf2))) + (testing "parse JSON-LD from raw script element" + (is (= (-> test-jsonld to-hick parse-json-ld) + jsonld->jf2)))) + +;; Open Graph, <head> tags -> JF2 + +(def link-and-meta-tags + {:open-graph + "<html><head> + <title>Fallback title</title> + <link rel=\"icon\" href=\"https://example.com/favicon.ico\" type=\"image/png\"> + <link rel=\"canonical\" href=\"https://example.com/open-graph\"> + <meta name=\"description\" content=\"Fallback description\"> + <meta property=\"og:title\" content=\"Open Graph title\"> + <meta property=\"og:url\" content=\"https://example.com/open-graph\"> + <meta property=\"og:description\" content=\"Open Graph description\"> + <meta property=\"article:author\" content=\"Open Graph author\"> + <meta property=\"article:published_time\" content=\"2024-01-02T03:04:05Z\"> + </head></html>" + + :twitter + "<html><head> + <title>Title fallback</title> + <link rel=\"canonical\" href=\"https://example.com/twitter\"> + <meta name=\"twitter:title\" content=\"Twitter title\"> + <meta name=\"twitter:url\" content=\"https://example.com/twitter\"> + <meta name=\"twitter:description\" content=\"Twitter description\"> + <meta name=\"twitter:creator\" content=\"@writer\"> + </head></html>" + + :profile-username + "<html><head> + <meta property=\"og:title\" content=\"Profile title\"> + <meta property=\"profile:username\" content=\"profile-username\"> + </head></html>" + + :profile-fullname + "<html><head> + <meta property=\"profile:first_name\" content=\"Rich\"> + <meta property=\"profile:last_name\" content=\"Hickey\"> + </head></html>" + + :basic + "<html><head> + <title>Document title</title> + <link rel=\"canonical\" href=\"https://example.com/cononical\"> + <meta name=\"description\" content=\"Document description\"> + </head></html>"}) + +(deftest jf2-parse-head-test + (testing "Open Graph to JF2" + (is (= (parse-meta (to-hick (:open-graph link-and-meta-tags))) + {:type "entry" + :name "Open Graph title" + :url "https://example.com/open-graph" + :author {:type "card" + :name "Open Graph author" + :url nil + :photo "https://example.com/favicon.ico"} + :published "2024-01-02T03:04:05Z" + :content "" + :wm-property :mention + :summary "Open Graph description"})) + (is (= (parse-meta (to-hick (:twitter link-and-meta-tags))) + {:type "entry" + :name "Twitter title" + :url "https://example.com/twitter" + :author {:type "card" + :name "@writer" + :url nil + :photo nil} + :published nil + :content "" + :wm-property :mention + :summary "Twitter description"})) + (is (= (parse-meta (to-hick (:profile-username link-and-meta-tags))) + {:type "entry" + :name "Profile title" + :url nil + :author {:type "card" + :name "profile-username" + :url nil + :photo nil} + :published nil + :content "" + :wm-property :mention + :summary nil})) + (is (= (parse-meta (to-hick (:profile-fullname link-and-meta-tags))) + {:type "entry" + :name nil + :url nil + :author {:type "card" + :name "Rich Hickey" + :url nil + :photo nil} + :published nil + :content "" + :wm-property :mention + :summary nil})) + (is (= (parse-meta (to-hick (:basic link-and-meta-tags))) + {:type "entry" + :name "Document title" + :url "https://example.com/cononical" + :author {:type "card" + :name nil + :url nil + :photo nil} + :published nil + :content "" + :wm-property :mention + :summary "Document description"})))) + +;; utilities + +(deftest supplement-merge-test + (testing "supplement-merge supplements A with values A lacks but B has" + (is (= (supplement-merge + {:a nil :b 1} {:a 1 :b 3}) + {:a 1 :b 1}))) + (testing "supplement-merge never overrides existing values, except nil" + (is (= (supplement-merge {:a "hi"} {:a "bye"}) {:a "hi"}))) + (testing "supplement-merge does deep merge as well" + (is (= (supplement-merge {:a {:aa "hello" :ab nil} + :b nil} + {:a {:aa "byebye" :ab "yes"} + :b nil}) + {:a {:aa "hello" :ab "yes"} :b nil}))) + (testing "supplement-merge adds missing keys" + (is (= (supplement-merge {:a "n"} {:a "m" :b "s"}) + {:a "n" :b "s"})))) + +;; compile all sources + +(def compiling-test-data + [{:html "<html><head></head><body> + <article class=\"h-entry\"> + <h1 class=\"p-name\">Microformats title</h1> + <a class=\"u-url\" href=\"https://example.com/microformats\">Permalink</a> + <time class=\"dt-published\" datetime=\"2024-02-03T04:05:06Z\">February 3</time> + <div class=\"p-author h-card\"> + <span class=\"p-name\">Microformats author</span> + <a class=\"u-url\" href=\"https://example.com/author\">Author</a> + </div> + <div class=\"e-content\">Microformats content</div> + <a class=\"u-in-reply-to\" href=\"https://example.com/original\">Original</a> + </article> + </body></html>" + :out {:type "entry" + :name "Microformats title" + :url "https://example.com/microformats" + :author {:type "card" + :name "Microformats author" + :url "https://example.com/author" + :photo nil} + :published "2024-02-03T04:05:06Z" + :content {:html "Microformats content" + :text "Microformats content"} + :wm-property :in-reply-to + :in-reply-to ["https://example.com/original"] + :summary nil}} + {:html "<html><head> + <title>Document title</title> + <link rel=\"canonical\" href=\"https://example.com/head-url\"> + <meta property=\"og:title\" content=\"Open Graph title\"> + <meta property=\"og:url\" content=\"https://example.com/og-url\"> + <meta property=\"og:description\" content=\"Open Graph summary\"> + <meta property=\"article:author\" content=\"Head author\"> + <meta property=\"article:published_time\" content=\"2024-03-04T05:06:07Z\"> + <script type=\"application/ld+json\">{ + \"@context\": \"https://schema.org\", + \"@type\": \"Article\", + \"headline\": \"JSON-LD title\", + \"url\": \"https://example.com/json-ld-url\", + \"datePublished\": \"2024-04-05T06:07:08Z\", + \"description\": \"JSON-LD summary\", + \"author\": { + \"@type\": \"Person\", + \"name\": \"JSON-LD author\", + \"url\": \"https://example.com/json-ld-author\", + \"image\": {\"url\": \"https://example.com/author.jpg\"} + } + }</script> + </head><body> + <article class=\"h-entry\"> + <h1 class=\"p-name\">Microformats title</h1> + <a class=\"u-url\" href=\"https://example.com/microformats-url\">Permalink</a> + <time class=\"dt-published\" datetime=\"2024-01-02T03:04:05Z\">January 2</time> + <div class=\"p-author h-card\"> + <span class=\"p-name\">Microformats author</span> + <a class=\"u-url\" href=\"https://example.com/microformats-author\">Author</a> + </div> + <div class=\"e-content\">Microformats content</div> + </article> + </body></html>" + :out {:type "entry" + :name "Microformats title" + :url "https://example.com/microformats-url" + :author {:type "card" + :name "Microformats author" + :url "https://example.com/microformats-author" + :photo "https://example.com/author.jpg"} + :published "2024-01-02T03:04:05Z" + :content {:html "Microformats content" + :text "Microformats content"} + :wm-property :mention + :summary "Open Graph summary"}} + {:html "<html><head> + <meta name=\"description\" content=\"Head description\"> + <meta name=\"twitter:title\" content=\"Twitter title\"> + <meta name=\"twitter:url\" content=\"https://example.com/twitter-url\"> + <meta name=\"twitter:creator\" content=\"@head-author\"> + <script type=\"application/ld+json\">{ + \"@context\": \"https://schema.org\", + \"@type\": \"Article\", + \"headline\": \"JSON-LD title\", + \"url\": \"https://example.com/json-ld-url\", + \"datePublished\": \"2024-05-06T07:08:09Z\", + \"author\": { + \"@type\": \"Person\", + \"name\": \"JSON-LD author\", + \"url\": \"https://example.com/json-ld-author\" + }, + \"description\": \"JSON-LD description\" + }</script> + </head><body></body></html>" + :out {:type "entry" + :name "Twitter title" + :url "https://example.com/twitter-url" + :author {:type "card" + :name "@head-author" + :url "https://example.com/json-ld-author" + :photo nil} + :published "2024-05-06T07:08:09Z" + :content "" + :wm-property :mention + :summary "Head description"}}]) + +(deftest jf2-parse-test + (testing "compiles JF2 from all available metadata sources" + (doseq [{:keys [html out]} compiling-test-data] + (is (= out (parse html)))))) diff --git a/test/weepinbell/microformats_test.clj b/test/weepinbell/microformats_test.clj deleted file mode 100644 index 5f17a5b..0000000 --- a/test/weepinbell/microformats_test.clj +++ /dev/null @@ -1,122 +0,0 @@ -(ns weepinbell.microformats-test
- (:require [weepinbell.microformats.reducer :refer [reduce-mf]]
- [weepinbell.microformats.searcher :as sr]
- [clojure.spec.alpha :as s]
- [clojure.test :refer [deftest testing is]]))
-
-(def testdata
- [{:target "https://test.com/"
- :in {:items [{:type ["h-entry"]
- :properties {:content [{:html "<div>test</div>"
- :text "test"}]
- :in-reply-to ["https://test.com"]
- :published [52341243232]
- :author [{:type ["h-card"]
- :properties {:name ["test"]
- :nickname ["testie"]
- :photo [{:alt "testie's selfie"
- :value "https://test.com/photo.png"}]
- :url ["https://test.com"]}}]}}]}
- :out {:author
- {:name "test",
- :url "https://test.com",
- :photo "https://test.com/photo.png"},
- :type :in-reply-to,
- :published 52341243232,
- :content {:html "<div>test</div>", :text "test"}}}
- {:target ""
- :in {:items [{:type ["h-entry"]
- :properties {:content [{:html "<div>test</div>"
- :text "test"}]
- :published [52341243232]}}
- {:type ["h-card"]
- :properties {:name ["test"]
- :nickname ["testie"]
- :photo [{:alt "testie's selfie"
- :value "https://test.com/photo.png"}]
- :url ["https://test.com"]}}]}
- :out {:author
- {:name "test",
- :url "https://test.com",
- :photo "https://test.com/photo.png"},
- :type :mention,
- :published 52341243232,
- :content {:html "<div>test</div>", :text "test"}}}])
-
-(def spec-check (partial s/valid? :weepinbell.microformats.reducer/reduced-microformats))
-(deftest reducer-test
- (testing "Normal microformats reduction"
- (->> testdata
- (map #(let [reduced (reduce-mf (:target %) (:in %))]
- (is (= reduced (:out %)))
- (is (spec-check reduced))))
- doall))
- (testing "spec check invalid reduced microformats"
- (is (not (spec-check {:author nil :type :mention :published 1 :content nil})))
- (is (not (spec-check [])))))
-
-(deftest searcher-test
- (testing "use-items gets a list of microformats items"
- (->> testdata
- (map #(is (= (sr/use-items (:in %))
- (sr/use-items (:items (:in %)))
- (get-in % [:in :items]))))
- doall))
- (testing "use-items returns nil if not items or cannot get items in it"
- (is (nil? (sr/use-items {:no-items nil})))
- (is (nil? (sr/use-items "no-items")))
- (is (nil? (sr/use-items [])))
- (is (nil? (sr/use-items 1))))
- (testing "use-props get properties of a microformats item"
- (->> testdata
- (map #(is (= (->> % sr/use-items sr/find-h-entry (sr/use-prop :content))
- (->> % sr/use-items sr/find-h-entry (sr/use-prop "content"))
- (get-in % [:items first :properties :content first]))))
- doall))
- (testing "use-props works with out :properties key as well"
- (is (= (sr/use-prop :key {:properties {:key ["value"]}})
- (sr/use-prop :key {:key ["value"]})
- "value")))
- (testing "find-type should find items with a specific type given a collection of items"
- (is (= ["h-entry"]
- (->> testdata first :in sr/use-items sr/find-h-entry :type)))
- (is (= ["h-card"]
- (->> testdata second :in sr/use-items sr/find-h-card :type))))
- (testing "find-type should recursively find qualified items inside items' children"
- (let [testdata-a {:items [{:type ["h-feed"]
- :children [{:type ["h-entry"]
- :properties {:content [{:html "<div>test</div>"
- :text "test"}]
- :published [52341243232]}}]}]}]
- (is (= ["h-entry"]
- (->> testdata-a sr/use-items sr/find-h-entry :type))))
- (testing "find-type should find all matched items on different layers"
- (let [testdata-a {:items [{:type ["h-feed"]
- :children [{:type ["h-entry"]
- :children [{:type ["h-card"]}]}
- {:type ["h-card"]}]}
- {:type ["h-card"]}]}]
- (is (= 3 (->> testdata-a sr/use-items sr/find-h-cards count)))))
- (testing "find-type should not return empty seq if nothing's matched"
- (let [testdata-a {:items [{:type ["h-feed"]
- :children []}]}]
- (is (->> testdata-a sr/use-items sr/find-h-entry empty?)))))
- (testing "search photos in h-card"
- (let [photo-url "https://test.com/photo.png"
- testdata-a {:type ["h-card"]
- :properties {:photo [photo-url]}}
- testdata-b {:type ["h-card"]
- :properties {:photo [{:alt "photo"
- :value photo-url}]}}
- testdata-c {:type ["h-entry"]
- :properties {:author [{:type ["h-card"]
- :properties {:photo [photo-url]}}]}}
- testdata-d {:type ["h-card"]
- :properties {:author [{:type ["h-card"]
- :properties {}}]
- :org [{:type ["h-card"]
- :properties {:photo [photo-url]}}]}}]
- (is (= photo-url (sr/search-photo testdata-a)))
- (is (= photo-url (sr/search-photo testdata-b)))
- (is (= photo-url (sr/search-photo testdata-c)))
- (is (= photo-url (sr/search-photo testdata-d))))))
diff --git a/test/weepinbell/migration_test.clj b/test/weepinbell/migration_test.clj new file mode 100644 index 0000000..6cf06e0 --- /dev/null +++ b/test/weepinbell/migration_test.clj @@ -0,0 +1,39 @@ +(ns weepinbell.migration-test
+ (:require [weepinbell.migration :as m]
+ [weepinbell.metadata.jf2-spec]
+ [fast-edn.core :as edn]
+ [weepinbell.storage.h2 :as h2]
+ [next.jdbc :as jdbc]
+ [clojure.spec.alpha :as s]
+ [clojure.test :refer [deftest testing is]]
+ [clojure.java.io :as io]))
+
+(def webmention-test-edn
+ (with-open [rdr (io/reader (io/resource "webmention-test.edn"))]
+ (->> rdr line-seq (map edn/read-string) doall)))
+
+(deftest test-migration-from-mf-to-jf2
+ (testing "Convert a Webmention object with microformats to one with jf2"
+ (let [data (->> webmention-test-edn
+ (filter #(some? (:microformats %)))
+ first)
+ converted (m/mf->jf2 data)]
+ (is (contains? converted :jf2))
+ (is (not (contains? converted :microformats)))
+ (is (s/valid? :weepinbell.metadata.jf2-spec/jf2
+ (:jf2 converted))))))
+
+(deftest test-migration-from-edn-to-h2
+ (testing "Migrating EDN file storage to H2 database storage"
+ (let [source-edn (io/resource "webmention-test.edn")
+ target-h2 "tmp/webmention-test-h2"]
+ ;; assert migration succeed with no error
+ (is (nil? (try
+ (m/edn->h2 source-edn target-h2)
+ (catch Throwable _ :fail))))
+ ;; assert data count
+ (let [ds (h2/make-datasource target-h2)
+ result (jdbc/execute! ds ["SELECT count(*) FROM webmention"])
+ len (get (first result) (keyword "count(*)"))]
+ (is (= len (count webmention-test-edn))))
+ (io/delete-file (str target-h2 ".mv.db")))))
diff --git a/test/weepinbell/retriever_test.clj b/test/weepinbell/retriever_test.clj index 98102ef..72a5bf7 100644 --- a/test/weepinbell/retriever_test.clj +++ b/test/weepinbell/retriever_test.clj @@ -1,35 +1,27 @@ (ns weepinbell.retriever-test
(:require [clojure.test :refer [deftest testing is]]
+ [weepinbell.utils.digest :refer [digest]]
[weepinbell.webserver.retriever :as ret]))
(def page-size 1)
+
+(defn- make-bare-wm [number]
+ {:webmention {:source (str number) :target (str number)
+ :id (digest (str number))
+ :modified (System/currentTimeMillis)}})
+(defn- make-typed-wm [type number]
+ (-> (make-bare-wm number)
+ (assoc :jf2 {:wm-property type})))
+
(deftest retrieve-by-domain-test
(testing "by-domain list paging"
(with-redefs [weepinbell.webserver.retriever/default-page-size page-size]
- (let [mock-data [{:webmention {:source "1" :target "1"}}
- {:webmention {:source "2" :target "2"}}]
+ (let [mock-data (mapv make-bare-wm (range 2))
mock-store {:get-by-domain (fn [& _] mock-data)}
resp0 (ret/by-domain "" 0 mock-store {})
resp1 (ret/by-domain "" 1 mock-store {})]
(is (= page-size (count (:body resp0))))
(is (= page-size (count (:body resp1)))))))
- (testing "by-domain: if page is a string that can be parsed to number, it should work"
- (with-redefs [weepinbell.webserver.retriever/default-page-size page-size]
- (let [mock-data [{:webmention {:source "1" :target "1"}}
- {:webmention {:source "2" :target "2"}}]
- mock-store {:get-by-domain (fn [& _] mock-data)}
- resp0 (ret/by-domain "" "0" mock-store {})
- resp1 (ret/by-domain "" 0 mock-store {})]
- (is (= resp0 resp1)))))
- (testing "by-domain: if page is not number, return the last page frame"
- (with-redefs [weepinbell.webserver.retriever/default-page-size page-size]
- (let [mock-data [{:webmention {:source "1" :target "1"}}
- {:webmention {:source "2" :target "2"}}]
- mock-store {:get-by-domain (fn [& _] mock-data)}
- resp (ret/by-domain "" true mock-store {})]
- (is (= 200 (:status resp)))
- (is (= (-> mock-data last :webmention)
- (-> resp :body last :webmention))))))
(testing "by-domain return 404 when list is empty"
(let [mock-store {:get-by-domain (fn [& _] [])}
resp (ret/by-domain "" nil mock-store {})]
@@ -40,8 +32,7 @@ (deftest retrieve-by-url-test
(testing "by-url list paging"
(with-redefs [weepinbell.webserver.retriever/default-page-size page-size]
- (let [mock-data [{:webmention {:source "1" :target "2"}}
- {:webmention {:source "1" :target "2"}}]
+ (let [mock-data (mapv make-bare-wm (range 2))
mock-store {:get-by-domain (fn [& _] mock-data)}
resp0 (ret/by-url "2" 0 mock-store {})
resp1 (ret/by-url "2" 1 mock-store {})]
@@ -57,15 +48,63 @@ (testing "by-url return 404 when list is empty"
(with-redefs [weepinbell.webserver.retriever/default-page-size page-size]
(let [mock-store {:get-by-domain (fn [& _])}
- resp (ret/by-url "" 0 mock-store {})]
+ resp (ret/by-url "" nil mock-store {})]
(is (= 404 (:status resp)))
(is (= "The requested webmention list does not exist."
(get-in resp [:body :message])))))))
-(deftest retrieve-by-id-test
- (testing "by-id return 404 when no webmention is matched"
- (let [mock-store {:get (fn [& _] [])}
- resp (ret/by-id "" mock-store {})]
+(defn- should-404 [data]
+ (let [mock-store {:get (fn [& _] data)}
+ resp (ret/by-id "" mock-store)]
(is (= 404 (:status resp)))
(is (= "The requested webmention does not exist."
- (get-in resp [:body :message]))))))
+ (get-in resp [:body :message])))))
+(deftest retrieve-by-id-test
+ (testing "by-id return 404 when no webmention is matched"
+ (doseq [no-data [{} [] nil]]
+ (should-404 no-data))))
+
+(deftest filter-by-jf2type-test
+ (testing "by-domain ?jf2type filter"
+ (let [like-wms (mapv (partial make-typed-wm :like-of) (range 2))
+ reply-wms (mapv (partial make-typed-wm :in-reply-to) (range 3))
+ mock-data (reduce into [] [like-wms reply-wms])
+ mock-store {:get-by-domain (fn [& _] mock-data)}
+ ;; responses
+ resp-like (ret/by-domain "" 0 mock-store {:type :like-of})
+ resp-reply (ret/by-domain "" 0 mock-store {:type :in-reply-to})]
+ (is (= reply-wms (:body resp-reply)))
+ (is (= like-wms (:body resp-like)))))
+ (testing "by-url ?jf2type filter"
+ (let [like-wms (mapv (partial make-typed-wm :like-of) (range 2))
+ reply-wms (mapv (partial make-typed-wm :in-reply-to) (range 3))
+ mock-data (reduce into [] [like-wms reply-wms])
+ mock-store {:get-by-domain (fn [& _] mock-data)}
+ ;; responses
+ resp-like (ret/by-url "" 0 mock-store {:type :like-of})
+ resp-reply (ret/by-url "" 0 mock-store {:type :in-reply-to})]
+ (is (= reply-wms (:body resp-reply)))
+ (is (= like-wms (:body resp-like))))))
+
+(deftest paging-edge-cases-test
+ (testing "if page is a string that can be parsed to number, it should work"
+ (with-redefs [weepinbell.webserver.retriever/default-page-size page-size]
+ (let [mock-data (mapv make-bare-wm (range 2))
+ mock-store {:get-by-domain (fn [& _] mock-data)}
+ resp0 (ret/by-domain "" "0" mock-store {})
+ resp1 (ret/by-domain "" 0 mock-store {})]
+ (is (= resp0 resp1)))))
+ (testing "if page is not number, return the last page frame"
+ (with-redefs [weepinbell.webserver.retriever/default-page-size page-size]
+ (let [mock-data (mapv make-bare-wm (range 2))
+ mock-store {:get-by-domain (fn [& _] mock-data)}
+ resp (ret/by-domain "" true mock-store {})]
+ (is (= 200 (:status resp)))
+ (is (= (-> mock-data last :webmention)
+ (-> resp :body last :webmention))))))
+ (testing "if page is larger than total amount of pages, return error"
+ (with-redefs [weepinbell.webserver.retriever/default-page-size page-size]
+ (let [mock-data [(make-bare-wm page-size)]
+ mock-store {:get-by-domain (fn [& _] mock-data)}
+ resp (ret/by-domain "" (inc page-size) mock-store {})]
+ (is (= 400 (:status resp)))))))
diff --git a/test/weepinbell/storage_test.clj b/test/weepinbell/storage_test.clj index 34fb104..a25634a 100644 --- a/test/weepinbell/storage_test.clj +++ b/test/weepinbell/storage_test.clj @@ -130,13 +130,13 @@ :source "https://source.net"
:target "https://target.net"
:modified (System/currentTimeMillis)}
- :microformats mf
+ :jf2 mf
:extra {:proxied-photo photo}}]
((:append storage) data)
(wait-til! ((:get storage) id))
(let [retrieved ((:get storage) id)]
(is (= photo (get-in retrieved [:extra :proxied-photo])))
- (is (= mf (:microformats retrieved))))
+ (is (= mf (:jf2 retrieved))))
((:delete storage) id)
(wait-til! (not ((:get storage) id)))))
diff --git a/test/weepinbell/wrapper_test.clj b/test/weepinbell/wrapper_test.clj index 74bfbcf..e405d4c 100644 --- a/test/weepinbell/wrapper_test.clj +++ b/test/weepinbell/wrapper_test.clj @@ -1,25 +1,105 @@ (ns weepinbell.wrapper-test
(:require [clojure.test :refer [deftest testing is]]
- [clojure.string :refer [includes?]]
+ [clojure.string :refer [includes? replace]]
+ [clojure.spec.alpha :as s]
[weepinbell.utils.digest :refer [digest]]
- [weepinbell.processor.wrapper :refer [wrap]]))
+ [weepinbell.processor.wrapper :refer [wrap]]
+ [weepinbell.metadata.jf2-spec]
+ [weepinbell.http.client]
+ [weepinbell.utils.url :refer [url=]]
+ [org.httpkit.server :refer [run-server]]
+ [clojure.java.io :as io]))
+
+(def photo-server-port 23232)
+
+(def fake-photo (str "http://localhost:" photo-server-port "/favicon.avif"))
(def normal-source-html
- "<div class=h-entry>
+ (str "<html><head><title>Name</title></head>
+ <div class=h-entry>
<div class=e-content>test</div>
<div class=\"p-author h-card\">
- <img alt=test src=\"https://source.net/favicon.png\" />
+ <img alt=test src=\"" fake-photo "\" />
</div>
- </div>")
+ </div>
+ </html>"))
(deftest happy-path-wrap
(testing "Wrapping a normal webmention"
- (let [wrapped (:new-data (wrap {:ok? true
+ (with-redefs [weepinbell.http.client/default-options {:timeout 100}]
+ (let [;; serve local fake photo
+ photo-digest (digest fake-photo)
+ local-photo (str "data/avatars/" photo-digest ".avif")
+ photo-handler (fn [_] {:status 200
+ :headers {"Content-Type" "image/png"}
+ :body "ReaLly4IMagE"})
+ photo-server (run-server photo-handler {:port photo-server-port})
+ ;; test wrapping
+ test-wrap (fn [] (wrap {:ok? true
:wm {:s "https://source.net"
:t "https://target.org"}
:source-html normal-source-html}
+ {:base-url "https://resource.com"}))
+ wrapped (:new-data (test-wrap))]
+ (is (= "https://source.net" (get-in wrapped [:webmention :source])))
+ (is (= "https://target.org" (get-in wrapped [:webmention :target])))
+ (is (= "b36497f812ccfde649996e21f8df83a6" (get-in wrapped [:webmention :id])))
+ (is (s/valid? :weepinbell.metadata.jf2-spec/jf2 (:jf2 wrapped)))
+ (is (.exists (io/file local-photo)))
+ (is (includes? (get-in wrapped [:extra :proxied-photo]) photo-digest))
+ ;; wrap again to test if using local avatars
+ (is (includes? (get-in (test-wrap) [:new-data :extra :proxied-photo]) photo-digest))
+ ;; clean up
+ (io/delete-file local-photo)
+ (photo-server))))
+ (testing "Wrapping a normal webmention with relative photo-url"
+ (with-redefs [weepinbell.http.client/default-options {:timeout 100}]
+ (let [;; serve local fake photo
+ photo-digest (digest fake-photo)
+ local-photo (str "data/avatars/" photo-digest ".avif")
+ photo-handler (fn [_] {:status 200
+ :headers {"Content-Type" "image/png"}
+ :body "ReaLly4IMagE"})
+ photo-server (run-server photo-handler {:port photo-server-port})
+ ;; test wrapping
+ source-html (replace normal-source-html fake-photo "/favicon.avif")
+ wrapped (:new-data (wrap {:ok? true
+ :wm {:s (str "http://localhost:" photo-server-port)
+ :t "https://target.org"}
+ :source-html source-html}
{:base-url "https://resource.com"}))]
- (is (= "https://source.net" (get-in wrapped [:webmention :source])))
- (is (= "https://target.org" (get-in wrapped [:webmention :target])))
- (is (= "b36497f812ccfde649996e21f8df83a6" (get-in wrapped [:webmention :id])))
- (is (includes? (get-in wrapped [:extra :proxied-photo]) (digest "https://source.net/favicon.png"))))))
+ (is (includes? (get-in wrapped [:extra :proxied-photo]) photo-digest))
+ ;; clean up
+ (io/delete-file local-photo)
+ (photo-server))))
+ (testing "Wrapping a webmention with no photo to proxy, checking JF2"
+ (let [source-html "<html><head><title>Name</title></head>
+ <div class=h-entry>
+ <div class=e-content>test</div>
+ <div class=\"p-author h-card\">
+ <a href=\"https://author.com/\" class=\"p-name u-url\">Author</a>
+ </div>
+ </div>
+ </html>"
+ wrapped (:new-data (wrap {:ok? true
+ :wm {:s "https://source.net"
+ :t "https://target.org"}
+ :source-html source-html}
+ {:base-url "https://resource.com"}))]
+ (is (= "Name" (get-in wrapped [:jf2 :name])))
+ (is (= "Author" (get-in wrapped [:jf2 :author :name])))
+ (is (url= "https://author.com/" (get-in wrapped [:jf2 :author :url])))
+ (is (= "test" (get-in wrapped [:jf2 :content :text])))))
+ (testing "Wrapper uses provided id"
+ (with-redefs [weepinbell.http.client/default-options {:timeout 100}]
+ (let [id "totally-accurate-SHA256-hash"
+ wrapped (:new-data (wrap {:ok? true
+ :id id
+ :wm {:s "https://source.net"
+ :t "https://target.org"}
+ :source-html normal-source-html}
+ {:base-url "https://resource.com"}))]
+ (is (= id (get-in wrapped [:webmention :id])))))))
+
+
+
|
