test(08.1-02): update tests to use schema-aligned scenarios
- Update MockAgentManager tests to use status-based scenarios - Change outcome:'crash' to status:'unrecoverable_error' with error field - Change outcome:'waiting_for_input' to status:'question' with question field - Change outcome:'success' to status:'done' with result field - Add tests for structured question data (options, multiSelect) - Add tests for getPendingQuestion and resume clearing pending question - Update E2E edge-cases tests with new scenario format - Update harness tests with new scenario format
This commit is contained in:
@@ -129,9 +129,9 @@ describe('MockAgentManager', () => {
|
|||||||
describe('spawn with configured delay', () => {
|
describe('spawn with configured delay', () => {
|
||||||
it('should not complete before delay expires', async () => {
|
it('should not complete before delay expires', async () => {
|
||||||
manager.setScenario('delayed-agent', {
|
manager.setScenario('delayed-agent', {
|
||||||
outcome: 'success',
|
status: 'done',
|
||||||
delay: 100,
|
delay: 100,
|
||||||
message: 'Delayed completion',
|
result: 'Delayed completion',
|
||||||
});
|
});
|
||||||
|
|
||||||
const agent = await manager.spawn({
|
const agent = await manager.spawn({
|
||||||
@@ -149,9 +149,9 @@ describe('MockAgentManager', () => {
|
|||||||
|
|
||||||
it('should complete after delay expires', async () => {
|
it('should complete after delay expires', async () => {
|
||||||
manager.setScenario('delayed-agent', {
|
manager.setScenario('delayed-agent', {
|
||||||
outcome: 'success',
|
status: 'done',
|
||||||
delay: 100,
|
delay: 100,
|
||||||
message: 'Delayed completion',
|
result: 'Delayed completion',
|
||||||
});
|
});
|
||||||
|
|
||||||
const agent = await manager.spawn({
|
const agent = await manager.spawn({
|
||||||
@@ -175,12 +175,12 @@ describe('MockAgentManager', () => {
|
|||||||
// spawn() with crash scenario
|
// spawn() with crash scenario
|
||||||
// ===========================================================================
|
// ===========================================================================
|
||||||
|
|
||||||
describe('spawn with crash scenario', () => {
|
describe('spawn with unrecoverable_error scenario', () => {
|
||||||
it('should emit agent:crashed and set result.success=false', async () => {
|
it('should emit agent:crashed and set result.success=false', async () => {
|
||||||
manager.setScenario('crash-agent', {
|
manager.setScenario('crash-agent', {
|
||||||
outcome: 'crash',
|
status: 'unrecoverable_error',
|
||||||
delay: 0,
|
delay: 0,
|
||||||
message: 'Something went terribly wrong',
|
error: 'Something went terribly wrong',
|
||||||
});
|
});
|
||||||
|
|
||||||
const agent = await manager.spawn({
|
const agent = await manager.spawn({
|
||||||
@@ -208,13 +208,13 @@ describe('MockAgentManager', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// ===========================================================================
|
// ===========================================================================
|
||||||
// spawn() with waiting_for_input scenario
|
// spawn() with question scenario
|
||||||
// ===========================================================================
|
// ===========================================================================
|
||||||
|
|
||||||
describe('spawn with waiting_for_input scenario', () => {
|
describe('spawn with question scenario', () => {
|
||||||
it('should emit agent:waiting and set status to waiting_for_input', async () => {
|
it('should emit agent:waiting and set status to waiting_for_input', async () => {
|
||||||
manager.setScenario('waiting-agent', {
|
manager.setScenario('waiting-agent', {
|
||||||
outcome: 'waiting_for_input',
|
status: 'question',
|
||||||
delay: 0,
|
delay: 0,
|
||||||
question: 'Should I continue?',
|
question: 'Should I continue?',
|
||||||
});
|
});
|
||||||
@@ -242,10 +242,10 @@ describe('MockAgentManager', () => {
|
|||||||
// resume() after waiting_for_input
|
// resume() after waiting_for_input
|
||||||
// ===========================================================================
|
// ===========================================================================
|
||||||
|
|
||||||
describe('resume after waiting_for_input', () => {
|
describe('resume after question', () => {
|
||||||
it('should emit agent:resumed and continue with scenario', async () => {
|
it('should emit agent:resumed and continue with scenario', async () => {
|
||||||
manager.setScenario('resume-agent', {
|
manager.setScenario('resume-agent', {
|
||||||
outcome: 'waiting_for_input',
|
status: 'question',
|
||||||
delay: 0,
|
delay: 0,
|
||||||
question: 'Need your input',
|
question: 'Need your input',
|
||||||
});
|
});
|
||||||
@@ -311,9 +311,9 @@ describe('MockAgentManager', () => {
|
|||||||
describe('stop', () => {
|
describe('stop', () => {
|
||||||
it('should cancel scheduled completion and emit agent:stopped', async () => {
|
it('should cancel scheduled completion and emit agent:stopped', async () => {
|
||||||
manager.setScenario('stoppable-agent', {
|
manager.setScenario('stoppable-agent', {
|
||||||
outcome: 'success',
|
status: 'done',
|
||||||
delay: 1000,
|
delay: 1000,
|
||||||
message: 'Should not see this',
|
result: 'Should not see this',
|
||||||
});
|
});
|
||||||
|
|
||||||
const agent = await manager.spawn({
|
const agent = await manager.spawn({
|
||||||
@@ -411,11 +411,11 @@ describe('MockAgentManager', () => {
|
|||||||
|
|
||||||
describe('setScenario overrides', () => {
|
describe('setScenario overrides', () => {
|
||||||
it('should use scenario override for specific agent name', async () => {
|
it('should use scenario override for specific agent name', async () => {
|
||||||
// Set crash scenario for one agent
|
// Set unrecoverable_error scenario for one agent
|
||||||
manager.setScenario('crasher', {
|
manager.setScenario('crasher', {
|
||||||
outcome: 'crash',
|
status: 'unrecoverable_error',
|
||||||
delay: 0,
|
delay: 0,
|
||||||
message: 'Intentional crash',
|
error: 'Intentional crash',
|
||||||
});
|
});
|
||||||
|
|
||||||
// Spawn two agents - one with override, one with default
|
// Spawn two agents - one with override, one with default
|
||||||
@@ -443,8 +443,9 @@ describe('MockAgentManager', () => {
|
|||||||
|
|
||||||
it('should allow clearing scenario override', async () => {
|
it('should allow clearing scenario override', async () => {
|
||||||
manager.setScenario('flip-flop', {
|
manager.setScenario('flip-flop', {
|
||||||
outcome: 'crash',
|
status: 'unrecoverable_error',
|
||||||
delay: 0,
|
delay: 0,
|
||||||
|
error: 'Crash for test',
|
||||||
});
|
});
|
||||||
|
|
||||||
// First spawn crashes
|
// First spawn crashes
|
||||||
@@ -489,7 +490,7 @@ describe('MockAgentManager', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should emit spawned before crashed', async () => {
|
it('should emit spawned before crashed', async () => {
|
||||||
manager.setScenario('crash-order', { outcome: 'crash', delay: 0 });
|
manager.setScenario('crash-order', { status: 'unrecoverable_error', delay: 0, error: 'Crash' });
|
||||||
await manager.spawn({ name: 'crash-order', taskId: 't1', prompt: 'p1' });
|
await manager.spawn({ name: 'crash-order', taskId: 't1', prompt: 'p1' });
|
||||||
await vi.advanceTimersByTimeAsync(0);
|
await vi.advanceTimersByTimeAsync(0);
|
||||||
|
|
||||||
@@ -503,8 +504,9 @@ describe('MockAgentManager', () => {
|
|||||||
|
|
||||||
it('should emit spawned before waiting', async () => {
|
it('should emit spawned before waiting', async () => {
|
||||||
manager.setScenario('wait-order', {
|
manager.setScenario('wait-order', {
|
||||||
outcome: 'waiting_for_input',
|
status: 'question',
|
||||||
delay: 0,
|
delay: 0,
|
||||||
|
question: 'Test question',
|
||||||
});
|
});
|
||||||
await manager.spawn({ name: 'wait-order', taskId: 't1', prompt: 'p1' });
|
await manager.spawn({ name: 'wait-order', taskId: 't1', prompt: 'p1' });
|
||||||
await vi.advanceTimersByTimeAsync(0);
|
await vi.advanceTimersByTimeAsync(0);
|
||||||
@@ -551,9 +553,9 @@ describe('MockAgentManager', () => {
|
|||||||
|
|
||||||
it('should use provided default scenario', async () => {
|
it('should use provided default scenario', async () => {
|
||||||
const customDefault: MockAgentScenario = {
|
const customDefault: MockAgentScenario = {
|
||||||
outcome: 'crash',
|
status: 'unrecoverable_error',
|
||||||
delay: 0,
|
delay: 0,
|
||||||
message: 'Default crash',
|
error: 'Default crash',
|
||||||
};
|
};
|
||||||
|
|
||||||
const customManager = new MockAgentManager({
|
const customManager = new MockAgentManager({
|
||||||
@@ -580,7 +582,7 @@ describe('MockAgentManager', () => {
|
|||||||
|
|
||||||
describe('clear', () => {
|
describe('clear', () => {
|
||||||
it('should remove all agents and cancel pending timers', async () => {
|
it('should remove all agents and cancel pending timers', async () => {
|
||||||
manager.setScenario('pending', { outcome: 'success', delay: 1000 });
|
manager.setScenario('pending', { status: 'done', delay: 1000 });
|
||||||
|
|
||||||
await manager.spawn({ name: 'pending', taskId: 't1', prompt: 'p1' });
|
await manager.spawn({ name: 'pending', taskId: 't1', prompt: 'p1' });
|
||||||
await manager.spawn({ name: 'another', taskId: 't2', prompt: 'p2' });
|
await manager.spawn({ name: 'another', taskId: 't2', prompt: 'p2' });
|
||||||
@@ -592,4 +594,83 @@ describe('MockAgentManager', () => {
|
|||||||
expect((await manager.list()).length).toBe(0);
|
expect((await manager.list()).length).toBe(0);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// ===========================================================================
|
||||||
|
// Structured question data (new schema tests)
|
||||||
|
// ===========================================================================
|
||||||
|
|
||||||
|
describe('structured question data', () => {
|
||||||
|
it('emits agent:waiting with structured question data', async () => {
|
||||||
|
manager.setScenario('test-agent', {
|
||||||
|
status: 'question',
|
||||||
|
question: 'Which database?',
|
||||||
|
options: [
|
||||||
|
{ label: 'PostgreSQL', description: 'Full-featured' },
|
||||||
|
{ label: 'SQLite', description: 'Lightweight' },
|
||||||
|
],
|
||||||
|
multiSelect: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
await manager.spawn({ name: 'test-agent', taskId: 'task-1', prompt: 'test' });
|
||||||
|
await vi.runAllTimersAsync();
|
||||||
|
|
||||||
|
const events = eventBus.emittedEvents.filter((e) => e.type === 'agent:waiting');
|
||||||
|
expect(events.length).toBe(1);
|
||||||
|
expect((events[0] as any).payload.options).toHaveLength(2);
|
||||||
|
expect((events[0] as any).payload.options[0].label).toBe('PostgreSQL');
|
||||||
|
expect((events[0] as any).payload.multiSelect).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('stores pending question for retrieval', async () => {
|
||||||
|
manager.setScenario('test-agent', {
|
||||||
|
status: 'question',
|
||||||
|
question: 'Which database?',
|
||||||
|
options: [{ label: 'PostgreSQL' }],
|
||||||
|
});
|
||||||
|
|
||||||
|
const agent = await manager.spawn({ name: 'test-agent', taskId: 'task-1', prompt: 'test' });
|
||||||
|
await vi.runAllTimersAsync();
|
||||||
|
|
||||||
|
const pending = await manager.getPendingQuestion(agent.id);
|
||||||
|
expect(pending?.question).toBe('Which database?');
|
||||||
|
expect(pending?.options).toHaveLength(1);
|
||||||
|
expect(pending?.options?.[0].label).toBe('PostgreSQL');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('clears pending question after resume', async () => {
|
||||||
|
manager.setScenario('resume-test', {
|
||||||
|
status: 'question',
|
||||||
|
question: 'Need your input',
|
||||||
|
options: [{ label: 'Option A' }, { label: 'Option B' }],
|
||||||
|
});
|
||||||
|
|
||||||
|
const agent = await manager.spawn({ name: 'resume-test', taskId: 'task-1', prompt: 'test' });
|
||||||
|
await vi.runAllTimersAsync();
|
||||||
|
|
||||||
|
// Verify question is pending
|
||||||
|
const pendingBefore = await manager.getPendingQuestion(agent.id);
|
||||||
|
expect(pendingBefore).not.toBeNull();
|
||||||
|
expect(pendingBefore?.question).toBe('Need your input');
|
||||||
|
|
||||||
|
// Resume the agent
|
||||||
|
await manager.resume(agent.id, 'Option A');
|
||||||
|
|
||||||
|
// Pending question should be cleared
|
||||||
|
const pendingAfter = await manager.getPendingQuestion(agent.id);
|
||||||
|
expect(pendingAfter).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns null for non-existent agent pending question', async () => {
|
||||||
|
const pending = await manager.getPendingQuestion('non-existent-id');
|
||||||
|
expect(pending).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns null for agent not in waiting state', async () => {
|
||||||
|
const agent = await manager.spawn({ name: 'running-agent', taskId: 'task-1', prompt: 'test' });
|
||||||
|
|
||||||
|
// Agent is running, not waiting
|
||||||
|
const pending = await manager.getPendingQuestion(agent.id);
|
||||||
|
expect(pending).toBeNull();
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -52,10 +52,10 @@ describe('E2E Edge Cases', () => {
|
|||||||
});
|
});
|
||||||
await vi.runAllTimersAsync();
|
await vi.runAllTimersAsync();
|
||||||
|
|
||||||
// Set crash scenario BEFORE dispatch
|
// Set unrecoverable_error scenario BEFORE dispatch
|
||||||
harness.setAgentScenario(`agent-${taskAId.slice(0, 6)}`, {
|
harness.setAgentScenario(`agent-${taskAId.slice(0, 6)}`, {
|
||||||
outcome: 'crash',
|
status: 'unrecoverable_error',
|
||||||
message: 'Token limit exceeded',
|
error: 'Token limit exceeded',
|
||||||
});
|
});
|
||||||
|
|
||||||
await harness.dispatchManager.queue(taskAId);
|
await harness.dispatchManager.queue(taskAId);
|
||||||
@@ -91,10 +91,10 @@ describe('E2E Edge Cases', () => {
|
|||||||
});
|
});
|
||||||
await vi.runAllTimersAsync();
|
await vi.runAllTimersAsync();
|
||||||
|
|
||||||
// Set crash scenario
|
// Set unrecoverable_error scenario
|
||||||
harness.setAgentScenario(`agent-${taskAId.slice(0, 6)}`, {
|
harness.setAgentScenario(`agent-${taskAId.slice(0, 6)}`, {
|
||||||
outcome: 'crash',
|
status: 'unrecoverable_error',
|
||||||
message: 'Token limit exceeded',
|
error: 'Token limit exceeded',
|
||||||
});
|
});
|
||||||
|
|
||||||
await harness.dispatchManager.queue(taskAId);
|
await harness.dispatchManager.queue(taskAId);
|
||||||
@@ -119,10 +119,10 @@ describe('E2E Edge Cases', () => {
|
|||||||
});
|
});
|
||||||
await vi.runAllTimersAsync();
|
await vi.runAllTimersAsync();
|
||||||
|
|
||||||
// Set crash scenario
|
// Set unrecoverable_error scenario
|
||||||
harness.setAgentScenario(`agent-${taskAId.slice(0, 6)}`, {
|
harness.setAgentScenario(`agent-${taskAId.slice(0, 6)}`, {
|
||||||
outcome: 'crash',
|
status: 'unrecoverable_error',
|
||||||
message: 'Out of memory',
|
error: 'Out of memory',
|
||||||
});
|
});
|
||||||
|
|
||||||
await harness.dispatchManager.queue(taskAId);
|
await harness.dispatchManager.queue(taskAId);
|
||||||
@@ -151,9 +151,9 @@ describe('E2E Edge Cases', () => {
|
|||||||
});
|
});
|
||||||
await vi.runAllTimersAsync();
|
await vi.runAllTimersAsync();
|
||||||
|
|
||||||
// Set waiting scenario
|
// Set question scenario
|
||||||
harness.setAgentScenario(`agent-${taskAId.slice(0, 6)}`, {
|
harness.setAgentScenario(`agent-${taskAId.slice(0, 6)}`, {
|
||||||
outcome: 'waiting_for_input',
|
status: 'question',
|
||||||
question: 'Which database should I use?',
|
question: 'Which database should I use?',
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -184,9 +184,9 @@ describe('E2E Edge Cases', () => {
|
|||||||
});
|
});
|
||||||
await vi.runAllTimersAsync();
|
await vi.runAllTimersAsync();
|
||||||
|
|
||||||
// Set waiting scenario
|
// Set question scenario
|
||||||
harness.setAgentScenario(`agent-${taskAId.slice(0, 6)}`, {
|
harness.setAgentScenario(`agent-${taskAId.slice(0, 6)}`, {
|
||||||
outcome: 'waiting_for_input',
|
status: 'question',
|
||||||
question: 'Which database should I use?',
|
question: 'Which database should I use?',
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -234,9 +234,9 @@ describe('E2E Edge Cases', () => {
|
|||||||
});
|
});
|
||||||
await vi.runAllTimersAsync();
|
await vi.runAllTimersAsync();
|
||||||
|
|
||||||
// Set waiting scenario
|
// Set question scenario
|
||||||
harness.setAgentScenario(`agent-${taskAId.slice(0, 6)}`, {
|
harness.setAgentScenario(`agent-${taskAId.slice(0, 6)}`, {
|
||||||
outcome: 'waiting_for_input',
|
status: 'question',
|
||||||
question: 'Which database should I use?',
|
question: 'Which database should I use?',
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -267,11 +267,11 @@ describe('TestHarness', () => {
|
|||||||
await vi.runAllTimersAsync();
|
await vi.runAllTimersAsync();
|
||||||
harness.clearEvents();
|
harness.clearEvents();
|
||||||
|
|
||||||
// Set crash scenario for the agent that will be spawned
|
// Set unrecoverable_error scenario for the agent that will be spawned
|
||||||
harness.setAgentScenario(`agent-${taskAId.slice(0, 6)}`, {
|
harness.setAgentScenario(`agent-${taskAId.slice(0, 6)}`, {
|
||||||
outcome: 'crash',
|
status: 'unrecoverable_error',
|
||||||
delay: 0,
|
delay: 0,
|
||||||
message: 'Test crash',
|
error: 'Test crash',
|
||||||
});
|
});
|
||||||
|
|
||||||
// Queue and dispatch
|
// Queue and dispatch
|
||||||
|
|||||||
Reference in New Issue
Block a user