import { createUser, db, insertComment, insertDump, insertPlaylist, test, } from "../helpers.ts"; import { assertEquals } from "@std/assert"; import { communityStatsOf, getCommunityFeed, getCommunityStats, listCommunityEvents, refreshCommunityStats, } from "../../services/community-service.ts"; const DAY = 86_400_000; const ago = (ms: number) => new Date(Date.now() - ms).toISOString(); const week = () => [new Date(Date.now() - 7 * DAY), new Date(Date.now() + 60_000)] as const; function vote(dumpId: string, n: number) { db.prepare(`UPDATE dumps SET vote_count = ? WHERE id = ?;`).run(n, dumpId); } test("communityStatsOf: the period's top public dumps, members, public playlists and totals", async () => { const ann = await createUser({ createdAt: ago(20 * DAY) }); const bob = await createUser({ invitedBy: ann.id, createdAt: ago(2 * DAY) }); const cy = await createUser({ createdAt: ago(DAY) }); const hit = insertDump({ userId: bob.id, title: "Hit" }); vote(hit.id, 5); const tie = insertDump({ userId: ann.id, title: "Tie", createdAt: ago(DAY) }); vote(tie.id, 2); const newer = insertDump({ userId: ann.id, title: "Newer" }); vote(newer.id, 2); insertDump({ userId: ann.id, title: "No votes" }); vote(insertDump({ userId: ann.id, title: "Private", isPrivate: true }).id, 9); vote( insertDump({ userId: ann.id, title: "Too old", createdAt: ago(10 * DAY) }) .id, 9, ); insertComment({ dumpId: hit.id, userId: cy.id, body: "nice" }); insertPlaylist({ userId: ann.id, title: "Small" }); const full = insertPlaylist({ userId: bob.id, title: "Full" }); insertPlaylist({ userId: bob.id, title: "Hidden", isPublic: false }); // Bob's private dump isn't counted: every reader gets the same figures. const secret = insertDump({ userId: bob.id, title: "Secret", isPrivate: true, }); db.prepare( `INSERT INTO playlist_dumps (playlist_id, dump_id, position, added_at) VALUES (?, ?, 0, ?), (?, ?, 1, ?), (?, ?, 2, ?);`, ).run( full.id, hit.id, ago(0), full.id, tie.id, ago(0), full.id, secret.id, ago(0), ); const stats = communityStatsOf(...week()); assertEquals(stats.top.map((d) => d.title), ["Hit", "Newer", "Tie"]); assertEquals(stats.top[0].ownerUsername, bob.username); assertEquals( stats.members.map((m) => [m.username, m.invitedByUsername]), [[bob.username, ann.username], [cy.username, undefined]], ); assertEquals( stats.playlists.map((p) => [p.title, p.dumpCount]), [["Full", 2], ["Small", 0]], ); assertEquals(stats.totals, { dumps: 4, upvotes: 0, comments: 1 }); assertEquals(stats.chatMessages, 0); }); test("getCommunityStats: serves the last refresh, of the week or the month", async () => { const ann = await createUser({ createdAt: ago(20 * DAY) }); vote( insertDump({ userId: ann.id, title: "Month", createdAt: ago(20 * DAY) }) .id, 1, ); refreshCommunityStats(); assertEquals(getCommunityStats("weekly").top, []); assertEquals(getCommunityStats("monthly").top.map((d) => d.title), [ "Month", ]); assertEquals(getCommunityStats("monthly").members.length, 1); vote( insertDump({ userId: ann.id, title: "Later", createdAt: ago(1000) }).id, 1, ); assertEquals(getCommunityStats("weekly").top, []); refreshCommunityStats(); assertEquals(getCommunityStats("weekly").top.map((d) => d.title), [ "Later", ]); }); function castVote(dumpId: string, userId: string, at: string) { db.prepare( `INSERT INTO votes (dump_id, user_id, created_at) VALUES (?, ?, ?);`, ).run(dumpId, userId, at); } test("listCommunityEvents: what was done in public, newest first, a dump's latest comment and upvote alone", async () => { const ann = await createUser({ createdAt: ago(9 * DAY) }); const bob = await createUser({ invitedBy: ann.id, createdAt: ago(8 * DAY) }); const hit = insertDump({ userId: ann.id, title: "Hit", createdAt: ago(7 * DAY), }); const secret = insertDump({ userId: ann.id, title: "Secret", isPrivate: true, }); insertComment({ dumpId: hit.id, userId: ann.id, createdAt: ago(6 * DAY) }); const last = insertComment({ dumpId: hit.id, userId: bob.id, createdAt: ago(5 * DAY), }); const gone = insertComment({ dumpId: hit.id, userId: ann.id }); db.prepare(`UPDATE comments SET deleted = 1 WHERE id = ?;`).run(gone); insertComment({ dumpId: secret.id, userId: ann.id }); castVote(hit.id, bob.id, ago(4 * DAY)); castVote(hit.id, ann.id, ago(3 * DAY)); castVote(secret.id, ann.id, ago(0)); const mix = insertPlaylist({ userId: bob.id, title: "Mix" }); insertPlaylist({ userId: bob.id, title: "Hidden", isPublic: false }); const events = listCommunityEvents(); assertEquals( events.map((e) => [e.type, e.username]), [ ["playlist_created", bob.username], ["dump_upvoted", ann.username], ["dump_commented", bob.username], ["dump_posted", ann.username], ["user_joined", bob.username], ["user_joined", ann.username], ], ); const [playlist, upvote, comment, , joined] = events; assertEquals(playlist.type === "playlist_created" && playlist.playlist, { title: "Mix", slug: mix.slug, ownerUsername: bob.username, }); assertEquals(upvote.type === "dump_upvoted" && upvote.dump, { title: "Hit", slug: hit.slug, ownerUsername: ann.username, }); assertEquals(upvote.id, `dump_upvoted:${hit.id}`); assertEquals(comment.type === "dump_commented" && comment.commentId, last); assertEquals( joined.type === "user_joined" && joined.invitedByUsername, ann.username, ); }); test("listCommunityEvents: keeps the 12 latest", async () => { const ann = await createUser({ createdAt: ago(DAY) }); for (let i = 0; i < 14; i++) { insertDump({ userId: ann.id, title: `Dump ${i}`, createdAt: ago(i * 1000), }); } const events = listCommunityEvents(); assertEquals(events.length, 12); assertEquals( events.map((e) => e.type === "dump_posted" && e.dump.title), Array.from({ length: 12 }, (_, i) => `Dump ${i}`), ); }); test("getCommunityFeed: the events as they are now, the week's figures as of the last refresh", async () => { const ann = await createUser({ createdAt: ago(20 * DAY) }); const hit = insertDump({ userId: ann.id, title: "Hit", createdAt: ago(1000), }); vote(hit.id, 3); refreshCommunityStats(); insertDump({ userId: ann.id, title: "Later" }); const feed = getCommunityFeed(); assertEquals( feed.events.map((e) => e.type === "dump_posted" && e.dump.title), ["Later", "Hit", false], ); assertEquals(feed.week.totals.dumps, 1); assertEquals(feed.week.top, { title: "Hit", slug: hit.slug, ownerUsername: ann.username, }); });