ap_test.ml 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310
  1. (*
  2. * _ _ ____ _
  3. * _| || |_/ ___| ___ _ __ _ __ ___ | |
  4. * |_ .. _\___ \ / _ \ '_ \| '_ \ / _ \| |
  5. * |_ _|___) | __/ |_) | |_) | (_) |_|
  6. * |_||_| |____/ \___| .__/| .__/ \___/(_)
  7. * |_| |_|
  8. *
  9. * Personal Social Web.
  10. *
  11. * as2_test.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. module A = Assrt
  30. let test_follower_state () =
  31. Logr.info (fun m -> m "%s.%s" "Ap_test" "test_follower_state");
  32. Ap.Followers.State.(Pending |> to_string) |>
  33. A.equals_string __LOC__ "pending"
  34. let test_inbox_follow () =
  35. Logr.info (fun m -> m "%s.%s" "Ap_test" "test_inbox_follow");
  36. let a = File.in_channel "data/follow.mastodon.json" Ezjsonm.from_channel
  37. |> As2_vocab.Activitypub.Decode.follow
  38. |> Result.get_ok in
  39. a.id |> A.equals_uri __LOC__ (Uri.of_string "https://alpaka.social/afe4ac8a-aaeb-41f5-a348-e5d133bdb931");
  40. a.actor |> A.equals_uri __LOC__ (Uri.of_string "https://alpaka.social/users/traunstein");
  41. a.object_ |> A.equals_uri __LOC__ (Uri.of_string "https://dev.seppo.social/2023-06-12/activitypub/profile.json");
  42. match a.state with
  43. | None -> ()
  44. | _ -> "-" |> A.equals_string __LOC__ ""
  45. let test_inbox_unfollow () =
  46. Logr.info (fun m -> m "%s.%s" "Ap_test" "test_inbox_unfollow");
  47. let o = File.in_channel "data/undo-follow.mastodon.json" Ezjsonm.from_channel
  48. |> As2_vocab.Activitypub.Decode.(undo follow)
  49. |> Result.get_ok in
  50. o.id |> Uri.to_string |> A.equals_string __LOC__ "https://alpaka.social/users/traunstein#follows/5219/undo";
  51. o.actor |> Uri.to_string |> A.equals_string __LOC__ "https://alpaka.social/users/traunstein";
  52. o.obj.id |> Uri.to_string |> A.equals_string __LOC__ "https://alpaka.social/a5fc3ab5-92f3-4641-a33e-96418e9bec78";
  53. o.obj.object_ |> Uri.to_string |> A.equals_string __LOC__ "https://dev.seppo.social/2023-06-12/activitypub/profile.json";
  54. assert (o.actor |> Uri.equal o.obj.actor);
  55. (*
  56. a.object_ |> A.equals_uri __LOC__ (Uri.of_string "https://dev.seppo.social/2023-06-12/activitypub/profile.json");
  57. match a.state with
  58. | None -> ()
  59. | _ -> "-" |> A.equals_string __LOC__ ""
  60. *)
  61. assert true
  62. let test_followers_page_json_raw () =
  63. Logr.info (fun m -> m "%s.%s" "Ap_test" "test_followers_page_json_raw");
  64. let fs = [
  65. "1/" |> Uri.of_string;
  66. "2/" |> Uri.of_string;
  67. "3/" |> Uri.of_string;
  68. "4/" |> Uri.of_string;
  69. "5/" |> Uri.of_string;
  70. ] in
  71. let base' = "https://example.com/u/" |> Uri.of_string in
  72. let base = "https://example.com/ap/followers/2.jsa" |> Uri.of_string in
  73. let p : Uri.t As2_vocab.Types.collection_page = {
  74. id = base;
  75. current = Some base;
  76. first = None;
  77. is_ordered = true;
  78. items = fs;
  79. last = Some (Http.reso ~base (Uri.of_string "0.jsa"));
  80. next = Some (Http.reso ~base (Uri.of_string "1.jsa"));
  81. part_of = Some (Http.reso ~base (Uri.of_string "index.jsa"));
  82. prev = Some (Http.reso ~base (Uri.of_string "3.jsa"));
  83. total_items= None;
  84. } in
  85. p |> As2_vocab.Encode.(collection_page ~base (uri ~base:base'))
  86. |> Ezjsonm.value_to_string ~minify:false
  87. |> A.equals_string __LOC__ {|{
  88. "@context": [
  89. "https://www.w3.org/ns/activitystreams",
  90. "https://w3id.org/security/v1",
  91. {
  92. "schema": "http://schema.org#",
  93. "PropertyValue": "schema:PropertyValue",
  94. "value": "schema:value",
  95. "@language": "und"
  96. }
  97. ],
  98. "type": "OrderedCollectionPage",
  99. "id": "https://example.com/ap/followers/2.jsa",
  100. "current": "https://example.com/ap/followers/2.jsa",
  101. "last": "https://example.com/ap/followers/0.jsa",
  102. "next": "https://example.com/ap/followers/1.jsa",
  103. "partOf": "https://example.com/ap/followers/index.jsa",
  104. "prev": "https://example.com/ap/followers/3.jsa",
  105. "orderedItems": [
  106. "https://example.com/u/1/",
  107. "https://example.com/u/2/",
  108. "https://example.com/u/3/",
  109. "https://example.com/u/4/",
  110. "https://example.com/u/5/"
  111. ]
  112. }|}
  113. let test_followers_page_json () =
  114. Logr.info (fun m -> m "%s.%s" "Ap_test" "test_followers_page_json");
  115. let foo = "https://example.com/foo/usr/" |> Uri.of_string in
  116. let base = "https://example.com/" |> Uri.of_string in
  117. let path = Ap.apub ^ "followers/" in
  118. let base = Http.reso ~base (Uri.make ~path ()) in
  119. [ "1/"; "2/"; "3/"; "4/"; "5/" ]
  120. |> List.rev
  121. |> List.fold_left (fun init path -> (Uri.make ~path () |> Http.reso ~base:foo) :: init) []
  122. |> Ap.Followers.Json.to_page_json ~base path ~finish:false 2
  123. |> Ezjsonm.value_to_string ~minify:false
  124. |> A.equals_string __LOC__ {|{
  125. "@context": [
  126. "https://www.w3.org/ns/activitystreams",
  127. "https://w3id.org/security/v1",
  128. {
  129. "schema": "http://schema.org#",
  130. "PropertyValue": "schema:PropertyValue",
  131. "value": "schema:value",
  132. "@language": "und"
  133. }
  134. ],
  135. "type": "OrderedCollectionPage",
  136. "id": "https://example.com/activitypub/followers/2.jsa",
  137. "current": "https://example.com/activitypub/followers/2.jsa",
  138. "last": "https://example.com/activitypub/followers/0.jsa",
  139. "next": "https://example.com/activitypub/followers/1.jsa",
  140. "partOf": "https://example.com/activitypub/followers/index.jsa",
  141. "prev": "https://example.com/activitypub/followers/3.jsa",
  142. "orderedItems": [
  143. "https://example.com/foo/usr/1/",
  144. "https://example.com/foo/usr/2/",
  145. "https://example.com/foo/usr/3/",
  146. "https://example.com/foo/usr/4/",
  147. "https://example.com/foo/usr/5/"
  148. ]
  149. }|}
  150. let test_followers_json () =
  151. Logr.info (fun m -> m "%s.%s" "Ap_test" "test_followers_json");
  152. let foo = "https://example.com/foo/usr/" |> Uri.of_string in
  153. let lst = [ "1/"; "2/"; "3/"; "4/"; "5/" ]
  154. |> List.rev
  155. |> List.fold_left (fun init path -> (Uri.make ~path () |> Http.reso ~base:foo) :: init) []
  156. in
  157. let base = "https://example.com/" |> Uri.of_string in
  158. let path = Ap.apub ^ "followers/" in
  159. let base = Http.reso ~base (Uri.make ~path ()) in
  160. let _ = File.mkdir_p File.pDir path in
  161. (* let oc = stdout in *)
  162. File.out_channel ~tmp:None "/dev/null" (fun oc ->
  163. Ap.Followers.Json.(List.fold_left
  164. (fold2pages 3 (flush_page ~base ~oc path))
  165. (0,0,[],0)
  166. lst
  167. |> flush_page ~base ~finish:true ~oc path);
  168. (* check existence of index.jsa *)
  169. (* check existence of 0.jsa *)
  170. (* check existence of 1.jsa *)
  171. "" |> A.equals_string __LOC__ ""
  172. )
  173. let test_followers_cdb_json () =
  174. Logr.info (fun m -> m "%s.%s" "Ap_test" "test_followers_cdb_json");
  175. File.out_channel ~tmp:None "/dev/null" (fun oc ->
  176. let base = "https://example.com/" |> Uri.of_string in
  177. let path = Ap.apub ^ "follower2/" in
  178. let base = Http.reso ~base (Uri.make ~path ()) in
  179. let path = "tmp/" ^ path in
  180. let _ = File.mkdir_p File.pDir path in
  181. let _ = Ap.Followers.Json.coll_of_cdb ~base ~oc ~pagesize:3 path (Mapcdb.Cdb "data/followers.cdb") in
  182. "" |> A.equals_string __LOC__ ""
  183. )
  184. let test_create_note_json () =
  185. Logr.info (fun m -> m "%s.%s" "Ap_test" "test_create_note_json");
  186. File.in_channel "data/create.note.mastodon2.json" (fun ic ->
  187. let c : As2_vocab.Types.note As2_vocab.Types.create = Ezjsonm.from_channel ic
  188. |> As2_vocab.Decode.(create note)
  189. |> Result.get_ok in
  190. assert (not c.direct_message);
  191. let n = c.obj in
  192. n.id |> Uri.to_string |> A.equals_string __LOC__ "https://bewegung.social/users/mro/statuses/111561416759041219";
  193. n.actor |> Uri.to_string |> A.equals_string __LOC__ "https://bewegung.social/users/mro";
  194. n.published |> Option.get |> Ptime.to_rfc3339 |> A.equals_string __LOC__ "2023-12-11T10:55:25-00:00";
  195. n.in_reply_to |> List.length |> A.equals_int __LOC__ 0;
  196. assert (n.summary |> Option.is_none);
  197. n.content |> A.equals_string __LOC__ {|<p>Good morning 😀 <a href="https://bewegung.social/tags/Social" class="mention hashtag" rel="tag">#<span>Social</span></a> <a href="https://bewegung.social/tags/Web" class="mention hashtag" rel="tag">#<span>Web</span></a>! <span class="h-card" translate="no"><a href="https://seppo.social/demo/" class="u-url mention">@<span>demo</span></a></span></p>|};
  198. n.tags |> List.length |> A.equals_int __LOC__ 3;
  199. (match n.tags with
  200. | [
  201. {ty=`Mention;name=n0;href=h0};
  202. {ty=`Hashtag;name=n1;href=h1};
  203. {ty=`Hashtag;name=n2;href=h2};
  204. ] ->
  205. n0 |> A.equals_string __LOC__ "@demo@seppo.social";
  206. h0 |> Uri.to_string |> A.equals_string __LOC__ "https://seppo.social/demo/activitypub/profile.jlda";
  207. n1 |> A.equals_string __LOC__ "#social";
  208. h1 |> Uri.to_string |> A.equals_string __LOC__ "https://bewegung.social/tags/social";
  209. n2 |> A.equals_string __LOC__ "#web";
  210. h2 |> Uri.to_string |> A.equals_string __LOC__ "https://bewegung.social/tags/web";
  211. ()
  212. | _ -> failwith "aua"
  213. (**) );
  214. assert true
  215. )
  216. let test_reject_json () =
  217. Logr.info (fun m -> m "%s.%s" "Ap_test" "test_reject_json");
  218. let reject me id =
  219. `O [("@context", `String "https://www.w3.org/ns/activitystreams");
  220. ("type", `String "Reject");
  221. ("actor", `String (me |> Uri.to_string));
  222. ("object", `String (id |> Uri.to_string))]
  223. in
  224. let me = "https://example.com/alice" |> Uri.of_string in
  225. let id = match File.in_channel "data/create.note.mastodon2.json" Ezjsonm.from_channel with
  226. | `O (_ :: ("id", `String id) :: _) -> id |> Uri.of_string
  227. | _ -> Uri.empty in
  228. id |> Uri.to_string |> A.equals_string __LOC__ {|https://bewegung.social/users/mro/statuses/111561416759041219/activity|};
  229. id
  230. |> reject me
  231. |> Ezjsonm.value_to_string
  232. |> A.equals_string __LOC__ {|{"@context":"https://www.w3.org/ns/activitystreams","type":"Reject","actor":"https://example.com/alice","object":"https://bewegung.social/users/mro/statuses/111561416759041219/activity"}|};
  233. assert true
  234. let test_subscribed () =
  235. for i = 0 to 10 do
  236. try i |> Printf.sprintf "/tmp/%d.xml" |> Unix.unlink
  237. with | Unix.Unix_error(Unix.ENOENT, "unlink", _) -> ()
  238. done ;
  239. let cdb = Mapcdb.Cdb "data/2024-04-30-131146-subscribed.cdb" in
  240. cdb
  241. |> Mapcdb.fold_left (fun c _ -> 1 + c) 0
  242. |> A.equals_int __LOC__ 52;
  243. let r = Ap.Followers.Atom.rule in
  244. r.target |> A.equals_string __LOC__ "activitypub/notify/index.xml";
  245. (match Ap.Followers.Atom.of_cdb
  246. ~cdb
  247. ~title:"My Title"
  248. ~xsl:(Rfc4287.xsl "my.xsl" "./tmp/noop")
  249. ~rel:(Some Rfc4287.Link.notify)
  250. ~page_size:5
  251. "./tmp/" with
  252. | Error _ -> failwith __LOC__
  253. | Ok s -> s)
  254. |> A.equals_string __LOC__ "./tmp/10.xml";
  255. "./tmp/0.xml" |> File.to_string |> A.equals_string __LOC__ {|<?xml version="1.0"?>
  256. <?xml-stylesheet type="text/xsl" href="../../themes/current/my.xsl"?>
  257. <feed xmlns="http://www.w3.org/2005/Atom">
  258. <title>My Title</title>
  259. <id>0.xml</id>
  260. <link rel="self" href="0.xml" title="1"/>
  261. <link rel="first" href="." title="last"/>
  262. <link rel="last" href="0.xml" title="1"/>
  263. <link rel="previous" href="1.xml" title="2"/>
  264. <link rel="notify" href="https://social.wohlfarth.name/users/alain" title=""/>
  265. <link rel="notify" href="https://hci.social/users/sigchi" title=""/>
  266. <link rel="notify" href="https://fosstodon.org/users/nlnetlabs" title=""/>
  267. <link rel="notify" href="https://mastodon.xyz/users/NGIZero" title=""/>
  268. <link rel="notify" href="https://w3c.social/users/w3c" title=""/>
  269. </feed>|};
  270. "./tmp/10.xml" |> File.to_string |> A.equals_string __LOC__ {|<?xml version="1.0"?>
  271. <?xml-stylesheet type="text/xsl" href="../../themes/current/my.xsl"?>
  272. <feed xmlns="http://www.w3.org/2005/Atom">
  273. <title>My Title</title>
  274. <id>10.xml</id>
  275. <link rel="self" href="10.xml" title="11"/>
  276. <link rel="first" href="." title="last"/>
  277. <link rel="last" href="0.xml" title="1"/>
  278. <link rel="previous" href="11.xml" title="12"/>
  279. <link rel="next" href="9.xml" title="10"/>
  280. <link rel="notify" href="https://mas.to/users/leostera" title=""/>
  281. <link rel="notify" href="https://social.network.europa.eu/users/EU_Commission" title="European Commission" rfc7033="acct:EU_Commission@social.network.europa.eu"/>
  282. </feed>|};
  283. "?" |> A.equals_string __LOC__ "?"
  284. let () =
  285. Unix.chdir "../../../test/";
  286. test_follower_state ();
  287. test_inbox_follow ();
  288. test_inbox_unfollow ();
  289. test_followers_page_json_raw ();
  290. test_followers_page_json ();
  291. test_followers_json ();
  292. test_followers_cdb_json ();
  293. test_create_note_json ();
  294. test_reject_json ();
  295. test_subscribed ();
  296. assert true