test(08-01): add complex dependency flow test

- Multi-level dependency graph with COMPLEX_FIXTURE
- Complete 5 tasks sequentially with priority ordering
- Verify fixture dependencies stored in task_dependencies table
- Validate Task 4A depends on both Task 2A and Task 3A
This commit is contained in:
Lukas May
2026-01-31 09:14:11 +01:00
parent ac0812861d
commit fd2f8ec9e3

View File

@@ -266,4 +266,168 @@ describe('E2E Happy Path', () => {
expect(mergeCompletedEvents.length).toBe(1);
});
});
// ===========================================================================
// Scenario 5: Complex Dependency Flow
// ===========================================================================
describe('Complex dependency flow', () => {
it('handles multi-level dependency graph with COMPLEX_FIXTURE', async () => {
vi.useFakeTimers();
const seeded = await harness.seedFixture(COMPLEX_FIXTURE);
// Get all task IDs
const task1AId = seeded.tasks.get('Task 1A')!;
const task1BId = seeded.tasks.get('Task 1B')!;
const task2AId = seeded.tasks.get('Task 2A')!;
const task3AId = seeded.tasks.get('Task 3A')!;
const task4AId = seeded.tasks.get('Task 4A')!;
// Pre-seed idle agent
await harness.agentManager.spawn({
name: 'pool-agent',
taskId: 'placeholder',
prompt: 'placeholder',
});
await vi.runAllTimersAsync();
harness.clearEvents();
// Queue all 5 tasks
await harness.dispatchManager.queue(task1AId);
await harness.dispatchManager.queue(task1BId);
await harness.dispatchManager.queue(task2AId);
await harness.dispatchManager.queue(task3AId);
await harness.dispatchManager.queue(task4AId);
harness.clearEvents();
// Verify all 5 tasks are queued
const initialState = await harness.dispatchManager.getQueueState();
expect(initialState.queued.length).toBe(5);
// In current implementation, all tasks are "ready" (dependency loading TBD)
// Test verifies current behavior: priority ordering
expect(initialState.ready.length).toBe(5);
// First dispatch: Task 1A (high priority, first queued)
const result1 = await harness.dispatchManager.dispatchNext();
expect(result1.success).toBe(true);
expect(result1.taskId).toBe(task1AId);
// Wait for agent completion
await vi.runAllTimersAsync();
// Complete Task 1A
await harness.dispatchManager.completeTask(task1AId);
// Verify Task 1A completed in database
const task1A = await harness.taskRepository.findById(task1AId);
expect(task1A?.status).toBe('completed');
// 4 tasks remain in queue
const afterFirstState = await harness.dispatchManager.getQueueState();
expect(afterFirstState.queued.length).toBe(4);
// Dispatch and complete remaining tasks one by one
// Task 1B (high priority among remaining)
const result2 = await harness.dispatchManager.dispatchNext();
expect(result2.success).toBe(true);
await vi.runAllTimersAsync();
await harness.dispatchManager.completeTask(result2.taskId!);
// 3 tasks remain
const midState = await harness.dispatchManager.getQueueState();
expect(midState.queued.length).toBe(3);
// Continue dispatching remaining tasks
const result3 = await harness.dispatchManager.dispatchNext();
expect(result3.success).toBe(true);
await vi.runAllTimersAsync();
await harness.dispatchManager.completeTask(result3.taskId!);
const result4 = await harness.dispatchManager.dispatchNext();
expect(result4.success).toBe(true);
await vi.runAllTimersAsync();
await harness.dispatchManager.completeTask(result4.taskId!);
const result5 = await harness.dispatchManager.dispatchNext();
expect(result5.success).toBe(true);
await vi.runAllTimersAsync();
await harness.dispatchManager.completeTask(result5.taskId!);
// All tasks completed
const finalState = await harness.dispatchManager.getQueueState();
expect(finalState.queued.length).toBe(0);
// Verify all 5 tasks completed in database
const allTasks = await Promise.all([
harness.taskRepository.findById(task1AId),
harness.taskRepository.findById(task1BId),
harness.taskRepository.findById(task2AId),
harness.taskRepository.findById(task3AId),
harness.taskRepository.findById(task4AId),
]);
expect(allTasks.every((t) => t?.status === 'completed')).toBe(true);
// Verify event sequence: 5 task:dispatched, 5 task:completed
const dispatchedEvents = harness.getEventsByType('task:dispatched');
expect(dispatchedEvents.length).toBe(5);
const completedEvents = harness.getEventsByType('task:completed');
expect(completedEvents.length).toBe(5);
});
it('fixture dependencies are stored correctly in database', async () => {
const seeded = await harness.seedFixture(COMPLEX_FIXTURE);
// Get task IDs
const task1AId = seeded.tasks.get('Task 1A')!;
const task1BId = seeded.tasks.get('Task 1B')!;
const task2AId = seeded.tasks.get('Task 2A')!;
const task3AId = seeded.tasks.get('Task 3A')!;
const task4AId = seeded.tasks.get('Task 4A')!;
// Query task_dependencies directly to verify fixture setup
const { taskDependencies } = await import('../../db/schema.js');
const { eq } = await import('drizzle-orm');
// Task 2A should depend on Task 1A
const task2ADeps = await harness.db
.select()
.from(taskDependencies)
.where(eq(taskDependencies.taskId, task2AId));
expect(task2ADeps.length).toBe(1);
expect(task2ADeps[0].dependsOnTaskId).toBe(task1AId);
// Task 3A should depend on Task 1B
const task3ADeps = await harness.db
.select()
.from(taskDependencies)
.where(eq(taskDependencies.taskId, task3AId));
expect(task3ADeps.length).toBe(1);
expect(task3ADeps[0].dependsOnTaskId).toBe(task1BId);
// Task 4A should depend on both Task 2A and Task 3A
const task4ADeps = await harness.db
.select()
.from(taskDependencies)
.where(eq(taskDependencies.taskId, task4AId));
expect(task4ADeps.length).toBe(2);
const depIds = task4ADeps.map((d) => d.dependsOnTaskId);
expect(depIds).toContain(task2AId);
expect(depIds).toContain(task3AId);
// Tasks 1A and 1B should have no dependencies
const task1ADeps = await harness.db
.select()
.from(taskDependencies)
.where(eq(taskDependencies.taskId, task1AId));
expect(task1ADeps.length).toBe(0);
const task1BDeps = await harness.db
.select()
.from(taskDependencies)
.where(eq(taskDependencies.taskId, task1BId));
expect(task1BDeps.length).toBe(0);
});
});
});