Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 43 additions & 3 deletions tests/ipcMain/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,15 @@ export class EventCollector {
`waitForEvent timeout: Expected "${eventType}" but got events: [${eventTypes.join(", ")}]`
);

// If there was a stream-error, log the error details
const errorEvent = this.events.find((e) => "type" in e && e.type === "stream-error");
if (errorEvent && "error" in errorEvent) {
console.error("Stream error details:", errorEvent.error);
if ("errorType" in errorEvent) {
console.error("Stream error type:", errorEvent.errorType);
}
}

return null;
}

Expand Down Expand Up @@ -186,12 +195,43 @@ export function createEventCollector(

/**
* Assert that a stream completed successfully
* Provides helpful error messages when assertions fail
*/
export function assertStreamSuccess(collector: EventCollector): void {
expect(collector.hasStreamEnd()).toBe(true);
expect(collector.hasError()).toBe(false);
const allEvents = collector.getEvents();
const eventTypes = allEvents.filter((e) => "type" in e).map((e) => (e as { type: string }).type);

// Check for stream-end
if (!collector.hasStreamEnd()) {
const errorEvent = allEvents.find((e) => "type" in e && e.type === "stream-error");
if (errorEvent && "error" in errorEvent) {
throw new Error(
`Stream did not complete successfully. Got stream-error: ${errorEvent.error}\n` +
`All events: [${eventTypes.join(", ")}]`
);
}
throw new Error(
`Stream did not emit stream-end event.\n` + `All events: [${eventTypes.join(", ")}]`
);
}

// Check for errors
if (collector.hasError()) {
const errorEvent = allEvents.find((e) => "type" in e && e.type === "stream-error");
const errorMsg = errorEvent && "error" in errorEvent ? errorEvent.error : "unknown";
throw new Error(
`Stream completed but also has error event: ${errorMsg}\n` +
`All events: [${eventTypes.join(", ")}]`
);
}

// Check for final message
const finalMessage = collector.getFinalMessage();
expect(finalMessage).toBeDefined();
if (!finalMessage) {
throw new Error(
`Stream completed but final message is missing.\n` + `All events: [${eventTypes.join(", ")}]`
);
}
}

/**
Expand Down
20 changes: 18 additions & 2 deletions tests/ipcMain/sendMessage.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -833,7 +833,15 @@ describeIntegration("IpcMain sendMessage integration tests", () => {

// Wait for stream to complete (longer timeout for tool policy tests)
const collector = createEventCollector(env.sentEvents, workspaceId);
await collector.waitForEvent("stream-end", 30000);

// Wait for either stream-end or stream-error
// (helpers will log diagnostic info on failure)
await Promise.race([
collector.waitForEvent("stream-end", 30000),
collector.waitForEvent("stream-error", 30000),
]);

// This will throw with detailed error info if stream didn't complete successfully
assertStreamSuccess(collector);

// Verify file still exists (bash tool was disabled, so deletion shouldn't have happened)
Expand Down Expand Up @@ -884,7 +892,15 @@ describeIntegration("IpcMain sendMessage integration tests", () => {

// Wait for stream to complete (longer timeout for tool policy tests)
const collector = createEventCollector(env.sentEvents, workspaceId);
await collector.waitForEvent("stream-end", 30000);

// Wait for either stream-end or stream-error
// (helpers will log diagnostic info on failure)
await Promise.race([
collector.waitForEvent("stream-end", 30000),
collector.waitForEvent("stream-error", 30000),
]);

// This will throw with detailed error info if stream didn't complete successfully
assertStreamSuccess(collector);

// Verify file content unchanged (file_edit tools and bash were disabled)
Expand Down