╭─────────────╮ │scroll to top│ ╰─────────────╯ ╭ admin.zig ────────────────────────────────────────────────────╮ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ╰───────────────────────────────────────────────────────────────╯
╭────╮ ╭ clone ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ╮ │http│ ssh │ │ ╰────╯ ╰ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ╯
333 const repo_id_hex = std.fmt.bytesToHex(repo_id, .lower); 334 const fork_id = evt.EventWithId.randomId(prng.random()); 335 const fork_id_hex = std.fmt.bytesToHex(fork_id, .lower); 336 337 var admin = try rp.Repo(.xit, evt.admin_repo_opts).init(io, allocator, .{ .path = admin_path }); 338 defer admin.deinit(io, allocator); 339 try evt.consume(.server, .admin, .xit, evt.admin_repo_opts, io, allocator, &admin, evt.events_ref, &.{ 340 .{ 341 .id = std.fmt.bytesToHex(user_id, .lower), 342 .timestamp = 1, 343 .author = author, 344 .event = .{ .user = .{ 345 .name = author.name, 346 .email = author.email, 347 .password_hash = "", 348 } }, 349 }, 350 .{ 351 .id = std.fmt.bytesToHex(repo_id, .lower), 352 .timestamp = 1, 353 .author = author, 354 .event = .{ .repo = .{ 355 .user_id = &user_id, 356 .name = "repo", 357 .description = "", 358 } }, 359 }, 360 }); 361 362 const target_path = try std.fs.path.join(allocator, &.{ repos_dir, &repo_id_hex }); 363 defer allocator.free(target_path); 364 var target = try rp.Repo(.xit, fork_repo_opts).init(io, allocator, .{ .path = target_path }); 365 defer target.deinit(io, allocator); 366 { 367 const file = try target.core.work_dir.createFile(io, "README", .{}); 368 defer file.close(io); 369 try file.writeStreamingAll(io, "repo\n"); 370 } 371 try target.add(io, allocator, &.{"README"}); 372 _ = try target.commit(io, allocator, .{ .message = "initial" }); 373 374 const draft_path = try fork.create(fork_repo_opts, io, allocator, repos_dir, &admin, .{ 375 .target_branch = "master", 376 .id = fork_id_hex, 377 .user_id = user_id, 378 .repo_id = repo_id, 379 .title = "add a feature", 380 .description = "a draft patch", 381 .tags = "enhancement", 382 .author = author, 383 .timestamp = 2, 384 }); 385 defer allocator.free(draft_path); 386 387 var arena = std.heap.ArenaAllocator.init(allocator); 388 defer arena.deinit(); 389 var moment = try evt.currentMoment(evt.admin_repo_opts, &admin); 390 var record = (try evt.Fork.readById(evt.AdminDB, evt.admin_repo_opts.hash, moment, &arena, &fork_id)) orelse return error.NotFound; 391 try std.testing.expectEqualSlices(u8, &user_id, record.event.user_id); 392 try std.testing.expectEqualSlices(u8, &repo_id, record.event.repo_id); 393 try std.testing.expectEqual(.draft, record.event.stage); 394 try std.testing.expectEqual(1, try indexedForkCount(moment, evt.Fork.user_id_to_fork_id_set_key, &user_id)); 395 const draft_key = evt.Fork.draftKey(&repo_id, &user_id); 396 try std.testing.expectEqual(1, try indexedForkCount(moment, evt.Fork.repo_user_to_draft_id_set_key, &draft_key)); 397 398 var draft_repo = try rp.Repo(.xit, fork_repo_opts).open(io, allocator, .{ .path = draft_path }); 399 defer draft_repo.deinit(io, allocator); 400 const draft_moment = try evt.currentMoment(fork_repo_opts, &draft_repo); 401 const draft = (try evt.Patch.readById(evt.EventDB(fork_repo_opts.hash), fork_repo_opts.hash, draft_moment, &arena, &fork_id)) orelse return error.NotFound; 402 try std.testing.expectEqualStrings("add a feature", draft.event.title); 403 try std.testing.expectEqual(null, draft.event.revision); 404 405 var wrong_user_id = user_id; 406 wrong_user_id[0] ^= 1; 407 try std.testing.expectError(error.InvalidPatchDraft, fork.remove(io, allocator, repos_dir, &admin, &fork_id_hex, &wrong_user_id, author)); 408 try std.Io.Dir.accessAbsolute(io, draft_path, .{}); 409 410 try fork.remove(io, allocator, repos_dir, &admin, &fork_id_hex, &user_id, author); 411 try std.testing.expectError(error.FileNotFound, std.Io.Dir.accessAbsolute(io, draft_path, .{})); 412 _ = arena.reset(.retain_capacity); 413 moment = try evt.currentMoment(evt.admin_repo_opts, &admin); 414 record = (try evt.Fork.readById(evt.AdminDB, evt.admin_repo_opts.hash, moment, &arena, &fork_id)) orelse return error.NotFound; 415 try std.testing.expect(record.removed); 416 try std.testing.expectEqual(0, try indexedForkCount(moment, evt.Fork.user_id_to_fork_id_set_key, &user_id)); 417 try std.testing.expectEqual(0, try indexedForkCount(moment, evt.Fork.repo_user_to_draft_id_set_key, &draft_key)); 418 419 try fork.remove(io, allocator, repos_dir, &admin, &fork_id_hex, &user_id, author); 420 } 421 422 fn indexedForkCount( 423 moment: evt.AdminDB.HashMap(.read_only), 424 index_key: []const u8, 425 parent_id: []const u8, 426 ) !u64 { 427 const index_cursor = try moment.getCursor(hash.hashInt(evt.admin_repo_opts.hash, index_key)) orelse return 0; 428 const index = try evt.AdminDB.HashMap(.read_only).init(index_cursor); 429 const ids_cursor = try index.getCursor(hash.hashInt(evt.admin_repo_opts.hash, parent_id)) orelse return 0; 430 const ids = try evt.AdminDB.SortedSet(.read_only).init(ids_cursor); 431 return try ids.count(); 432 } 433