t_rfc4287.ml 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364
  1. (*
  2. * _ _ ____ _
  3. * _| || |_/ ___| ___ _ __ _ __ ___ | |
  4. * |_ .. _\___ \ / _ \ '_ \| '_ \ / _ \| |
  5. * |_ _|___) | __/ |_) | |_) | (_) |_|
  6. * |_||_| |____/ \___| .__/| .__/ \___/(_)
  7. * |_| |_|
  8. *
  9. * Personal Social Web.
  10. *
  11. * Copyright (C) The #Seppo contributors. All rights reserved.
  12. *
  13. * This program is free software: you can redistribute it and/or modify
  14. * it under the terms of the GNU General Public License as published by
  15. * the Free Software Foundation, either version 3 of the License, or
  16. * (at your option) any later version.
  17. *
  18. * This program is distributed in the hope that it will be useful,
  19. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  20. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  21. * GNU General Public License for more details.
  22. *
  23. * You should have received a copy of the GNU General Public License
  24. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  25. *)
  26. open Alcotest
  27. open Seppo_lib
  28. open Rfc4287
  29. let set_up () =
  30. Mirage_crypto_rng_lwt.initialize (module Mirage_crypto_rng.Fortuna);
  31. Unix.chdir "../../../test/"
  32. let mk_sample () =
  33. let tag path = Category.((Label (Single path)), (Term (Single path)), tagu) in
  34. let e = {Rfc4287.Entry.empty with
  35. id = "o/p-12/#23" |> Uri.of_string;
  36. in_reply_to= [Uri.empty |> Inreplyto.make];
  37. lang = Rfc4646 "en";
  38. author = {Rfc4287.Person.empty with
  39. name = "fediverse";
  40. uri = Some (Uri.of_string "https://fediverse@mro.name");
  41. (* Uri.make ~userinfo:"fediverse" ~host:"mro.name" () *)};
  42. title = "#Announce Seppo.Social v0.1 and Request for Comments.";
  43. published = Rfc3339.T "2023-02-11T11:07:23+01:00";
  44. updated = Rfc3339.T "2023-02-11T11:07:23+01:00";
  45. links = [ "https://seppo.social/en/downloads/seppo-Linux-x86_64-0.1/" |> Uri.of_string |> Link.make ];
  46. categories = [
  47. tag "ActivityPub";
  48. tag "Announce";
  49. tag "Fediverse";
  50. tag "Media";
  51. tag "permacomputing";
  52. tag "Seppo";
  53. tag "Social";
  54. tag "webfinger";
  55. ];
  56. content = {|I am happy to announce the premiere release of #Seppo!, Personal #Social #Media under funding of NLnet.nl.
  57. Find it at https://Seppo.Social/downloads/
  58. It has no notable user facing #ActivityPub features so far, but
  59. - easy setup of instance & account,
  60. - #webfinger discoverability (from e.g. mastodon search),
  61. - a welcoming, long-term reliable website.
  62. I made this embarrassingly limited release to build awareness for low-barrier-entry internet services in general and especially in the field of personal communication as well as letting the #fediverse and #permacomputing communities know.
  63. Your comments are very much appreciated.|};
  64. } in
  65. e
  66. let tail x =
  67. Assrt.equals_string __LOC__ "ok" (if x |> Result.is_ok then "ok" else "no")
  68. let tc_compute_links () =
  69. let base = "https://example.com/sub/" |> Uri.of_string in
  70. let self,first,last,prev,next = ("o/p",2) |> Rfc4287.Feed.compute_links ~max:7 ~base in
  71. self |> Uri.to_string |> Assrt.equals_string __LOC__ "https://example.com/sub/o/p-2/";
  72. first |> Uri.to_string |> Assrt.equals_string __LOC__ "https://example.com/sub/o/p/";
  73. last |> Uri.to_string |> Assrt.equals_string __LOC__ "https://example.com/sub/o/p-0/";
  74. prev |> Option.get |> Uri.to_string |> Assrt.equals_string __LOC__ "https://example.com/sub/o/p-3/";
  75. next |> Option.get |> Uri.to_string |> Assrt.equals_string __LOC__ "https://example.com/sub/o/p-1/";
  76. assert true
  77. let tc_encode () =
  78. Logr.info (fun m -> m "rfc4287_test.test_encode");
  79. let _ = match {|(6:author(4:name9:fediverse3:uri26:https://fediverse@mro.name))|} |> Csexp.parse_string with
  80. | Ok (List [ Atom "author"; List [
  81. Atom "name"; Atom "fediverse";
  82. Atom "uri"; Atom _uri;
  83. ] ]) -> ()
  84. | _ -> failwith __LOC__
  85. in
  86. let e = mk_sample () in
  87. e
  88. |> Entry.encode
  89. |> Csexp.to_string
  90. |> Assrt.equals_string __LOC__ {|(2:id10:o/p-12/#2311:in-reply-to((3:ref0:))4:lang2:en5:title53:#Announce Seppo.Social v0.1 and Request for Comments.6:author(4:name9:fediverse3:uri26:https://fediverse@mro.name)9:published25:2023-02-11T11:07:23+01:007:updated25:2023-02-11T11:07:23+01:005:links((4:href57:https://seppo.social/en/downloads/seppo-Linux-x86_64-0.1/))10:categories((5:label11:ActivityPub4:term11:ActivityPub6:scheme4:o/t/)(5:label8:Announce4:term8:Announce6:scheme4:o/t/)(5:label9:Fediverse4:term9:Fediverse6:scheme4:o/t/)(5:label5:Media4:term5:Media6:scheme4:o/t/)(5:label14:permacomputing4:term14:permacomputing6:scheme4:o/t/)(5:label5:Seppo4:term5:Seppo6:scheme4:o/t/)(5:label6:Social4:term6:Social6:scheme4:o/t/)(5:label9:webfinger4:term9:webfinger6:scheme4:o/t/))7:content635:I am happy to announce the premiere release of #Seppo!, Personal #Social #Media under funding of NLnet.nl.
  91. Find it at https://Seppo.Social/downloads/
  92. It has no notable user facing #ActivityPub features so far, but
  93. - easy setup of instance & account,
  94. - #webfinger discoverability (from e.g. mastodon search),
  95. - a welcoming, long-term reliable website.
  96. I made this embarrassingly limited release to build awareness for low-barrier-entry internet services in general and especially in the field of personal communication as well as letting the #fediverse and #permacomputing communities know.
  97. Your comments are very much appreciated.)|};
  98. e |> Entry.encode |> Csexp.to_string |> String.length |> Assrt.equals_int __LOC__ 1396;
  99. match e
  100. |> Entry.encode
  101. |> Entry.decode with
  102. | Error e -> e |> Assrt.equals_string __LOC__ {||}
  103. | Ok e ->
  104. let Rfc4646 lang = e.lang
  105. and titl = e.title
  106. and Rfc3339.T publ = e.published
  107. and Rfc3339.T upda = e.updated
  108. and cont = e.content
  109. and li_a, li_b = match e.links with
  110. | [ {href; rel=None; title=None; rfc7565=None} ] -> (href,"")
  111. | _ -> (Uri.make (), "ouch 301")
  112. and ca_a, ca_b, ca_c = match e.categories with
  113. | (Label (Single a), Term (Single b), c) :: _ -> (a,b,c)
  114. | _ -> ("ouch 302", "", Uri.make ())
  115. in
  116. lang |> Assrt.equals_string __LOC__ "en";
  117. titl |> Assrt.equals_string __LOC__ "#Announce Seppo.Social v0.1 and Request for Comments.";
  118. publ |> Assrt.equals_string __LOC__ "2023-02-11T11:07:23+01:00";
  119. upda |> Assrt.equals_string __LOC__ "2023-02-11T11:07:23+01:00";
  120. e.links |> List.length |> Assrt.equals_int __LOC__ 1;
  121. li_a |> Uri.to_string |> Assrt.equals_string __LOC__ "https://seppo.social/en/downloads/seppo-Linux-x86_64-0.1/";
  122. li_b |> Assrt.equals_string __LOC__ "";
  123. e.categories |> List.length |> Assrt.equals_int __LOC__ 8;
  124. ca_a |> Assrt.equals_string __LOC__ "webfinger";
  125. ca_b |> Assrt.equals_string __LOC__ "webfinger";
  126. ca_c |> Uri.to_string |> Assrt.equals_string __LOC__ "o/t/";
  127. cont |> Assrt.equals_string __LOC__ {|I am happy to announce the premiere release of #Seppo!, Personal #Social #Media under funding of NLnet.nl.
  128. Find it at https://Seppo.Social/downloads/
  129. It has no notable user facing #ActivityPub features so far, but
  130. - easy setup of instance & account,
  131. - #webfinger discoverability (from e.g. mastodon search),
  132. - a welcoming, long-term reliable website.
  133. I made this embarrassingly limited release to build awareness for low-barrier-entry internet services in general and especially in the field of personal communication as well as letting the #fediverse and #permacomputing communities know.
  134. Your comments are very much appreciated.|};
  135. e |> Storage.feed_urls
  136. |> List.map Uri.to_string |> String.concat " ; "
  137. |> Assrt.equals_string __LOC__ "o/p/ ; activitypub/outbox/ ; o/d/2023-02-11/ ; o/t/webfinger/ ; o/t/Social/ ; o/t/Seppo/ ; o/t/permacomputing/ ; o/t/Media/ ; o/t/Fediverse/ ; o/t/Announce/ ; o/t/ActivityPub/";
  138. assert true
  139. let tc_decode_2023 () =
  140. let e : Rfc4287.Entry.t =
  141. {|(2:id10:o/p-12/#2311:in-reply-to((3:ref0:))4:lang2:en5:title53:#Announce Seppo.Social v0.1 and Request for Comments.6:author20://fediverse@mro.name9:published25:2023-02-11T11:07:23+01:007:updated25:2023-02-11T11:07:23+01:005:links((4:href57:https://seppo.social/en/downloads/seppo-Linux-x86_64-0.1/))10:categories((5:label11:ActivityPub4:term11:ActivityPub6:scheme4:o/t/)(5:label8:Announce4:term8:Announce6:scheme4:o/t/)(5:label9:Fediverse4:term9:Fediverse6:scheme4:o/t/)(5:label5:Media4:term5:Media6:scheme4:o/t/)(5:label14:permacomputing4:term14:permacomputing6:scheme4:o/t/)(5:label5:Seppo4:term5:Seppo6:scheme4:o/t/)(5:label6:Social4:term6:Social6:scheme4:o/t/)(5:label9:webfinger4:term9:webfinger6:scheme4:o/t/))7:content635:I am happy to announce the premiere release of #Seppo!, Personal #Social #Media under funding of NLnet.nl.
  142. Find it at https://Seppo.Social/downloads/
  143. It has no notable user facing #ActivityPub features so far, but
  144. - easy setup of instance & account,
  145. - #webfinger discoverability (from e.g. mastodon search),
  146. - a welcoming, long-term reliable website.
  147. I made this embarrassingly limited release to build awareness for low-barrier-entry internet services in general and especially in the field of personal communication as well as letting the #fediverse and #permacomputing communities know.
  148. Your comments are very much appreciated.)|}
  149. |> Csexp.parse_string
  150. |> Result.get_ok
  151. |> Rfc4287.Entry.decode
  152. |> Result.get_ok in
  153. e.title |> Assrt.equals_string __LOC__ {|#Announce Seppo.Social v0.1 and Request for Comments.|};
  154. e.author.name |> Assrt.equals_string __LOC__ {|fediverse|};
  155. e.author.uri |> Option.get |> Uri.to_string |> Assrt.equals_string __LOC__ {|//fediverse@mro.name|};
  156. ()
  157. let tc_decode_2024 () =
  158. let e : Rfc4287.Entry.t =
  159. {|(2:id10:o/p-12/#2311:in-reply-to((3:ref0:))4:lang2:en5:title53:#Announce Seppo.Social v0.1 and Request for Comments.6:author(4:name9:fediverse3:uri26:https://fediverse@mro.name)9:published25:2023-02-11T11:07:23+01:007:updated25:2023-02-11T11:07:23+01:005:links((4:href57:https://seppo.social/en/downloads/seppo-Linux-x86_64-0.1/))10:categories((5:label11:ActivityPub4:term11:ActivityPub6:scheme4:o/t/)(5:label8:Announce4:term8:Announce6:scheme4:o/t/)(5:label9:Fediverse4:term9:Fediverse6:scheme4:o/t/)(5:label5:Media4:term5:Media6:scheme4:o/t/)(5:label14:permacomputing4:term14:permacomputing6:scheme4:o/t/)(5:label5:Seppo4:term5:Seppo6:scheme4:o/t/)(5:label6:Social4:term6:Social6:scheme4:o/t/)(5:label9:webfinger4:term9:webfinger6:scheme4:o/t/))7:content635:I am happy to announce the premiere release of #Seppo!, Personal #Social #Media under funding of NLnet.nl.
  160. Find it at https://Seppo.Social/downloads/
  161. It has no notable user facing #ActivityPub features so far, but
  162. - easy setup of instance & account,
  163. - #webfinger discoverability (from e.g. mastodon search),
  164. - a welcoming, long-term reliable website.
  165. I made this embarrassingly limited release to build awareness for low-barrier-entry internet services in general and especially in the field of personal communication as well as letting the #fediverse and #permacomputing communities know.
  166. Your comments are very much appreciated.)|}
  167. |> Csexp.parse_string
  168. |> Result.get_ok
  169. |> Rfc4287.Entry.decode
  170. |> Result.get_ok in
  171. e.title |> Assrt.equals_string __LOC__ {|#Announce Seppo.Social v0.1 and Request for Comments.|};
  172. e.author.name |> Assrt.equals_string __LOC__ {|fediverse|};
  173. e.author.uri |> Option.get |> Uri.to_string |> Assrt.equals_string __LOC__ {|https://fediverse@mro.name|};
  174. ()
  175. let tc_from_plain_text () =
  176. Logr.info (fun m -> m "rfc4287_test.test_from_plain_text");
  177. let published = Rfc3339.T "2023-02-14T01:23:45+01:00" in
  178. let author = {Rfc4287.Person.empty with
  179. name = "fediverse";
  180. uri = Some (Uri.of_string "https://fediverse@mro.name");
  181. (* Uri.make ~userinfo:"fediverse" ~host:"mro.name" () *)} in
  182. let lang = Rfc4646 "nl" in
  183. let uri = Uri.of_string "https://nlnet.nl/projects/Seppo/" in
  184. (let* n = Entry.from_text_plain ~published ~author ~lang ~uri "Hello, world!" "a new Note." in
  185. let ti = n.title in
  186. let co = n.content in
  187. ti |> Assrt.equals_string __LOC__ "Hello, world!";
  188. n.links |> List.length |> Assrt.equals_int __LOC__ 1;
  189. n.categories |> List.length |> Assrt.equals_int __LOC__ 0;
  190. co |> Assrt.equals_string __LOC__ "a new Note.";
  191. n |> Entry.encode
  192. |> Csexp.to_string
  193. |> Assrt.equals_string __LOC__ {|(2:id7:aseggdb11:in-reply-to()4:lang2:nl5:title13:Hello, world!6:author(4:name9:fediverse3:uri26:https://fediverse@mro.name)9:published25:2023-02-14T01:23:45+01:007:updated25:2023-02-14T01:23:45+01:005:links((4:href32:https://nlnet.nl/projects/Seppo/))10:categories()7:content11:a new Note.)|};
  194. Ok n)
  195. |> tail
  196. (**
  197. * inspired by https://code.mro.name/mro/ShaarliGo/src/cb798ebfae17431732e37a94ee80b29bd3b78911/atom.go#L302
  198. * https://opam.ocaml.org/packages/base32/
  199. * https://opam.ocaml.org/packages/base64/
  200. *)
  201. let tc_id_make () =
  202. Logr.info (fun m -> m "rfc4287_test.test_id_make");
  203. let assrt l id iso =
  204. match iso |> Ptime.of_rfc3339 with
  205. | Ok (t,_,_) ->
  206. let f = Entry.id_make t |> Uri.to_string in
  207. Assrt.equals_string l id f
  208. | _ -> "" |> Assrt.equals_string "" "-"
  209. in
  210. "1970-01-01T00:00:00+00:00" |> assrt "rfc4287_test.test_id_make 0" "2222222";
  211. "2023-01-01T00:00:00+00:00" |> assrt "rfc4287_test.test_id_make 1" "as35e22";
  212. "2081-01-01T00:00:00+00:00" |> assrt "rfc4287_test.test_id_make 2" "s9y3s22";
  213. "2120-01-01T00:00:00+00:00" |> assrt "rfc4287_test.test_id_make 4" "2sd6e22";
  214. assert true
  215. let tc_entry_atom () =
  216. Logr.info (fun m -> m "rfc4287_test.test_entry_atom");
  217. let base = Uri.of_string "https://example.com/sub/" in
  218. let published = Rfc3339.T "2023-02-14T01:23:45+01:00" in
  219. let author = {Rfc4287.Person.empty with
  220. name = "fediverse";
  221. uri = Some (Uri.of_string "https://fediverse@mro.name");
  222. (* Uri.make ~userinfo:"fediverse" ~host:"mro.name" () *)} in
  223. let lang = Rfc4646 "nl" in
  224. let uri = Uri.of_string "https://nlnet.nl/projects/Seppo/" in
  225. let e0 = Entry.from_text_plain ~published ~author ~lang ~uri "Hello, world!" "a new Note."
  226. |> Result.get_ok in
  227. let buf = Buffer.create 1024 in
  228. let attr = [
  229. ((Xmlm.ns_xmlns,"xmlns"), Xml.ns_a);
  230. ((Xmlm.ns_xmlns,"wf"), Xml.ns_rfc7033);
  231. ((Xmlm.ns_xmlns,"as"), Xml.ns_as);
  232. ] in
  233. let e = Entry.to_atom ~attr ~base e0 in
  234. Xml.to_buf e buf;
  235. buf |> Buffer.to_bytes |> Bytes.to_string
  236. |> Assrt.equals_string __LOC__ {|<?xml version="1.0"?>
  237. <entry xml:lang="nl" xmlns="http://www.w3.org/2005/Atom" xmlns:wf="urn:ietf:rfc:7033" xmlns:as="https://www.w3.org/ns/activitystreams">
  238. <id>https://example.com/sub/aseggdb</id>
  239. <title type="text">Hello, world!</title>
  240. <updated>2023-02-14T01:23:45+01:00</updated>
  241. <published>2023-02-14T01:23:45+01:00</published>
  242. <as:sensitive>false</as:sensitive>
  243. <author>
  244. <name>fediverse</name>
  245. <wf:uri>acct:fediverse@mro.name</wf:uri>
  246. <uri>https://fediverse@mro.name</uri></author>
  247. <link rel="self" href="https://example.com/sub/aseggdb"/>
  248. <link href="https://nlnet.nl/projects/Seppo/"/>
  249. <content type="text">a new Note.</content>
  250. </entry>|}
  251. let tc_feed_atom () =
  252. Logr.info (fun m -> m "rfc4287_test.test_feed_atom");
  253. let published = Rfc3339.T "2023-02-14T01:23:45+01:00" in
  254. let author = {Rfc4287.Person.empty with
  255. name = "fediverse";
  256. uri = Some (Uri.of_string "https://fediverse@mro.name");
  257. (* Uri.make ~userinfo:"fediverse" ~host:"mro.name" () *)} in
  258. let lang = Rfc4646 "nl" in
  259. let uri = Uri.of_string "https://nlnet.nl/projects/Seppo/" in
  260. let e0 = Entry.from_text_plain ~published ~author ~lang ~uri "Hello, world!" "a new Note."
  261. |> Result.get_ok in
  262. let fe = Feed.to_atom
  263. ~author:{author with name = "sepp"}
  264. ~base:(Uri.make ~scheme:"https" ~host:"example.com" ~path:"/sub/" ())
  265. ~lang:(Rfc4646 "nl")
  266. ~self:(Uri.make ~path:"o/p-11/" ())
  267. ~prev:(Some (Uri.make ~path:"o/p-10/" ()))
  268. ~next:None
  269. ~first:(Uri.make ~path:"o/p/" ())
  270. ~last:(Uri.make ~path:"o/p-0/" ())
  271. ~title:"My fancy #Seppo!"
  272. ~updated:(Rfc3339.T "2023-02-27T12:34:56+01:00")
  273. [e0] in
  274. let buf = Buffer.create 1024 in
  275. Xml.to_buf fe buf;
  276. buf |> Buffer.to_bytes |> Bytes.to_string
  277. |> Assrt.equals_string __LOC__ {|<?xml version="1.0"?>
  278. <feed xmlns="http://www.w3.org/2005/Atom" xmlns:thr="http://purl.org/syndication/thread/1.0" xmlns:wf="urn:ietf:rfc:7033" xmlns:as="https://www.w3.org/ns/activitystreams" xml:lang="nl" xml:base="https://example.com/sub/">
  279. <id>https://example.com/sub/o/p-11/</id>
  280. <title type="text">My fancy #Seppo!</title>
  281. <updated>2023-02-27T12:34:56+01:00</updated>
  282. <generator uri="Seppo.Social">Seppo - Personal Social Web</generator>
  283. <link rel="self" href="o/p-11/" title="12"/>
  284. <link rel="first" href="o/p/" title="last"/>
  285. <link rel="last" href="o/p-0/" title="1"/>
  286. <link rel="previous" href="o/p-10/" title="11"/>
  287. <entry xml:lang="nl">
  288. <id>https://example.com/sub/aseggdb</id>
  289. <title type="text">Hello, world!</title>
  290. <updated>2023-02-14T01:23:45+01:00</updated>
  291. <published>2023-02-14T01:23:45+01:00</published>
  292. <as:sensitive>false</as:sensitive>
  293. <author>
  294. <name>fediverse</name>
  295. <wf:uri>acct:fediverse@mro.name</wf:uri>
  296. <uri>https://fediverse@mro.name</uri></author>
  297. <link rel="self" href="https://example.com/sub/aseggdb"/>
  298. <link href="https://nlnet.nl/projects/Seppo/"/>
  299. <content type="text">a new Note.</content>
  300. </entry>
  301. </feed>|}
  302. let tc_xsl () =
  303. "o/p-1/index.xml"
  304. |> Rfc4287.xsl "posts.xsl"
  305. |> Option.value ~default:"?"
  306. |> Assrt.equals_string __LOC__ "../../themes/current/posts.xsl"
  307. let () =
  308. run
  309. "seppo_suite" [
  310. __FILE__ , [
  311. "set_up", `Quick, set_up;
  312. "tc_compute_links ", `Quick, tc_compute_links ;
  313. "tc_encode ", `Quick, tc_encode ;
  314. "tc_decode_2023 ", `Quick, tc_decode_2023 ;
  315. "tc_decode_2024 ", `Quick, tc_decode_2024 ;
  316. "tc_from_plain_text ", `Quick, tc_from_plain_text ;
  317. "tc_id_make ", `Quick, tc_id_make ;
  318. "tc_entry_atom ", `Quick, tc_entry_atom ;
  319. "tc_feed_atom ", `Quick, tc_feed_atom ;
  320. "tc_xsl ", `Quick, tc_xsl ;
  321. ]
  322. ]