import { expect, test } from "@playwright/test"; import { prisma } from "@rallly/database"; import dayjs from "dayjs"; /** * This test suite tests the house-keeping API endpoints: * 1. delete-inactive-polls: Marks inactive polls as deleted * 2. remove-deleted-polls: Permanently removes polls that have been marked as deleted for more than 7 days */ test.describe("House-keeping API", () => { // Store created poll IDs for cleanup const createdPollIds: string[] = []; const createdUserIds: string[] = []; // API Secret for authentication const API_SECRET = process.env.API_SECRET; test.beforeAll(async () => { // Clean up any existing test data await cleanup(); }); test.afterAll(async () => { // Clean up test data await cleanup(); }); async function cleanup() { // Delete test users - related polls will be deleted automatically due to cascade if (createdUserIds.length > 0) { await prisma.user.deleteMany({ where: { id: { in: createdUserIds, }, }, }); createdUserIds.length = 0; } // Delete polls that don't have a user (not covered by cascade delete) if (createdPollIds.length > 0) { await prisma.poll.deleteMany({ where: { id: { in: createdPollIds, }, userId: null, }, }); createdPollIds.length = 0; } } test("should mark inactive polls as deleted for users without active subscriptions", async ({ request, baseURL, }) => { // Create test users const regularUser = await prisma.user.create({ data: { name: "Regular User", email: "regular-user@example.com", }, }); createdUserIds.push(regularUser.id); const proUser = await prisma.user.create({ data: { name: "Pro User", email: "pro-user@example.com", subscription: { create: { id: "sub_test_pro", priceId: "price_test", amount: 1000, status: "active", active: true, currency: "USD", interval: "month", periodStart: new Date(), periodEnd: dayjs().add(30, "day").toDate(), }, }, }, }); createdUserIds.push(proUser.id); // Create test polls // 1. Old poll from regular user (should be marked as deleted) const oldPollRegularUser = await prisma.poll.create({ data: { id: "old-poll-regular-user", title: "Old Poll Regular User", participantUrlId: "old-poll-regular-user-participant", adminUrlId: "old-poll-regular-user-admin", userId: regularUser.id, touchedAt: dayjs().subtract(35, "day").toDate(), // 35 days old }, }); createdPollIds.push(oldPollRegularUser.id); // 2. Old poll from pro user (should NOT be marked as deleted) const oldPollProUser = await prisma.poll.create({ data: { id: "old-poll-pro-user", title: "Old Poll Pro User", participantUrlId: "old-poll-pro-user-participant", adminUrlId: "old-poll-pro-user-admin", userId: proUser.id, touchedAt: dayjs().subtract(35, "day").toDate(), // 35 days old }, }); createdPollIds.push(oldPollProUser.id); // 3. Recent poll from regular user (should NOT be marked as deleted) const recentPollRegularUser = await prisma.poll.create({ data: { id: "recent-poll-regular-user", title: "Recent Poll Regular User", participantUrlId: "recent-poll-regular-user-participant", adminUrlId: "recent-poll-regular-user-admin", userId: regularUser.id, touchedAt: dayjs().subtract(15, "day").toDate(), // 15 days old }, }); createdPollIds.push(recentPollRegularUser.id); // 4. Old poll with future options from regular user (should NOT be marked as deleted) const oldPollWithFutureOptions = await prisma.poll.create({ data: { id: "old-poll-with-future-options", title: "Old Poll With Future Options", participantUrlId: "old-poll-with-future-options-participant", adminUrlId: "old-poll-with-future-options-admin", userId: regularUser.id, touchedAt: dayjs().subtract(35, "day").toDate(), // 35 days old options: { create: { startTime: dayjs().add(10, "day").toDate(), // Future date duration: 60, }, }, }, }); createdPollIds.push(oldPollWithFutureOptions.id); // 5. Old poll without a user (should be marked as deleted) const oldPollNoUser = await prisma.poll.create({ data: { id: "old-poll-no-user", title: "Old Poll No User", participantUrlId: "old-poll-no-user-participant", adminUrlId: "old-poll-no-user-admin", touchedAt: dayjs().subtract(35, "day").toDate(), // 35 days old }, }); createdPollIds.push(oldPollNoUser.id); // 6. Old poll with recent views from regular user (should NOT be marked as deleted) const oldPollWithRecentViews = await prisma.poll.create({ data: { id: "old-poll-with-recent-views", title: "Old Poll With Recent Views", participantUrlId: "old-poll-with-recent-views-participant", adminUrlId: "old-poll-with-recent-views-admin", userId: regularUser.id, touchedAt: dayjs().subtract(35, "day").toDate(), // 35 days old views: { create: { viewedAt: dayjs().subtract(15, "day").toDate(), // Viewed 15 days ago }, }, }, }); createdPollIds.push(oldPollWithRecentViews.id); // Call the delete-inactive-polls endpoint const response = await request.post( `${baseURL}/api/house-keeping/delete-inactive-polls`, { headers: { Authorization: `Bearer ${API_SECRET}`, }, }, ); expect(response.ok()).toBeTruthy(); const responseData = await response.json(); expect(responseData.success).toBeTruthy(); // We expect 2 polls to be marked as deleted: // - Old poll from regular user // - Old poll without a user expect(responseData.summary.markedDeleted).toBe(2); // Verify the state of each poll const updatedOldPollRegularUser = await prisma.poll.findUnique({ where: { id: oldPollRegularUser.id }, }); expect(updatedOldPollRegularUser?.deleted).toBe(true); expect(updatedOldPollRegularUser?.deletedAt).not.toBeNull(); const updatedOldPollProUser = await prisma.poll.findUnique({ where: { id: oldPollProUser.id }, }); expect(updatedOldPollProUser?.deleted).toBe(false); expect(updatedOldPollProUser?.deletedAt).toBeNull(); const updatedRecentPollRegularUser = await prisma.poll.findUnique({ where: { id: recentPollRegularUser.id }, }); expect(updatedRecentPollRegularUser?.deleted).toBe(false); expect(updatedRecentPollRegularUser?.deletedAt).toBeNull(); const updatedOldPollWithFutureOptions = await prisma.poll.findUnique({ where: { id: oldPollWithFutureOptions.id }, }); expect(updatedOldPollWithFutureOptions?.deleted).toBe(false); expect(updatedOldPollWithFutureOptions?.deletedAt).toBeNull(); const updatedOldPollNoUser = await prisma.poll.findUnique({ where: { id: oldPollNoUser.id }, }); expect(updatedOldPollNoUser?.deleted).toBe(true); expect(updatedOldPollNoUser?.deletedAt).not.toBeNull(); const updatedOldPollWithRecentViews = await prisma.poll.findUnique({ where: { id: oldPollWithRecentViews.id }, }); expect(updatedOldPollWithRecentViews?.deleted).toBe(false); expect(updatedOldPollWithRecentViews?.deletedAt).toBeNull(); }); test("should permanently remove polls that have been marked as deleted for more than 7 days", async ({ request, baseURL, }) => { // Create a poll that was marked as deleted more than 7 days ago const oldDeletedPoll = await prisma.poll.create({ data: { id: "old-deleted-poll", title: "Old Deleted Poll", participantUrlId: "old-deleted-poll-participant", adminUrlId: "old-deleted-poll-admin", deleted: true, deletedAt: dayjs().subtract(8, "day").toDate(), // Deleted 8 days ago }, }); createdPollIds.push(oldDeletedPoll.id); // Create a poll that was marked as deleted less than 7 days ago const recentDeletedPoll = await prisma.poll.create({ data: { id: "recent-deleted-poll", title: "Recent Deleted Poll", participantUrlId: "recent-deleted-poll-participant", adminUrlId: "recent-deleted-poll-admin", deleted: true, deletedAt: dayjs().subtract(3, "day").toDate(), // Deleted 3 days ago }, }); createdPollIds.push(recentDeletedPoll.id); // Call the remove-deleted-polls endpoint const response = await request.post( `${baseURL}/api/house-keeping/remove-deleted-polls`, { headers: { Authorization: `Bearer ${API_SECRET}`, }, data: {}, // Empty JSON body }, ); expect(response.ok()).toBeTruthy(); const responseData = await response.json(); expect(responseData.success).toBeTruthy(); // We expect 1 poll to be permanently deleted expect(responseData.summary.deleted.polls).toBe(1); // Verify that the old deleted poll is gone const checkOldDeletedPoll = await prisma.poll.findUnique({ where: { id: oldDeletedPoll.id }, }); expect(checkOldDeletedPoll).toBeNull(); // Verify that the recent deleted poll still exists const checkRecentDeletedPoll = await prisma.poll.findUnique({ where: { id: recentDeletedPoll.id }, }); expect(checkRecentDeletedPoll).not.toBeNull(); expect(checkRecentDeletedPoll?.deleted).toBe(true); }); });