╭─────────────╮ │scroll to top│ ╰─────────────╯ ╭ notify.zig ───────────────────────────────────────────────────╮ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ╰───────────────────────────────────────────────────────────────╯
╭────╮ ╭ clone ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ╮ │http│ ssh │ │ ╰────╯ ╰ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ╯
45 46 var arena = std.heap.ArenaAllocator.init(allocator); 47 defer arena.deinit(); 48 49 var prng = std.Random.DefaultPrng.init(std.testing.random_seed); 50 const owner_id = evt.EventWithId.randomId(prng.random()); 51 const recipient_id = evt.EventWithId.randomId(prng.random()); 52 for ([_]*const [evt.event_id_size]u8{ &owner_id, &recipient_id }) |user_id| { 53 const path = try evt.userRepoPath(allocator, users_dir, user_id); 54 defer allocator.free(path); 55 var user_repo = try evt.initUserRepo(io, allocator, path); 56 defer user_repo.deinit(io, allocator); 57 } 58 const location = try evt.createRepo(io, allocator, users_dir, &owner_id, author, "repo", "", .private, repo_opts.hash); 59 60 // 61 // notifs 62 // 63 64 const thread_a = evt.EventWithId.randomId(prng.random()); 65 const thread_b = evt.EventWithId.randomId(prng.random()); 66 var child_ids: [4][evt.event_id_size]u8 = undefined; 67 for (&child_ids) |*id| id.* = evt.EventWithId.randomId(prng.random()); 68 const notif_a: notify.Notif = .{ 69 .repo_id = location.repo_id, 70 .owner_name = "owner", 71 .repo_name = "repo", 72 .thread_id = thread_a, 73 .thread_kind = .issue, 74 .title = "a", 75 .last_child_id = child_ids[0], 76 }; 77 var notif_b = notif_a; 78 notif_b.thread_id = thread_b; 79 notif_b.title = "b"; 80 notif_b.last_child_id = child_ids[1]; 81 const id_a = notify.idOf(&location.repo_id, &thread_a); 82 83 try notify.deliver(io, allocator, users_dir, &recipient_id, &.{ notif_a, notif_b }); 84 try std.testing.expectEqual(2, try unreadCount(io, allocator, users_dir, &recipient_id)); 85 try std.testing.expectEqual(2, (try readRecent(io, allocator, &arena, users_dir, &recipient_id)).len); 86 87 // a bump updates the thread's one notif. times tie within a second, so order isn't checked. 88 var bump = notif_a; 89 bump.last_child_id = child_ids[2]; 90 try notify.deliver(io, allocator, users_dir, &recipient_id, &.{bump}); 91 bump.last_child_id = child_ids[3]; 92 try notify.deliver(io, allocator, users_dir, &recipient_id, &.{bump}); 93 { 94 const recent = try readRecent(io, allocator, &arena, users_dir, &recipient_id); 95 try std.testing.expectEqual(2, recent.len); 96 for (recent) |item| { 97 if (std.mem.eql(u8, &item.notif.thread_id, &thread_a)) try std.testing.expectEqualSlices(u8, &child_ids[3], &item.notif.last_child_id); 98 } 99 } 100 101 // a read of a child the thread has moved past leaves it unread 102 try notify.markRead(io, allocator, users_dir, &recipient_id, &id_a, &child_ids[2]); 103 try std.testing.expectEqual(2, try unreadCount(io, allocator, users_dir, &recipient_id)); 104 try notify.markRead(io, allocator, users_dir, &recipient_id, &id_a, &child_ids[3]); 105 try std.testing.expectEqual(1, try unreadCount(io, allocator, users_dir, &recipient_id)); 106 for (try readRecent(io, allocator, &arena, users_dir, &recipient_id)) |item| { 107 try std.testing.expectEqual(!std.mem.eql(u8, &item.notif.thread_id, &thread_a), item.unread); 108 } 109 110 try notify.dismiss(io, allocator, users_dir, &recipient_id, &id_a); 111 { 112 const recent = try readRecent(io, allocator, &arena, users_dir, &recipient_id); 113 try std.testing.expectEqual(1, recent.len); 114 try std.testing.expectEqualSlices(u8, &thread_b, &recent[0].notif.thread_id); 115 } 116 117 // 118 // notify records 119 // 120 121 // muting a thread drops its notif 122 try notify.mute(io, allocator, users_dir, &recipient_id, location, &thread_b, author); 123 try std.testing.expectEqual(0, (try readRecent(io, allocator, &arena, users_dir, &recipient_id)).len); 124 125 // subscribing replaces the mute record and indexes the subscriber in the owner's repo 126 const id_b = notify.idOf(&location.repo_id, &thread_b); 127 try std.testing.expectEqual(.mute, try readMode(io, allocator, &arena, users_dir, &recipient_id, &location.repo_id, &thread_b)); 128 try notify.subscribe(io, allocator, users_dir, &recipient_id, location, &thread_b, author); 129 try std.testing.expectEqual(.subscribe, try readMode(io, allocator, &arena, users_dir, &recipient_id, &location.repo_id, &thread_b)); 130 try notify.deliver(io, allocator, users_dir, &recipient_id, &.{notif_b}); 131 try std.testing.expectEqual(1, (try readRecent(io, allocator, &arena, users_dir, &recipient_id)).len); 132 { 133 const subscribers = try readSubscribers(io, allocator, &arena, users_dir, &owner_id, &id_b); 134 try std.testing.expectEqual(1, subscribers.len); 135 try std.testing.expectEqualSlices(u8, &recipient_id, &subscribers[0]); 136 } 137 138 // muting drops the subscriber from the index 139 try notify.mute(io, allocator, users_dir, &recipient_id, location, &thread_b, author); 140 try std.testing.expectEqual(0, (try readSubscribers(io, allocator, &arena, users_dir, &owner_id, &id_b)).len); 141 142 // past the cap, the oldest go. times tie within a second, so which one goes isn't checked. 143 { 144 const many = try allocator.alloc(notify.Notif, notify.max_notifs + 1); 145 defer allocator.free(many); 146 for (many, 0..) |*notif, i| { 147 notif.* = notif_a; 148 notif.thread_id = @splat(0); 149 std.mem.writeInt(u64, notif.thread_id[0..@sizeOf(u64)], i, .big); 150 } 151 try notify.deliver(io, allocator, users_dir, &recipient_id, many); 152 } 153 try std.testing.expectEqual(notify.max_notifs, (try readRecent(io, allocator, &arena, users_dir, &recipient_id)).len); 154 } 155