(** Tests for Firehose (Event Stream) Client and Repository Sync *) open Atproto_sync open Atproto_ipld module Repo_sync = Atproto_sync.Repo_sync (** {1 Test Helpers} *) (** Build a frame from header and payload CBOR values *) let make_frame header_cbor payload_cbor = let header_bytes = Dag_cbor.encode header_cbor in let payload_bytes = Dag_cbor.encode payload_cbor in header_bytes ^ payload_bytes (** Build a message frame header *) let message_header event_type = Dag_cbor.Map [ ("op", Dag_cbor.Int 1L); ("t", Dag_cbor.String event_type) ] (** Build an error frame header *) let error_header () = Dag_cbor.Map [ ("op", Dag_cbor.Int (-1L)) ] (** Create a test CID *) let test_cid = (* A valid CID for testing - CIDv1, dag-cbor, sha2-256 *) match Cid.of_string "bafyreie5cvv4h45feadgeuwhbcutmh6t2ceseocckahdoe6uat64zmz454" with | Ok cid -> cid | Error _ -> failwith "Invalid test CID" (** {1 Frame Decoding Tests} *) let test_decode_commit_event () = let payload = Dag_cbor.Map [ ("seq", Dag_cbor.Int 12345L); ("repo", Dag_cbor.String "did:plc:test123"); ("rev", Dag_cbor.String "3jui7kd2z2y2a"); ("since", Dag_cbor.String "3jui7kd2z2y2b"); ("commit", Dag_cbor.Link test_cid); ("blocks", Dag_cbor.Bytes "\x00\x01\x02\x03"); ( "ops", Dag_cbor.Array [ Dag_cbor.Map [ ("action", Dag_cbor.String "create"); ("path", Dag_cbor.String "app.bsky.feed.post/abc123"); ("cid", Dag_cbor.Link test_cid); ]; ] ); ("tooBig", Dag_cbor.Bool false); ] in let frame = make_frame (message_header "#commit") payload in match Firehose.decode_frame frame with | Ok (Firehose.Commit evt) -> Alcotest.(check int64) "seq" 12345L evt.seq; Alcotest.(check string) "repo" "did:plc:test123" evt.repo; Alcotest.(check string) "rev" "3jui7kd2z2y2a" evt.rev; Alcotest.(check (option string)) "since" (Some "3jui7kd2z2y2b") evt.since; Alcotest.(check string) "blocks" "\x00\x01\x02\x03" evt.blocks; Alcotest.(check int) "ops count" 1 (List.length evt.ops); let op = List.hd evt.ops in Alcotest.(check bool) "action is create" true (op.action = `Create); Alcotest.(check string) "op path" "app.bsky.feed.post/abc123" op.path; Alcotest.(check bool) "too_big" false evt.too_big | Ok _ -> Alcotest.fail "Expected Commit event" | Error e -> Alcotest.fail (Firehose.error_to_string e) let test_decode_identity_event () = let payload = Dag_cbor.Map [ ("seq", Dag_cbor.Int 99L); ("did", Dag_cbor.String "did:plc:user123"); ("time", Dag_cbor.String "2024-01-15T10:30:00Z"); ("handle", Dag_cbor.String "alice.bsky.social"); ] in let frame = make_frame (message_header "#identity") payload in match Firehose.decode_frame frame with | Ok (Firehose.Identity evt) -> Alcotest.(check int64) "seq" 99L evt.seq; Alcotest.(check string) "did" "did:plc:user123" evt.did; Alcotest.(check string) "time" "2024-01-15T10:30:00Z" evt.time; Alcotest.(check (option string)) "handle" (Some "alice.bsky.social") evt.handle | Ok _ -> Alcotest.fail "Expected Identity event" | Error e -> Alcotest.fail (Firehose.error_to_string e) let test_decode_identity_event_no_handle () = let payload = Dag_cbor.Map [ ("seq", Dag_cbor.Int 100L); ("did", Dag_cbor.String "did:plc:user456"); ("time", Dag_cbor.String "2024-01-15T11:00:00Z"); ] in let frame = make_frame (message_header "#identity") payload in match Firehose.decode_frame frame with | Ok (Firehose.Identity evt) -> Alcotest.(check int64) "seq" 100L evt.seq; Alcotest.(check (option string)) "handle" None evt.handle | Ok _ -> Alcotest.fail "Expected Identity event" | Error e -> Alcotest.fail (Firehose.error_to_string e) let test_decode_account_event () = let payload = Dag_cbor.Map [ ("seq", Dag_cbor.Int 200L); ("did", Dag_cbor.String "did:plc:account123"); ("time", Dag_cbor.String "2024-01-15T12:00:00Z"); ("active", Dag_cbor.Bool true); ("status", Dag_cbor.String "active"); ] in let frame = make_frame (message_header "#account") payload in match Firehose.decode_frame frame with | Ok (Firehose.Account evt) -> Alcotest.(check int64) "seq" 200L evt.seq; Alcotest.(check string) "did" "did:plc:account123" evt.did; Alcotest.(check bool) "active" true evt.active; Alcotest.(check (option string)) "status" (Some "active") evt.status | Ok _ -> Alcotest.fail "Expected Account event" | Error e -> Alcotest.fail (Firehose.error_to_string e) let test_decode_handle_event () = let payload = Dag_cbor.Map [ ("seq", Dag_cbor.Int 300L); ("did", Dag_cbor.String "did:plc:handle123"); ("time", Dag_cbor.String "2024-01-15T13:00:00Z"); ("handle", Dag_cbor.String "newhandle.bsky.social"); ] in let frame = make_frame (message_header "#handle") payload in match Firehose.decode_frame frame with | Ok (Firehose.Handle evt) -> Alcotest.(check int64) "seq" 300L evt.seq; Alcotest.(check string) "did" "did:plc:handle123" evt.did; Alcotest.(check string) "handle" "newhandle.bsky.social" evt.handle | Ok _ -> Alcotest.fail "Expected Handle event" | Error e -> Alcotest.fail (Firehose.error_to_string e) let test_decode_tombstone_event () = let payload = Dag_cbor.Map [ ("seq", Dag_cbor.Int 400L); ("did", Dag_cbor.String "did:plc:deleted123"); ("time", Dag_cbor.String "2024-01-15T14:00:00Z"); ] in let frame = make_frame (message_header "#tombstone") payload in match Firehose.decode_frame frame with | Ok (Firehose.Tombstone evt) -> Alcotest.(check int64) "seq" 400L evt.seq; Alcotest.(check string) "did" "did:plc:deleted123" evt.did; Alcotest.(check string) "time" "2024-01-15T14:00:00Z" evt.time | Ok _ -> Alcotest.fail "Expected Tombstone event" | Error e -> Alcotest.fail (Firehose.error_to_string e) let test_decode_info_event () = let payload = Dag_cbor.Map [ ("name", Dag_cbor.String "OutdatedCursor"); ("message", Dag_cbor.String "Cursor is outdated"); ] in let frame = make_frame (message_header "#info") payload in match Firehose.decode_frame frame with | Ok (Firehose.Info msg) -> Alcotest.(check string) "name" "OutdatedCursor" msg.name; Alcotest.(check (option string)) "message" (Some "Cursor is outdated") msg.message | Ok _ -> Alcotest.fail "Expected Info event" | Error e -> Alcotest.fail (Firehose.error_to_string e) let test_decode_stream_error () = let payload = Dag_cbor.Map [ ("error", Dag_cbor.String "FutureCursor") ] in let frame = make_frame (error_header ()) payload in match Firehose.decode_frame frame with | Ok (Firehose.StreamError msg) -> Alcotest.(check string) "error" "FutureCursor" msg | Ok _ -> Alcotest.fail "Expected StreamError event" | Error e -> Alcotest.fail (Firehose.error_to_string e) let test_decode_unknown_event_type () = let payload = Dag_cbor.Map [ ("foo", Dag_cbor.String "bar") ] in let frame = make_frame (message_header "#unknown") payload in match Firehose.decode_frame frame with | Error (Firehose.Protocol_error msg) -> Alcotest.(check bool) "contains unknown type" true (String.length msg > 0) | Ok _ -> Alcotest.fail "Expected Protocol_error" | Error e -> Alcotest.fail ("Wrong error: " ^ Firehose.error_to_string e) let test_decode_invalid_cbor () = match Firehose.decode_frame "not valid cbor" with | Error (Firehose.Decode_error _) -> () | Ok _ -> Alcotest.fail "Expected Decode_error" | Error e -> Alcotest.fail ("Wrong error: " ^ Firehose.error_to_string e) let test_decode_missing_payload () = let header = Dag_cbor.encode (message_header "#commit") in match Firehose.decode_frame header with | Error (Firehose.Decode_error msg) -> Alcotest.(check bool) "mentions payload" true (String.length msg > 0) | Ok _ -> Alcotest.fail "Expected Decode_error" | Error e -> Alcotest.fail ("Wrong error: " ^ Firehose.error_to_string e) (** {1 Helper Function Tests} *) let test_event_seq () = let commit_evt = Firehose.Commit { seq = 123L; repo = "did:plc:test"; rev = "abc"; since = None; commit = test_cid; blocks = ""; ops = []; too_big = false; } in let identity_evt = Firehose.Identity { seq = 456L; did = "did:plc:test"; time = ""; handle = None } in let info_evt = Firehose.Info { name = "test"; message = None } in let stream_error = Firehose.StreamError "test error" in Alcotest.(check (option int64)) "commit seq" (Some 123L) (Firehose.event_seq commit_evt); Alcotest.(check (option int64)) "identity seq" (Some 456L) (Firehose.event_seq identity_evt); Alcotest.(check (option int64)) "info seq" None (Firehose.event_seq info_evt); Alcotest.(check (option int64)) "error seq" None (Firehose.event_seq stream_error) let test_event_did () = let commit_evt = Firehose.Commit { seq = 0L; repo = "did:plc:repo123"; rev = ""; since = None; commit = test_cid; blocks = ""; ops = []; too_big = false; } in let identity_evt = Firehose.Identity { seq = 0L; did = "did:plc:identity456"; time = ""; handle = None } in let info_evt = Firehose.Info { name = ""; message = None } in Alcotest.(check (option string)) "commit did" (Some "did:plc:repo123") (Firehose.event_did commit_evt); Alcotest.(check (option string)) "identity did" (Some "did:plc:identity456") (Firehose.event_did identity_evt); Alcotest.(check (option string)) "info did" None (Firehose.event_did info_evt) (** {1 Config Tests} *) let test_config_no_cursor () = let uri = Uri.of_string "wss://bsky.network/xrpc/com.atproto.sync.subscribeRepos" in let cfg = Firehose.config ~uri () in let built = Firehose.build_uri cfg in Alcotest.(check string) "uri without cursor" "wss://bsky.network/xrpc/com.atproto.sync.subscribeRepos" (Uri.to_string built) let test_config_with_cursor () = let uri = Uri.of_string "wss://bsky.network/xrpc/com.atproto.sync.subscribeRepos" in let cfg = Firehose.config ~uri ~cursor:12345L () in let built = Firehose.build_uri cfg in Alcotest.(check bool) "uri has cursor param" true (String.length (Uri.to_string built) > 50) (** {1 Repo_sync Tests} *) let test_memory_blockstore () = let store = Repo_sync.create_memory_blockstore () in (* Test put and get *) store.put test_cid "test data"; Alcotest.(check (option string)) "get returns data" (Some "test data") (store.get test_cid); (* Test missing block *) let other_cid = match Cid.of_string "bafyreib5uam2ik53lqxqxqxu5ebhxyppafxhgq6ysuvvxe4qjg5ynpz7t4" with | Ok cid -> cid | Error _ -> failwith "Invalid CID" in Alcotest.(check (option string)) "missing block returns None" None (store.get other_cid) let test_diff_from_commit_event () = let commit_event : Firehose.commit_event = { seq = 12345L; repo = "did:plc:test123"; rev = "3jui7kd2z2y2a"; since = None; commit = test_cid; blocks = ""; ops = [ { action = `Create; path = "app.bsky.feed.post/abc123"; cid = Some test_cid; }; { action = `Update; path = "app.bsky.actor.profile/self"; cid = Some test_cid; }; { action = `Delete; path = "app.bsky.feed.like/xyz789"; cid = None }; ]; too_big = false; } in let diff = Repo_sync.diff_from_commit_event commit_event in Alcotest.(check int) "diff count" 3 (List.length diff); let entry1 = List.nth diff 0 in Alcotest.(check bool) "first is Create" true (entry1.action = Repo_sync.Create); Alcotest.(check string) "first collection" "app.bsky.feed.post" entry1.collection; Alcotest.(check string) "first rkey" "abc123" entry1.rkey; let entry2 = List.nth diff 1 in Alcotest.(check bool) "second is Update" true (entry2.action = Repo_sync.Update); let entry3 = List.nth diff 2 in Alcotest.(check bool) "third is Delete" true (entry3.action = Repo_sync.Delete); Alcotest.(check bool) "third cid is None" true (entry3.cid = None) let test_sync_state_from_commit_event () = let commit_event : Firehose.commit_event = { seq = 12345L; repo = "did:plc:testdid"; rev = "3jui7kd2z2y2a"; since = None; commit = test_cid; blocks = ""; ops = []; too_big = false; } in let state = Repo_sync.sync_state_from_commit_event commit_event in Alcotest.(check string) "did" "did:plc:testdid" state.did; Alcotest.(check string) "rev" "3jui7kd2z2y2a" state.rev; Alcotest.(check string) "commit cid" (Cid.to_string test_cid) (Cid.to_string state.commit) let test_load_car_blocks () = (* Create a simple CAR file with one block *) let block_data = Dag_cbor.encode (Dag_cbor.String "hello world") in let block_cid = Cid.of_dag_cbor block_data in let car_data = Car.write ~roots:[ block_cid ] ~blocks:[ { cid = block_cid; data = block_data } ] in let store = Repo_sync.create_memory_blockstore () in match Repo_sync.load_car_blocks store car_data with | Ok roots -> Alcotest.(check int) "one root" 1 (List.length roots); Alcotest.(check string) "root matches" (Cid.to_string block_cid) (Cid.to_string (List.hd roots)); Alcotest.(check (option string)) "block loaded" (Some block_data) (store.get block_cid) | Error e -> Alcotest.fail (Repo_sync.error_to_string e) let test_load_car_blocks_invalid () = let store = Repo_sync.create_memory_blockstore () in match Repo_sync.load_car_blocks store "not a CAR file" with | Error (Repo_sync.Invalid_car _) -> () | Error e -> Alcotest.fail ("Wrong error: " ^ Repo_sync.error_to_string e) | Ok _ -> Alcotest.fail "Expected Invalid_car error" let test_parse_commit () = let commit_cbor = Dag_cbor.Map [ ("did", Dag_cbor.String "did:plc:test123"); ("version", Dag_cbor.Int 3L); ("data", Dag_cbor.Link test_cid); ("rev", Dag_cbor.String "3jui7kd2z2y2a"); ] in let data = Dag_cbor.encode commit_cbor in match Repo_sync.parse_commit data with | Ok commit -> Alcotest.(check string) "did" "did:plc:test123" commit.did; Alcotest.(check int) "version" 3 commit.version; Alcotest.(check string) "rev" "3jui7kd2z2y2a" commit.rev; Alcotest.(check bool) "prev is None" true (commit.prev = None) | Error e -> Alcotest.fail (Repo_sync.error_to_string e) let test_parse_commit_with_prev () = let prev_cid = match Cid.of_string "bafyreib5uam2ik53lqxqxqxu5ebhxyppafxhgq6ysuvvxe4qjg5ynpz7t4" with | Ok cid -> cid | Error _ -> failwith "Invalid CID" in let commit_cbor = Dag_cbor.Map [ ("did", Dag_cbor.String "did:plc:test123"); ("version", Dag_cbor.Int 3L); ("data", Dag_cbor.Link test_cid); ("rev", Dag_cbor.String "3jui7kd2z2y2a"); ("prev", Dag_cbor.Link prev_cid); ] in let data = Dag_cbor.encode commit_cbor in match Repo_sync.parse_commit data with | Ok commit -> Alcotest.(check bool) "prev is Some" true (commit.prev <> None) | Error e -> Alcotest.fail (Repo_sync.error_to_string e) let test_parse_commit_invalid () = (* Missing required fields *) let invalid_cbor = Dag_cbor.Map [ ("did", Dag_cbor.String "did:plc:test") ] in let data = Dag_cbor.encode invalid_cbor in match Repo_sync.parse_commit data with | Error (Repo_sync.Invalid_commit _) -> () | Error e -> Alcotest.fail ("Wrong error: " ^ Repo_sync.error_to_string e) | Ok _ -> Alcotest.fail "Expected Invalid_commit error" let test_cursor_roundtrip () = let cursor_str = "12345" in match Repo_sync.cursor_of_string cursor_str with | Some cursor -> Alcotest.(check int64) "cursor seq" 12345L cursor.seq; Alcotest.(check string) "cursor to string" "12345" (Repo_sync.cursor_to_string cursor) | None -> Alcotest.fail "Failed to parse cursor" let test_cursor_invalid () = match Repo_sync.cursor_of_string "not a number" with | None -> () | Some _ -> Alcotest.fail "Expected None for invalid cursor" let test_cursor_of_event () = let commit_event = Firehose.Commit { seq = 99999L; repo = "did:plc:test"; rev = "abc"; since = None; commit = test_cid; blocks = ""; ops = []; too_big = false; } in match Repo_sync.cursor_of_event commit_event with | Some cursor -> Alcotest.(check int64) "cursor seq" 99999L cursor.seq | None -> Alcotest.fail "Expected cursor from commit event" let test_cursor_of_event_no_seq () = let info_event = Firehose.Info { name = "test"; message = None } in match Repo_sync.cursor_of_event info_event with | None -> () | Some _ -> Alcotest.fail "Expected None for event without seq" let test_apply_diff_create () = let store = Repo_sync.create_memory_blockstore () in let record_data = Dag_cbor.encode (Dag_cbor.String "record content") in let record_cid = Cid.of_dag_cbor record_data in store.put record_cid record_data; let diff = [ { Repo_sync.action = Repo_sync.Create; collection = "app.bsky.feed.post"; rkey = "abc123"; cid = Some record_cid; }; ] in let received = ref [] in let on_record entry data = received := (entry, data) :: !received in let result = Repo_sync.apply_diff ~store ~on_record diff in Alcotest.(check int) "applied" 1 result.applied; Alcotest.(check int) "skipped" 0 result.skipped; Alcotest.(check int) "errors" 0 (List.length result.errors); Alcotest.(check int) "received callbacks" 1 (List.length !received) let test_apply_diff_delete () = let store = Repo_sync.create_memory_blockstore () in let diff = [ { Repo_sync.action = Repo_sync.Delete; collection = "app.bsky.feed.post"; rkey = "abc123"; cid = None; }; ] in let received = ref [] in let on_record entry data = received := (entry, data) :: !received in let result = Repo_sync.apply_diff ~store ~on_record diff in Alcotest.(check int) "applied" 1 result.applied; Alcotest.(check int) "skipped" 0 result.skipped; (* Verify callback received None for data *) match !received with | [ (_, None) ] -> () | _ -> Alcotest.fail "Expected delete callback with None data" let test_apply_diff_missing_block () = let store = Repo_sync.create_memory_blockstore () in (* Create a CID but don't add the block to store *) let diff = [ { Repo_sync.action = Repo_sync.Create; collection = "app.bsky.feed.post"; rkey = "abc123"; cid = Some test_cid; }; ] in let received = ref [] in let on_record entry data = received := (entry, data) :: !received in let result = Repo_sync.apply_diff ~store ~on_record diff in Alcotest.(check int) "applied" 0 result.applied; Alcotest.(check int) "skipped" 1 result.skipped; Alcotest.(check int) "errors" 1 (List.length result.errors) let test_process_commit_event () = (* Create a CAR file with a block *) let record_data = Dag_cbor.encode (Dag_cbor.String "post content") in let record_cid = Cid.of_dag_cbor record_data in let car_data = Car.write ~roots:[ record_cid ] ~blocks:[ { cid = record_cid; data = record_data } ] in let commit_event : Firehose.commit_event = { seq = 12345L; repo = "did:plc:test123"; rev = "3jui7kd2z2y2a"; since = None; commit = test_cid; blocks = car_data; ops = [ { action = `Create; path = "app.bsky.feed.post/abc123"; cid = Some record_cid; }; ]; too_big = false; } in let store = Repo_sync.create_memory_blockstore () in match Repo_sync.process_commit_event ~store commit_event with | Ok diff -> Alcotest.(check int) "diff count" 1 (List.length diff); (* Verify block was loaded *) Alcotest.(check (option string)) "block loaded" (Some record_data) (store.get record_cid) | Error e -> Alcotest.fail (Repo_sync.error_to_string e) let test_error_to_string () = let errors = [ Repo_sync.Parse_error "test"; Repo_sync.Invalid_car "bad car"; Repo_sync.Missing_block test_cid; Repo_sync.Invalid_commit "bad commit"; Repo_sync.Sync_error "sync failed"; ] in List.iter (fun e -> let s = Repo_sync.error_to_string e in Alcotest.(check bool) "error string not empty" true (String.length s > 0)) errors (** {1 Commit Proof Fixture Tests} *) (** Load commit-proof-fixtures.json *) let load_commit_proof_fixtures () = (* During tests, the working directory is _build/default/test/sync *) let paths = [ "../../../../test/fixtures/firehose/commit-proof-fixtures.json"; "../../../test/fixtures/firehose/commit-proof-fixtures.json"; "../../test/fixtures/firehose/commit-proof-fixtures.json"; "test/fixtures/firehose/commit-proof-fixtures.json"; ] in let rec try_paths = function | [] -> failwith "Could not find commit-proof-fixtures.json" | path :: rest -> if Sys.file_exists path then ( let ic = open_in path in let content = really_input_string ic (in_channel_length ic) in close_in ic; content) else try_paths rest in match Atproto_json.decode (try_paths paths) with | Ok json -> ( match Atproto_json.to_array_opt json with | Some fixtures -> fixtures | None -> failwith "Expected array of fixtures") | Error e -> failwith ("JSON parse error: " ^ e) (** Extract string from JSON *) let json_string json = match Atproto_json.to_string_opt json with | Some s -> s | None -> failwith "Expected string" (** Extract string list from JSON *) let json_string_list json = match Atproto_json.to_array_opt json with | Some items -> List.map json_string items | None -> failwith "Expected array of strings" (** Get field from JSON object *) let json_field name json = match Atproto_json.to_object_opt json with | Some pairs -> ( match Atproto_json.get name pairs with | Some v -> v | None -> failwith ("Expected object with field " ^ name)) | None -> failwith ("Expected object with field " ^ name) module Mst = Atproto_mst (** Test a single commit-proof fixture *) let test_commit_proof_fixture fixture () = let comment = json_string (json_field "comment" fixture) in let leaf_value_str = json_string (json_field "leafValue" fixture) in let keys = json_string_list (json_field "keys" fixture) in let adds = json_string_list (json_field "adds" fixture) in let dels = json_string_list (json_field "dels" fixture) in let root_before_str = json_string (json_field "rootBeforeCommit" fixture) in let root_after_str = json_string (json_field "rootAfterCommit" fixture) in (* Parse the leaf value CID - all values in the MST point to this *) let leaf_value = match Cid.of_string leaf_value_str with | Ok cid -> cid | Error e -> failwith ("Invalid leaf value CID: " ^ Cid.error_to_string e) in (* Parse expected root CIDs *) let expected_root_before = match Cid.of_string root_before_str with | Ok cid -> cid | Error e -> failwith ("Invalid rootBeforeCommit CID: " ^ Cid.error_to_string e) in let expected_root_after = match Cid.of_string root_after_str with | Ok cid -> cid | Error e -> failwith ("Invalid rootAfterCommit CID: " ^ Cid.error_to_string e) in (* Create blockstore and MST module *) let store = Mst.Memory_blockstore.create () in let module M = Mst.Make (Mst.Memory_blockstore) in (* Build initial MST from keys *) let entries = List.map (fun k -> (k, leaf_value)) keys in let root_before = M.of_entries store entries in (* Verify root before commit *) Alcotest.(check string) (Printf.sprintf "[%s] rootBeforeCommit" comment) (Cid.to_string expected_root_before) (Cid.to_string root_before); (* Apply adds *) let root_with_adds = List.fold_left (fun root key -> M.add store root key leaf_value) root_before adds in (* Apply deletes *) let root_after = List.fold_left (fun root key -> M.delete store root key) root_with_adds dels in (* Verify root after commit *) Alcotest.(check string) (Printf.sprintf "[%s] rootAfterCommit" comment) (Cid.to_string expected_root_after) (Cid.to_string root_after) (** Generate test cases from fixtures *) let commit_proof_tests () = let fixtures = load_commit_proof_fixtures () in List.mapi (fun i fixture -> let comment = try json_string (json_field "comment" fixture) with _ -> Printf.sprintf "fixture %d" i in Alcotest.test_case comment `Quick (test_commit_proof_fixture fixture)) fixtures (** {1 Test Runner} *) let () = Alcotest.run "Sync" [ ( "frame_decoding", [ Alcotest.test_case "decode commit event" `Quick test_decode_commit_event; Alcotest.test_case "decode identity event" `Quick test_decode_identity_event; Alcotest.test_case "decode identity (no handle)" `Quick test_decode_identity_event_no_handle; Alcotest.test_case "decode account event" `Quick test_decode_account_event; Alcotest.test_case "decode handle event" `Quick test_decode_handle_event; Alcotest.test_case "decode tombstone event" `Quick test_decode_tombstone_event; Alcotest.test_case "decode info event" `Quick test_decode_info_event; Alcotest.test_case "decode stream error" `Quick test_decode_stream_error; Alcotest.test_case "decode unknown event type" `Quick test_decode_unknown_event_type; Alcotest.test_case "decode invalid cbor" `Quick test_decode_invalid_cbor; Alcotest.test_case "decode missing payload" `Quick test_decode_missing_payload; ] ); ( "helpers", [ Alcotest.test_case "event_seq" `Quick test_event_seq; Alcotest.test_case "event_did" `Quick test_event_did; ] ); ( "config", [ Alcotest.test_case "config no cursor" `Quick test_config_no_cursor; Alcotest.test_case "config with cursor" `Quick test_config_with_cursor; ] ); ( "repo_sync_blockstore", [ Alcotest.test_case "memory blockstore" `Quick test_memory_blockstore; Alcotest.test_case "load car blocks" `Quick test_load_car_blocks; Alcotest.test_case "load car blocks invalid" `Quick test_load_car_blocks_invalid; ] ); ( "repo_sync_diff", [ Alcotest.test_case "diff from commit event" `Quick test_diff_from_commit_event; Alcotest.test_case "sync state from commit event" `Quick test_sync_state_from_commit_event; ] ); ( "repo_sync_commit", [ Alcotest.test_case "parse commit" `Quick test_parse_commit; Alcotest.test_case "parse commit with prev" `Quick test_parse_commit_with_prev; Alcotest.test_case "parse commit invalid" `Quick test_parse_commit_invalid; ] ); ( "repo_sync_cursor", [ Alcotest.test_case "cursor roundtrip" `Quick test_cursor_roundtrip; Alcotest.test_case "cursor invalid" `Quick test_cursor_invalid; Alcotest.test_case "cursor of event" `Quick test_cursor_of_event; Alcotest.test_case "cursor of event no seq" `Quick test_cursor_of_event_no_seq; ] ); ( "repo_sync_apply", [ Alcotest.test_case "apply diff create" `Quick test_apply_diff_create; Alcotest.test_case "apply diff delete" `Quick test_apply_diff_delete; Alcotest.test_case "apply diff missing block" `Quick test_apply_diff_missing_block; Alcotest.test_case "process commit event" `Quick test_process_commit_event; ] ); ( "repo_sync_errors", [ Alcotest.test_case "error to string" `Quick test_error_to_string ] ); ("commit_proof_fixtures", commit_proof_tests ()); ]