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
1 change: 0 additions & 1 deletion .github/scripts/quality-check-scope.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@ const REPOSITORY_ONLY_FILES = new Set([
"scripts/monorepo-layout.test.mjs",
"scripts/release-notes.mjs",
"scripts/release-notes.test.mjs",
"scripts/session-toolbar-layout.test.mjs",
]);

function applicationForPath(file) {
Expand Down
7 changes: 7 additions & 0 deletions .release-notes/codex-trim-redundant-tests.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# 改善 Windows 下的 Workflow MCP 验证稳定性

<!-- release-target: v2 -->

## Bug 修复

- 修复 Windows 环境中验证 Workflow MCP 退出行为时,连接正常关闭仍被误报为失败的问题。
43 changes: 16 additions & 27 deletions apps/main-1.0/src/core/claude-statusline-install.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,33 +63,22 @@ describe("installClaudeStatuslineBridge", () => {
}
});

it("is idempotent when our bridge is already configured", () => {
const homeDir = makeHome();
try {
const settingsPath = writeSettings(homeDir, {
statusLine: { type: "command", command: `node "${SCRIPT}"` },
});
const before = readFileSync(settingsPath, "utf8");

const result = installClaudeStatuslineBridge({ homeDir, scriptPath: SCRIPT });

expect(result.status).toBe("already");
expect(readFileSync(settingsPath, "utf8")).toBe(before);
} finally {
rmSync(homeDir, { recursive: true, force: true });
}
});

it("recognizes the global bin name as already installed", () => {
const homeDir = makeHome();
try {
writeSettings(homeDir, {
statusLine: { type: "command", command: "agent-recall-claude-statusline" },
});
const result = installClaudeStatuslineBridge({ homeDir, scriptPath: SCRIPT });
expect(result.status).toBe("already");
} finally {
rmSync(homeDir, { recursive: true, force: true });
it("preserves existing bridge commands, including the global executable", () => {
for (const command of [`node "${SCRIPT}"`, "agent-recall-claude-statusline"]) {
const homeDir = makeHome();
try {
const settingsPath = writeSettings(homeDir, {
statusLine: { type: "command", command },
});
const before = readFileSync(settingsPath, "utf8");

const result = installClaudeStatuslineBridge({ homeDir, scriptPath: SCRIPT });

expect(result.status).toBe("already");
expect(readFileSync(settingsPath, "utf8")).toBe(before);
} finally {
rmSync(homeDir, { recursive: true, force: true });
}
}
});

Expand Down
23 changes: 13 additions & 10 deletions apps/main-1.0/src/core/summary-endpoint.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ import {
resolveSummaryEndpointFromSettings,
} from "./summary-endpoint";

const codexConfigDir = path.resolve("fixtures", "summary-codex");
const claudeConfigDir = path.resolve("fixtures", "summary-claude");

function settingsWith(overrides: Partial<AppSettings>): AppSettings {
return { ...structuredClone(defaultSettings), ...overrides };
}
Expand All @@ -27,12 +30,12 @@ describe("summary exec endpoints", () => {

it("points each CLI at its own directory without touching the other", () => {
const settings = settingsWith({
summaryCodexConfigDir: "/tmp/summary-codex",
summaryClaudeConfigDir: "/tmp/summary-claude",
summaryCodexConfigDir: codexConfigDir,
summaryClaudeConfigDir: claudeConfigDir,
});

expect(buildCodexExecEndpoint(settings).env).toEqual({ CODEX_HOME: "/tmp/summary-codex" });
expect(buildClaudeExecEndpoint(settings).env).toEqual({ CLAUDE_CONFIG_DIR: "/tmp/summary-claude" });
expect(buildCodexExecEndpoint(settings).env).toEqual({ CODEX_HOME: codexConfigDir });
expect(buildClaudeExecEndpoint(settings).env).toEqual({ CLAUDE_CONFIG_DIR: claudeConfigDir });
});

it("expands a leading ~ so a hand-typed path reaches the right directory", () => {
Expand Down Expand Up @@ -73,25 +76,25 @@ describe("summary exec endpoints", () => {
describe("resolveSummaryEndpointFromSettings", () => {
it("routes each source to its own CLI and directory", () => {
const settings = settingsWith({
summaryCodexConfigDir: "/tmp/summary-codex",
summaryClaudeConfigDir: "/tmp/summary-claude",
summaryCodexConfigDir: codexConfigDir,
summaryClaudeConfigDir: claudeConfigDir,
});

const codex = resolveSummaryEndpointFromSettings({ ...settings, summarySource: "codex" });
expect(codex?.apiFormat).toBe("codex_exec");
expect(codex?.env).toEqual({ CODEX_HOME: "/tmp/summary-codex" });
expect(codex?.env).toEqual({ CODEX_HOME: codexConfigDir });

const claude = resolveSummaryEndpointFromSettings({ ...settings, summarySource: "claude" });
expect(claude?.apiFormat).toBe("claude_exec");
expect(claude?.env).toEqual({ CLAUDE_CONFIG_DIR: "/tmp/summary-claude" });
expect(claude?.env).toEqual({ CLAUDE_CONFIG_DIR: claudeConfigDir });
});

it("keeps a custom route on HTTP and out of both config directories", () => {
const endpoint = resolveSummaryEndpointFromSettings(settingsWith({
summarySource: "custom",
summaryApiConfigMode: "custom",
summaryCodexConfigDir: "/tmp/summary-codex",
summaryClaudeConfigDir: "/tmp/summary-claude",
summaryCodexConfigDir: codexConfigDir,
summaryClaudeConfigDir: claudeConfigDir,
summaryReasoningEffort: "low",
summaryApiConfig: {
...defaultSettings.summaryApiConfig,
Expand Down
26 changes: 0 additions & 26 deletions apps/main-1.0/src/main/live-session-worker-wiring.test.ts

This file was deleted.

20 changes: 0 additions & 20 deletions apps/main-1.0/src/main/main-startup-wiring.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,26 +46,6 @@ describe("main process startup wiring", () => {
expect(toggleBlock).toContain("showWindow({ focusSearch: true })");
});

it("migrates SSH sessions through remote writeback and SSH Resume", () => {
const source = readFileSync(new URL("./index.ts", import.meta.url), "utf8");

expect(source).toContain('session.environmentKind === "wsl" || session.environmentKind === "ssh"');
expect(source).toContain('target !== sshMigrationTarget(session.source)');
expect(source).toContain('{ allowSsh: session.environmentKind === "ssh" }');
expect(source).toContain("createSourceRemoteRestoreDependencies(environment, progress)");
expect(source).toContain('environment.kind === "ssh" ? inspectSshMigrationCli(environment, target)');
expect(source).toContain("getRemoteMigrationCliVersionCommand(command, args)");
expect(source).toContain('const sshArgs = buildRemoteInteractiveSshArgs(environment, "").slice(0, -1)');
expect(source).toContain("await openResumeInTerminal(session, getSettings(), { sshArgs })");
});

it("uses Electron networking for direct summary providers", () => {
const source = readFileSync(new URL("./index.ts", import.meta.url), "utf8");

expect(source).toContain("fetch: electronSummaryFetch");
expect(source).toContain("net.fetch(input, init)");
});

it("checks summary source support before reading messages or invoking the provider", () => {
const source = readFileSync(new URL("./index.ts", import.meta.url), "utf8");
const summarizeOneSession = source.match(
Expand Down
36 changes: 0 additions & 36 deletions apps/main-1.0/src/main/session-query-wiring.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,42 +4,6 @@ import { describe, expect, it } from "vitest";
const source = readFileSync(new URL("./index.ts", import.meta.url), "utf8");

describe("main process session query wiring", () => {
it("keeps heavy catalog and usage reads off the Electron main thread", () => {
expect(source).toContain(
'new LocalSessionQueryService(path.join(__dirname, "session-query-worker.js")',
);
expect(source).toContain(
'sessionCatalogQueries().searchSessionPage(visibleSearchOptions(options))',
);
expect(source).toContain(
'sessionStatsQueries().getStats(visibleStatsOptions(options))',
);
expect(source).toContain(
'sessionStatsQueries().getStatsTrend(visibleStatsOptions(options))',
);
expect(source).toContain(
'sessionCatalogQueries().listProjects({ ...options, ...visibleProjectOptions() })',
);

expect(source).not.toContain(
'ipcMain.handle("search:session-page", (_event, options: SearchOptions) => store.searchSessionPage',
);
expect(source).not.toContain(
'ipcMain.handle("stats:get", (_event, options?: SessionStatsOptions) => store.getStats',
);
expect(source).not.toContain(
'ipcMain.handle("stats:trend", (_event, options?: SessionStatsOptions) => store.getStatsTrend',
);
});

it("isolates usage scans from interactive catalog queries and stops both workers", () => {
expect(source).toContain(
"localSessionStatsQueryService ??= createLocalSessionQueryService()",
);
expect(source).toContain("localSessionQueryService?.stop()");
expect(source).toContain("localSessionStatsQueryService?.stop()");
});

it("does not lazily create query workers after shutdown has started", () => {
expect(source).toContain("if (quitStarted || localSessionWorkersStopping)");
expect(source).toMatch(
Expand Down
17 changes: 0 additions & 17 deletions apps/main-2.0/scripts/check-source-entrypoints.test.mjs

This file was deleted.

20 changes: 0 additions & 20 deletions apps/main-2.0/scripts/workflow-mcp-package.test.mjs

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,8 +1,22 @@
import { describe, expect, test } from "vitest";
import { isWorkflowRunNodeStatus } from "./agent-hub-persistence";
import { isPersistedAppStateV5 } from "./agent-hub-persisted-migrations";

describe("AgentHub Workflow persistence", () => {
function state(version: number) {
return { version, workDir: "C:/repo", sessions: [], messages: [], events: [], tasks: [], taskMessages: [], taskEvents: [], teams: [], teamRuns: [] };
}

describe("AgentHub persistence validation", () => {
test("accepts the durable status for a human-approved review result", () => {
expect(isWorkflowRunNodeStatus("completed_with_override")).toBe(true);
});

test("accepts only the current V5 state", () => {
expect(isPersistedAppStateV5(state(5))).toBe(true);
expect(isPersistedAppStateV5(state(4))).toBe(false);
});

test("rejects a non-array workflow conversation collection", () => {
expect(isPersistedAppStateV5({ ...state(5), workflowNodeConversations: {} })).toBe(false);
});
});
4 changes: 4 additions & 0 deletions apps/main-2.0/src/automation/engine/mcp/server.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -557,6 +557,10 @@ describe("MCP server tools", () => {
// Accept the TCP connection but never respond, so the tool call hangs
// the same way a gateway request in flight while its host dies does.
blackholeSockets.add(socket);
socket.on("error", (error: NodeJS.ErrnoException) => {
// Windows may reset this fixture connection when the child exits mid-request.
if (error.code !== "ECONNRESET") throw error;
});
socket.once("close", () => blackholeSockets.delete(socket));
});
await new Promise<void>((resolve, reject) => {
Expand Down
23 changes: 13 additions & 10 deletions apps/main-2.0/src/core/summary-endpoint.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ import {
resolveSummaryEndpointFromSettings,
} from "./summary-endpoint";

const codexConfigDir = path.resolve("fixtures", "summary-codex");
const claudeConfigDir = path.resolve("fixtures", "summary-claude");

function settingsWith(overrides: Partial<AppSettings>): AppSettings {
return { ...structuredClone(defaultSettings), ...overrides };
}
Expand All @@ -27,12 +30,12 @@ describe("summary exec endpoints", () => {

it("points each CLI at its own directory without touching the other", () => {
const settings = settingsWith({
summaryCodexConfigDir: "/tmp/summary-codex",
summaryClaudeConfigDir: "/tmp/summary-claude",
summaryCodexConfigDir: codexConfigDir,
summaryClaudeConfigDir: claudeConfigDir,
});

expect(buildCodexExecEndpoint(settings).env).toEqual({ CODEX_HOME: "/tmp/summary-codex" });
expect(buildClaudeExecEndpoint(settings).env).toEqual({ CLAUDE_CONFIG_DIR: "/tmp/summary-claude" });
expect(buildCodexExecEndpoint(settings).env).toEqual({ CODEX_HOME: codexConfigDir });
expect(buildClaudeExecEndpoint(settings).env).toEqual({ CLAUDE_CONFIG_DIR: claudeConfigDir });
});

it("expands a leading ~ so a hand-typed path reaches the right directory", () => {
Expand Down Expand Up @@ -73,25 +76,25 @@ describe("summary exec endpoints", () => {
describe("resolveSummaryEndpointFromSettings", () => {
it("routes each source to its own CLI and directory", () => {
const settings = settingsWith({
summaryCodexConfigDir: "/tmp/summary-codex",
summaryClaudeConfigDir: "/tmp/summary-claude",
summaryCodexConfigDir: codexConfigDir,
summaryClaudeConfigDir: claudeConfigDir,
});

const codex = resolveSummaryEndpointFromSettings({ ...settings, summarySource: "codex" });
expect(codex?.apiFormat).toBe("codex_exec");
expect(codex?.env).toEqual({ CODEX_HOME: "/tmp/summary-codex" });
expect(codex?.env).toEqual({ CODEX_HOME: codexConfigDir });

const claude = resolveSummaryEndpointFromSettings({ ...settings, summarySource: "claude" });
expect(claude?.apiFormat).toBe("claude_exec");
expect(claude?.env).toEqual({ CLAUDE_CONFIG_DIR: "/tmp/summary-claude" });
expect(claude?.env).toEqual({ CLAUDE_CONFIG_DIR: claudeConfigDir });
});

it("keeps a custom route on HTTP and out of both config directories", () => {
const endpoint = resolveSummaryEndpointFromSettings(settingsWith({
summarySource: "custom",
summaryApiConfigMode: "custom",
summaryCodexConfigDir: "/tmp/summary-codex",
summaryClaudeConfigDir: "/tmp/summary-claude",
summaryCodexConfigDir: codexConfigDir,
summaryClaudeConfigDir: claudeConfigDir,
summaryReasoningEffort: "low",
summaryApiConfig: {
...defaultSettings.summaryApiConfig,
Expand Down
Loading
Loading