example.com -> example.org
[Seppo.git] / test / t_as2_vocab.ml
blobe613b67c3fe42f61ca19bddba304a614dcbe22b7
2 open Alcotest
3 open Seppo_lib
4 module A = Assrt
6 let set_up = "set_up", `Quick, (fun () ->
7 Unix.chdir "../../../test/"
10 let tc_err = "tc_err", `Quick, (fun () ->
11 match {|[]|}
12 |> Ezjsonm.from_string
13 |> As2_vocab.Activitypub.Decode.obj with
14 | Error (e : Decoders_ezjsonm.Decode.error) ->
15 e |> Format.asprintf "%a" Decoders_ezjsonm.Decode.pp_error
16 |> check string __LOC__ {|I tried the following decoders but they all failed:
17 "core_obj" decoder: Expected a string, but got []
18 "core_obj event" decoder:
19 Expected an object with an attribute "type", but got [] |};
20 e |> Decoders_ezjsonm.Decode.string_of_error
21 |> check string __LOC__ {|I tried the following decoders but they all failed:
22 "core_obj" decoder: Expected a string, but got []
23 "core_obj event" decoder:
24 Expected an object with an attribute "type", but got [] |};
25 | _ -> failwith __LOC__
27 (* diaspora profile json e.g.
29 https://pod.diaspora.software/u/hq
30 https://pod.diaspora.software/people/7bca7c80311b01332d046c626dd55703
34 let tc_person = "tc_person", `Quick, (fun () ->
35 let empty : As2_vocab.Types.person = {
36 id = Uri.empty;
37 inbox = Uri.empty;
38 outbox = Uri.empty;
39 followers = None;
40 following = None;
41 attachment = [];
42 discoverable = false;
43 generator = None;
44 icon = [];
45 image = None;
46 manually_approves_followers = false;
47 name = None;
48 name_map = [];
49 preferred_username = None;
50 preferred_username_map = [];
51 public_key = {
52 id = Uri.empty;
53 owner = None;
54 pem = "";
55 signatureAlgorithm = None;
57 published = Some Ptime.epoch;
58 summary = None;
59 summary_map = [];
60 url = [];
61 } in
62 let base = (Uri.of_string "https://example.com/su/") in
63 let lang = As2_vocab.Constants.ActivityStreams.und in
64 {empty with id=Uri.make ~path:"id/" ()}
65 |> As2_vocab.Encode.person ~lang ~base
66 |> Ezjsonm.value_to_string ~minify:false
67 |> check string __LOC__ {|{
68 "@context": [
69 "https://www.w3.org/ns/activitystreams",
70 "https://w3id.org/security/v1",
72 "schema": "http://schema.org#",
73 "PropertyValue": "schema:PropertyValue",
74 "value": "schema:value",
75 "@language": "und"
78 "type": "Person",
79 "id": "https://example.com/su/id/",
80 "inbox": "https://example.com/su/",
81 "outbox": "https://example.com/su/",
82 "publicKey": {
83 "@context": [
85 "@language": null
88 "id": "https://example.com/su/",
89 "publicKeyPem": ""
91 "published": "1970-01-01T00:00:00Z",
92 "manuallyApprovesFollowers": false,
93 "discoverable": false
94 }|};
95 let p = {|{
96 "@context": [
97 "https://www.w3.org/ns/activitystreams",
98 "https://w3id.org/security/v1",
100 "schema": "http://schema.org#",
101 "PropertyValue": "schema:PropertyValue",
102 "value": "schema:value",
103 "@language": "und"
106 "type": "Person",
107 "id": "https://example.com/su/id/",
108 "inbox": "https://example.com/su/",
109 "outbox": "https://example.com/su/",
110 "publicKey": {
111 "id": "https://example.com/su/",
112 "owner": "https://example.com/su/",
113 "publicKeyPem": ""
115 "published": "1970-01-01T00:00:00Z",
116 "manuallyApprovesFollowers": false,
117 "discoverable": false,
118 "attachment": []
120 |> Ezjsonm.value_from_string
121 |> As2_vocab.Decode.person
122 |> Result.get_ok in
123 p.id
124 |> Uri.to_string
125 |> check string __LOC__ "https://example.com/su/id/"
128 let tc_actor_3rd = "tc_actor_3rd", `Quick, (fun () ->
129 Logr.info (fun m -> m "%s.%s" "As2_vocab" "actor_3rd");
130 let ok loc fn na =
131 let j = fn |> File.in_channel Ezjsonm.from_channel in
132 let p = j |> As2_vocab.Decode.person |> Result.get_ok in
133 p.name |> Option.get |> check string loc na
134 and oki loc fn id =
135 let j = fn |> File.in_channel Ezjsonm.from_channel in
136 let p = j |> As2_vocab.Decode.person |> Result.get_ok in
137 p.id |> Uri.to_string |> check string loc (id)
138 and err loc fn e =
139 let j = fn |> File.in_channel Ezjsonm.from_channel in
140 j |> As2_vocab.Decode.person |> Result.get_error
141 |> Decoders_ezjsonm.Decode.string_of_error
142 |> check string loc e
143 and fal _loc fn =
144 let j = fn |> File.in_channel Ezjsonm.from_channel in
145 assert (j |> As2_vocab.Decode.person |> Result.is_error)
147 (* ok __LOC__ "data/ap/actor/lemmy.0.json" ""; *)
148 ok __LOC__ "data/ap/actor/akkoma.0.json" "Sean Tilley";
149 ok __LOC__ "data/ap/actor/akkoma.1.json" "Kinetix";
150 ok __LOC__ "data/ap/actor/bonfire.0.json" "stpaultim";
151 ok __LOC__ "data/ap/actor/bridgy.0.json" "Tantek ร‡elik";
152 ok __LOC__ "data/ap/actor/friendica.0.json" "Michael Vogel";
153 ok __LOC__ "data/ap/actor/gnusocial.0.json" "Diogo Peralta Cordeiro";
154 ok __LOC__ "data/ap/actor/gnusocial.1.json" "admin de gnusocial.net";
155 ok __LOC__ "data/ap/actor/gnusocial.2.json" "diogo";
156 ok __LOC__ "data/ap/actor/gotosocial.0.json" "Gerben";
157 ok __LOC__ "data/ap/actor/gotosocial.1.json" "Gerben";
158 oki __LOC__ "data/ap/actor/gotosocial.1b.json" "https://social.nlnet.nl/users/gerben";
159 ok __LOC__ "data/ap/actor/honk.0.json" "boyter";
160 ok __LOC__ "data/ap/actor/mastodon.0.json" "Yet Another #Seppo! ๐ŸŒป";
161 ok __LOC__ "data/ap/actor/mastodon.1.json" "#Seppo";
162 ok __LOC__ "data/ap/actor/mastodon.2.json" "Marcus Rohrmoser ๐ŸŒ";
163 ok __LOC__ "data/ap/actor/mini.0.json" "Yet Another #Seppo! ๐ŸŒป";
164 ok __LOC__ "data/ap/actor/misskey.0.json" "ใ—ใ‚…ใ„ใ‚";
165 ok __LOC__ "data/ap/actor/peertube.0.json" "edps";
166 ok __LOC__ "data/ap/actor/peertube.1.json" {|Q3 Marcus|};
167 ok __LOC__ "data/ap/actor/peertube.2.json" {|edps|};
168 ok __LOC__ "data/ap/actor/peertube.3.json" {|Framasoft|};
169 ok __LOC__ "data/ap/actor/pixelfed.0.json" {|#Seppo|};
170 ok __LOC__ "data/ap/actor/pleroma.0.json" "@fediverse@mro.name";
171 ok __LOC__ "data/ap/actor/smithereen.0.json" {|ะ“ั€ะธะณะพั€ะธะน ะšะปัŽัˆะฝะธะบะพะฒ|};
172 ok __LOC__ "data/ap/actor/snac.0.json" {|The Real Grunfink|};
173 ok __LOC__ "data/ap/actor/threads.0.json" "Ben Savage";
174 ok __LOC__ "data/ap/actor/tootik.0.json" {|EOIN GAIRLEOG|};
175 ok __LOC__ "data/ap/actor/zap.0.json" "mike";
176 ok __LOC__ "data/ap/profile/@actapopuli@fediverse.blog.json" "actapopuli";
177 ok __LOC__ "data/ap/profile/@administrator@gnusocial.net.json" {|admin de gnusocial.net|};
178 ok __LOC__ "data/ap/profile/@dansup@pixelfed.social.json" {|dansup|};
179 ok __LOC__ "data/ap/profile/@gargron@mastodon.social.json" {|Eugen Rochko|};
180 ok __LOC__ "data/ap/profile/@kainoa@calckey.social.json" {|Kainoa |};
181 ok __LOC__ "data/ap/profile/@karolat@stereophonic.space.json" {|karolat|};
182 ok __LOC__ "data/ap/profile/@manton@manton.org.json" {|Manton Reece|};
183 ok __LOC__ "data/ap/profile/@matt@write.as.json" {|Matt|};
184 ok __LOC__ "data/ap/profile/@mike@macgirvin.com.json" {|Mike Macgirvin|};
185 ok __LOC__ "data/ap/profile/@peertube@framapiaf.org.json" {|PeerTube|};
186 ok __LOC__ "data/ap/profile/@syuilo@misskey.io.json" {|:peroro_sama:ใ—ใ‚…ใ„ใ‚:peroro_sama:|};
187 ok __LOC__ "data/ap/profile/@tobias@friendi.ca.json" {|Tobias|};
189 err __LOC__ "data/ap/actor/gotosocial.1b.json" {|Expected an object with an attribute "inbox", but got
190 {"@context":["https://www.w3.org/ns/activitystreams","https://w3id.org/security/v1"],"id":"https://social.nlnet.nl/users/gerben","preferredUsername":"gerben","publicKey":{"id":"https://social.nlnet.nl/users/gerben/main-key","owner":"https://social.nlnet.nl/users/gerben","publicKeyPem":"-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA35UDYAz/pgrIi4O1UDf9\n9W1RnEPVtj730gfujlIQtzWIsBs/8n6zQbK0QBtRSYKhwzkUDMpNUh+Mm/CONjKI\nSwps6mEknlmi3VbyviGYs7LX7SuJAfLIl1hT2eftXQLObKgzkbUcVbS0zwNlBeIX\nPJpHyAVphuI4a02Be458g3y3nSK7Imb4dN2GUhLX2FDOZ6s/EC9HcglZiiz+9hI2\n0Rb4tXjsjbBXe6ofowdXDPmD20Al3v816m4kYfgGmLK86ItFGj8u+hoK8tcC0yWk\ni7KUL3bg9iqp7wBhkjTZ/vVVIf7RG8q2gPRe1BVsi+GBDmV7dmupgicT6UbmAi5h\nTwIDAQAB\n-----END PUBLIC KEY-----\n"},"type":"Person"}|};
192 err __LOC__ "data/ap/actor/friendica.1.json" {|I tried the following decoders but they all failed:
193 "type" decoder:
194 in field "type":
195 expected Person (received Organization), but got "Organization"
196 "type" decoder:
197 in field "type":
198 expected Service (received Organization), but got "Organization" |};
199 err __LOC__ "data/ap/profile/@lemmy_support@lemmy.ml.json" {|I tried the following decoders but they all failed:
200 "type" decoder:
201 in field "type": expected Person (received Group), but got "Group"
202 "type" decoder:
203 in field "type": expected Service (received Group), but got "Group" |};
204 err __LOC__ "data/ap/actor/mobilizon.0.json" {|I tried the following decoders but they all failed:
205 "type" decoder:
206 in field "type": expected Person (received Group), but got "Group"
207 "type" decoder:
208 in field "type": expected Service (received Group), but got "Group" |};
209 fal __LOC__ "data/ap/actor/diaspora.0.json";
212 ok __LOC__ "data/ap/actor/natur.0.json" "ใ—ใ‚…ใ„ใ‚";
213 ok __LOC__ "data/ap/actor/natur.1.json" "ใ—ใ‚…ใ„ใ‚";
214 ok __LOC__ "data/ap/actor/sharkey.0.json" {||};
215 ok __LOC__ "data/ap/actor/sharkey.1.json" {||};
217 ok __LOC__ "data/ap/profile/@framasoft@mobilizon.fr.json" {||};
218 ok __LOC__ "data/ap/profile/@Greensky@open.audio.json" {||};
222 let tc_actor_decode = "tc_actor_decode", `Quick, (fun () ->
223 let fn = "data/ap/actor/peertube.3.json" in
224 let j = fn |> File.in_channel Ezjsonm.from_channel in
225 let p = j |> As2_vocab.Decode.person |> Result.get_ok in
226 p.name |> Option.get |> check string __LOC__ "Framasoft";
227 p.icon |> List.length |> check int __LOC__ 2;
231 let tc_actor_encode = "tc_actor_encode", `Quick, (fun () ->
232 let base = Uri.empty
233 and lang = None
234 and minify = false
236 let s = "data/ap/actor/peertube.3.json"
237 |> File.in_channel Ezjsonm.from_channel
238 |> As2_vocab.Decode.person |> Result.get_ok
239 |> As2_vocab.Encode.person ~base ~lang
240 |> Ezjsonm.value_to_string ~minify in
241 s |>
242 check string
244 check string
246 __LOC__ {|{
247 "type": "Person",
248 "id": "https://framatube.org/accounts/framasoft",
249 "inbox": "https://framatube.org/accounts/framasoft/inbox",
250 "outbox": "https://framatube.org/accounts/framasoft/outbox",
251 "followers": "https://framatube.org/accounts/framasoft/followers",
252 "following": "https://framatube.org/accounts/framasoft/following",
253 "name": "Framasoft",
254 "url": "https://framatube.org/accounts/framasoft",
255 "preferredUsername": "framasoft",
256 "publicKey": {
257 "@context": [
259 "@language": null
262 "id": "https://framatube.org/accounts/framasoft#main-key",
263 "owner": "https://framatube.org/accounts/framasoft",
264 "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuRh3frgIg866D0y0FThp\nSUkJImMcHGkUvpYQYv2iUgarZZtEbwT8PfQf0bJazy+cP8KqQmMDf5PBhT7dfdny\nf/GKGMw9Olc+QISeKDj3sqZ3Csrm4KV4avMGCfth6eSU7LozojeSGCXdUFz/8UgE\nfhV4mJjEX/FbwRYoKlagv5rY9mkX5XomzZU+z9j6ZVXyofwOwJvmI1hq0SYDv2bc\neB/RgIh/H0nyMtF8o+0CT42FNEET9j9m1BKOBtPzwZHmitKRkEmui5cK256s1laB\nT61KHpcD9gQKkQ+I3sFEzCBUJYfVo6fUe+GehBZuAfq4qDhd15SfE4K9veDscDFI\nTwIDAQAB\n-----END PUBLIC KEY-----"
266 "published": "2018-03-01T15:16:17Z",
267 "manuallyApprovesFollowers": false,
268 "discoverable": false,
269 "icon": [
271 "type": "Image",
272 "url": "https://framatube.org/lazy-static/avatars/1dbda4f0-1f7f-40f2-b962-85fd0c144661.png"
275 "type": "Image",
276 "url": "https://framatube.org/lazy-static/avatars/f73876f5-1d45-4f8a-942a-d3d5d5ac5dc1.png"
282 let tc_actor_encode_issue35 = "tc_actor_encode", `Quick, (fun () ->
283 let base = Uri.empty
284 and url = [ "https://example.com" |> Uri.of_string ]
286 let (<:) = function
287 | (_, None) -> fun _ -> []
288 | (field, Some vl) -> fun ty -> [field, ty vl] in
289 let (@?.) field vl = (field, match vl with | [] -> None | l -> Some l) in
290 let list (a : 'a Decoders_ezjsonm.Encode.encoder) (b : 'a list) : Ezjsonm.value = match b with
291 | [] -> Decoders_ezjsonm.Encode.null (* none => omit *)
292 | [b] -> Decoders_ezjsonm.Encode.encode_value a b (* single => value *)
293 | b -> Decoders_ezjsonm.Encode.list a b in
294 let uri ~base u = u |> Uri.resolve "https" base |> Uri.to_string |> Decoders_ezjsonm.Encode.string in
295 (match
296 "url" @?. url <: list (uri ~base);
297 with
298 | [("url",j)] -> j
299 |> Ezjsonm.value_to_string
300 |> check string __LOC__ {|"https://example.com"|}
301 | _ -> fail __LOC__
304 let tc_note_decode = "tc_note_dcode", `Quick, (fun () ->
305 let j = "data/ap/note/mastodon.json" |> File.in_channel Ezjsonm.from_channel in
306 let n = j |> As2_vocab.Decode.note |> Result.get_ok in
307 n.id |> Uri.to_string |> check string __LOC__ "https://digitalcourage.social/users/mro/statuses/111403080326863922";
308 (match n.in_reply_to with
309 | [u] -> u |> Uri.to_string |> check string __LOC__ "https://chaos.social/users/qyliss/statuses/111403054651938519"
310 | _ -> failwith "none");
311 let n = "data/ap/inbox/create/note/akkoma.json" |> File.in_channel Ezjsonm.from_channel
312 |> As2_vocab.Decode.(create note) |> Result.get_ok in
313 let _,co = n.obj.content_map |> List.hd in
315 |> Assrt.equals_string __LOC__
316 {|<p>Oh yeah! Also: trying to flip the pencil over to use an eraser doesnโ€™t work. You have to select an eraser first. </p><p>What the fuck? My shitty Wacom Bamboo tablet from ten years ago can do that!</p>|}
319 let tc_create_article_decode = "tc_create_article_decode", `Quick, (fun () ->
320 "data/ap/inbox/create/article/friendica.json"
321 |> File.in_channel Ezjsonm.from_channel
322 |> As2_vocab.Decode.(create note) |> Result.get_error
323 |> Decoders_ezjsonm.Decode.string_of_error
324 |> check string __LOC__ "in field \"object\":\n in field \"type\": expected Note (received Article), but got \"Article\""
327 let tc_reject_decode = "tc_reject_decode", `Quick, (fun () ->
328 Logr.info (fun m -> m "%s.%s" "As2_vocab" "reject_decode");
329 let j = "data/ap/inbox/reject/follow/2024-08-17-124924-steve.json" |> File.in_channel Ezjsonm.from_channel in
330 let n = j |> As2_vocab.Decode.(reject follow) |> Result.get_ok in
331 n.id
332 |> Uri.to_string
333 |> check string __LOC__ {|https://social.technoetic.com/users/steve#rejects/follows/|};
334 n.obj.id
335 |> Uri.to_string
336 |> check string __LOC__ {|https://social.technoetic.com/users/steve#subscribe|}
339 let tc_webfinger_sunshine = "tc_webfinger_sunshine", `Quick, (fun () ->
340 let q = "data/webfinger/mini.json" |> File.in_channel Ezjsonm.from_channel
341 |> As2_vocab.Activitypub.Decode.Webfinger.query_result
342 |> Result.get_ok in
343 q.subject |> check string __LOC__ "acct:ursi@example.com";
344 q.links |> List.length |> check int __LOC__ 3;
345 let q = "data/webfinger/zap.json" |> File.in_channel Ezjsonm.from_channel
346 |> As2_vocab.Activitypub.Decode.Webfinger.query_result
347 |> Result.get_ok in
348 q.subject |> check string __LOC__ "acct:mike@macgirvin.com";
349 q.links |> List.length |> check int __LOC__ 3;
350 let q = "data/webfinger/atom.json" |> File.in_channel Ezjsonm.from_channel
351 |> As2_vocab.Activitypub.Decode.Webfinger.query_result
352 |> Result.get_ok in
353 q.subject |> check string __LOC__ "acct:ursi@example.com";
354 q.links |> List.length |> check int __LOC__ 3
357 let tc_profile_sunshine = "tc_profile_sunshine", `Quick, (fun () ->
358 let q = "data/ap/actor/mini.0.json" |> File.in_channel Ezjsonm.from_channel
359 |> As2_vocab.Activitypub.Decode.person
360 |> Result.get_ok in
361 q.name |> Option.get |> check string __LOC__ "Yet Another #Seppo! ๐ŸŒป";
362 q.preferred_username |> Option.get |> check string __LOC__ "ursi";
363 q.attachment |> List.length |> check int "" 0;
364 let q = "data/ap/actor/mastodon.0.json" |> File.in_channel Ezjsonm.from_channel
365 |> As2_vocab.Activitypub.Decode.person
366 |> Result.get_ok in
367 (match q.attachment with
368 | [a;b;_] ->
369 a.name |> check string __LOC__ "Support";
370 b.value |> check string __LOC__ {|<a href="https://seppo.social">Seppo.Social</a>|};
371 | _ -> check int "" 0 1
373 q.image |> Option.get |> Uri.to_string |> check string __LOC__ "https://example.com/me-banner.jpg";
374 "" |> check string __LOC__ "";
375 let q = "data/ap/actor/akkoma.0.json" |> File.in_channel Ezjsonm.from_channel
376 |> As2_vocab.Activitypub.Decode.person
377 |> Result.get_ok in
378 q.name |> Option.get |> check string __LOC__ "Sean Tilley";
379 let q = "data/ap/actor/gnusocial.0.json" |> File.in_channel Ezjsonm.from_channel
380 |> As2_vocab.Activitypub.Decode.person
381 |> Result.get_ok in
382 q.preferred_username |> Option.get |> check string __LOC__ "diogo";
383 let q = "data/ap/actor/lemmy.0.json" |> File.in_channel Ezjsonm.from_channel
384 |> As2_vocab.Activitypub.Decode.person
385 |> Result.get_ok in
386 q.preferred_username |> Option.get |> check string __LOC__ "nutomic";
387 let q = "data/ap/actor/mastodon.0.json" |> File.in_channel Ezjsonm.from_channel
388 |> As2_vocab.Activitypub.Decode.person
389 |> Result.get_ok in
390 q.preferred_username |> Option.get |> check string __LOC__ "ursi";
391 let q = "data/ap/actor/peertube.0.json" |> File.in_channel Ezjsonm.from_channel
392 |> As2_vocab.Activitypub.Decode.person
393 |> Result.get_ok in
394 q.preferred_username |> Option.get |> check string __LOC__ "edps";
395 let q = "data/ap/actor/zap.0.json" |> File.in_channel Ezjsonm.from_channel
396 |> As2_vocab.Activitypub.Decode.person
397 |> Result.get_ok in
398 q.preferred_username |> Option.get |> check string __LOC__ "mike";
399 let q = "data/ap/actor/bridgy.0.json" |> File.in_channel Ezjsonm.from_channel
400 |> As2_vocab.Activitypub.Decode.person
401 |> Result.get_ok in
402 q.preferred_username |> Option.get |> check string __LOC__ "tantek.com";
403 assert true
406 let tc_encode = "tc_encode", `Quick, (fun () ->
407 let minify = false in
408 let base = Uri.of_string "http://example.com/foo/" in
409 let module E = Decoders_ezjsonm.Encode in
410 E.encode_string E.obj [("k", `String "v")]
411 |> check string __LOC__ {|{"k":"v"}|};
412 Ezjsonm.value_to_string (E.obj [("k", `String "v")])
413 |> check string __LOC__ {|{"k":"v"}|};
415 let e = {Rfc4287.Entry.empty with
416 id = Uri.make ~path:"a/b/" ?fragment:(Some "c") ();
417 lang = Rfc4287.Rfc4646 "de";
418 title = "uhu";
419 published = (Rfc3339.T "2023-03-07T01:23:45Z") ;
420 updated = (Rfc3339.T "2023-03-07T01:23:46Z");
421 categories = [
422 ((Label (Single "lbl")),(Term (Single "term")),Uri.make ~path:"t/" ());
424 content = "Das war aber einfach";
425 } in
426 let n = e |> Ap.Note.of_rfc4287 in
427 let j = n |> As2_vocab.Encode.note ~base in
428 j |> Ezjsonm.value_to_string ~minify
429 |> Assrt.equals_string __LOC__
431 "type": "Note",
432 "id": "http://example.com/foo/a/b/#c",
433 "attributedTo": "http://example.com/foo/activitypub/actor.jsa",
434 "to": "https://www.w3.org/ns/activitystreams#Public",
435 "cc": "http://example.com/foo/activitypub/subscribers/index.jsa",
436 "mediaType": "text/plain; charset=utf8",
437 "contentMap": {
438 "de": "Das war aber einfach"
440 "sensitive": false,
441 "summaryMap": {
442 "de": "uhu"
444 "published": "2023-03-07T01:23:45Z",
445 "tags": {
446 "type": "Hashtag",
447 "href": "http://example.com/foo/t/term/",
448 "name": "#lbl"
450 }|};
452 let co : 'a As2_vocab.Types.collection_page = {
453 id = Uri.of_string "http://example.com/foo/";
454 current = None;
455 first = None;
456 is_ordered = true;
457 items = [Ap.Note.Create.make n];
458 last = None;
459 next = None;
460 part_of = None;
461 prev = None;
462 total_items= None;
463 } in
464 let j = As2_vocab.Encode.collection_page ~base
465 (As2_vocab.Encode.create ~base
466 (As2_vocab.Encode.note ~base))
467 co in
468 j |> Ezjsonm.value_to_string ~minify
469 |> Assrt.equals_string __LOC__ {|{
470 "@context": [
471 "https://www.w3.org/ns/activitystreams",
472 "https://w3id.org/security/v1",
474 "schema": "http://schema.org#",
475 "PropertyValue": "schema:PropertyValue",
476 "value": "schema:value",
477 "@language": "und"
480 "type": "OrderedCollectionPage",
481 "id": "http://example.com/foo/",
482 "orderedItems": [
484 "type": "Create",
485 "id": "http://example.com/foo/a/b/#c/Create",
486 "actor": "http://example.com/foo/activitypub/actor.jsa",
487 "published": "2023-03-07T01:23:45Z",
488 "to": "https://www.w3.org/ns/activitystreams#Public",
489 "cc": "http://example.com/foo/activitypub/subscribers/index.jsa",
490 "directMessage": false,
491 "object": {
492 "type": "Note",
493 "id": "http://example.com/foo/a/b/#c",
494 "attributedTo": "http://example.com/foo/activitypub/actor.jsa",
495 "to": "https://www.w3.org/ns/activitystreams#Public",
496 "cc": "http://example.com/foo/activitypub/subscribers/index.jsa",
497 "mediaType": "text/plain; charset=utf8",
498 "contentMap": {
499 "de": "Das war aber einfach"
501 "sensitive": false,
502 "summaryMap": {
503 "de": "uhu"
505 "published": "2023-03-07T01:23:45Z",
506 "tags": {
507 "type": "Hashtag",
508 "href": "http://example.com/foo/t/term/",
509 "name": "#lbl"
514 }|};
515 assert true
518 (* https://www.w3.org/TR/activitystreams-core/#ex17-jsonld *)
519 let tc_ex15_note = "tc_ex15_note", `Quick, (fun () ->
520 let j = "data/ap/note/as2_core.ex15.json" |> File.in_channel Ezjsonm.value_from_channel in
521 (match j with
522 | `O [
523 "@context", _ ;
524 "summary", _ ;
525 "type", `String "Create" ;
526 "actor", _ ;
527 "object", `O [
528 "type", `String "Note" ;
530 ] -> assert true
531 | _ -> assert false);
532 let p = j
533 |> As2_vocab.Decode.(create note)
534 |> Result.is_error in
535 assert p
538 _p.obj.summary |> Option.get |> check string "" "";
541 (* https://github.com/mattjbray/ocaml-decoders *)
542 type role = Admin | User
544 type user =
545 { lang : string
546 ; txt : role list
549 let tc_example= "tc_exampl", `Quick, (fun () ->
550 Logr.info (fun m -> m "%s.%s" "As2_vocab" "example");
551 let module My_encoders(E : Decoders.Encode.S) = struct
552 open E
554 let user : role encoder =
555 function
556 | Admin -> string "ADMIN"
557 | User -> string "USER"
559 let user : user encoder =
560 fun u ->
562 [ ("name", string u.lang)
563 ; ("roles", list user u.txt)
565 end in
566 let module E = Decoders_ezjsonm.Encode in
567 let module My_ezjson_encoders = My_encoders(Decoders_ezjsonm.Encode) in
568 let open My_ezjson_encoders in
569 let users =
570 [ {lang = "Alice"; txt = [Admin; User]}
571 ; {lang = "Bob"; txt = [User]}
572 ] in
573 E.encode_string E.obj [("users", E.list user users)]
574 |> check string __LOC__ {|{"users":[{"name":"Alice","roles":["ADMIN","USER"]},{"name":"Bob","roles":["USER"]}]}|}
577 type _i18n =
578 { lang : string
579 ; txt : string
582 let tc_encode_content_map = "tc_encode_content_map", `Quick, (fun () ->
583 Logr.info (fun m -> m "%s.%s" "As2_vocab" "encode_content_map");
584 let l = [("a","A");("b","B")] in
585 let module E = Decoders_ezjsonm.Encode in
586 let j = l |> List.map (fun (k,v) -> (k,E.string v)) in
587 E.encode_string E.obj j
588 |> check string __LOC__ {|{"a":"A","b":"B"}|}
591 let tc_decode_content_map = "tc_decode_content_map", `Quick, (fun () ->
592 Logr.info (fun m -> m "%s.%s" "As2_vocab" "decode_content_map");
593 let s = Ezjsonm.value_from_string {|{"a":"A","b":"B"}|} in
594 let module D = Decoders_ezjsonm.Decode in
595 let l = D.key_value_pairs D.string s
596 |> Result.get_ok in
597 (match l with
598 | [("a","A");("b","B")] -> ()
599 | _ -> "" |> check string __LOC__ {|{"a":"A","b":"B"}|});
600 let s = Ezjsonm.value_from_string {|{"contentMap":{"a":"A","b":"B","b":"C"}}|} in
601 let l = D.field "contentMap" (D.key_value_pairs D.string) s
602 |> Result.get_ok in
603 match l with
604 | [("a","A");("b","B");("b","C")] -> ()
605 | _ -> "" |> check string __LOC__ {|{"a":"A","b":"B"}|}
608 let tc_decode_natur = "tc_decode_natur", `Quick, (fun () ->
609 (* https://codeberg.org/seppo/seppo/issues/5 *)
610 Logr.info (fun m -> m "%s.%s" "As2_vocab" "decode_natur");
611 let j = "data/ap/actor/natur.0.json" |> File.in_channel Ezjsonm.from_channel in
612 let e = j |> As2_vocab.Decode.person |> Result.get_error in
613 let s = e |> Decoders_ezjsonm.Decode.string_of_error in
614 s |> check string __LOC__ {|Expected an object with an attribute "publicKey", but got
615 {"id":"https://dev.rdf-pub.org/d613b246-8984-4654-903d-8d44143aca40","type":"Person","inboxSparql":"https://dev.rdf-pub.org/d613b246-8984-4654-903d-8d44143aca40/inbox/sparql","rdfpub:oauth2Issuer":"https://login.m4h.network/auth/realms/LOA","rdfpub:oauth2IssuerPreferredUserName":"max@login.m4h.network","rdfpub:oauth2IssuerUserId":"1813bdc1-152c-4c27-92a6-6cdfe401ef3d@login.m4h.network","outboxSparql":"https://dev.rdf-pub.org/d613b246-8984-4654-903d-8d44143aca40/outbox/sparql","identifier":"52ff7eb2-0b7c-4388-9894-b40a27714c1b","version":{"type":"xsd:integer","@value":"1"},"owl:sameAs":{"id":"https://dev.rdf-pub.org/05a75688-c517-4ae1-842c-5da3d8460627"},"inbox":"https://dev.rdf-pub.org/d613b246-8984-4654-903d-8d44143aca40/inbox","endpoints":{"oauthAuthorizationEndpoint":"https://dev.rdf-pub.org/oauth/oauthAuthorizationEndpoint","oauthTokenEndpoint":"https://dev.rdf-pub.org/oauth/oauthTokenEndpoint"},"name":"max","outbox":"https://dev.rdf-pub.org/d613b246-8984-4654-903d-8d44143aca40/outbox","published":"2024-01-14T15:59:42.102+01:00","@context":["https://schema.org/docs/jsonldcontext.json","https://rdf-pub.org/schema/rdf-pub-context.json","https://www.w3.org/ns/activitystreams"]}|}
618 let tc_decode_sharkey= "tc_decode_sharke", `Quick, (fun () ->
619 (* https://joinsharkey.org *)
620 Logr.info (fun m -> m "%s.%s" "As2_vocab" "decode_sharkey");
621 let j = "data/ap/actor/sharkey.0.json" |> File.in_channel Ezjsonm.from_channel in
622 let p = j |> As2_vocab.Decode.person |> Result.get_ok in
623 p.name
624 |> Option.value ~default:"-"
625 |> check string __LOC__ {|-|};
626 ("data/ap/actor/sharkey.1.json" |> File.in_channel Ezjsonm.from_channel
627 |> As2_vocab.Decode.person
628 |> Result.get_ok)
629 .name
630 |> Option.value ~default:"-"
631 |> check string __LOC__ {|wakest the shark possum|}
634 let () =
636 "seppo_suite" [
637 __FILE__ , [
638 set_up;
639 tc_actor_3rd;
640 tc_actor_decode;
641 tc_actor_encode;
642 tc_actor_encode_issue35;
643 tc_profile_sunshine;
644 tc_note_decode;
645 tc_create_article_decode;
646 tc_err;
647 tc_person;
648 tc_reject_decode;
649 tc_webfinger_sunshine;
650 tc_encode;
651 tc_ex15_note;
652 tc_example;
653 tc_encode_content_map;
654 tc_decode_content_map;
655 tc_decode_natur;
656 tc_decode_sharkey;
659 assert true