mirror of
https://github.com/Sendouc/sendou.ink.git
synced 2026-09-27 13:47:56 -05:00
Smarter notification mark as read (#3330)
This commit is contained in:
216
app/features/notifications/NotificationRepository.server.test.ts
Normal file
216
app/features/notifications/NotificationRepository.server.test.ts
Normal file
@@ -0,0 +1,216 @@
|
||||
import { beforeEach, describe, expect, test } from "vitest";
|
||||
import * as NotificationFactory from "~/db/seed/factories/NotificationFactory";
|
||||
import * as UserFactory from "~/db/seed/factories/UserFactory";
|
||||
import { withUserId } from "~/utils/Test";
|
||||
import * as NotificationRepository from "./NotificationRepository.server";
|
||||
|
||||
const users = UserFactory.pool();
|
||||
|
||||
const seenStatusOf = async (userId: number) => {
|
||||
const notifications = await NotificationRepository.findByUserId(userId);
|
||||
return notifications.map(({ type, seen }) => ({ type, seen }));
|
||||
};
|
||||
|
||||
describe("markAsSeenByType", () => {
|
||||
beforeEach(async () => {
|
||||
await users.create(3);
|
||||
});
|
||||
|
||||
test("marks unseen notification of the type as seen", async () => {
|
||||
await NotificationFactory.create({
|
||||
notification: { type: "SQ_READY_CHECK" },
|
||||
users: [{ userId: users.id(1) }],
|
||||
});
|
||||
|
||||
await NotificationRepository.markAsSeenByType({
|
||||
userIds: [users.id(1)],
|
||||
type: "SQ_READY_CHECK",
|
||||
});
|
||||
|
||||
expect(await seenStatusOf(users.id(1))).toEqual([
|
||||
{ type: "SQ_READY_CHECK", seen: 1 },
|
||||
]);
|
||||
});
|
||||
|
||||
test("leaves other users' notifications unseen", async () => {
|
||||
await NotificationFactory.create({
|
||||
notification: { type: "SQ_READY_CHECK" },
|
||||
users: [{ userId: users.id(1) }, { userId: users.id(2) }],
|
||||
});
|
||||
|
||||
await NotificationRepository.markAsSeenByType({
|
||||
userIds: [users.id(1)],
|
||||
type: "SQ_READY_CHECK",
|
||||
});
|
||||
|
||||
expect(await seenStatusOf(users.id(2))).toEqual([
|
||||
{ type: "SQ_READY_CHECK", seen: 0 },
|
||||
]);
|
||||
});
|
||||
|
||||
test("leaves notifications of other types unseen", async () => {
|
||||
await NotificationFactory.create({
|
||||
notification: { type: "SQ_READY_CHECK" },
|
||||
users: [{ userId: users.id(1) }],
|
||||
});
|
||||
await NotificationFactory.create({
|
||||
notification: { type: "SQ_NEW_MATCH", meta: { matchId: 1 } },
|
||||
users: [{ userId: users.id(1) }],
|
||||
});
|
||||
|
||||
await NotificationRepository.markAsSeenByType({
|
||||
userIds: [users.id(1)],
|
||||
type: "SQ_READY_CHECK",
|
||||
});
|
||||
|
||||
expect(await seenStatusOf(users.id(1))).toEqual([
|
||||
{ type: "SQ_NEW_MATCH", seen: 0 },
|
||||
{ type: "SQ_READY_CHECK", seen: 1 },
|
||||
]);
|
||||
});
|
||||
|
||||
test("meta filter only matches notifications with the given values", async () => {
|
||||
await NotificationFactory.create({
|
||||
notification: { type: "SQ_NEW_MATCH", meta: { matchId: 1 } },
|
||||
users: [{ userId: users.id(1) }],
|
||||
});
|
||||
await NotificationFactory.create({
|
||||
notification: { type: "SQ_NEW_MATCH", meta: { matchId: 2 } },
|
||||
users: [{ userId: users.id(1) }],
|
||||
});
|
||||
|
||||
await NotificationRepository.markAsSeenByType({
|
||||
userIds: [users.id(1)],
|
||||
type: "SQ_NEW_MATCH",
|
||||
meta: { matchId: 2 },
|
||||
});
|
||||
|
||||
expect(await seenStatusOf(users.id(1))).toEqual([
|
||||
{ type: "SQ_NEW_MATCH", seen: 1 },
|
||||
{ type: "SQ_NEW_MATCH", seen: 0 },
|
||||
]);
|
||||
});
|
||||
|
||||
test("matches on a subset of meta keys, both string and number valued", async () => {
|
||||
await NotificationFactory.create({
|
||||
notification: {
|
||||
type: "SCRIM_NEW_REQUEST",
|
||||
meta: { fromUserId: 1, fromUsername: "alice", scrimPostId: 7 },
|
||||
},
|
||||
users: [{ userId: users.id(1) }],
|
||||
});
|
||||
await NotificationFactory.create({
|
||||
notification: {
|
||||
type: "SCRIM_NEW_REQUEST",
|
||||
meta: { fromUserId: 2, fromUsername: "bob", scrimPostId: 7 },
|
||||
},
|
||||
users: [{ userId: users.id(1) }],
|
||||
});
|
||||
|
||||
await NotificationRepository.markAsSeenByType({
|
||||
userIds: [users.id(1)],
|
||||
type: "SCRIM_NEW_REQUEST",
|
||||
meta: { fromUsername: "alice" },
|
||||
});
|
||||
|
||||
expect(await seenStatusOf(users.id(1))).toEqual([
|
||||
{ type: "SCRIM_NEW_REQUEST", seen: 0 },
|
||||
{ type: "SCRIM_NEW_REQUEST", seen: 1 },
|
||||
]);
|
||||
});
|
||||
|
||||
test("marks the notification as seen for every given user", async () => {
|
||||
await NotificationFactory.create({
|
||||
notification: { type: "SQ_READY_CHECK" },
|
||||
users: [
|
||||
{ userId: users.id(1) },
|
||||
{ userId: users.id(2) },
|
||||
{ userId: users.id(3) },
|
||||
],
|
||||
});
|
||||
|
||||
await NotificationRepository.markAsSeenByType({
|
||||
userIds: [users.id(1), users.id(3)],
|
||||
type: "SQ_READY_CHECK",
|
||||
});
|
||||
|
||||
expect(await seenStatusOf(users.id(1))).toEqual([
|
||||
{ type: "SQ_READY_CHECK", seen: 1 },
|
||||
]);
|
||||
expect(await seenStatusOf(users.id(2))).toEqual([
|
||||
{ type: "SQ_READY_CHECK", seen: 0 },
|
||||
]);
|
||||
expect(await seenStatusOf(users.id(3))).toEqual([
|
||||
{ type: "SQ_READY_CHECK", seen: 1 },
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("markOwnAsSeen", () => {
|
||||
beforeEach(async () => {
|
||||
await users.create(2);
|
||||
});
|
||||
|
||||
test("returns the actor's id when a notification flips to seen", async () => {
|
||||
const notification = await NotificationFactory.create({
|
||||
notification: { type: "SQ_READY_CHECK" },
|
||||
users: [{ userId: users.id(1) }],
|
||||
});
|
||||
|
||||
const changedUserIds = await withUserId(users.id(1), () =>
|
||||
NotificationRepository.markOwnAsSeen([notification.id]),
|
||||
);
|
||||
|
||||
expect(changedUserIds).toEqual([users.id(1)]);
|
||||
expect(await seenStatusOf(users.id(1))).toEqual([
|
||||
{ type: "SQ_READY_CHECK", seen: 1 },
|
||||
]);
|
||||
});
|
||||
|
||||
test("returns no user ids when the notifications were already seen", async () => {
|
||||
const notification = await NotificationFactory.create({
|
||||
notification: { type: "SQ_READY_CHECK" },
|
||||
users: [{ userId: users.id(1), seen: 1 }],
|
||||
});
|
||||
|
||||
const changedUserIds = await withUserId(users.id(1), () =>
|
||||
NotificationRepository.markOwnAsSeen([notification.id]),
|
||||
);
|
||||
|
||||
expect(changedUserIds).toEqual([]);
|
||||
});
|
||||
|
||||
test("returns the actor's id once even if many notifications flip", async () => {
|
||||
const notifications = await Promise.all([
|
||||
NotificationFactory.create({
|
||||
notification: { type: "SQ_READY_CHECK" },
|
||||
users: [{ userId: users.id(1) }],
|
||||
}),
|
||||
NotificationFactory.create({
|
||||
notification: { type: "SQ_NEW_MATCH", meta: { matchId: 1 } },
|
||||
users: [{ userId: users.id(1) }],
|
||||
}),
|
||||
]);
|
||||
|
||||
const changedUserIds = await withUserId(users.id(1), () =>
|
||||
NotificationRepository.markOwnAsSeen(notifications.map(({ id }) => id)),
|
||||
);
|
||||
|
||||
expect(changedUserIds).toEqual([users.id(1)]);
|
||||
});
|
||||
|
||||
test("leaves another user's copy of the notification unseen", async () => {
|
||||
const notification = await NotificationFactory.create({
|
||||
notification: { type: "SQ_READY_CHECK" },
|
||||
users: [{ userId: users.id(1) }, { userId: users.id(2) }],
|
||||
});
|
||||
|
||||
await withUserId(users.id(1), () =>
|
||||
NotificationRepository.markOwnAsSeen([notification.id]),
|
||||
);
|
||||
|
||||
expect(await seenStatusOf(users.id(2))).toEqual([
|
||||
{ type: "SQ_READY_CHECK", seen: 0 },
|
||||
]);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user