* 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
354 lines
13 KiB
JavaScript
354 lines
13 KiB
JavaScript
const request = require('supertest');
|
|
const app = require('../../app');
|
|
const { Task } = require('../../models');
|
|
const { createTestUser } = require('../helpers/testUtils');
|
|
|
|
describe('Subtask Ordering', () => {
|
|
let agent;
|
|
let testUser;
|
|
|
|
beforeEach(async () => {
|
|
await Task.destroy({ where: {}, truncate: true });
|
|
|
|
testUser = await createTestUser();
|
|
|
|
// Create authenticated agent
|
|
agent = request.agent(app);
|
|
await agent.post('/api/login').send({
|
|
email: testUser.email,
|
|
password: 'password123',
|
|
});
|
|
});
|
|
|
|
describe('Creating subtasks with order', () => {
|
|
it('should create subtasks with sequential order values', async () => {
|
|
const taskData = {
|
|
name: 'Parent Task',
|
|
subtasks: [
|
|
{ name: 'First Subtask', isNew: true },
|
|
{ name: 'Second Subtask', isNew: true },
|
|
{ name: 'Third Subtask', isNew: true },
|
|
{ name: 'Fourth Subtask', isNew: true },
|
|
],
|
|
};
|
|
|
|
const response = await agent
|
|
.post('/api/task')
|
|
.send(taskData)
|
|
.expect(201);
|
|
|
|
// Fetch subtasks from database directly
|
|
const subtasks = await Task.findAll({
|
|
where: { parent_task_id: response.body.id },
|
|
order: [['order', 'ASC']],
|
|
});
|
|
|
|
expect(subtasks).toHaveLength(4);
|
|
expect(subtasks[0].name).toBe('First Subtask');
|
|
expect(subtasks[0].order).toBe(1);
|
|
expect(subtasks[1].name).toBe('Second Subtask');
|
|
expect(subtasks[1].order).toBe(2);
|
|
expect(subtasks[2].name).toBe('Third Subtask');
|
|
expect(subtasks[2].order).toBe(3);
|
|
expect(subtasks[3].name).toBe('Fourth Subtask');
|
|
expect(subtasks[3].order).toBe(4);
|
|
});
|
|
|
|
it('should handle empty subtask names and maintain order', async () => {
|
|
const taskData = {
|
|
name: 'Parent Task',
|
|
subtasks: [
|
|
{ name: 'First', isNew: true },
|
|
{ name: '', isNew: true }, // Should be ignored
|
|
{ name: 'Second', isNew: true },
|
|
{ name: ' ', isNew: true }, // Should be ignored
|
|
{ name: 'Third', isNew: true },
|
|
],
|
|
};
|
|
|
|
const response = await agent
|
|
.post('/api/task')
|
|
.send(taskData)
|
|
.expect(201);
|
|
|
|
const subtasks = await Task.findAll({
|
|
where: { parent_task_id: response.body.id },
|
|
order: [['order', 'ASC']],
|
|
});
|
|
|
|
expect(subtasks).toHaveLength(3);
|
|
expect(subtasks[0].name).toBe('First');
|
|
expect(subtasks[0].order).toBe(1);
|
|
expect(subtasks[1].name).toBe('Second');
|
|
expect(subtasks[1].order).toBe(2);
|
|
expect(subtasks[2].name).toBe('Third');
|
|
expect(subtasks[2].order).toBe(3);
|
|
});
|
|
});
|
|
|
|
describe('Retrieving subtasks in order', () => {
|
|
it('should return subtasks in correct order via API', async () => {
|
|
// Create parent task with subtasks
|
|
const taskData = {
|
|
name: 'Parent Task',
|
|
subtasks: [
|
|
{ name: 'Zebra', isNew: true },
|
|
{ name: 'Apple', isNew: true },
|
|
{ name: 'Middle', isNew: true },
|
|
],
|
|
};
|
|
|
|
const createResponse = await agent
|
|
.post('/api/task')
|
|
.send(taskData)
|
|
.expect(201);
|
|
|
|
// Get subtasks via API
|
|
const response = await agent
|
|
.get(`/api/task/${createResponse.body.id}/subtasks`)
|
|
.expect(200);
|
|
|
|
expect(response.body).toHaveLength(3);
|
|
// Should be in creation order, not alphabetical
|
|
expect(response.body[0].name).toBe('Zebra');
|
|
expect(response.body[1].name).toBe('Apple');
|
|
expect(response.body[2].name).toBe('Middle');
|
|
});
|
|
|
|
it('should return subtasks in correct order when fetching parent task', async () => {
|
|
const taskData = {
|
|
name: 'Parent Task',
|
|
subtasks: [
|
|
{ name: 'Task 1', isNew: true },
|
|
{ name: 'Task 2', isNew: true },
|
|
{ name: 'Task 3', isNew: true },
|
|
],
|
|
};
|
|
|
|
const createResponse = await agent
|
|
.post('/api/task')
|
|
.send(taskData)
|
|
.expect(201);
|
|
|
|
// Fetch task with subtasks
|
|
const response = await agent
|
|
.get(`/api/task/${createResponse.body.uid}`)
|
|
.expect(200);
|
|
|
|
expect(response.body.subtasks).toHaveLength(3);
|
|
expect(response.body.subtasks[0].name).toBe('Task 1');
|
|
expect(response.body.subtasks[1].name).toBe('Task 2');
|
|
expect(response.body.subtasks[2].name).toBe('Task 3');
|
|
});
|
|
});
|
|
|
|
describe('Updating subtask order', () => {
|
|
it('should update order when subtasks are reordered', async () => {
|
|
// Create task with subtasks
|
|
const createResponse = await agent
|
|
.post('/api/task')
|
|
.send({
|
|
name: 'Parent Task',
|
|
subtasks: [
|
|
{ name: 'First', isNew: true },
|
|
{ name: 'Second', isNew: true },
|
|
{ name: 'Third', isNew: true },
|
|
],
|
|
})
|
|
.expect(201);
|
|
|
|
// Fetch subtasks
|
|
const subtasks = await Task.findAll({
|
|
where: { parent_task_id: createResponse.body.id },
|
|
order: [['order', 'ASC']],
|
|
});
|
|
|
|
// Reorder subtasks: Third, First, Second
|
|
const updateData = {
|
|
name: 'Parent Task',
|
|
subtasks: [
|
|
{ id: subtasks[2].id, name: 'Third' }, // Was 3rd, now 1st
|
|
{ id: subtasks[0].id, name: 'First' }, // Was 1st, now 2nd
|
|
{ id: subtasks[1].id, name: 'Second' }, // Was 2nd, now 3rd
|
|
],
|
|
};
|
|
|
|
await agent
|
|
.patch(`/api/task/${createResponse.body.uid}`)
|
|
.send(updateData)
|
|
.expect(200);
|
|
|
|
// Verify new order
|
|
const updatedSubtasks = await Task.findAll({
|
|
where: { parent_task_id: createResponse.body.id },
|
|
order: [['order', 'ASC']],
|
|
});
|
|
|
|
expect(updatedSubtasks[0].name).toBe('Third');
|
|
expect(updatedSubtasks[0].order).toBe(1);
|
|
expect(updatedSubtasks[1].name).toBe('First');
|
|
expect(updatedSubtasks[1].order).toBe(2);
|
|
expect(updatedSubtasks[2].name).toBe('Second');
|
|
expect(updatedSubtasks[2].order).toBe(3);
|
|
});
|
|
|
|
it('should maintain order when adding new subtasks to existing ones', async () => {
|
|
// Create task with 2 subtasks
|
|
const createResponse = await agent
|
|
.post('/api/task')
|
|
.send({
|
|
name: 'Parent Task',
|
|
subtasks: [
|
|
{ name: 'First', isNew: true },
|
|
{ name: 'Second', isNew: true },
|
|
],
|
|
})
|
|
.expect(201);
|
|
|
|
const existingSubtasks = await Task.findAll({
|
|
where: { parent_task_id: createResponse.body.id },
|
|
order: [['order', 'ASC']],
|
|
});
|
|
|
|
// Add new subtasks while keeping existing ones
|
|
const updateData = {
|
|
name: 'Parent Task',
|
|
subtasks: [
|
|
{ id: existingSubtasks[0].id, name: 'First' },
|
|
{ id: existingSubtasks[1].id, name: 'Second' },
|
|
{ name: 'Third', isNew: true },
|
|
{ name: 'Fourth', isNew: true },
|
|
],
|
|
};
|
|
|
|
await agent
|
|
.patch(`/api/task/${createResponse.body.uid}`)
|
|
.send(updateData)
|
|
.expect(200);
|
|
|
|
const allSubtasks = await Task.findAll({
|
|
where: { parent_task_id: createResponse.body.id },
|
|
order: [['order', 'ASC']],
|
|
});
|
|
|
|
expect(allSubtasks).toHaveLength(4);
|
|
expect(allSubtasks[0].name).toBe('First');
|
|
expect(allSubtasks[0].order).toBe(1);
|
|
expect(allSubtasks[1].name).toBe('Second');
|
|
expect(allSubtasks[1].order).toBe(2);
|
|
expect(allSubtasks[2].name).toBe('Third');
|
|
expect(allSubtasks[2].order).toBe(3);
|
|
expect(allSubtasks[3].name).toBe('Fourth');
|
|
expect(allSubtasks[3].order).toBe(4);
|
|
});
|
|
|
|
it('should handle deleting and reordering subtasks simultaneously', async () => {
|
|
// Create task with 4 subtasks
|
|
const createResponse = await agent
|
|
.post('/api/task')
|
|
.send({
|
|
name: 'Parent Task',
|
|
subtasks: [
|
|
{ name: 'First', isNew: true },
|
|
{ name: 'Second', isNew: true },
|
|
{ name: 'Third', isNew: true },
|
|
{ name: 'Fourth', isNew: true },
|
|
],
|
|
})
|
|
.expect(201);
|
|
|
|
const subtasks = await Task.findAll({
|
|
where: { parent_task_id: createResponse.body.id },
|
|
order: [['order', 'ASC']],
|
|
});
|
|
|
|
// Keep only 1st and 3rd, in reverse order
|
|
const updateData = {
|
|
name: 'Parent Task',
|
|
subtasks: [
|
|
{ id: subtasks[2].id, name: 'Third' },
|
|
{ id: subtasks[0].id, name: 'First' },
|
|
],
|
|
};
|
|
|
|
await agent
|
|
.patch(`/api/task/${createResponse.body.uid}`)
|
|
.send(updateData)
|
|
.expect(200);
|
|
|
|
const remainingSubtasks = await Task.findAll({
|
|
where: { parent_task_id: createResponse.body.id },
|
|
order: [['order', 'ASC']],
|
|
});
|
|
|
|
expect(remainingSubtasks).toHaveLength(2);
|
|
expect(remainingSubtasks[0].name).toBe('Third');
|
|
expect(remainingSubtasks[0].order).toBe(1);
|
|
expect(remainingSubtasks[1].name).toBe('First');
|
|
expect(remainingSubtasks[1].order).toBe(2);
|
|
});
|
|
});
|
|
|
|
describe('Edge cases', () => {
|
|
it('should handle many subtasks (100+) with correct ordering', async () => {
|
|
const subtasks = Array.from({ length: 100 }, (_, i) => ({
|
|
name: `Subtask ${i + 1}`,
|
|
isNew: true,
|
|
}));
|
|
|
|
const createResponse = await agent
|
|
.post('/api/task')
|
|
.send({
|
|
name: 'Parent Task',
|
|
subtasks,
|
|
})
|
|
.expect(201);
|
|
|
|
const createdSubtasks = await Task.findAll({
|
|
where: { parent_task_id: createResponse.body.id },
|
|
order: [['order', 'ASC']],
|
|
});
|
|
|
|
expect(createdSubtasks).toHaveLength(100);
|
|
createdSubtasks.forEach((subtask, index) => {
|
|
expect(subtask.name).toBe(`Subtask ${index + 1}`);
|
|
expect(subtask.order).toBe(index + 1);
|
|
});
|
|
});
|
|
|
|
it('should handle concurrent subtask creation correctly', async () => {
|
|
// Create parent task
|
|
const createResponse = await agent
|
|
.post('/api/task')
|
|
.send({ name: 'Parent Task' })
|
|
.expect(201);
|
|
|
|
// Add subtasks in multiple concurrent requests
|
|
const promises = [
|
|
agent.patch(`/api/task/${createResponse.body.uid}`).send({
|
|
name: 'Parent Task',
|
|
subtasks: [
|
|
{ name: 'Batch 1 - Task 1', isNew: true },
|
|
{ name: 'Batch 1 - Task 2', isNew: true },
|
|
],
|
|
}),
|
|
// Note: This test demonstrates the limitation - concurrent updates
|
|
// may result in inconsistent state. In practice, the UI prevents this.
|
|
];
|
|
|
|
await Promise.all(promises);
|
|
|
|
const subtasks = await Task.findAll({
|
|
where: { parent_task_id: createResponse.body.id },
|
|
order: [['order', 'ASC']],
|
|
});
|
|
|
|
// Verify all subtasks have order values
|
|
expect(subtasks.length).toBeGreaterThan(0);
|
|
subtasks.forEach((subtask) => {
|
|
expect(subtask.order).toBeDefined();
|
|
expect(subtask.order).toBeGreaterThan(0);
|
|
});
|
|
});
|
|
});
|
|
});
|