╭ changed ─────────╮ │src/test/event.zig│ ╰──────────────────╯ ╭───────────╮ │← all files│ ╰───────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │738 try std.testing.expectEqualStrings("ziglings", repo_event.name); │ │739 │ │740 // get the repos created by the user │ │741 - const user_id_to_repos_cursor = try haxy_moment.getCursor(hash.hashInt(repo_opts.hash, "user-id->repos")) orelse return error.NotFound; │ │742 - const user_id_to_repos = try Repo.DB.HashMap(.read_only).init(user_id_to_repos_cursor); │ │741 + const user_id_to_repo_id_set_cursor = try haxy_moment.getCursor(hash.hashInt(repo_opts.hash, "user-id->repo-id-set")) orelse return error.NotFound;│ │742 + const user_id_to_repo_id_set = try Repo.DB.HashMap(.read_only).init(user_id_to_repo_id_set_cursor); │ │743 │ │744 - const user_repos_cursor = try user_id_to_repos.getCursor(hash.hashInt(repo_opts.hash, &user_event_id)) orelse return error.NotFound; │ │745 - const user_repos = try Repo.DB.CountedHashSet(.read_only).init(user_repos_cursor); │ │744 + const user_repos_cursor = try user_id_to_repo_id_set.getCursor(hash.hashInt(repo_opts.hash, &user_event_id)) orelse return error.NotFound; │ │745 + const user_repos = try Repo.DB.SortedSet(.read_only).init(user_repos_cursor); │ │746 │ │747 try std.testing.expectEqual(1, try user_repos.count()); │ │748 │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │749 - try std.testing.expect(null != try user_repos.getCursor(hash.hashInt(repo_opts.hash, &repo_event_id)));│ │749 + // the set is keyed by orderKey([created-ts][event-id]) │ │750 + const order_key = evt.orderKey(repo_event.created_ts, &repo_event_id); │ │751 + try std.testing.expect(try user_repos.contains(&order_key)); │ │752 } │ │753 │ │754 // │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │775 try std.testing.expect(null == try event_id_to_repo.getCursor(hash.hashInt(repo_opts.hash, &repo_event_id))); │ │776 │ │777 // get the repos created by the user │ │776 - const user_id_to_repos_cursor = try haxy_moment.getCursor(hash.hashInt(repo_opts.hash, "user-id->repos")) orelse return error.NotFound; │ │777 - const user_id_to_repos = try Repo.DB.HashMap(.read_only).init(user_id_to_repos_cursor); │ │778 + const user_id_to_repo_id_set_cursor = try haxy_moment.getCursor(hash.hashInt(repo_opts.hash, "user-id->repo-id-set")) orelse return error.NotFound;│ │779 + const user_id_to_repo_id_set = try Repo.DB.HashMap(.read_only).init(user_id_to_repo_id_set_cursor); │ │780 │ │779 - const user_repos_cursor = try user_id_to_repos.getCursor(hash.hashInt(repo_opts.hash, &user_event_id)) orelse return error.NotFound; │ │780 - const user_repos = try Repo.DB.CountedHashSet(.read_only).init(user_repos_cursor); │ │781 + const user_repos_cursor = try user_id_to_repo_id_set.getCursor(hash.hashInt(repo_opts.hash, &user_event_id)) orelse return error.NotFound; │ │782 + const user_repos = try Repo.DB.SortedSet(.read_only).init(user_repos_cursor); │ │783 │ │784 + // removing the repo emptied the user's set │ │785 try std.testing.expectEqual(0, try user_repos.count()); │ │783 - try std.testing.expect(null == try user_repos.getCursor(hash.hashInt(repo_opts.hash, &repo_event_id))); │ │786 } │ │787 │ │788 // │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────╮ │846 var pw_buf: [evt.User.password_hash_max_len]u8 = undefined; │ │847 const pw = try evt.User.hashPassword("pw", &pw_buf, io); │ │848 │ │847 - // asserts the timestamp->repo-id map holds exactly `expected` ids in that order│ │849 + // asserts the repo-id-set holds exactly `expected` ids in that order │ │850 const Check = struct { │ │851 fn order( │ │852 comptime DB: type, │ ╰─────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │854 moment: DB.HashMap(.read_only), │ │855 expected: []const *const [evt.event_id_size]u8, │ │856 ) !void { │ │855 - const cursor = try moment.getCursor(hash.hashInt(hash_kind, "created-ts->repo-id")) orelse return error.NotFound;│ │856 - const map = try DB.SortedMap(.read_only).init(cursor); │ │857 - try std.testing.expectEqual(expected.len, try map.count()); │ │857 + const cursor = try moment.getCursor(hash.hashInt(hash_kind, "repo-id-set")) orelse return error.NotFound; │ │858 + const set = try DB.SortedSet(.read_only).init(cursor); │ │859 + try std.testing.expectEqual(expected.len, try set.count()); │ │860 for (expected, 0..) |id, i| { │ │859 - const kv = (try map.getIndexKeyValuePair(@intCast(i))) orelse return error.NotFound; │ │860 - var buf: [evt.event_id_size]u8 = undefined; │ │861 - _ = try kv.value_cursor.readBytes(&buf); │ │862 - try std.testing.expectEqualSlices(u8, id, &buf); │ │861 + const kv = (try set.getIndexKeyValuePair(@intCast(i))) orelse return error.NotFound; │ │862 + // the key is orderKey ([timestamp][event-id]); its trailing bytes are the id │ │863 + var order_key: [@sizeOf(u64) + evt.event_id_size]u8 = undefined; │ │864 + _ = try kv.key_cursor.readBytes(&order_key); │ │865 + try std.testing.expectEqualSlices(u8, id, order_key[@sizeOf(u64)..]); │ │866 } │ │867 } │ │868 }; │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │882 const moment = try evt.currentMoment(repo_opts, &repo); │ │883 try Check.order(Repo.DB, repo_opts.hash, moment, &.{ &repo_ids[0], &repo_ids[1], &repo_ids[2], &repo_ids[3] }); │ │884 │ │882 - // the single user shows up in its own ordered map │ │883 - const ucur = try moment.getCursor(hash.hashInt(repo_opts.hash, "created-ts->user-id")) orelse return error.NotFound;│ │884 - const umap = try Repo.DB.SortedMap(.read_only).init(ucur); │ │885 - try std.testing.expectEqual(1, try umap.count()); │ │885 + // the single user shows up in its own ordered set │ │886 + const ucur = try moment.getCursor(hash.hashInt(repo_opts.hash, "user-id-set")) orelse return error.NotFound; │ │887 + const uset = try Repo.DB.SortedSet(.read_only).init(ucur); │ │888 + try std.testing.expectEqual(1, try uset.count()); │ │889 } │ │890 │ │891 // delete repo1 -> dense order (no tombstone) │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯