t_as2.ml 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237
  1. (*
  2. * _ _ ____ _
  3. * _| || |_/ ___| ___ _ __ _ __ ___ | |
  4. * |_ .. _\___ \ / _ \ '_ \| '_ \ / _ \| |
  5. * |_ _|___) | __/ |_) | |_) | (_) |_|
  6. * |_||_| |____/ \___| .__/| .__/ \___/(_)
  7. * |_| |_|
  8. *
  9. * Personal Social Web.
  10. *
  11. * t_as2.ml
  12. *
  13. * Copyright (C) The #Seppo contributors. All rights reserved.
  14. *
  15. * This program is free software: you can redistribute it and/or modify
  16. * it under the terms of the GNU General Public License as published by
  17. * the Free Software Foundation, either version 3 of the License, or
  18. * (at your option) any later version.
  19. *
  20. * This program is distributed in the hope that it will be useful,
  21. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  22. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  23. * GNU General Public License for more details.
  24. *
  25. * You should have received a copy of the GNU General Public License
  26. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  27. *)
  28. open Seppo_lib
  29. open Alcotest
  30. let set_up = "setup", `Quick, (fun () ->
  31. Mirage_crypto_rng_unix.use_default ();
  32. Unix.chdir "../../../test/"
  33. )
  34. let tc_digest_sha256 = "tc_digest_sha256", `Quick, (fun () ->
  35. Logr.debug (fun m -> m "as2_test.test_digest_sha256");
  36. let h =
  37. "" |> Digestif.SHA256.digest_string
  38. |> Digestif.SHA256.to_hex
  39. in
  40. (* https://de.wikipedia.org/wiki/SHA-2 *)
  41. h
  42. |> check string __LOC__
  43. "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855";
  44. "" |> Digestif.SHA256.digest_string
  45. |> Digestif.SHA256.to_raw_string
  46. |> Base64.encode_exn
  47. (* printf "%s" "" | openssl dgst -sha256 -binary | base64 *)
  48. |> check string __LOC__
  49. "47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU="
  50. )
  51. let tc_digst = "tc_digst", `Quick, (fun () ->
  52. Logr.debug (fun m -> m "as2_test.test_digst");
  53. "" |> Ap.PubKeyPem.digest_base64
  54. |> check string __LOC__
  55. "SHA-256=47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=";
  56. assert true
  57. )
  58. let tc_person = "tc_person", `Quick, (fun () ->
  59. Logr.debug (fun m -> m "as2_test.test_person");
  60. let pubdate = Ptime_clock.now ()
  61. and pem = "foo"
  62. and pro = ({
  63. title = "Sepp"; (* similar atom:subtitle *)
  64. bio = "sum"; (* similar atom:description *)
  65. language = Rfc4287.Rfc4646 "de";
  66. timezone = Timedesc.Time_zone.utc;
  67. posts_per_page = 50;
  68. } : Cfg.Profile.t)
  69. and uid = "sepp"
  70. and base = Uri.of_string "https://example.com/subb/" in
  71. let Rfc4287.Rfc4646 lang = pro.language in
  72. let lang : string option = Some lang in
  73. let p = Ap.Person.prsn pubdate (pem, (pro, (Auth.Uid uid, base))) in
  74. p |> As2_vocab.Encode.person ~lang ~base
  75. |> Ezjsonm.value_to_string ~minify:false |>
  76. check string
  77. __LOC__ {|{
  78. "@context": [
  79. "https://www.w3.org/ns/activitystreams",
  80. "https://w3id.org/security/v1",
  81. {
  82. "schema": "http://schema.org#",
  83. "PropertyValue": "schema:PropertyValue",
  84. "value": "schema:value",
  85. "@language": "de"
  86. }
  87. ],
  88. "type": "Person",
  89. "id": "https://example.com/subb/activitypub/actor.jsa",
  90. "inbox": "https://example.com/subb/seppo.cgi/activitypub/inbox.jsa",
  91. "outbox": "https://example.com/subb/activitypub/outbox/index.jsa",
  92. "followers": "https://example.com/subb/activitypub/subscribers/index.jsa",
  93. "following": "https://example.com/subb/activitypub/subscribed_to/index.jsa",
  94. "name": "Sepp",
  95. "url": "https://example.com/subb/",
  96. "preferredUsername": "sepp",
  97. "summary": "sum",
  98. "summaryMap": {
  99. "de": "sum"
  100. },
  101. "publicKey": {
  102. "@context": [
  103. {
  104. "@language": null
  105. }
  106. ],
  107. "id": "https://example.com/subb/activitypub/actor.jsa#main-key",
  108. "owner": "https://example.com/subb/activitypub/actor.jsa",
  109. "publicKeyPem": "foo",
  110. "signatureAlgorithm": "https://www.w3.org/2001/04/xmldsig-more#rsa-sha256"
  111. },
  112. "manuallyApprovesFollowers": false,
  113. "discoverable": true,
  114. "generator": {
  115. "type": "Link",
  116. "href": "https://seppo.mro.name",
  117. "name": "Seppo - Personal Social Web"
  118. },
  119. "icon": {
  120. "type": "Image",
  121. "url": "https://example.com/subb/me-avatar.jpg"
  122. },
  123. "image": {
  124. "type": "Image",
  125. "url": "https://example.com/subb/me-banner.jpg"
  126. }
  127. }|};
  128. let _tos0 = Ezxmlm.to_string in
  129. let tos ?(decl = false) ?(indent = None) doc =
  130. let buf = Buffer.create 512 in
  131. let o = Xmlm.make_output ~decl (`Buffer buf) ~nl:true ~indent in
  132. let id x = x in
  133. Xmlm.output_doc_tree id o (None, doc);
  134. Buffer.contents buf
  135. in
  136. p
  137. |> Ap.Person.Rdf.encode
  138. ~token:(Some "foo")
  139. ~is_in_subscribers:(Some As2.No_p_yes.No)
  140. ~am_subscribed_to:(Some As2.No_p_yes.Pending)
  141. ~blocked:(Some As2.No_p_yes.Yes)
  142. ~lang ~base
  143. |> tos
  144. |> check string __LOC__ {|<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:seppo="http://seppo.mro.name/2023/ns#" xml:base="https://example.com/subb/">
  145. <rdf:Description rdf:about="">
  146. <seppo:token>foo</seppo:token>
  147. <seppo:is_subscriber>no</seppo:is_subscriber>
  148. <seppo:am_subscribed_to>pending</seppo:am_subscribed_to>
  149. <seppo:is_blocked>yes</seppo:is_blocked>
  150. </rdf:Description>
  151. <as:Person xmlns:as="https://www.w3.org/ns/activitystreams#" xmlns:ldp="http://www.w3.org/ns/ldp#" xmlns:schema="http://schema.org#" xmlns:toot="http://joinmastodon.org/ns#" rdf:about="" xml:lang="de">
  152. <as:id rdf:resource="https://example.com/subb/activitypub/actor.jsa"/>
  153. <as:preferredUsername>sepp</as:preferredUsername>
  154. <as:manuallyApprovesFollowers rdf:datatype="http://www.w3.org/2001/XMLSchema#boolean">false</as:manuallyApprovesFollowers>
  155. <toot:discoverable rdf:datatype="http://www.w3.org/2001/XMLSchema#boolean">true</toot:discoverable>
  156. <as:generator/>
  157. <as:name>Sepp</as:name>
  158. <as:url rdf:resource="https://example.com/subb/"/>
  159. <as:summary xml:lang="de">sum</as:summary>
  160. <as:summary>sum</as:summary>
  161. <as:icon>
  162. <as:Image>
  163. <as:url rdf:resource="https://example.com/subb/me-avatar.jpg"/></as:Image></as:icon>
  164. <as:image>
  165. <as:Image>
  166. <as:url rdf:resource="https://example.com/subb/me-banner.jpg"/></as:Image></as:image>
  167. <as:following rdf:resource="https://example.com/subb/activitypub/subscribed_to/index.jsa"/>
  168. <as:followers rdf:resource="https://example.com/subb/activitypub/subscribers/index.jsa"/>
  169. <ldp:inbox rdf:resource="https://example.com/subb/seppo.cgi/activitypub/inbox.jsa"/>
  170. <as:outbox rdf:resource="https://example.com/subb/activitypub/outbox/index.jsa"/>
  171. </as:Person></rdf:RDF>
  172. |};
  173. ()
  174. )
  175. let tc_actor = "tc_actor", `Quick, (fun () ->
  176. Logr.debug (fun m -> m "as2_test.test_actor");
  177. Logr.info (fun m -> m "test_actor");
  178. ("data/ap/actor/friendica.0.json"
  179. |> Ap.Person.from_file |> Result.get_ok).inbox
  180. |> Uri.to_string
  181. |> check string __LOC__ "https://pirati.ca/inbox/heluecht";
  182. ("data/ap/actor/mastodon.2.json"
  183. |> Ap.Person.from_file |> Result.get_ok).inbox
  184. |> Uri.to_string
  185. |> check string __LOC__ "https://digitalcourage.social/users/mro/inbox";
  186. ("data/ap/actor/gnusocial.2.json"
  187. |> Ap.Person.from_file |> Result.get_ok).inbox
  188. |> Uri.to_string
  189. |> check string __LOC__ "https://social.hackersatporto.com/user/1/inbox.json";
  190. ("data/ap/actor/pleroma.0.json"
  191. |> Ap.Person.from_file |> Result.get_ok).inbox
  192. |> Uri.to_string
  193. |> check string __LOC__ "https://pleroma.tilde.zone/users/mro/inbox";
  194. assert true
  195. )
  196. let tc_examine_response = "tc_examine_response", `Quick, (fun () ->
  197. {|{"error":"Unable to fetch key JSON at https://example.com/activitypub/#main-key"}|}
  198. |> As2.examine_response
  199. |> Result.get_error
  200. |> check string __LOC__ "Unable to fetch key JSON at https://example.com/activitypub/#main-key";
  201. assert true
  202. )
  203. module Note = struct
  204. let tc_decode = "tc_decode", `Quick, (fun () ->
  205. Logr.info (fun m -> m "%s.%s" "As2_test.Note" "decode");
  206. let j = "data/ap/note/mastodon.json" |> File.in_channel Ezjsonm.from_channel in
  207. let n = j |> As2_vocab.Decode.note |> Result.get_ok in
  208. n.id |> Uri.to_string |> check string __LOC__ "https://digitalcourage.social/users/mro/statuses/111403080326863922";
  209. match n.in_reply_to with
  210. | [u] -> u |> Uri.to_string |> check string __LOC__ "https://chaos.social/users/qyliss/statuses/111403054651938519"
  211. | _ -> failwith "ouch"
  212. )
  213. end
  214. let () =
  215. run
  216. "seppo_suite" [
  217. __FILE__ , [
  218. set_up;
  219. tc_digest_sha256;
  220. tc_digst;
  221. tc_person;
  222. tc_actor;
  223. tc_examine_response;
  224. Note.tc_decode;
  225. ]
  226. ];
  227. assert true