Skip to content

Commit b1c94a1

Browse files
committed
test: address review feedback — assert node_output, test failure message format
- Assert node_output field in node_skipped_prior_success event test - Add test for empty-string fallback when node ID has undefined output - Add dedicated test verifying failure message names failing nodes - Update stale comment from 'no successful nodes' to new behavior - Add edge-case test for only node_skipped_prior_success rows (no node_completed)
1 parent 4996847 commit b1c94a1

2 files changed

Lines changed: 108 additions & 1 deletion

File tree

packages/core/src/db/workflow-events.test.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,27 @@ describe('workflow-events', () => {
227227
);
228228
});
229229

230+
test('returns outputs when only node_skipped_prior_success rows exist (no node_completed)', async () => {
231+
mockQuery.mockResolvedValueOnce(
232+
createQueryResult([
233+
{
234+
step_name: 'node-x',
235+
data: { reason: 'prior_success', node_output: 'skipped output X' },
236+
},
237+
{
238+
step_name: 'node-y',
239+
data: { reason: 'prior_success', node_output: 'skipped output Y' },
240+
},
241+
])
242+
);
243+
244+
const result = await getCompletedDagNodeOutputs('run-all-skipped');
245+
246+
expect(result.size).toBe(2);
247+
expect(result.get('node-x')).toBe('skipped output X');
248+
expect(result.get('node-y')).toBe('skipped output Y');
249+
});
250+
230251
test('parses JSON string data (SQLite path)', async () => {
231252
mockQuery.mockResolvedValueOnce(
232253
createQueryResult([

packages/workflows/src/dag-executor.test.ts

Lines changed: 87 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2973,6 +2973,54 @@ describe('executeDagWorkflow -- resume with priorCompletedNodes', () => {
29732973
(call[0] as { step_name: string }).step_name === 'step1'
29742974
);
29752975
expect(skippedEvent).toBeDefined();
2976+
expect(skippedEvent[0].data.node_output).toBe('prior output');
2977+
});
2978+
2979+
it('emits node_skipped_prior_success with empty output when node ID not in map', async () => {
2980+
const store = createMockStore();
2981+
const mockDeps = createMockDeps(store);
2982+
const platform = createMockPlatform();
2983+
const workflowRun = makeWorkflowRun('resume-empty-output');
2984+
2985+
// priorCompletedNodes has step1 but with undefined value to test the ?? '' fallback
2986+
const priorCompletedNodes = new Map<string, string>([
2987+
['step1', undefined as unknown as string],
2988+
]);
2989+
2990+
await executeDagWorkflow(
2991+
mockDeps,
2992+
platform,
2993+
'conv-resume',
2994+
testDir,
2995+
{
2996+
name: 'two-step',
2997+
nodes: [
2998+
{ id: 'step1', command: 'step1' },
2999+
{ id: 'step2', command: 'step2', depends_on: ['step1'] },
3000+
],
3001+
},
3002+
workflowRun,
3003+
'claude',
3004+
undefined,
3005+
join(testDir, 'artifacts'),
3006+
join(testDir, 'logs'),
3007+
'main',
3008+
'docs/',
3009+
minimalConfig,
3010+
undefined,
3011+
undefined,
3012+
priorCompletedNodes
3013+
);
3014+
3015+
const eventCalls = (store.createWorkflowEvent as ReturnType<typeof mock>).mock.calls;
3016+
const skippedEvent = eventCalls.find(
3017+
(call: unknown[]) =>
3018+
(call[0] as { event_type: string }).event_type === 'node_skipped_prior_success' &&
3019+
(call[0] as { step_name: string }).step_name === 'step1'
3020+
);
3021+
expect(skippedEvent).toBeDefined();
3022+
// The ?? '' fallback kicks in when the map value is undefined
3023+
expect(skippedEvent[0].data.node_output).toBe('');
29763024
});
29773025

29783026
it('runs all nodes when priorCompletedNodes is empty', async () => {
@@ -4664,7 +4712,7 @@ describe('executeDagWorkflow -- terminal node output selection', () => {
46644712

46654713
expect(store.failWorkflowRun).toHaveBeenCalled();
46664714
// The "unknown provider" detail surfaces on the node_failed event; the
4667-
// workflow-level fail message is a generic "no successful nodes" summary.
4715+
// workflow-level fail message names the failing node(s).
46684716
const eventCalls = (store.createWorkflowEvent as ReturnType<typeof mock>).mock.calls;
46694717
const nodeFailedEvents = eventCalls.filter(
46704718
(call: unknown[]) => (call[0] as Record<string, unknown>).event_type === 'node_failed'
@@ -4677,6 +4725,44 @@ describe('executeDagWorkflow -- terminal node output selection', () => {
46774725
expect(nodeFailedData.error).toContain("unknown provider 'claud'");
46784726
});
46794727

4728+
it('failure message names the failing node instead of generic summary', async () => {
4729+
const store = createMockStore();
4730+
const mockDeps = createMockDeps(store);
4731+
const platform = createMockPlatform();
4732+
const workflowRun = makeWorkflowRun();
4733+
4734+
await executeDagWorkflow(
4735+
mockDeps,
4736+
platform,
4737+
'conv-dag',
4738+
testDir,
4739+
{
4740+
name: 'fail-msg-test',
4741+
nodes: [
4742+
{
4743+
id: 'fail-node',
4744+
command: 'my-cmd',
4745+
provider: 'nonexistent',
4746+
},
4747+
],
4748+
},
4749+
workflowRun,
4750+
'claude',
4751+
undefined,
4752+
join(testDir, 'artifacts'),
4753+
join(testDir, 'logs'),
4754+
'main',
4755+
'docs/',
4756+
minimalConfig
4757+
);
4758+
4759+
expect(store.failWorkflowRun).toHaveBeenCalled();
4760+
const failCall = (store.failWorkflowRun as ReturnType<typeof mock>).mock.calls[0];
4761+
const failMsg = failCall[1] as string;
4762+
expect(failMsg).toContain('fail-node failed');
4763+
expect(failMsg).not.toContain('no successful nodes');
4764+
});
4765+
46804766
it('excludes intermediate nodes with dependents from terminal set (fan-in DAG)', async () => {
46814767
let callCount = 0;
46824768
mockSendQueryDag.mockImplementation(async function* () {

0 commit comments

Comments
 (0)