* Fix test issues * Fix uid issue * Fix id wrong param * Fix test issues * fixup! Fix test issues * fixup! fixup! Fix test issues * fixup! fixup! fixup! Fix test issues * fixup! fixup! fixup! fixup! Fix test issues
215 lines
7.4 KiB
JavaScript
215 lines
7.4 KiB
JavaScript
const request = require('supertest');
|
|
const app = require('../../app');
|
|
const { Task, sequelize } = require('../../models');
|
|
const { createTestUser } = require('../helpers/testUtils');
|
|
|
|
describe('Smart Recurring Task Deletion', () => {
|
|
let agent;
|
|
let user;
|
|
let parentTask;
|
|
|
|
beforeAll(async () => {
|
|
await sequelize.sync({ force: true });
|
|
});
|
|
|
|
beforeEach(async () => {
|
|
agent = request.agent(app);
|
|
user = await createTestUser();
|
|
|
|
// Login
|
|
await agent.post('/api/login').send({
|
|
email: user.email,
|
|
password: 'password123',
|
|
});
|
|
|
|
// Create a recurring parent task
|
|
parentTask = await Task.create({
|
|
name: 'Daily Exercise',
|
|
recurrence_type: 'daily',
|
|
recurrence_interval: 1,
|
|
user_id: user.id,
|
|
status: Task.STATUS.NOT_STARTED,
|
|
});
|
|
});
|
|
|
|
afterEach(async () => {
|
|
await sequelize.query('DELETE FROM tasks');
|
|
await sequelize.query('DELETE FROM users');
|
|
});
|
|
|
|
afterAll(async () => {
|
|
await sequelize.close();
|
|
});
|
|
|
|
it('should delete future instances and orphan past instances when deleting recurring parent', async () => {
|
|
const now = new Date();
|
|
|
|
// Create a completed past instance
|
|
const pastInstance = await Task.create({
|
|
name: 'Daily Exercise - Aug 15',
|
|
recurrence_type: 'none',
|
|
recurring_parent_id: parentTask.id,
|
|
user_id: user.id,
|
|
status: Task.STATUS.COMPLETED,
|
|
completed_at: new Date(now.getTime() - 24 * 60 * 60 * 1000), // Yesterday
|
|
due_date: new Date(now.getTime() - 24 * 60 * 60 * 1000),
|
|
});
|
|
|
|
// Create an in-progress instance
|
|
const inProgressInstance = await Task.create({
|
|
name: 'Daily Exercise - Today',
|
|
recurrence_type: 'none',
|
|
recurring_parent_id: parentTask.id,
|
|
user_id: user.id,
|
|
status: Task.STATUS.IN_PROGRESS,
|
|
due_date: now,
|
|
});
|
|
|
|
// Create future instances
|
|
const futureInstance1 = await Task.create({
|
|
name: 'Daily Exercise - Tomorrow',
|
|
recurrence_type: 'none',
|
|
recurring_parent_id: parentTask.id,
|
|
user_id: user.id,
|
|
status: Task.STATUS.NOT_STARTED,
|
|
due_date: new Date(now.getTime() + 24 * 60 * 60 * 1000), // Tomorrow
|
|
});
|
|
|
|
const futureInstance2 = await Task.create({
|
|
name: 'Daily Exercise - Day After',
|
|
recurrence_type: 'none',
|
|
recurring_parent_id: parentTask.id,
|
|
user_id: user.id,
|
|
status: Task.STATUS.NOT_STARTED,
|
|
due_date: new Date(now.getTime() + 2 * 24 * 60 * 60 * 1000), // Day after tomorrow
|
|
});
|
|
|
|
// Verify initial setup
|
|
const initialChildTasks = await Task.findAll({
|
|
where: { recurring_parent_id: parentTask.id },
|
|
});
|
|
expect(initialChildTasks).toHaveLength(4);
|
|
|
|
// Delete the parent task
|
|
const response = await agent.delete(`/api/task/${parentTask.uid}`);
|
|
|
|
expect(response.status).toBe(200);
|
|
expect(response.body.message).toBe('Task successfully deleted');
|
|
|
|
// Verify parent is deleted
|
|
const deletedParent = await Task.findByPk(parentTask.id);
|
|
expect(deletedParent).toBeNull();
|
|
|
|
// Verify future instances are deleted
|
|
const deletedFuture1 = await Task.findByPk(futureInstance1.id);
|
|
const deletedFuture2 = await Task.findByPk(futureInstance2.id);
|
|
expect(deletedFuture1).toBeNull();
|
|
expect(deletedFuture2).toBeNull();
|
|
|
|
// Verify past instances still exist but are orphaned
|
|
const orphanedPast = await Task.findByPk(pastInstance.id);
|
|
const orphanedInProgress = await Task.findByPk(inProgressInstance.id);
|
|
|
|
expect(orphanedPast).not.toBeNull();
|
|
expect(orphanedPast.recurring_parent_id).toBeNull();
|
|
|
|
expect(orphanedInProgress).not.toBeNull();
|
|
expect(orphanedInProgress.recurring_parent_id).toBeNull();
|
|
});
|
|
|
|
it('should handle edge case where all instances are future', async () => {
|
|
const now = new Date();
|
|
|
|
// Create only future instances
|
|
const futureInstance1 = await Task.create({
|
|
name: 'Future Task 1',
|
|
recurrence_type: 'none',
|
|
recurring_parent_id: parentTask.id,
|
|
user_id: user.id,
|
|
status: Task.STATUS.NOT_STARTED,
|
|
due_date: new Date(now.getTime() + 24 * 60 * 60 * 1000),
|
|
});
|
|
|
|
const futureInstance2 = await Task.create({
|
|
name: 'Future Task 2',
|
|
recurrence_type: 'none',
|
|
recurring_parent_id: parentTask.id,
|
|
user_id: user.id,
|
|
status: Task.STATUS.NOT_STARTED,
|
|
due_date: new Date(now.getTime() + 2 * 24 * 60 * 60 * 1000),
|
|
});
|
|
|
|
// Delete parent
|
|
const response = await agent.delete(`/api/task/${parentTask.uid}`);
|
|
|
|
expect(response.status).toBe(200);
|
|
|
|
// All instances should be deleted
|
|
const remainingTasks = await Task.findAll({
|
|
where: { recurring_parent_id: parentTask.id },
|
|
});
|
|
expect(remainingTasks).toHaveLength(0);
|
|
|
|
const deletedFuture1 = await Task.findByPk(futureInstance1.id);
|
|
const deletedFuture2 = await Task.findByPk(futureInstance2.id);
|
|
expect(deletedFuture1).toBeNull();
|
|
expect(deletedFuture2).toBeNull();
|
|
});
|
|
|
|
it('should handle edge case where all instances are past', async () => {
|
|
const now = new Date();
|
|
|
|
// Create only past instances
|
|
const pastInstance1 = await Task.create({
|
|
name: 'Past Task 1',
|
|
recurrence_type: 'none',
|
|
recurring_parent_id: parentTask.id,
|
|
user_id: user.id,
|
|
status: Task.STATUS.COMPLETED,
|
|
completed_at: new Date(now.getTime() - 2 * 24 * 60 * 60 * 1000),
|
|
due_date: new Date(now.getTime() - 2 * 24 * 60 * 60 * 1000),
|
|
});
|
|
|
|
const pastInstance2 = await Task.create({
|
|
name: 'Past Task 2',
|
|
recurrence_type: 'none',
|
|
recurring_parent_id: parentTask.id,
|
|
user_id: user.id,
|
|
status: Task.STATUS.IN_PROGRESS,
|
|
due_date: new Date(now.getTime() - 24 * 60 * 60 * 1000),
|
|
});
|
|
|
|
// Delete parent
|
|
const response = await agent.delete(`/api/task/${parentTask.uid}`);
|
|
|
|
expect(response.status).toBe(200);
|
|
|
|
// All instances should be orphaned but still exist
|
|
const orphanedTasks = await Task.findAll({
|
|
where: { id: [pastInstance1.id, pastInstance2.id] },
|
|
});
|
|
expect(orphanedTasks).toHaveLength(2);
|
|
|
|
orphanedTasks.forEach((task) => {
|
|
expect(task.recurring_parent_id).toBeNull();
|
|
});
|
|
});
|
|
|
|
it('should still work for non-recurring tasks (no child tasks)', async () => {
|
|
// Create a standalone task
|
|
const standaloneTask = await Task.create({
|
|
name: 'One-time Task',
|
|
recurrence_type: 'none',
|
|
user_id: user.id,
|
|
status: Task.STATUS.NOT_STARTED,
|
|
});
|
|
|
|
const response = await agent.delete(`/api/task/${standaloneTask.uid}`);
|
|
|
|
expect(response.status).toBe(200);
|
|
expect(response.body.message).toBe('Task successfully deleted');
|
|
|
|
const deletedTask = await Task.findByPk(standaloneTask.id);
|
|
expect(deletedTask).toBeNull();
|
|
});
|
|
});
|