v4: a dump or playlist going public notifies its followers — a private dump notified no one when posted, and nothing told its followers when it was made public: the poster's followers never learnt of it, nor did the followers of the public playlists holding it. Likewise, the dumps added to a playlist while it was private were never notified to its followers. updateDump and replaceFileDump now call notifyIfWentPublic, which notifies the poster's followers (user_dump_posted) and, through the new notifyPlaylistFollowersNowPublic, the followers of each public playlist holding the dump (playlist_dump_added). updatePlaylist calls the same function when a playlist goes public, for each public dump in it, from the bottom up. A follower hears of a dump once both it and the playlist are public; the source keys stop a dump made private and public again from notifying twice. 256 backend tests pass, and deno check, deno fmt and doc:lint are clean.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
khannurien
2026-10-09 10:23:08 +00:00
parent 256857f72e
commit 7713c5a1b5
5 changed files with 142 additions and 6 deletions

View File

@@ -601,6 +601,46 @@ test("PUT /api/dumps/:id: going private hides the dump from everyone else", asyn
assertEquals((await get(`/api/dumps/${id}`)).status, 200);
});
test("PUT /api/dumps/:id: going public notifies the poster's and its playlists' followers, once", async () => {
const owner = await createUser();
const fan = await createUser();
const listener = await createUser();
db.prepare(
`INSERT INTO follows (id, follower_id, followed_user_id, created_at) VALUES (?, ?, ?, ?);`,
).run(crypto.randomUUID(), fan.id, owner.id, new Date().toISOString());
const mix = insertPlaylist({ userId: owner.id, title: "Mix" });
const hidden = insertPlaylist({
userId: owner.id,
title: "Hidden",
isPublic: false,
});
await put(`/api/me/follow/playlists/${mix.id}`, { auth: listener.auth });
const { id } = insertDump({ userId: owner.id, isPrivate: true });
for (const p of [mix, hidden]) {
await post(`/api/playlists/${p.id}/dumps/${id}`, { auth: owner.auth });
}
db.exec(`DELETE FROM notifications;`); // the follow notified the owner
const setPrivate = (isPrivate: boolean) =>
put(`/api/dumps/${id}`, { auth: owner.auth, json: { isPrivate } });
await setPrivate(false);
const notes = db.prepare(
`SELECT user_id, type, data FROM notifications ORDER BY type;`,
).all() as { user_id: string; type: string; data: string }[];
assertEquals(
notes.map((n) => [n.user_id, n.type, JSON.parse(n.data).playlistId]),
[
[listener.id, "playlist_dump_added", mix.id],
[fan.id, "user_dump_posted", undefined],
],
);
// Neither going private and public again nor an edit notifies twice.
await setPrivate(true);
await setPrivate(false);
await put(`/api/dumps/${id}`, { auth: owner.auth, json: { title: "New" } });
assertEquals(count(`SELECT COUNT(*) as n FROM notifications;`), 2);
});
test("PUT /api/dumps/:id: a file dump edits title, comment and privacy", async () => {
const owner = await createUser();
const { id } = insertDump({