101 lines
3.0 KiB
TypeScript
101 lines
3.0 KiB
TypeScript
import {
|
|
count,
|
|
createUser,
|
|
db,
|
|
get,
|
|
insertDump,
|
|
post,
|
|
put,
|
|
test,
|
|
} from "../helpers.ts";
|
|
import type {
|
|
DumpCommentedData,
|
|
DumpUpvotedData,
|
|
Notification,
|
|
} from "../../../shared/notification.ts";
|
|
import type { PaginatedData } from "../../../shared/api.ts";
|
|
import { assertEquals } from "@std/assert";
|
|
|
|
test("notifications: listed newest first, marked read one or all, never someone else's", async () => {
|
|
const me = await createUser();
|
|
const other = await createUser();
|
|
const { id: dumpId } = insertDump({ userId: me.id, title: "Mine" });
|
|
// Two notifications for me, one for other.
|
|
await put(`/api/me/follow/users/${me.id}`, { auth: other.auth });
|
|
await post(`/api/dumps/${dumpId}/comments`, {
|
|
auth: other.auth,
|
|
json: { body: "hey" },
|
|
});
|
|
await put(`/api/me/follow/users/${other.id}`, { auth: me.auth });
|
|
|
|
const list = await get<PaginatedData<Notification>>("/api/me/notifications", {
|
|
auth: me.auth,
|
|
});
|
|
assertEquals(list.data.items.length, 2);
|
|
assertEquals(
|
|
list.data.items.map((n) => n.type),
|
|
["dump_commented", "user_followed"],
|
|
);
|
|
assertEquals(
|
|
list.data.items.every((n) => !n.read),
|
|
true,
|
|
);
|
|
|
|
assertEquals(
|
|
(await post("/api/me/notifications/read", { auth: me.auth })).status,
|
|
204,
|
|
);
|
|
assertEquals(
|
|
count(
|
|
`SELECT COUNT(*) as n FROM notifications WHERE user_id = ? AND read = 0;`,
|
|
me.id,
|
|
),
|
|
0,
|
|
);
|
|
assertEquals((await get("/api/me/notifications")).status, 401);
|
|
});
|
|
|
|
test("notifications: names and item paths are joined when read, so they follow renames", async () => {
|
|
const me = await createUser();
|
|
const fan = await createUser();
|
|
const { id: dumpId } = insertDump({ userId: me.id, title: "Mine" });
|
|
await post(`/api/dumps/${dumpId}/comments`, {
|
|
auth: fan.auth,
|
|
json: { body: "nice" },
|
|
});
|
|
db.prepare(`UPDATE users SET username = 'fan_renamed' WHERE id = ?;`)
|
|
.run(fan.id);
|
|
db.prepare(`UPDATE dumps SET slug = 'mine-now' WHERE id = ?;`).run(dumpId);
|
|
const [note] = (await get<PaginatedData<Notification>>(
|
|
"/api/me/notifications",
|
|
{ auth: me.auth },
|
|
)).data.items;
|
|
assertEquals(note.type, "dump_commented");
|
|
const data = note.data as DumpCommentedData;
|
|
assertEquals(data.commenterUsername, "fan_renamed");
|
|
assertEquals([data.dumpOwnerUsername, data.dumpSlug], [
|
|
me.username,
|
|
"mine-now",
|
|
]);
|
|
// Once the dump is gone, the notification has no path to it.
|
|
db.prepare(`DELETE FROM dumps WHERE id = ?;`).run(dumpId);
|
|
db.prepare(
|
|
`INSERT INTO notifications (id, user_id, type, data, created_at) VALUES (?, ?, 'dump_upvoted', ?, ?);`,
|
|
).run(
|
|
crypto.randomUUID(),
|
|
me.id,
|
|
JSON.stringify({
|
|
voterId: fan.id,
|
|
voterUsername: "x",
|
|
dumpId,
|
|
dumpTitle: "Mine",
|
|
}),
|
|
new Date().toISOString(),
|
|
);
|
|
const [upvote] = (await get<PaginatedData<Notification>>(
|
|
"/api/me/notifications",
|
|
{ auth: me.auth },
|
|
)).data.items;
|
|
assertEquals((upvote.data as DumpUpvotedData).dumpSlug, undefined);
|
|
});
|