145 lines
5.3 KiB
TypeScript
145 lines
5.3 KiB
TypeScript
// src/tests/integration/notification.integration.test.ts
|
|
import { describe, it, expect, beforeAll, afterAll } from 'vitest';
|
|
import supertest from 'supertest';
|
|
import app from '../../../server';
|
|
import { createAndLoginUser } from '../utils/testHelpers';
|
|
import { cleanupDb } from '../utils/cleanup';
|
|
import type { UserProfile, Notification } from '../../types';
|
|
import { getPool } from '../../services/db/connection.db';
|
|
|
|
/**
|
|
* @vitest-environment node
|
|
*/
|
|
|
|
const request = supertest(app);
|
|
|
|
describe('Notification API Routes Integration Tests', () => {
|
|
let testUser: UserProfile;
|
|
let authToken: string;
|
|
const createdUserIds: string[] = [];
|
|
|
|
beforeAll(async () => {
|
|
// 1. Create a user for the tests
|
|
const { user, token } = await createAndLoginUser({
|
|
email: `notification-user-${Date.now()}@example.com`,
|
|
fullName: 'Notification Test User',
|
|
request,
|
|
});
|
|
testUser = user;
|
|
authToken = token;
|
|
createdUserIds.push(user.user.user_id);
|
|
|
|
// 2. Seed some notifications for this user directly in the DB for predictable testing
|
|
const notificationsToCreate = [
|
|
{ content: 'Your first unread notification', is_read: false },
|
|
{ content: 'Your second unread notification', is_read: false },
|
|
{ content: 'An old, read notification', is_read: true },
|
|
];
|
|
|
|
for (const n of notificationsToCreate) {
|
|
await getPool().query(
|
|
`INSERT INTO public.notifications (user_id, content, is_read, link_url)
|
|
VALUES ($1, $2, $3, '/dashboard')`,
|
|
[testUser.user.user_id, n.content, n.is_read],
|
|
);
|
|
}
|
|
});
|
|
|
|
afterAll(async () => {
|
|
// Notifications are deleted via CASCADE when the user is deleted.
|
|
await cleanupDb({
|
|
userIds: createdUserIds,
|
|
});
|
|
});
|
|
|
|
describe('GET /api/users/notifications', () => {
|
|
it('should fetch unread notifications for the authenticated user by default', async () => {
|
|
const response = await request
|
|
.get('/api/users/notifications')
|
|
.set('Authorization', `Bearer ${authToken}`);
|
|
|
|
expect(response.status).toBe(200);
|
|
const notifications: Notification[] = response.body;
|
|
expect(notifications).toHaveLength(2); // Only the two unread ones
|
|
expect(notifications.every((n) => !n.is_read)).toBe(true);
|
|
});
|
|
|
|
it('should fetch all notifications when includeRead=true', async () => {
|
|
const response = await request
|
|
.get('/api/users/notifications?includeRead=true')
|
|
.set('Authorization', `Bearer ${authToken}`);
|
|
|
|
expect(response.status).toBe(200);
|
|
const notifications: Notification[] = response.body;
|
|
expect(notifications).toHaveLength(3); // All three notifications
|
|
});
|
|
|
|
it('should respect pagination with limit and offset', async () => {
|
|
// Fetch with limit=1, should get the latest unread notification
|
|
const response1 = await request
|
|
.get('/api/users/notifications?limit=1')
|
|
.set('Authorization', `Bearer ${authToken}`);
|
|
|
|
expect(response1.status).toBe(200);
|
|
const notifications1: Notification[] = response1.body;
|
|
expect(notifications1).toHaveLength(1);
|
|
expect(notifications1[0].content).toBe('Your second unread notification'); // Assuming DESC order
|
|
|
|
// Fetch with limit=1 and offset=1, should get the older unread notification
|
|
const response2 = await request
|
|
.get('/api/users/notifications?limit=1&offset=1')
|
|
.set('Authorization', `Bearer ${authToken}`);
|
|
|
|
expect(response2.status).toBe(200);
|
|
const notifications2: Notification[] = response2.body;
|
|
expect(notifications2).toHaveLength(1);
|
|
expect(notifications2[0].content).toBe('Your first unread notification');
|
|
});
|
|
|
|
it('should return 401 if user is not authenticated', async () => {
|
|
const response = await request.get('/api/users/notifications');
|
|
expect(response.status).toBe(401);
|
|
});
|
|
});
|
|
|
|
describe('POST /api/users/notifications/:notificationId/mark-read', () => {
|
|
it('should mark a single notification as read', async () => {
|
|
const pool = getPool();
|
|
const unreadNotifRes = await pool.query(
|
|
`SELECT notification_id FROM public.notifications WHERE user_id = $1 AND is_read = false ORDER BY created_at ASC LIMIT 1`,
|
|
[testUser.user.user_id],
|
|
);
|
|
const notificationIdToMark = unreadNotifRes.rows[0].notification_id;
|
|
|
|
const response = await request
|
|
.post(`/api/users/notifications/${notificationIdToMark}/mark-read`)
|
|
.set('Authorization', `Bearer ${authToken}`);
|
|
|
|
expect(response.status).toBe(204);
|
|
|
|
// Verify in the database
|
|
const verifyRes = await pool.query(
|
|
`SELECT is_read FROM public.notifications WHERE notification_id = $1`,
|
|
[notificationIdToMark],
|
|
);
|
|
expect(verifyRes.rows[0].is_read).toBe(true);
|
|
});
|
|
});
|
|
|
|
describe('POST /api/users/notifications/mark-all-read', () => {
|
|
it('should mark all unread notifications as read', async () => {
|
|
const response = await request
|
|
.post('/api/users/notifications/mark-all-read')
|
|
.set('Authorization', `Bearer ${authToken}`);
|
|
|
|
expect(response.status).toBe(204);
|
|
|
|
// Verify in the database
|
|
const finalUnreadCountRes = await getPool().query(
|
|
`SELECT COUNT(*) FROM public.notifications WHERE user_id = $1 AND is_read = false`,
|
|
[testUser.user.user_id],
|
|
);
|
|
expect(Number(finalUnreadCountRes.rows[0].count)).toBe(0);
|
|
});
|
|
});
|
|
}); |