╭ changed ─────────╮ │src/test/event.zig│ ╰──────────────────╯ ╭───────────╮ │← all files│ ╰───────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │108 // │ │109 // read and parse all of the events from the repo │ │110 // │ │111 + │ │112 + const Commit = struct { │ │113 + parent_oid: ?[hash.byteLen(repo_opts.hash)]u8, │ │114 + oid: [hash.byteLen(repo_opts.hash)]u8, │ │115 + message: []const u8, │ │116 + │ │117 + fn readCommitsFromRepo(inner_arena: *std.heap.ArenaAllocator, inner_repo: *Repo, branch_name: []const u8) ![]@This() {│ │118 + const inner_allocator = inner_arena.allocator(); │ │119 + │ │120 + var commits: std.ArrayList(@This()) = .empty; │ │121 + │ │122 + const ref = try inner_repo.readRef(io, .{ .kind = .head, .name = branch_name }) orelse return error.RefNotFound; │ │123 + var commit_iter = try inner_repo.log(io, inner_allocator, &.{ref}); │ │124 │ │125 + // read the message from each commit │ │126 + while (try commit_iter.next()) |commit_object| { │ │127 + try commit_object.object_reader.seekTo(commit_object.content.commit.message_position); │ │128 + const message = try commit_object.object_reader.interface.allocRemaining(inner_allocator, .unlimited); │ │129 + │ │130 + var commit: @This() = .{ │ │131 + .parent_oid = undefined, │ │132 + .oid = undefined, │ │133 + .message = message, │ │134 + }; │ │135 + │ │136 + _ = try std.fmt.hexToBytes(&commit.oid, &commit_object.oid); │ │137 + │ │138 + if (commit_object.content.commit.metadata.parent_oids) |parent_oids| { │ │139 + if (parent_oids.len > 0) { │ │140 + _ = try std.fmt.hexToBytes(&(commit.parent_oid orelse unreachable), &parent_oids[0]); │ │141 + } else { │ │142 + commit.parent_oid = null; │ │143 + } │ │144 + } else { │ │145 + commit.parent_oid = null; │ │146 + } │ │147 + │ │148 + try commits.append(inner_allocator, commit); │ │149 + } │ │150 + │ │151 + return commits.items; │ │152 + } │ │153 + }; │ │154 + │ │155 var events: std.ArrayList(evt.RepoEvent(repo_opts.hash)) = .empty; │ │156 defer events.deinit(allocator); │ │157 │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │158 { │ │116 - const Commit = struct { │ │117 - oid: [hash.byteLen(repo_opts.hash)]u8, │ │118 - message: []const u8, │ │119 - }; │ │120 - var commits: std.ArrayList(Commit) = .empty; │ │121 - defer commits.deinit(allocator); │ │159 + const commits = try Commit.readCommitsFromRepo(&arena, &repo, "haxy/meta"); │ │160 + │ │161 + // parse commit messages as JSON into event values. │ │162 + // add events in reverse order so the earliest event is first. │ │163 + for (0..commits.len) |i| { │ │164 + const commit = commits[commits.len - i - 1]; │ │165 + const event = try std.json.parseFromSliceLeaky(evt.Event, arena.allocator(), commit.message, .{}); │ │166 + try events.append(allocator, .{ │ │167 + .parent_oid = commit.parent_oid, │ │168 + .oid = commit.oid, │ │169 + .event = event, │ │170 + }); │ │171 + } │ │172 + } │ │173 │ │123 - const ref_haxy_meta = try repo.readRef(io, .{ .kind = .head, .name = "haxy/meta" }) orelse return error.RefNotFound; │ │124 - var commit_iter = try repo.log(io, allocator, &.{ref_haxy_meta}); │ │125 - defer commit_iter.deinit(); │ │174 + const first_oid = events.items[0].oid; │ │175 │ │127 - // read the message from each commit │ │128 - while (try commit_iter.next()) |commit_object| { │ │129 - defer commit_object.deinit(); │ │176 + // │ │177 + // consume events into the database │ │178 + // │ │179 │ │131 - try commit_object.object_reader.seekTo(commit_object.content.commit.message_position); │ │132 - const message = try commit_object.object_reader.interface.allocRemaining(arena.allocator(), .unlimited); │ │180 + { │ │181 + try evt.consume(Repo.DB, repo_opts.hash, io, &repo.core.db, repo.core.db_file, events.items); │ │182 + │ │183 + const history = try Repo.DB.ArrayList(.read_only).init(repo.core.db.rootCursor().readOnly()); │ │184 │ │134 - var commit: Commit = .{ │ │135 - .oid = undefined, │ │136 - .message = message, │ │137 - }; │ │138 - _ = try std.fmt.hexToBytes(&commit.oid, &commit_object.oid); │ │185 + // read the moment we just created │ │186 + const moment_cursor = try history.getCursor(-1) orelse return error.NotFound; │ │187 + const moment = try Repo.DB.HashMap(.read_only).init(moment_cursor); │ │188 + │ │189 + const haxy_cursor = try moment.getCursor(hash.hashInt(repo_opts.hash, "haxy")) orelse return error.NotFound; │ │190 + const haxy = try Repo.DB.HashMap(.read_only).init(haxy_cursor); │ │191 + │ │192 + const object_id_to_views_cursor = try haxy.getCursor(hash.hashInt(repo_opts.hash, "object-id->views")) orelse return error.NotFound; │ │193 + const object_id_to_views = try Repo.DB.HashMap(.read_only).init(object_id_to_views_cursor); │ │194 + │ │195 + // get the last object id │ │196 + const last_object_id_cursor = try haxy.getCursor(hash.hashInt(repo_opts.hash, "last-object-id")) orelse return error.NotFound; │ │197 + var last_object_id: [hash.byteLen(repo_opts.hash)]u8 = undefined; │ │198 + _ = try last_object_id_cursor.readBytes(&last_object_id); │ │199 │ │140 - try commits.append(allocator, commit); │ │200 + // get the latest views (the views generated by the last object id) │ │201 + const views_cursor = try object_id_to_views.getCursor(hash.bytesToInt(repo_opts.hash, &last_object_id)) orelse return error.NotFound; │ │202 + const views = try Repo.DB.HashMap(.read_only).init(views_cursor); │ │203 + │ │204 + // get the map of issues │ │205 + const event_id_to_issue_cursor = try views.getCursor(hash.hashInt(repo_opts.hash, "event-id->issue")) orelse return error.NotFound; │ │206 + const event_id_to_issue = try Repo.DB.HashMap(.read_only).init(event_id_to_issue_cursor); │ │207 + │ │208 + // get the issue out of the map that was edited │ │209 + const first_issue_cursor = try event_id_to_issue.getCursor(hash.hashInt(repo_opts.hash, &first_event_id)) orelse return error.NotFound;│ │210 + const first_issue_map = try Repo.DB.HashMap(.read_only).init(first_issue_cursor); │ │211 + const first_issue = try evt.EventData.read(Repo.DB, repo_opts.hash, arena.allocator(), first_issue_map, .issue); │ │212 + │ │213 + // the description was correctly edited │ │214 + try std.testing.expectEqualStrings(events_to_consume[1].data.issue.description, first_issue.issue.description); │ │215 + │ │216 + // the tags were correctly edited │ │217 + try std.testing.expectEqualStrings(events_to_consume[1].data.issue.tags, first_issue.issue.tags); │ │218 + } │ │219 + │ │220 + // │ │221 + // define new test events without the edit event │ │222 + // and insert them into the repo │ │223 + // │ │224 + │ │225 + const rebased_events_to_consume = [_]evt.Event{ │ │226 + events_to_consume[2], │ │227 + events_to_consume[3], │ │228 + }; │ │229 + │ │230 + { │ │231 + var json: std.Io.Writer.Allocating = .init(std.testing.allocator); │ │232 + defer json.deinit(); │ │233 + │ │234 + for (rebased_events_to_consume, 0..) |event, i| { │ │235 + json.clearRetainingCapacity(); │ │236 + │ │237 + try std.json.Stringify.value(event, .{}, &json.writer); │ │238 + │ │239 + // commit the event into a special branch │ │240 + _ = try repo.commitAtRef( │ │241 + io, │ │242 + allocator, │ │243 + .{ │ │244 + .message = json.written(), │ │245 + // the first event should have the first oid as its parent, │ │246 + // because we want to keep the very first commit. │ │247 + // and the rest will have null which causes them │ │248 + // to have the previous commit as their parent │ │249 + .parent_oids = if (i == 0) &.{std.fmt.bytesToHex(first_oid, .lower)} else null, │ │250 + }, │ │251 + null, │ │252 + .{ .kind = .head, .name = "haxy/meta" }, │ │253 + ); │ │254 } │ │255 + } │ │256 │ │257 + // │ │258 + // read and parse all of the events from the repo │ │259 + // │ │260 + │ │261 + events.clearRetainingCapacity(); │ │262 + │ │263 + { │ │264 + const commits = try Commit.readCommitsFromRepo(&arena, &repo, "haxy/meta"); │ │265 + │ │266 // parse commit messages as JSON into event values. │ │267 // add events in reverse order so the earliest event is first. │ │145 - for (0..commits.items.len) |i| { │ │146 - const commit = commits.items[commits.items.len - i - 1]; │ │268 + for (0..commits.len) |i| { │ │269 + const commit = commits[commits.len - i - 1]; │ │270 const event = try std.json.parseFromSliceLeaky(evt.Event, arena.allocator(), commit.message, .{}); │ │148 - try events.append(allocator, .{ .oid = commit.oid, .event = event }); │ │271 + try events.append(allocator, .{ │ │272 + .parent_oid = commit.parent_oid, │ │273 + .oid = commit.oid, │ │274 + .event = event, │ │275 + }); │ │276 } │ │277 } │ │278 │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │280 // consume events into the database │ │281 // │ │282 │ │156 - try evt.consume(Repo.DB, repo_opts.hash, io, &repo.core.db, repo.core.db_file, events.items); │ │283 + { │ │284 + try evt.consume(Repo.DB, repo_opts.hash, io, &repo.core.db, repo.core.db_file, events.items); │ │285 │ │158 - const history = try Repo.DB.ArrayList(.read_only).init(repo.core.db.rootCursor().readOnly()); │ │286 + const history = try Repo.DB.ArrayList(.read_only).init(repo.core.db.rootCursor().readOnly()); │ │287 │ │160 - // read the moment we just created │ │161 - const moment_cursor = try history.getCursor(-1) orelse return error.NotFound; │ │162 - const moment = try Repo.DB.HashMap(.read_only).init(moment_cursor); │ │288 + // read the moment we just created │ │289 + const moment_cursor = try history.getCursor(-1) orelse return error.NotFound; │ │290 + const moment = try Repo.DB.HashMap(.read_only).init(moment_cursor); │ │291 │ │164 - const haxy_cursor = try moment.getCursor(hash.hashInt(repo_opts.hash, "haxy")) orelse return error.NotFound; │ │165 - const haxy = try Repo.DB.HashMap(.read_only).init(haxy_cursor); │ │292 + const haxy_cursor = try moment.getCursor(hash.hashInt(repo_opts.hash, "haxy")) orelse return error.NotFound; │ │293 + const haxy = try Repo.DB.HashMap(.read_only).init(haxy_cursor); │ │294 │ │167 - const object_id_to_views_cursor = try haxy.getCursor(hash.hashInt(repo_opts.hash, "object-id->views")) orelse return error.NotFound; │ │168 - const object_id_to_views = try Repo.DB.HashMap(.read_only).init(object_id_to_views_cursor); │ │295 + const object_id_to_views_cursor = try haxy.getCursor(hash.hashInt(repo_opts.hash, "object-id->views")) orelse return error.NotFound;│ │296 + const object_id_to_views = try Repo.DB.HashMap(.read_only).init(object_id_to_views_cursor); │ │297 │ │170 - // make sure the issue from the first event was correctly edited │ │171 - { │ │298 // get the last object id │ │299 const last_object_id_cursor = try haxy.getCursor(hash.hashInt(repo_opts.hash, "last-object-id")) orelse return error.NotFound; │ │300 var last_object_id: [hash.byteLen(repo_opts.hash)]u8 = undefined; │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │313 const first_issue_map = try Repo.DB.HashMap(.read_only).init(first_issue_cursor); │ │314 const first_issue = try evt.EventData.read(Repo.DB, repo_opts.hash, arena.allocator(), first_issue_map, .issue);│ │315 │ │190 - // make sure the issue's description was correctly edited │ │191 - try std.testing.expectEqualStrings(events_to_consume[1].data.issue.description, first_issue.issue.description); │ │316 + // the description is no longer edited │ │317 + try std.testing.expectEqualStrings(events_to_consume[0].data.issue.description, first_issue.issue.description); │ │318 │ │193 - // make sure the issue's tags were correctly edited │ │194 - try std.testing.expectEqualStrings(events_to_consume[1].data.issue.tags, first_issue.issue.tags); │ │319 + // the tags are no longer edited │ │320 + try std.testing.expectEqualStrings(events_to_consume[0].data.issue.tags, first_issue.issue.tags); │ │321 } │ │322 } │ │323 │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯