~mro/seppo

02c031fc2668ca9408da6afa5d5d8c9fc044ff57 — Marcus Rohrmoser 29 days ago e9223dd
add a sensitive property to atom/entry.
6 files changed, 96 insertions(+), 75 deletions(-)

M as2_vocab/constants.ml
M lib/iweb.ml
M lib/rfc4287.ml
M lib/xml.ml
M test/t_ap.ml
M test/t_rfc4287.ml
M as2_vocab/constants.ml => as2_vocab/constants.ml +1 -1
@@ 47,7 47,7 @@ end
module ActivityStreams = struct
  let ns_as  = "https://www.w3.org/ns/activitystreams"
  let ns_sec = "https://w3id.org/security/v1"
  let public = Uri.of_string "https://www.w3.org/ns/activitystreams#Public"
  let public = Some "Public" |> Uri.with_fragment (ns_as |> Uri.of_string)

  let und = Some "und"


M lib/iweb.ml => lib/iweb.ml +2 -4
@@ 999,10 999,8 @@ module Post = struct
        assert (l |> Uri.host|> Option.is_some);
        [ Rfc4287.Link.make l ] in
    let os = Option.value ~default:"" in
    Ok ({
        id         = Uri.empty;
    Ok {Rfc4287.Entry.empty with
        (* assumes an antry has one language for title, tags, content. *)
        in_reply_to = [];
        lang;
        author;
        title      = r.tit |> os;


@@ 1014,7 1012,7 @@ module Post = struct
            let t = Rfc4287.Category.Term (Rfc4287.Single s) in
            (l,t,Uri.empty) :: i) [];
        content    = r.dsc |> os;
      } : Rfc4287.Entry.t)
       }

  let of_rfc4287
      tpl (e : Rfc4287.Entry.t) : t =

M lib/rfc4287.ml => lib/rfc4287.ml +10 -4
@@ 330,6 330,7 @@ module Entry = struct
    title      : string;           (* https://www.rfc-editor.org/rfc/rfc4287#section-4.2.14 *)
    published  : Rfc3339.t;        (* https://www.rfc-editor.org/rfc/rfc4287#section-4.2.9 *)
    updated    : Rfc3339.t;        (* https://www.rfc-editor.org/rfc/rfc4287#section-4.2.15 *)
    sensitive  : bool;
    links      : Link.t list;      (* https://www.rfc-editor.org/rfc/rfc4287#section-4.2.7 *)
    categories : Category.t list;  (* https://www.rfc-editor.org/rfc/rfc4287#section-4.2.2 *)
    content    : string;           (* https://www.rfc-editor.org/rfc/rfc4287#section-4.1.3 *)


@@ 347,6 348,7 @@ module Entry = struct
      title       = "";
      published   = Rfc3339.epoch;
      updated     = Rfc3339.epoch;
      sensitive   = false;
      links       = [];
      categories  = [];
      content     = "";


@@ 417,11 419,12 @@ module Entry = struct
      and lang         = Rfc4646 lang
      and author       = person |> Person.decode |> Result.fold ~ok:(fun x -> x) ~error:(fun _ -> Person.empty)
      and published    = Rfc3339.T published
      and updated      = Rfc3339.T updated in
      and updated      = Rfc3339.T updated 
      and sensitive    = false in
      let* in_reply_to = in_reply_to|> list Inreplyto.decode in
      let* links       = links      |> list Link.decode in
      let* categories  = categories |> list Category.decode in
      Ok { id; in_reply_to; lang; author; title; published; updated; links; categories; content }
      Ok { id; in_reply_to; lang; author; title; published; updated; sensitive; links; categories; content }
    | _ -> Error ("can't decode '" ^ (Csexp.to_string s) ^ "'")

  let decode_channel ic =


@@ 441,7 444,8 @@ module Entry = struct
    and links = (if uri |> Uri.host |> Option.is_none
                 then links
                 else (uri |> Link.make) :: links)
    and updated    = published in
    and updated    = published
    and sensitive  = false in
    let* t         = published |> Rfc3339.to_ptime in
    let id         = t |> id_make in
    (*


@@ 450,7 454,7 @@ module Entry = struct
     * - via and thanks -> link via
     * - emojis -> tags
     *)
    Ok { id; in_reply_to; lang; author; published; updated; links; title; categories; content }
    Ok { id; in_reply_to; lang; author; published; updated; sensitive; links; title; categories; content }

  let from_channel ?(published = Ptime_clock.now ()) ?(author = Person.empty) ~lang ~tz ic =
    Logr.debug (fun m -> m "Rfc4287.from_channel");


@@ 517,6 521,7 @@ module Entry = struct
         :: sep 2 :: `El (((ns_a,"title"),[(("","type"),"text")]),[`Data e.title])
         :: sep 2 :: Rfc3339.to_xml "updated" e.updated
         :: sep 2 :: Rfc3339.to_xml "published" e.published
         :: sep 2 :: `El (((ns_as,"sensitive"),[]), [`Data (match e.sensitive with | false -> "false" | true -> "true")])
         :: sep 2 :: `El (((ns_a,"author"),[]), autho )
         :: sep 2 :: (Link.link ~rfc7565:None ~title:None ~href:self ~rel:Link.self |> Link.to_atom)
         :: tl


@@ 616,6 621,7 @@ module Feed = struct
        ((Xmlm.ns_xmlns,"xmlns"),ns_a);
        ((Xmlm.ns_xmlns,"thr"),ns_thr);
        ((Xmlm.ns_xmlns,"wf"),ns_rfc7033);
        ((Xmlm.ns_xmlns,"as"),ns_as);
        ((Xmlm.ns_xml,"lang"),lang);
        ((Xmlm.ns_xml,"base"),base |> Uri.to_string);
      ]),

M lib/xml.ml => lib/xml.ml +10 -9
@@ 1,16 1,17 @@

open Astring

let ns_rdf    = "http://www.w3.org/1999/02/22-rdf-syntax-ns#"
let ns_xsd    = "http://www.w3.org/2001/XMLSchema#"
(* and ns_sec    = As2_vocab.Constants.ActivityStreams.ns_sec ^ "#" *)
let ns_a      = "http://www.w3.org/2005/Atom" (* https://www.rfc-editor.org/rfc/rfc4287#section-2 *)
let ns_thr    = "http://purl.org/syndication/thread/1.0" (* https://www.rfc-editor.org/rfc/rfc4685#section-2 *)
let ns_seppo  = "http://seppo.social/2023/ns#"
let ns_rdf        = "http://www.w3.org/1999/02/22-rdf-syntax-ns#"
let ns_xsd        = "http://www.w3.org/2001/XMLSchema"
(* and ns_sec         = As2_vocab.Constants.ActivityStreams.ns_sec ^ "#" *)
let ns_a          = "http://www.w3.org/2005/Atom" (* https://www.rfc-editor.org/rfc/rfc4287#section-2 *)
let ns_thr        = "http://purl.org/syndication/thread/1.0" (* https://www.rfc-editor.org/rfc/rfc4685#section-2 *)
let ns_seppo      = "http://seppo.social/2023/ns#"
let ns_backoffice = "http://seppo.social/2023/backoffice#"
let ns_rfc7033 = "urn:ietf:rfc:7033"
let ns_rfc7565 = "urn:ietf:rfc:7565"
let ns_xhtml   = "http://www.w3.org/1999/xhtml"
let ns_rfc7033    = "urn:ietf:rfc:7033"
let ns_rfc7565    = "urn:ietf:rfc:7565"
let ns_as         = As2_vocab.Constants.ActivityStreams.ns_as
let ns_xhtml      = "http://www.w3.org/1999/xhtml"

(* for testing purpose only *)
let to_buf ?(xsl = None) ?(readme = None) ?(indent = None) (x : _ Xmlm.frag) (dst : Buffer.t) =

M test/t_ap.ml => test/t_ap.ml +14 -11
@@ 31,11 31,9 @@ open Seppo_lib
open Alcotest
module A = Assrt

let set_up = "setup", `Quick, (fun () ->
    Mirage_crypto_rng_lwt.initialize (module Mirage_crypto_rng.Fortuna);
    Unix.chdir "../../../test/"
  )

let set_up () =
  Mirage_crypto_rng_lwt.initialize (module Mirage_crypto_rng.Fortuna);
  Unix.chdir "../../../test/"

let tc_follower_state () =
  Logr.info (fun m -> m "%s.%s" "Ap_test" "test_follower_state");


@@ 430,8 428,9 @@ let tc_to_rfc4287_loaded () =
  and tz = Timedesc.Time_zone.utc
  and base = "https://example.com/fe/" |> Uri.of_string
  and attr = [ ((Xmlm.ns_xmlns,"xmlns"),Xml.ns_a);
               ((Xmlm.ns_xmlns,"thr"),Xml.ns_thr);
               ((Xmlm.ns_xmlns,"wf"),Xml.ns_rfc7033); ] in
               ((Xmlm.ns_xmlns,"thr")  ,Xml.ns_thr);
               ((Xmlm.ns_xmlns,"as")     ,Xml.ns_as);
               ((Xmlm.ns_xmlns,"wf")   ,Xml.ns_rfc7033); ] in
  let apu = load_note "note-Hjcb9bqwCgk.json" in
  let ato = apu |> Ap.Note.to_rfc4287 ~tz ~now in
  ato.author.name |> Assrt.equals_string __LOC__ "https://mastodon.social/users/nicholas_saunders";


@@ 444,11 443,12 @@ I'm inferring that this has something to with the Goat, but have no idea.  Guess
  let buf = 1024 |> Buffer.create in
  buf |> Xml.to_buf (ato |> Rfc4287.Entry.to_atom ~attr ~base);
  buf |> Buffer.to_bytes |> Bytes.to_string |> Assrt.equals_string __LOC__ {|<?xml version="1.0"?>
<entry xml:lang="en" xmlns="http://www.w3.org/2005/Atom" xmlns:thr="http://purl.org/syndication/thread/1.0" xmlns:wf="urn:ietf:rfc:7033">
<entry xml:lang="en" xmlns="http://www.w3.org/2005/Atom" xmlns:thr="http://purl.org/syndication/thread/1.0" xmlns:as="https://www.w3.org/ns/activitystreams" xmlns:wf="urn:ietf:rfc:7033">
    <id>https://mastodon.social/users/nicholas_saunders/statuses/112615088973179734</id>
    <title type="text"></title>
    <updated>2024-06-14T12:58:07Z</updated>
    <published>2024-06-14T12:58:07Z</published>
    <as:sensitive>false</as:sensitive>
    <author>
      <name>https://mastodon.social/users/nicholas_saunders</name>
      <wf:uri></wf:uri>


@@ 472,11 472,12 @@ I'm inferring that this has something to with the Goat, but have no idea.  Guess
  buf |> Buffer.clear;
  buf |> Xml.to_buf (ato |> Rfc4287.Entry.to_atom ~attr ~base);
  buf |> Buffer.to_bytes |> Bytes.to_string |> Assrt.equals_string __LOC__ {|<?xml version="1.0"?>
<entry xml:lang="und" xmlns="http://www.w3.org/2005/Atom" xmlns:thr="http://purl.org/syndication/thread/1.0" xmlns:wf="urn:ietf:rfc:7033">
<entry xml:lang="und" xmlns="http://www.w3.org/2005/Atom" xmlns:thr="http://purl.org/syndication/thread/1.0" xmlns:as="https://www.w3.org/ns/activitystreams" xmlns:wf="urn:ietf:rfc:7033">
    <id>https://benjojo.co.uk/u/benjojo/h/89L5knBH4TGG4qY4VT</id>
    <title type="text"></title>
    <updated>2024-08-12T10:40:45Z</updated>
    <published>2024-08-12T10:40:45Z</published>
    <as:sensitive>false</as:sensitive>
    <author>
      <name>benjojo</name>
      <wf:uri>acct:benjojo@benjojo.co.uk</wf:uri>


@@ 535,7 536,7 @@ let tc_decode_create_notes () =
   | Ok s -> s)
  |> Assrt.equals_string __LOC__ {|<?xml version="1.0"?>
<?xml-stylesheet type="text/xsl" href="../../themes/current/posts.xsl"?>
<feed xmlns="http://www.w3.org/2005/Atom" xmlns:thr="http://purl.org/syndication/thread/1.0" xmlns:wf="urn:ietf:rfc:7033" xml:lang="de" xml:base="http://example.com/">
<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="de" xml:base="http://example.com/">
  <id>http://example.com/seppo.cgi/p-37/</id>
  <title type="text">ti</title>
  <updated>2024-06-19 16:15:14+02:00</updated>


@@ 548,6 549,7 @@ let tc_decode_create_notes () =
    <title type="text"></title>
    <updated>2024-06-13T09:55:01+02:00</updated>
    <published>2024-06-13T09:55:01+02:00</published>
    <as:sensitive>false</as:sensitive>
    <author>
      <name>https://mastodon.social/users/nicholas_saunders</name>
      <wf:uri></wf:uri>


@@ 563,6 565,7 @@ let tc_decode_create_notes () =
    <title type="text"></title>
    <updated>2024-06-13T04:26:03+02:00</updated>
    <published>2024-06-13T04:26:03+02:00</published>
    <as:sensitive>false</as:sensitive>
    <author>
      <name>https://mastodon.social/users/nicholas_saunders</name>
      <wf:uri></wf:uri>


@@ 579,7 582,7 @@ let () =
  run
    "seppo_suite" [
    __FILE__ , [
      set_up;
      "set_up",                    `Quick, set_up;
      "tc_follower_state",         `Quick, tc_follower_state;
      "tc_inbox_follow",           `Quick, tc_inbox_follow;
      "tc_inbox_unfollow",         `Quick, tc_inbox_unfollow;

M test/t_rfc4287.ml => test/t_rfc4287.ml +59 -46
@@ 24,34 24,40 @@
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *)

open Alcotest
open Seppo_lib
open Rfc4287

let set_up () =
  Mirage_crypto_rng_lwt.initialize (module Mirage_crypto_rng.Fortuna);
  Unix.chdir "../../../test/"


let mk_sample () =
  let tag path = Category.((Label (Single path)), (Term (Single path)), tagu) in
  let e : Entry.t = {
    id         = "o/p-12/#23" |> Uri.of_string;
    in_reply_to= [Uri.empty |> Inreplyto.make];
    lang       = Rfc4646 "en";
    author     = {Rfc4287.Person.empty with
                  name = "fediverse";
                  uri = Some (Uri.of_string "https://fediverse@mro.name");
                  (* Uri.make ~userinfo:"fediverse" ~host:"mro.name" () *)};
    title      = "#Announce Seppo.Social v0.1 and Request for Comments.";
    published  = Rfc3339.T "2023-02-11T11:07:23+01:00";
    updated    = Rfc3339.T "2023-02-11T11:07:23+01:00";
    links      = [ "https://seppo.social/en/downloads/seppo-Linux-x86_64-0.1/" |> Uri.of_string  |> Link.make ];
    categories = [
      tag "ActivityPub";
      tag "Announce";
      tag "Fediverse";
      tag "Media";
      tag "permacomputing";
      tag "Seppo";
      tag "Social";
      tag "webfinger";
    ];
    content    = {|I am happy to announce the premiere release of #Seppo!, Personal #Social #Media under funding of NLnet.nl.
  let e = {Rfc4287.Entry.empty with
           id         = "o/p-12/#23" |> Uri.of_string;
           in_reply_to= [Uri.empty |> Inreplyto.make];
           lang       = Rfc4646 "en";
           author     = {Rfc4287.Person.empty with
                         name = "fediverse";
                         uri = Some (Uri.of_string "https://fediverse@mro.name");
                         (* Uri.make ~userinfo:"fediverse" ~host:"mro.name" () *)};
           title      = "#Announce Seppo.Social v0.1 and Request for Comments.";
           published  = Rfc3339.T "2023-02-11T11:07:23+01:00";
           updated    = Rfc3339.T "2023-02-11T11:07:23+01:00";
           links      = [ "https://seppo.social/en/downloads/seppo-Linux-x86_64-0.1/" |> Uri.of_string  |> Link.make ];
           categories = [
             tag "ActivityPub";
             tag "Announce";
             tag "Fediverse";
             tag "Media";
             tag "permacomputing";
             tag "Seppo";
             tag "Social";
             tag "webfinger";
           ];
           content    = {|I am happy to announce the premiere release of #Seppo!, Personal #Social #Media under funding of NLnet.nl.

Find it at https://Seppo.Social/downloads/



@@ 64,13 70,13 @@ It has no notable user facing #ActivityPub features so far, but
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.

Your comments are very much appreciated.|};
  } in
          } in
  e

let tail x =
  Assrt.equals_string __LOC__ "ok" (if x |> Result.is_ok then "ok" else "no")

let test_compute_links () =
let tc_compute_links () =
  let base = "https://example.com/sub/" |> Uri.of_string in
  let self,first,last,prev,next = ("o/p",2) |> Rfc4287.Feed.compute_links ~max:7 ~base in
  self  |> Uri.to_string |> Assrt.equals_string __LOC__ "https://example.com/sub/o/p-2/";


@@ 80,7 86,7 @@ let test_compute_links () =
  next  |> Option.get |> Uri.to_string |> Assrt.equals_string __LOC__ "https://example.com/sub/o/p-1/";
  assert true

let test_encode () =
let tc_encode () =
  Logr.info (fun m -> m "rfc4287_test.test_encode");
  let _ = match {|(6:author(4:name9:fediverse3:uri26:https://fediverse@mro.name))|} |> Csexp.parse_string with
    | Ok (List [ Atom "author"; List [


@@ 153,7 159,7 @@ Your comments are very much appreciated.|};
    |> 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/";
    assert true

let test_decode_2023 () =
let tc_decode_2023 () =
  let e : Rfc4287.Entry.t =
    {|(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.



@@ 177,7 183,7 @@ Your comments are very much appreciated.)|}
  e.author.uri |> Option.get |> Uri.to_string |> Assrt.equals_string __LOC__ {|//fediverse@mro.name|};
  ()

let test_decode_2024 () =
let tc_decode_2024 () =
  let e : Rfc4287.Entry.t =
    {|(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.



@@ 201,7 207,7 @@ Your comments are very much appreciated.)|}
  e.author.uri |> Option.get |> Uri.to_string |> Assrt.equals_string __LOC__ {|https://fediverse@mro.name|};
  ()

let test_from_plain_text () =
let tc_from_plain_text () =
  Logr.info (fun m -> m "rfc4287_test.test_from_plain_text");
  let published = Rfc3339.T "2023-02-14T01:23:45+01:00" in
  let author     = {Rfc4287.Person.empty with


@@ 228,7 234,7 @@ let test_from_plain_text () =
 * https://opam.ocaml.org/packages/base32/
 * https://opam.ocaml.org/packages/base64/
*)
let test_id_make () =
let tc_id_make () =
  Logr.info (fun m -> m "rfc4287_test.test_id_make");
  let assrt l id iso =
    match iso |> Ptime.of_rfc3339 with


@@ 243,7 249,7 @@ let test_id_make () =
  "2120-01-01T00:00:00+00:00" |> assrt "rfc4287_test.test_id_make 4" "2sd6e22";
  assert true

let test_entry_atom () =
let tc_entry_atom () =
  Logr.info (fun m -> m "rfc4287_test.test_entry_atom");
  let base = Uri.of_string "https://example.com/sub/" in
  let published = Rfc3339.T "2023-02-14T01:23:45+01:00" in


@@ 259,16 265,18 @@ let test_entry_atom () =
  let attr = [
    ((Xmlm.ns_xmlns,"xmlns"), Xml.ns_a);
    ((Xmlm.ns_xmlns,"wf"), Xml.ns_rfc7033);
    ((Xmlm.ns_xmlns,"as"), Xml.ns_as);
  ] in
  let e = Entry.to_atom ~attr ~base e0 in
  Xml.to_buf e buf;
  buf |> Buffer.to_bytes |> Bytes.to_string
  |> Assrt.equals_string __LOC__ {|<?xml version="1.0"?>
<entry xml:lang="nl" xmlns="http://www.w3.org/2005/Atom" xmlns:wf="urn:ietf:rfc:7033">
<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">
    <id>https://example.com/sub/aseggdb</id>
    <title type="text">Hello, world!</title>
    <updated>2023-02-14T01:23:45+01:00</updated>
    <published>2023-02-14T01:23:45+01:00</published>
    <as:sensitive>false</as:sensitive>
    <author>
      <name>fediverse</name>
      <wf:uri>acct:fediverse@mro.name</wf:uri>


@@ 278,7 286,7 @@ let test_entry_atom () =
    <content type="text">a new Note.</content>
  </entry>|}

let test_feed_atom () =
let tc_feed_atom () =
  Logr.info (fun m -> m "rfc4287_test.test_feed_atom");

  let published = Rfc3339.T "2023-02-14T01:23:45+01:00" in


@@ 306,7 314,7 @@ let test_feed_atom () =
  Xml.to_buf fe buf;
  buf |> Buffer.to_bytes |> Bytes.to_string
  |> Assrt.equals_string __LOC__ {|<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xmlns:thr="http://purl.org/syndication/thread/1.0" xmlns:wf="urn:ietf:rfc:7033" xml:lang="nl" xml:base="https://example.com/sub/">
<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/">
  <id>https://example.com/sub/o/p-11/</id>
  <title type="text">My fancy #Seppo!</title>
  <updated>2023-02-27T12:34:56+01:00</updated>


@@ 320,6 328,7 @@ let test_feed_atom () =
    <title type="text">Hello, world!</title>
    <updated>2023-02-14T01:23:45+01:00</updated>
    <published>2023-02-14T01:23:45+01:00</published>
    <as:sensitive>false</as:sensitive>
    <author>
      <name>fediverse</name>
      <wf:uri>acct:fediverse@mro.name</wf:uri>


@@ 330,21 339,25 @@ let test_feed_atom () =
  </entry>
  </feed>|}

let test_xsl () =
let tc_xsl () =
  "o/p-1/index.xml"
  |> Rfc4287.xsl "posts.xsl"
  |> Option.value ~default:"?"
  |> Assrt.equals_string __LOC__ "../../themes/current/posts.xsl"

let () =
  Unix.chdir "../../../test/";
  test_compute_links ();
  test_encode ();
  test_decode_2023 ();
  test_decode_2024 ();
  test_from_plain_text ();
  test_id_make ();
  test_entry_atom ();
  test_feed_atom ();
  test_xsl ();
  assert true
  run
    "seppo_suite" [
    __FILE__ , [
      "set_up",              `Quick, set_up;
      "tc_compute_links ",   `Quick, tc_compute_links ;
      "tc_encode ",          `Quick, tc_encode ;
      "tc_decode_2023 ",     `Quick, tc_decode_2023 ;
      "tc_decode_2024 ",     `Quick, tc_decode_2024 ;
      "tc_from_plain_text ", `Quick, tc_from_plain_text ;
      "tc_id_make ",         `Quick, tc_id_make ;
      "tc_entry_atom ",      `Quick, tc_entry_atom ;
      "tc_feed_atom ",       `Quick, tc_feed_atom ;
      "tc_xsl ",             `Quick, tc_xsl ;
    ]
  ]