t_as2.ml 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236
  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_lwt.initialize (module Mirage_crypto_rng.Fortuna);
  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 (`Hex h) =
  37. "" |> Cstruct.of_string |> Mirage_crypto.Hash.SHA256.digest
  38. |> Hex.of_cstruct
  39. in
  40. (* https://de.wikipedia.org/wiki/SHA-2 *)
  41. h
  42. |> check string __LOC__
  43. "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855";
  44. "" |> Cstruct.of_string |> Mirage_crypto.Hash.SHA256.digest
  45. |> Cstruct.to_string |> Base64.encode_exn
  46. (* printf "%s" "" | openssl dgst -sha256 -binary | base64 *)
  47. |> check string __LOC__
  48. "47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU="
  49. )
  50. let tc_digst = "tc_digst", `Quick, (fun () ->
  51. Logr.debug (fun m -> m "as2_test.test_digst");
  52. "" |> Ap.PubKeyPem.digest_base64
  53. |> check string __LOC__
  54. "SHA-256=47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=";
  55. assert true
  56. )
  57. let tc_person = "tc_person", `Quick, (fun () ->
  58. Logr.debug (fun m -> m "as2_test.test_person");
  59. let pubdate = Ptime_clock.now ()
  60. and pem = "foo"
  61. and pro = ({
  62. title = "Sepp"; (* similar atom:subtitle *)
  63. bio = "sum"; (* similar atom:description *)
  64. language = Rfc4287.Rfc4646 "de";
  65. timezone = Timedesc.Time_zone.utc;
  66. posts_per_page = 50;
  67. } : Cfg.Profile.t)
  68. and uid = "sepp"
  69. and base = Uri.of_string "https://example.com/subb/" in
  70. let Rfc4287.Rfc4646 lang = pro.language in
  71. let lang : string option = Some lang in
  72. let p = Ap.Person.prsn pubdate (pem, (pro, (Auth.Uid uid, base))) in
  73. p |> As2_vocab.Encode.person ~lang ~base
  74. |> Ezjsonm.value_to_string ~minify:false |>
  75. check string
  76. __LOC__ {|{
  77. "@context": [
  78. "https://www.w3.org/ns/activitystreams",
  79. "https://w3id.org/security/v1",
  80. {
  81. "schema": "http://schema.org#",
  82. "PropertyValue": "schema:PropertyValue",
  83. "value": "schema:value",
  84. "@language": "de"
  85. }
  86. ],
  87. "type": "Person",
  88. "id": "https://example.com/subb/activitypub/actor.jsa",
  89. "inbox": "https://example.com/subb/seppo.cgi/activitypub/inbox.jsa",
  90. "outbox": "https://example.com/subb/activitypub/outbox/index.jsa",
  91. "followers": "https://example.com/subb/activitypub/subscribers/index.jsa",
  92. "following": "https://example.com/subb/activitypub/subscribed_to/index.jsa",
  93. "name": "Sepp",
  94. "url": "https://example.com/subb/",
  95. "preferredUsername": "sepp",
  96. "summary": "sum",
  97. "summaryMap": {
  98. "de": "sum"
  99. },
  100. "publicKey": {
  101. "@context": [
  102. {
  103. "@language": null
  104. }
  105. ],
  106. "id": "https://example.com/subb/activitypub/actor.jsa#main-key",
  107. "owner": "https://example.com/subb/activitypub/actor.jsa",
  108. "publicKeyPem": "foo",
  109. "signatureAlgorithm": "https://www.w3.org/2001/04/xmldsig-more#rsa-sha256"
  110. },
  111. "manuallyApprovesFollowers": false,
  112. "discoverable": true,
  113. "generator": {
  114. "type": "Link",
  115. "href": "https://seppo.social",
  116. "name": "Seppo - Personal Social Web"
  117. },
  118. "icon": {
  119. "type": "Image",
  120. "url": "https://example.com/subb/me-avatar.jpg"
  121. },
  122. "image": {
  123. "type": "Image",
  124. "url": "https://example.com/subb/me-banner.jpg"
  125. }
  126. }|};
  127. let _tos0 = Ezxmlm.to_string in
  128. let tos ?(decl = false) ?(indent = None) doc =
  129. let buf = Buffer.create 512 in
  130. let o = Xmlm.make_output ~decl (`Buffer buf) ~nl:true ~indent in
  131. let id x = x in
  132. Xmlm.output_doc_tree id o (None, doc);
  133. Buffer.contents buf
  134. in
  135. p
  136. |> Ap.Person.Rdf.encode
  137. ~token:(Some "foo")
  138. ~is_in_subscribers:(Some As2.No_p_yes.No)
  139. ~am_subscribed_to:(Some As2.No_p_yes.Pending)
  140. ~blocked:(Some As2.No_p_yes.Yes)
  141. ~lang ~base
  142. |> tos
  143. |> check string __LOC__ {|<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:seppo="http://seppo.social/2023/ns#" xml:base="https://example.com/subb/">
  144. <rdf:Description rdf:about="">
  145. <seppo:token>foo</seppo:token>
  146. <seppo:is_subscriber>no</seppo:is_subscriber>
  147. <seppo:am_subscribed_to>pending</seppo:am_subscribed_to>
  148. <seppo:is_blocked>yes</seppo:is_blocked>
  149. </rdf:Description>
  150. <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">
  151. <as:id rdf:resource="https://example.com/subb/activitypub/actor.jsa"/>
  152. <as:preferredUsername>sepp</as:preferredUsername>
  153. <as:manuallyApprovesFollowers rdf:datatype="http://www.w3.org/2001/XMLSchema#boolean">false</as:manuallyApprovesFollowers>
  154. <toot:discoverable rdf:datatype="http://www.w3.org/2001/XMLSchema#boolean">true</toot:discoverable>
  155. <as:generator/>
  156. <as:name>Sepp</as:name>
  157. <as:url rdf:resource="https://example.com/subb/"/>
  158. <as:summary xml:lang="de">sum</as:summary>
  159. <as:summary>sum</as:summary>
  160. <as:icon>
  161. <as:Image>
  162. <as:url rdf:resource="https://example.com/subb/me-avatar.jpg"/></as:Image></as:icon>
  163. <as:image>
  164. <as:Image>
  165. <as:url rdf:resource="https://example.com/subb/me-banner.jpg"/></as:Image></as:image>
  166. <as:following rdf:resource="https://example.com/subb/activitypub/subscribed_to/index.jsa"/>
  167. <as:followers rdf:resource="https://example.com/subb/activitypub/subscribers/index.jsa"/>
  168. <ldp:inbox rdf:resource="https://example.com/subb/seppo.cgi/activitypub/inbox.jsa"/>
  169. <as:outbox rdf:resource="https://example.com/subb/activitypub/outbox/index.jsa"/>
  170. </as:Person></rdf:RDF>
  171. |};
  172. ()
  173. )
  174. let tc_actor = "tc_actor", `Quick, (fun () ->
  175. Logr.debug (fun m -> m "as2_test.test_actor");
  176. Logr.info (fun m -> m "test_actor");
  177. ("data/ap/actor/friendica.0.json"
  178. |> Ap.Person.from_file |> Result.get_ok).inbox
  179. |> Uri.to_string
  180. |> check string __LOC__ "https://pirati.ca/inbox/heluecht";
  181. ("data/ap/actor/mastodon.2.json"
  182. |> Ap.Person.from_file |> Result.get_ok).inbox
  183. |> Uri.to_string
  184. |> check string __LOC__ "https://digitalcourage.social/users/mro/inbox";
  185. ("data/ap/actor/gnusocial.2.json"
  186. |> Ap.Person.from_file |> Result.get_ok).inbox
  187. |> Uri.to_string
  188. |> check string __LOC__ "https://social.hackersatporto.com/user/1/inbox.json";
  189. ("data/ap/actor/pleroma.0.json"
  190. |> Ap.Person.from_file |> Result.get_ok).inbox
  191. |> Uri.to_string
  192. |> check string __LOC__ "https://pleroma.tilde.zone/users/mro/inbox";
  193. assert true
  194. )
  195. let tc_examine_response = "tc_examine_response", `Quick, (fun () ->
  196. {|{"error":"Unable to fetch key JSON at https://example.com/activitypub/#main-key"}|}
  197. |> As2.examine_response
  198. |> Result.get_error
  199. |> check string __LOC__ "Unable to fetch key JSON at https://example.com/activitypub/#main-key";
  200. assert true
  201. )
  202. module Note = struct
  203. let tc_decode = "tc_decode", `Quick, (fun () ->
  204. Logr.info (fun m -> m "%s.%s" "As2_test.Note" "decode");
  205. let j = "data/ap/note/mastodon.json" |> File.in_channel Ezjsonm.from_channel in
  206. let n = j |> As2_vocab.Decode.note |> Result.get_ok in
  207. n.id |> Uri.to_string |> check string __LOC__ "https://digitalcourage.social/users/mro/statuses/111403080326863922";
  208. match n.in_reply_to with
  209. | [u] -> u |> Uri.to_string |> check string __LOC__ "https://chaos.social/users/qyliss/statuses/111403054651938519"
  210. | _ -> failwith "ouch"
  211. )
  212. end
  213. let () =
  214. run
  215. "seppo_suite" [
  216. __FILE__ , [
  217. set_up;
  218. tc_digest_sha256;
  219. tc_digst;
  220. tc_person;
  221. tc_actor;
  222. tc_examine_response;
  223. Note.tc_decode;
  224. ]
  225. ];
  226. assert true