╭─────────────╮ │scroll to top│ ╰─────────────╯ ╭ undo.zig ─────────────────────────────────────────────────────╮ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ╰───────────────────────────────────────────────────────────────╯
╭ find file ─ ─ ─ ─ ─ ─ ─ ─ ─ ─╮ ╭────╮ ╭ clone ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─╮ │ │ │http│ ssh │ │ ╰ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─╯ ╰────╯ ╰ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─╯
162 defer arena.deinit(); 163 const path = try temp.dir.realPathFileAlloc(io, ".", arena.allocator()); 164 const opts: xit.repo.RepoOpts(.xit) = .{ .is_test = true }; 165 var repo = try xit.repo.Repo(.xit, opts).init(io, allocator, .{ .path = path }); 166 defer repo.deinit(io, allocator); 167 _ = try repo.commitAtRef(io, allocator, .{ .message = "example commit" }, null, .{ .kind = .head, .name = "master" }); 168 const data = try Undo.init(opts, &arena, &repo, null, "", null, .{ .location = .{ .repo = "" } }); 169 try std.testing.expect(std.mem.startsWith(u8, data.items[0].description, "commit -m \"example commit\"")); 170 } 171 172 test "malformed undo history is shown as an error" { 173 var temp = std.testing.tmpDir(.{}); 174 defer temp.cleanup(); 175 var arena = std.heap.ArenaAllocator.init(allocator); 176 defer arena.deinit(); 177 const aa = arena.allocator(); 178 179 // a server with one user who owns the repo 180 const base = try temp.dir.realPathFileAlloc(io, ".", aa); 181 const admin_path = try std.Io.Dir.path.join(aa, &.{ base, "admin" }); 182 const users_dir = try std.Io.Dir.path.join(aa, &.{ base, "users" }); 183 var admin = try xit.repo.Repo(.xit, evt.admin_repo_opts).init(io, allocator, .{ .path = admin_path }); 184 defer admin.deinit(io, allocator); 185 const owner: [evt.event_id_size]u8 = @splat(1); 186 const repo_id: [evt.event_id_size]u8 = @splat(4); 187 const author: evt.CommitAuthor = .{ .name = "test", .email = "test@example.test" }; 188 try evt.consume(.{ .server = .{ .users_dir = users_dir } }, .admin, .xit, evt.admin_repo_opts, io, allocator, &admin, evt.events_ref, &.{ 189 .{ .id = std.fmt.bytesToHex(owner, .lower), .author = author, .event = .{ .user = .{ .name = "alice", .email = "alice@example.test", .password_hash = "unused" } } }, 190 }); 191 var user_repo = try evt.initUserRepo(io, allocator, try evt.userRepoPath(aa, users_dir, &owner)); 192 defer user_repo.deinit(io, allocator); 193 try evt.consume(.{ .server = .{ .users_dir = users_dir } }, .user, .xit, evt.user_repo_opts, io, allocator, &user_repo, evt.events_ref, &.{ 194 .{ .id = std.fmt.bytesToHex(repo_id, .lower), .author = author, .event = .{ .repo = .{ .user_id = std.fmt.bytesToHex(owner, .lower), .name = "demo", .description = "", .read_access = .public } } }, 195 }); 196 const path = try evt.repoPath(aa, users_dir, &owner, &repo_id); 197 const Repo = xit.repo.Repo(.xit, .{}); 198 var repo = try Repo.init(io, allocator, .{ .path = path }); 199 defer repo.deinit(io, allocator); 200 201 // append an invalid undo record without damaging the repository data. 202 const Corrupt = struct { 203 pub fn run(_: @This(), cursor: *Repo.DB.Cursor(.read_write)) !void { 204 const moment = try Repo.DB.HashMap(.read_write).init(cursor.*); 205 try moment.put(xit.hash.hashInt(.sha1, "undo"), .{ .bytes = "bad" }); 206 } 207 }; 208 const history = try Repo.DB.ArrayList(.read_write).init(repo.core.db.rootCursor()); 209 try history.appendContext(.{ .slot = try history.getSlot(-1) }, Corrupt{}); 210 211 var session = try ui.Session.init(&arena, &admin, .{ .user_id = &owner }); 212 session.io = io; 213 session.users_dir = users_dir; 214 // tabs switch in-page, so every tab carries the undo data and its failure 215 const files = try ui.Repo.init(&arena, &session, ui.RoutablePage.repoFilesRoute("alice:demo", null, "", "", 0).?); 216 try std.testing.expectEqual(0, files.undo.?.items.len); 217 try std.testing.expectEqualStrings("InvalidUndoRecord", files.undo.?.failure.?); 218 const undo = try ui.Repo.init(&arena, &session, ui.RoutablePage.repoUndoRoute("alice:demo", null).?); 219 try std.testing.expectEqualStrings("InvalidUndoRecord", undo.undo.?.failure.?); 220 221 var root = ui.Widget{ .repo_undo = try Undo.View.init(allocator, &undo.undo.?, &session) }; 222 defer root.deinit(allocator); 223 try root.build(allocator, constraint, root.getFocus()); 224 const html = try @import("../web.zig").generateHtml(allocator, &root, &session); 225 defer allocator.free(html); 226 try std.testing.expect(std.mem.indexOf(u8, html, "InvalidUndoRecord") != null); 227 } 228