tududi/backend/tests/integration/notification-soft-delete.test.js
Chris 542be2c1e9
Fix bug 366 (#764)
* Optimize DB

* Clean up names

* fixup! Clean up names

* fixup! fixup! Clean up names
2026-01-07 18:18:07 +02:00

232 lines
8.2 KiB
JavaScript

const request = require('supertest');
const app = require('../../app');
const { User, Notification, Task } = require('../../models');
const { createTestUser } = require('../helpers/testUtils');
describe('Notification Soft Delete', () => {
let user, agent, task;
beforeEach(async () => {
user = await createTestUser({
email: `test_${Date.now()}@example.com`,
});
// Create authenticated agent
agent = request.agent(app);
await agent.post('/api/login').send({
email: user.email,
password: 'password123',
});
// Create a test task directly in database
task = await Task.create({
name: 'Test Task',
user_id: user.id,
status: 0,
});
});
describe('DELETE /api/notifications/:id - Soft Delete', () => {
it('should soft delete (dismiss) a notification', async () => {
// Create a notification
const notification = await Notification.createNotification({
userId: user.id,
type: 'task_due_soon',
title: 'Test Notification',
message: 'This is a test',
sources: [],
data: { taskUid: task.uid },
});
// Delete (dismiss) the notification
const deleteResponse = await agent.delete(
`/api/notifications/${notification.id}`
);
expect(deleteResponse.status).toBe(200);
expect(deleteResponse.body.message).toBe(
'Notification dismissed successfully'
);
// Verify the notification still exists in database but is dismissed
const dismissedNotification = await Notification.findByPk(
notification.id
);
expect(dismissedNotification).not.toBeNull();
expect(dismissedNotification.dismissed_at).not.toBeNull();
expect(dismissedNotification.isDismissed()).toBe(true);
});
it('should not allow dismissing an already dismissed notification', async () => {
// Create and dismiss a notification
const notification = await Notification.createNotification({
userId: user.id,
type: 'task_due_soon',
title: 'Test Notification',
message: 'This is a test',
sources: [],
data: { taskUid: task.uid },
});
await notification.dismiss();
// Try to dismiss again
const deleteResponse = await agent.delete(
`/api/notifications/${notification.id}`
);
expect(deleteResponse.status).toBe(404);
expect(deleteResponse.body.error).toBe('Notification not found');
});
it('should hide dismissed notifications from GET /api/notifications', async () => {
// Create two notifications
const notification1 = await Notification.createNotification({
userId: user.id,
type: 'task_due_soon',
title: 'Notification 1',
message: 'This is test 1',
sources: [],
});
const notification2 = await Notification.createNotification({
userId: user.id,
type: 'task_overdue',
title: 'Notification 2',
message: 'This is test 2',
sources: [],
});
// Dismiss the first notification
await agent.delete(`/api/notifications/${notification1.id}`);
// Get notifications
const getResponse = await agent.get('/api/notifications');
expect(getResponse.status).toBe(200);
expect(getResponse.body.total).toBe(1);
expect(getResponse.body.notifications.length).toBe(1);
expect(getResponse.body.notifications[0].id).toBe(notification2.id);
});
it('should exclude dismissed notifications from unread count', async () => {
// Create two unread notifications
const notification1 = await Notification.createNotification({
userId: user.id,
type: 'task_due_soon',
title: 'Notification 1',
message: 'This is test 1',
sources: [],
});
await Notification.createNotification({
userId: user.id,
type: 'task_overdue',
title: 'Notification 2',
message: 'This is test 2',
sources: [],
});
// Check unread count (should be 2)
let countResponse = await agent.get(
'/api/notifications/unread-count'
);
expect(countResponse.body.count).toBe(2);
// Dismiss one notification
await agent.delete(`/api/notifications/${notification1.id}`);
// Check unread count again (should be 1)
countResponse = await agent.get('/api/notifications/unread-count');
expect(countResponse.body.count).toBe(1);
});
it('should not recreate dismissed notifications in cron jobs', async () => {
// Update task with due date in the past
const dueDate = new Date(Date.now() - 1000 * 60 * 60); // 1 hour ago
await task.update({
due_date: dueDate,
});
// Run the due task service
const {
checkDueTasks,
} = require('../../modules/tasks/dueTaskService');
let result = await checkDueTasks();
// Should create 1 notification
expect(result.notificationsCreated).toBe(1);
// Get the notification
const notifications = await Notification.findAll({
where: { user_id: user.id },
});
expect(notifications.length).toBe(1);
const notification = notifications[0];
// Dismiss the notification
await notification.dismiss();
// Run the service again
result = await checkDueTasks();
// Should not create a new notification (dismissed one should be skipped)
expect(result.notificationsCreated).toBe(0);
// Verify only one notification exists (the dismissed one)
const allNotifications = await Notification.findAll({
where: { user_id: user.id },
});
expect(allNotifications.length).toBe(1);
expect(allNotifications[0].isDismissed()).toBe(true);
});
});
describe('Notification model - isDismissed and dismiss methods', () => {
it('should correctly identify dismissed notifications', async () => {
const notification = await Notification.createNotification({
userId: user.id,
type: 'task_due_soon',
title: 'Test',
message: 'Test',
sources: [],
});
// Reload from database to get actual values
await notification.reload();
expect(notification.dismissed_at).toBeNull();
expect(notification.isDismissed()).toBe(false);
await notification.dismiss();
expect(notification.isDismissed()).toBe(true);
expect(notification.dismissed_at).not.toBeNull();
});
it('should not change dismissed_at if already dismissed', async () => {
const notification = await Notification.createNotification({
userId: user.id,
type: 'task_due_soon',
title: 'Test',
message: 'Test',
sources: [],
});
await notification.dismiss();
const firstDismissedAt = notification.dismissed_at;
// Wait a bit
await new Promise((resolve) => setTimeout(resolve, 10));
// Try to dismiss again
await notification.dismiss();
// dismissed_at should be the same
expect(notification.dismissed_at.getTime()).toBe(
firstDismissedAt.getTime()
);
});
});
});