Skip to content

Commit 70997d3

Browse files
cameroncookecodex
andcommitted
test(runtime): Align rebased tests with fragment contracts
Update the rebased test suite to match the current fragment and runtime-status APIs. Replace stale progress-event expectations with fragment-based assertions, move infrastructure status helpers onto the runtime-only status type, and repair the small broken test helpers surfaced by typecheck after the rebase. This keeps the branch working on top of main without reintroducing old ProgressEvent assumptions. Co-Authored-By: OpenAI Codex <noreply@openai.com>
1 parent b4faec8 commit 70997d3

8 files changed

Lines changed: 50 additions & 43 deletions

File tree

src/cli/__tests__/register-tool-commands.test.ts

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -89,22 +89,16 @@ function mockInvokeDirectThroughHandler() {
8989
.spyOn(DefaultToolInvoker.prototype, 'invokeDirect')
9090
.mockImplementation(async (tool, args, opts) => {
9191
const handlerContext: ToolHandlerContext = opts.handlerContext ?? {
92-
emit: (event) => {
93-
if (!('timestamp' in event)) {
94-
opts.renderSession?.emit(event);
95-
}
92+
emit: (fragment) => {
93+
opts.onProgress?.(fragment);
94+
opts.renderSession?.emit(fragment);
9695
},
9796
attach: (image) => {
9897
opts.renderSession?.attach(image);
9998
},
100-
liveProgressEnabled: false,
99+
liveProgressEnabled: Boolean(opts.onProgress),
101100
};
102101

103-
if (opts.onProgress) {
104-
handlerContext.liveProgressEnabled = true;
105-
handlerContext.emitProgress = opts.onProgress;
106-
}
107-
108102
await tool.handler(args, handlerContext);
109103

110104
if (handlerContext.structuredOutput && opts.onStructuredOutput) {

src/daemon/__tests__/tool-invoke-streaming.test.ts

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ describe('daemon tool.invoke streaming', () => {
5757
);
5858
});
5959

60-
it('does not stream progress events outside live CLI contexts and returns a terminal structured result', async () => {
60+
it('streams fragments to the daemon client callback and still returns a terminal structured result', async () => {
6161
const tool: ToolDefinition = {
6262
cliName: 'stream-tool',
6363
mcpName: 'stream_tool',
@@ -67,8 +67,18 @@ describe('daemon tool.invoke streaming', () => {
6767
mcpSchema: {},
6868
stateful: true,
6969
handler: async (_params, ctx) => {
70-
ctx.emitProgress?.({ type: 'status', level: 'info', message: 'Starting build' });
71-
ctx.emitProgress?.({ type: 'artifact', name: 'Build Log', path: '/tmp/build.log' });
70+
ctx.emit({
71+
kind: 'infrastructure',
72+
fragment: 'status',
73+
level: 'info',
74+
message: 'Starting build',
75+
});
76+
ctx.emit({
77+
kind: 'transcript',
78+
fragment: 'process-line',
79+
stream: 'stderr',
80+
line: 'Build Log: /tmp/build.log',
81+
});
7282
ctx.nextSteps = [{ label: 'Open the build log' }];
7383
ctx.structuredOutput = {
7484
schema: 'xcodebuildmcp.output.simulator-list',
@@ -105,13 +115,13 @@ describe('daemon tool.invoke streaming', () => {
105115
'stream_tool',
106116
{},
107117
{
108-
onProgress: (event) => {
109-
progress.push(event.type);
118+
onFragment: (fragment) => {
119+
progress.push(fragment.fragment);
110120
},
111121
},
112122
);
113123

114-
expect(progress).toEqual([]);
124+
expect(progress).toEqual(['status', 'process-line']);
115125
expect(result).toEqual({
116126
structuredOutput: {
117127
schema: 'xcodebuildmcp.output.simulator-list',

src/mcp/tools/doctor/__tests__/doctor.test.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -134,17 +134,17 @@ describe('doctor tool', () => {
134134
describe('Handler Behavior (Complete Literal Returns)', () => {
135135
it('does not emit progress events from the executor', async () => {
136136
const executeDoctor = createDoctorExecutor(createDeps());
137-
const emitProgress = vi.fn();
137+
const emitFragment = vi.fn();
138138

139139
const result = await executeDoctor(
140140
{},
141141
{
142142
liveProgressEnabled: false,
143-
emitProgress,
143+
emitFragment,
144144
},
145145
);
146146

147-
expect(emitProgress).not.toHaveBeenCalled();
147+
expect(emitFragment).not.toHaveBeenCalled();
148148
expect(result.didError).toBe(false);
149149
});
150150

src/runtime/__tests__/tool-invoker.test.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import { beforeEach, describe, expect, it, vi } from 'vitest';
22
import { z } from 'zod';
33
import type { ToolResponse } from '../../types/common.ts';
4-
import type { DomainFragment } from '../../types/domain-fragments.ts';
4+
import type { AnyFragment, DomainFragment } from '../../types/domain-fragments.ts';
5+
import type { RuntimeStatusFragment } from '../../types/runtime-status.ts';
56
import type { DaemonToolResult, ToolInvokeResult } from '../../daemon/protocol.ts';
67
import type { ToolDefinition } from '../types.ts';
78
import { createToolCatalog } from '../tool-catalog.ts';
@@ -18,7 +19,7 @@ const daemonClientMock = {
1819
(
1920
name: string,
2021
args: Record<string, unknown>,
21-
options?: { onFragment?: (fragment: DomainFragment) => void },
22+
options?: { onFragment?: (fragment: AnyFragment) => void },
2223
) => Promise<ToolInvokeResult>
2324
>(),
2425
listTools: vi.fn<() => Promise<Array<{ name: string }>>>(),
@@ -46,7 +47,7 @@ vi.mock('../../cli/daemon-control.ts', () => ({
4647
function statusFragment(
4748
level: 'info' | 'warning' | 'error' | 'success',
4849
message: string,
49-
): DomainFragment {
50+
): RuntimeStatusFragment {
5051
return { kind: 'infrastructure', fragment: 'status', level, message };
5152
}
5253

@@ -153,7 +154,7 @@ function emitNextStepsHandler(
153154
});
154155
}
155156

156-
function emitErrorEventsHandler(events: DomainFragment[]): ToolDefinition['handler'] {
157+
function emitErrorEventsHandler(events: AnyFragment[]): ToolDefinition['handler'] {
157158
return vi.fn(async (_params, ctx) => {
158159
for (const event of events) {
159160
ctx.emit(event);

src/utils/__tests__/session-aware-tool-factory.test.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@ import {
1717
} from '../config-store.ts';
1818
import { createRenderSession } from '../../rendering/render.ts';
1919
import type { ToolHandlerContext } from '../../rendering/types.ts';
20-
import type { DomainFragment } from '../../types/domain-fragments.ts';
20+
import type { AnyFragment } from '../../types/domain-fragments.ts';
21+
import type { RuntimeStatusFragment } from '../../types/runtime-status.ts';
2122
import { renderCliTextTranscript } from '../renderers/cli-text-renderer.ts';
2223

2324
const cwd = '/repo';
@@ -30,7 +31,7 @@ async function initConfigStoreForTest(overrides?: RuntimeConfigOverrides): Promi
3031
function statusFragment(
3132
level: 'info' | 'warning' | 'error' | 'success',
3233
message: string,
33-
): DomainFragment {
34+
): RuntimeStatusFragment {
3435
return { kind: 'infrastructure', fragment: 'status', level, message };
3536
}
3637

@@ -39,7 +40,7 @@ function invokeAndCollect(
3940
args: Record<string, unknown>,
4041
): Promise<{ text: string; isError: boolean }> {
4142
const session = createRenderSession('text');
42-
const items: DomainFragment[] = [];
43+
const items: AnyFragment[] = [];
4344
const ctx: ToolHandlerContext = {
4445
liveProgressEnabled: false,
4546
emit: (event) => {
@@ -237,7 +238,7 @@ describe('createSessionAwareTool', () => {
237238
const handlerNoXor = createSessionAwareTool<z.infer<typeof internalSchemaNoXor>>({
238239
internalSchema: internalSchemaNoXor,
239240
logicFunction: (async () => {
240-
ctx.emit(statusFragment('success', 'OK'));
241+
const ctx = getHandlerContext();
241242
ctx.emit(statusFragment('success', 'OK'));
242243
}) as (params: z.infer<typeof internalSchemaNoXor>, executor: unknown) => Promise<void>,
243244
getExecutor: () => createMockExecutor({ success: true }),
@@ -340,7 +341,7 @@ describe('createSessionAwareTool', () => {
340341
internalSchema: envSchema,
341342
logicFunction: async (params) => {
342343
const ctx = getHandlerContext();
343-
ctx.emit(statusLine('success', JSON.stringify(params.env)));
344+
ctx.emit(statusFragment('success', JSON.stringify(params.env)));
344345
},
345346
getExecutor: () => createMockExecutor({ success: true }),
346347
requirements: [{ allOf: ['scheme'] }],

src/utils/__tests__/typed-tool-factory.test.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ import { createMockExecutor } from '../../test-utils/mock-executors.ts';
1010
import { createRenderSession } from '../../rendering/render.ts';
1111
import type { ToolHandlerContext } from '../../rendering/types.ts';
1212
import { getHandlerContext } from '../typed-tool-factory.ts';
13-
import type { DomainFragment } from '../../types/domain-fragments.ts';
13+
import type { AnyFragment } from '../../types/domain-fragments.ts';
14+
import type { RuntimeStatusFragment } from '../../types/runtime-status.ts';
1415
import { renderCliTextTranscript } from '../renderers/cli-text-renderer.ts';
1516

1617
const testSchema = z.object({
@@ -23,7 +24,7 @@ type TestParams = z.infer<typeof testSchema>;
2324
function statusFragment(
2425
level: 'info' | 'warning' | 'error' | 'success',
2526
message: string,
26-
): DomainFragment {
27+
): RuntimeStatusFragment {
2728
return { kind: 'infrastructure', fragment: 'status', level, message };
2829
}
2930

@@ -37,7 +38,7 @@ function invokeAndCollect(
3738
args: Record<string, unknown>,
3839
): Promise<{ text: string; isError: boolean }> {
3940
const session = createRenderSession('text');
40-
const items: DomainFragment[] = [];
41+
const items: AnyFragment[] = [];
4142
const ctx: ToolHandlerContext = {
4243
liveProgressEnabled: false,
4344
emit: (event) => {

src/utils/__tests__/xcodebuild-output.test.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { describe, expect, it } from 'vitest';
22
import { createXcodebuildPipeline } from '../xcodebuild-pipeline.ts';
33
import type { StartedPipeline } from '../xcodebuild-pipeline.ts';
44
import { finalizeInlineXcodebuild } from '../xcodebuild-output.ts';
5-
import type { AnyFragment, DomainFragment } from '../../types/domain-fragments.ts';
5+
import type { AnyFragment } from '../../types/domain-fragments.ts';
66

77
function startPipeline(emit: (fragment: AnyFragment) => void = () => {}): StartedPipeline {
88
const pipeline = createXcodebuildPipeline({
@@ -16,7 +16,7 @@ function startPipeline(emit: (fragment: AnyFragment) => void = () => {}): Starte
1616

1717
describe('xcodebuild-output', () => {
1818
it('does not emit fallback events (fallback is handled by domain result creators)', () => {
19-
const emitted: DomainFragment[] = [];
19+
const emitted: AnyFragment[] = [];
2020
const started = startPipeline((fragment) => emitted.push(fragment));
2121
emitted.length = 0;
2222

@@ -38,7 +38,7 @@ describe('xcodebuild-output', () => {
3838
});
3939

4040
it('logs parser debug info without emitting progress events during finalize', () => {
41-
const emitted: DomainFragment[] = [];
41+
const emitted: AnyFragment[] = [];
4242
const started = startPipeline((fragment) => emitted.push(fragment));
4343
emitted.length = 0;
4444

@@ -62,7 +62,7 @@ describe('xcodebuild-output', () => {
6262
});
6363

6464
it('returns finalized state without synthesizing footer events beyond the build summary', () => {
65-
const emitted: DomainFragment[] = [];
65+
const emitted: AnyFragment[] = [];
6666
const started = startPipeline((fragment) => emitted.push(fragment));
6767
emitted.length = 0;
6868

src/utils/__tests__/xcodebuild-pipeline.test.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { describe, expect, it, beforeEach, afterEach, vi } from 'vitest';
22
import { createXcodebuildPipeline } from '../xcodebuild-pipeline.ts';
33
import { STAGE_RANK } from '../../types/domain-fragments.ts';
4-
import type { DomainFragment } from '../../types/domain-fragments.ts';
4+
import type { AnyFragment, DomainFragment } from '../../types/domain-fragments.ts';
55
import { renderCliTextTranscript } from '../renderers/cli-text-renderer.ts';
66
import type { StructuredToolOutput } from '../../rendering/types.ts';
77

@@ -18,7 +18,7 @@ describe('xcodebuild-pipeline', () => {
1818
});
1919

2020
it('produces MCP content from xcodebuild test output', () => {
21-
const emittedEvents: DomainFragment[] = [];
21+
const emittedEvents: AnyFragment[] = [];
2222
const pipeline = createXcodebuildPipeline({
2323
operation: 'TEST',
2424
toolName: 'test_sim',
@@ -89,7 +89,7 @@ describe('xcodebuild-pipeline', () => {
8989
});
9090

9191
it('handles build output with warnings and errors', () => {
92-
const emittedEvents: DomainFragment[] = [];
92+
const emittedEvents: AnyFragment[] = [];
9393
const pipeline = createXcodebuildPipeline({
9494
operation: 'BUILD',
9595
toolName: 'build_sim',
@@ -119,7 +119,7 @@ describe('xcodebuild-pipeline', () => {
119119

120120
it('supports multi-phase with minimumStage', () => {
121121
// Phase 1: build-for-testing
122-
const phase1Events: DomainFragment[] = [];
122+
const phase1Events: AnyFragment[] = [];
123123
const phase1 = createXcodebuildPipeline({
124124
operation: 'TEST',
125125
toolName: 'test_sim',
@@ -141,7 +141,7 @@ describe('xcodebuild-pipeline', () => {
141141
| 'COMPILING'
142142
| undefined;
143143

144-
const phase2Events: DomainFragment[] = [];
144+
const phase2Events: AnyFragment[] = [];
145145
const phase2 = createXcodebuildPipeline({
146146
operation: 'TEST',
147147
toolName: 'test_sim',
@@ -167,7 +167,7 @@ describe('xcodebuild-pipeline', () => {
167167
});
168168

169169
it('emitFragment passes tool-originated events through the pipeline', () => {
170-
const emittedEvents: DomainFragment[] = [];
170+
const emittedEvents: AnyFragment[] = [];
171171
const pipeline = createXcodebuildPipeline({
172172
operation: 'TEST',
173173
toolName: 'test_sim',
@@ -218,7 +218,7 @@ describe('xcodebuild-pipeline', () => {
218218
});
219219

220220
it('renders test discovery in cli-text mode', () => {
221-
const emittedEvents: DomainFragment[] = [
221+
const emittedEvents: AnyFragment[] = [
222222
{
223223
kind: 'test-result',
224224
fragment: 'test-discovery',
@@ -280,7 +280,7 @@ describe('xcodebuild-pipeline', () => {
280280
process.env.XCODEBUILDMCP_RUNTIME = 'cli';
281281
process.env.XCODEBUILDMCP_CLI_OUTPUT_FORMAT = 'json';
282282

283-
const emittedEvents: DomainFragment[] = [];
283+
const emittedEvents: AnyFragment[] = [];
284284
const pipeline = createXcodebuildPipeline({
285285
operation: 'BUILD',
286286
toolName: 'build_sim',

0 commit comments

Comments
 (0)