Fix subtask completion auto-completing parent task (#792)
This commit is contained in:
parent
5dbf12be98
commit
d2a9a6dc5a
3 changed files with 30 additions and 59 deletions
|
|
@ -128,25 +128,7 @@ async function handleParentChildOnStatusChange(
|
|||
task.Subtasks = directSubtasksQuery;
|
||||
}
|
||||
|
||||
if (task.parent_task_id) {
|
||||
if (newStatus === Task.STATUS.DONE || newStatus === 'done') {
|
||||
const parentUpdated = await checkAndUpdateParentTaskCompletion(
|
||||
task.parent_task_id,
|
||||
userId
|
||||
);
|
||||
if (parentUpdated) {
|
||||
parentChildLogicExecuted = true;
|
||||
}
|
||||
} else if (oldStatus === Task.STATUS.DONE || oldStatus === 'done') {
|
||||
const parentUpdated = await undoneParentTaskIfNeeded(
|
||||
task.parent_task_id,
|
||||
userId
|
||||
);
|
||||
if (parentUpdated) {
|
||||
parentChildLogicExecuted = true;
|
||||
}
|
||||
}
|
||||
} else if (task.Subtasks && task.Subtasks.length > 0) {
|
||||
if (task.Subtasks && task.Subtasks.length > 0) {
|
||||
if (newStatus === Task.STATUS.DONE) {
|
||||
const subtasksUpdated = await completeAllSubtasks(task.id, userId);
|
||||
if (subtasksUpdated) {
|
||||
|
|
|
|||
|
|
@ -140,8 +140,8 @@ describe('Subtasks Completion Logic Integration', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('Subtask Completion Affects Parent Task', () => {
|
||||
it('should complete parent task when all subtasks are completed', async () => {
|
||||
describe('Subtask Completion Does Not Affect Parent Task', () => {
|
||||
it('should NOT auto-complete parent task when all subtasks are completed', async () => {
|
||||
// Create parent task
|
||||
const parentTask = await Task.create({
|
||||
name: 'Parent Task',
|
||||
|
|
@ -179,13 +179,13 @@ describe('Subtasks Completion Logic Integration', () => {
|
|||
const res2 = await toggleTaskCompletion(subtask2.id);
|
||||
expect(res2.status).toBe(200);
|
||||
|
||||
// Parent should now be completed
|
||||
// Parent should still be incomplete - user must manually complete it
|
||||
updatedParent = await Task.findByPk(parentTask.id);
|
||||
expect(updatedParent.status).toBe(Task.STATUS.DONE);
|
||||
expect(updatedParent.completed_at).not.toBeNull();
|
||||
expect(updatedParent.status).toBe(Task.STATUS.NOT_STARTED);
|
||||
expect(updatedParent.completed_at).toBeNull();
|
||||
});
|
||||
|
||||
it('should undone parent task when any subtask is undone', async () => {
|
||||
it('should NOT auto-undone parent task when a subtask is undone', async () => {
|
||||
// Create completed parent task
|
||||
const parentTask = await Task.create({
|
||||
name: 'Parent Task',
|
||||
|
|
@ -218,10 +218,10 @@ describe('Subtasks Completion Logic Integration', () => {
|
|||
let res = await toggleTaskCompletion(subtask1.id);
|
||||
expect(res.status).toBe(200);
|
||||
|
||||
// Parent should be undone
|
||||
// Parent should remain completed - user must manually undone it
|
||||
const updatedParent = await Task.findByPk(parentTask.id);
|
||||
expect(updatedParent.status).toBe(Task.STATUS.NOT_STARTED);
|
||||
expect(updatedParent.completed_at).toBeNull();
|
||||
expect(updatedParent.status).toBe(Task.STATUS.DONE);
|
||||
expect(updatedParent.completed_at).not.toBeNull();
|
||||
|
||||
// Other subtask should remain done
|
||||
const updatedSubtask2 = await Task.findByPk(subtask2.id);
|
||||
|
|
@ -249,7 +249,7 @@ describe('Subtasks Completion Logic Integration', () => {
|
|||
});
|
||||
|
||||
describe('Complex Completion Scenarios', () => {
|
||||
it('should handle mixed subtask states correctly', async () => {
|
||||
it('should handle mixed subtask states correctly without affecting parent', async () => {
|
||||
// Create parent task
|
||||
const parentTask = await Task.create({
|
||||
name: 'Parent Task',
|
||||
|
|
@ -296,12 +296,12 @@ describe('Subtasks Completion Logic Integration', () => {
|
|||
const res2 = await toggleTaskCompletion(subtask3.id);
|
||||
expect(res2.status).toBe(200);
|
||||
|
||||
// Parent should now be completed
|
||||
// Parent should still be incomplete - user must manually complete it
|
||||
updatedParent = await Task.findByPk(parentTask.id);
|
||||
expect(updatedParent.status).toBe(Task.STATUS.DONE);
|
||||
expect(updatedParent.status).toBe(Task.STATUS.NOT_STARTED);
|
||||
});
|
||||
|
||||
it('should handle rapid completion toggles correctly', async () => {
|
||||
it('should handle rapid completion toggles without affecting parent', async () => {
|
||||
// Create parent task
|
||||
const parentTask = await Task.create({
|
||||
name: 'Parent Task',
|
||||
|
|
@ -329,20 +329,9 @@ describe('Subtasks Completion Logic Integration', () => {
|
|||
const res3 = await toggleTaskCompletion(subtask.id);
|
||||
expect(res3.status).toBe(200);
|
||||
|
||||
// Final state should be consistent
|
||||
const updatedSubtask = await Task.findByPk(subtask.id);
|
||||
// Parent should remain unchanged regardless of subtask toggles
|
||||
const updatedParent = await Task.findByPk(parentTask.id);
|
||||
|
||||
// Final state should be consistent
|
||||
expect(
|
||||
updatedSubtask.status === Task.STATUS.DONE
|
||||
? updatedParent.status
|
||||
: updatedParent.status
|
||||
).toBe(
|
||||
updatedSubtask.status === Task.STATUS.DONE
|
||||
? Task.STATUS.DONE
|
||||
: Task.STATUS.NOT_STARTED
|
||||
);
|
||||
expect(updatedParent.status).toBe(Task.STATUS.NOT_STARTED);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -377,7 +366,7 @@ describe('Subtasks Completion Logic Integration', () => {
|
|||
expect(deletedSubtask).toBeNull();
|
||||
});
|
||||
|
||||
it('should handle concurrent completion updates', async () => {
|
||||
it('should handle concurrent completion updates without affecting parent', async () => {
|
||||
// Create parent task
|
||||
const parentTask = await Task.create({
|
||||
name: 'Parent Task',
|
||||
|
|
@ -414,9 +403,9 @@ describe('Subtasks Completion Logic Integration', () => {
|
|||
expect(result.status).toBe(200);
|
||||
});
|
||||
|
||||
// Final state should be consistent
|
||||
// Parent should remain unchanged - user must manually complete it
|
||||
const updatedParent = await Task.findByPk(parentTask.id);
|
||||
expect(updatedParent.status).toBe(Task.STATUS.DONE);
|
||||
expect(updatedParent.status).toBe(Task.STATUS.NOT_STARTED);
|
||||
});
|
||||
|
||||
it('should handle deleted parent task gracefully (FK constraints disabled)', async () => {
|
||||
|
|
@ -451,7 +440,7 @@ describe('Subtasks Completion Logic Integration', () => {
|
|||
});
|
||||
|
||||
describe('Performance Considerations', () => {
|
||||
it('should handle many subtasks efficiently', async () => {
|
||||
it('should handle many subtasks efficiently without affecting parent', async () => {
|
||||
// Create parent task
|
||||
const parentTask = await Task.create({
|
||||
name: 'Parent Task',
|
||||
|
|
@ -488,9 +477,9 @@ describe('Subtasks Completion Logic Integration', () => {
|
|||
// Should complete within reasonable time (adjust threshold as needed)
|
||||
expect(endTime - startTime).toBeLessThan(10000); // 10 seconds
|
||||
|
||||
// Parent should be completed
|
||||
// Parent should remain unchanged - user must manually complete it
|
||||
const updatedParent = await Task.findByPk(parentTask.id);
|
||||
expect(updatedParent.status).toBe(Task.STATUS.DONE);
|
||||
expect(updatedParent.status).toBe(Task.STATUS.NOT_STARTED);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -403,7 +403,7 @@ describe('Subtasks API', () => {
|
|||
});
|
||||
});
|
||||
|
||||
it('should complete parent when all subtasks are done', async () => {
|
||||
it('should NOT auto-complete parent when all subtasks are done', async () => {
|
||||
const parentTask = await Task.create({
|
||||
name: 'Parent Task',
|
||||
user_id: testUser.id,
|
||||
|
|
@ -439,13 +439,13 @@ describe('Subtasks API', () => {
|
|||
const res2 = await toggleTaskCompletion(subtask2.id);
|
||||
expect(res2.status).toBe(200);
|
||||
|
||||
// Parent should now be completed
|
||||
// Parent should still be not started - user must manually complete it
|
||||
updatedParent = await Task.findByPk(parentTask.id);
|
||||
expect(updatedParent.status).toBe(Task.STATUS.DONE);
|
||||
expect(updatedParent.completed_at).not.toBeNull();
|
||||
expect(updatedParent.status).toBe(Task.STATUS.NOT_STARTED);
|
||||
expect(updatedParent.completed_at).toBeNull();
|
||||
});
|
||||
|
||||
it('should undone parent when subtask is undone', async () => {
|
||||
it('should NOT auto-undone parent when subtask is undone', async () => {
|
||||
const parentTask = await Task.create({
|
||||
name: 'Parent Task',
|
||||
user_id: testUser.id,
|
||||
|
|
@ -476,10 +476,10 @@ describe('Subtasks API', () => {
|
|||
let res = await toggleTaskCompletion(subtask1.id);
|
||||
expect(res.status).toBe(200);
|
||||
|
||||
// Parent should be undone
|
||||
// Parent should remain completed - user must manually undone it
|
||||
const updatedParent = await Task.findByPk(parentTask.id);
|
||||
expect(updatedParent.status).toBe(Task.STATUS.NOT_STARTED);
|
||||
expect(updatedParent.completed_at).toBeNull();
|
||||
expect(updatedParent.status).toBe(Task.STATUS.DONE);
|
||||
expect(updatedParent.completed_at).not.toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue