test(dashboard): drop redundant field-presence tests in SettingsModal
Each of these tests asserts only that a labeled form field exists in a section. The consolidated "all settings fields are present across all sections" test (line 497) covers the same space, and the accompanying payload-roundtrip tests implicitly require the field to be present before toggling it. Dropped 7 presence-only tests: - Recycle worktrees, Show quick chat button, Auto-completion mode - Include task ID, Auto-resolve conflicts, Add author attribution - Smart conflict resolution, groupOverlappingFiles type=checkbox 4,317 → 4,206 LOC (-111). 197 tests still passing. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -870,16 +870,6 @@ describe("SettingsModal", () => {
|
|||||||
expect(payload.smartConflictResolution).toBe(true);
|
expect(payload.smartConflictResolution).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("groupOverlappingFiles input has type checkbox", async () => {
|
|
||||||
render(<SettingsModal onClose={onClose} addToast={addToast} />);
|
|
||||||
await waitFor(() => expect(fetchSettings).toHaveBeenCalled());
|
|
||||||
|
|
||||||
fireEvent.click(screen.getByText("Scheduling"));
|
|
||||||
const checkbox = screen.getByLabelText("Serialize tasks with overlapping files");
|
|
||||||
expect(checkbox).toBeTruthy();
|
|
||||||
expect(checkbox.getAttribute("type")).toBe("checkbox");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("does not render heartbeat multiplier control in Scheduling section", async () => {
|
it("does not render heartbeat multiplier control in Scheduling section", async () => {
|
||||||
render(<SettingsModal onClose={onClose} addToast={addToast} />);
|
render(<SettingsModal onClose={onClose} addToast={addToast} />);
|
||||||
await waitFor(() => expect(fetchSettings).toHaveBeenCalled());
|
await waitFor(() => expect(fetchSettings).toHaveBeenCalled());
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
|
|||||||
// Mock external dependencies
|
// Mock external dependencies
|
||||||
vi.mock("./pi.js", () => ({
|
vi.mock("./pi.js", () => ({
|
||||||
createFnAgent: vi.fn(),
|
createFnAgent: vi.fn(),
|
||||||
|
describeModel: vi.fn(() => "mock-provider/mock-model"),
|
||||||
promptWithFallback: vi.fn(async (session, prompt, options) => {
|
promptWithFallback: vi.fn(async (session, prompt, options) => {
|
||||||
if (options === undefined) {
|
if (options === undefined) {
|
||||||
await session.prompt(prompt);
|
await session.prompt(prompt);
|
||||||
@@ -5791,6 +5792,178 @@ describe("aiMergeTask — in-merge verification fix", () => {
|
|||||||
expect(fixAgentCall[0].cwd).toBe("/tmp/root");
|
expect(fixAgentCall[0].cwd).toBe("/tmp/root");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("logs fix-agent startup metadata, streams callbacks, and logs rerun lifecycle", async () => {
|
||||||
|
let capturedFixOptions: any;
|
||||||
|
|
||||||
|
mockedExecSync.mockImplementation((cmd: any) => {
|
||||||
|
const cmdStr = String(cmd);
|
||||||
|
if (cmdStr.includes("rev-parse --verify")) return Buffer.from("abc123");
|
||||||
|
if (cmdStr.includes("git log")) return "- feat: something" as any;
|
||||||
|
if (cmdStr.includes("merge-base")) return Buffer.from("abc123");
|
||||||
|
if (cmdStr.includes("git diff") && cmdStr.includes("--stat")) return "1 file changed" as any;
|
||||||
|
if (cmdStr.includes("merge --squash")) return Buffer.from("");
|
||||||
|
if (cmdStr.includes("vitest run")) {
|
||||||
|
const err = new Error("Test failed") as any;
|
||||||
|
err.status = 1;
|
||||||
|
err.stdout = "";
|
||||||
|
err.stderr = "";
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
if (cmdStr.includes("diff --cached --quiet")) return "1" as any;
|
||||||
|
if (cmdStr.includes("diff --cached")) return "" as any;
|
||||||
|
if (cmdStr.includes("branch -d") || cmdStr.includes("branch -D")) return Buffer.from("");
|
||||||
|
if (cmdStr.includes("worktree remove")) return Buffer.from("");
|
||||||
|
if (cmdStr === "git rev-parse HEAD" || cmdStr.startsWith("git rev-parse HEAD ")) return "mergedcommit123";
|
||||||
|
return Buffer.from("");
|
||||||
|
});
|
||||||
|
|
||||||
|
mockedCreateFnAgent.mockImplementation(async (opts: any) => {
|
||||||
|
const isFixAgent = opts.systemPrompt?.includes("verification fix agent");
|
||||||
|
if (!isFixAgent) {
|
||||||
|
return {
|
||||||
|
session: {
|
||||||
|
prompt: vi.fn().mockResolvedValue(undefined),
|
||||||
|
dispose: vi.fn(),
|
||||||
|
},
|
||||||
|
} as any;
|
||||||
|
}
|
||||||
|
|
||||||
|
capturedFixOptions = opts;
|
||||||
|
return {
|
||||||
|
session: {
|
||||||
|
prompt: vi.fn().mockImplementation(async () => {
|
||||||
|
opts.onText?.("working on fix");
|
||||||
|
opts.onThinking?.("diagnosing");
|
||||||
|
opts.onToolStart?.("Bash", { command: "vitest run" });
|
||||||
|
opts.onToolEnd?.("Bash", false, "still failing");
|
||||||
|
}),
|
||||||
|
dispose: vi.fn(),
|
||||||
|
},
|
||||||
|
} as any;
|
||||||
|
});
|
||||||
|
|
||||||
|
const store = createMockStore(
|
||||||
|
{ id: "FN-050", worktree: "/tmp/root/.worktrees/KB-050" },
|
||||||
|
[{ id: "FN-050", worktree: "/tmp/root/.worktrees/KB-050", column: "in-review" } as Task],
|
||||||
|
);
|
||||||
|
(store.getSettings as ReturnType<typeof vi.fn>).mockResolvedValue({
|
||||||
|
...DEFAULT_SETTINGS,
|
||||||
|
testCommand: "vitest run",
|
||||||
|
verificationFixRetries: 1,
|
||||||
|
});
|
||||||
|
|
||||||
|
await expect(aiMergeTask(store, "/tmp/root", "FN-050")).rejects.toMatchObject({
|
||||||
|
name: "VerificationError",
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(capturedFixOptions).toBeDefined();
|
||||||
|
expect(capturedFixOptions.onText).toBeTypeOf("function");
|
||||||
|
expect(capturedFixOptions.onThinking).toBeTypeOf("function");
|
||||||
|
expect(capturedFixOptions.onToolStart).toBeTypeOf("function");
|
||||||
|
expect(capturedFixOptions.onToolEnd).toBeTypeOf("function");
|
||||||
|
|
||||||
|
expect(store.appendAgentLog).toHaveBeenCalledWith("FN-050", "Bash", "tool", "vitest run", "merger");
|
||||||
|
|
||||||
|
const logMessages = (store.logEntry as ReturnType<typeof vi.fn>).mock.calls
|
||||||
|
.map((call: any[]) => call[1])
|
||||||
|
.filter((message: unknown): message is string => typeof message === "string");
|
||||||
|
|
||||||
|
const startupLog = logMessages.find((message) =>
|
||||||
|
message.includes("In-merge verification fix agent started"),
|
||||||
|
);
|
||||||
|
expect(startupLog).toBeDefined();
|
||||||
|
expect(startupLog).toContain("model: mock-provider/mock-model");
|
||||||
|
expect(startupLog).toContain("agentId: merger");
|
||||||
|
expect(startupLog).toMatch(/runId: merge-FN-050-/);
|
||||||
|
|
||||||
|
const rerunIdx = logMessages.findIndex((message) =>
|
||||||
|
message.includes("Re-running deterministic merge verification (attempt 1)"),
|
||||||
|
);
|
||||||
|
expect(rerunIdx).toBeGreaterThan(-1);
|
||||||
|
|
||||||
|
const verificationAfterRerunIdx = logMessages.findIndex(
|
||||||
|
(message, index) =>
|
||||||
|
index > rerunIdx && message.includes("[verification] Running test command: vitest run"),
|
||||||
|
);
|
||||||
|
expect(verificationAfterRerunIdx).toBeGreaterThan(rerunIdx);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("logs rerun lifecycle before verification in build-failure fix path", async () => {
|
||||||
|
mockedExecSync.mockImplementation((cmd: any) => {
|
||||||
|
const cmdStr = String(cmd);
|
||||||
|
if (cmdStr.includes("rev-parse --verify")) return Buffer.from("abc123");
|
||||||
|
if (cmdStr.includes("git log")) return "- feat: something" as any;
|
||||||
|
if (cmdStr.includes("merge-base")) return Buffer.from("abc123");
|
||||||
|
if (cmdStr.includes("git diff") && cmdStr.includes("--stat")) return "1 file changed" as any;
|
||||||
|
if (cmdStr.includes("merge --squash")) return Buffer.from("");
|
||||||
|
if (cmdStr.includes("pnpm build")) {
|
||||||
|
const err = new Error("Build failed") as any;
|
||||||
|
err.status = 1;
|
||||||
|
err.stdout = "";
|
||||||
|
err.stderr = "";
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
if (cmdStr.includes("diff --cached --quiet")) return "1" as any;
|
||||||
|
if (cmdStr.includes("diff --cached")) return "" as any;
|
||||||
|
if (cmdStr.includes("branch -d") || cmdStr.includes("branch -D")) return Buffer.from("");
|
||||||
|
if (cmdStr.includes("worktree remove")) return Buffer.from("");
|
||||||
|
if (cmdStr === "git rev-parse HEAD" || cmdStr.startsWith("git rev-parse HEAD ")) return "mergedcommit123";
|
||||||
|
return Buffer.from("");
|
||||||
|
});
|
||||||
|
|
||||||
|
mockedCreateFnAgent.mockImplementation(async (opts: any) => {
|
||||||
|
const isFixAgent = opts.systemPrompt?.includes("verification fix agent");
|
||||||
|
if (isFixAgent) {
|
||||||
|
return {
|
||||||
|
session: {
|
||||||
|
prompt: vi.fn().mockResolvedValue(undefined),
|
||||||
|
dispose: vi.fn(),
|
||||||
|
},
|
||||||
|
} as any;
|
||||||
|
}
|
||||||
|
|
||||||
|
const reportTool = opts.customTools?.find((t: any) => t.name === "fn_report_build_failure");
|
||||||
|
return {
|
||||||
|
session: {
|
||||||
|
prompt: vi.fn().mockImplementation(async () => {
|
||||||
|
await reportTool?.execute("tool-call-build", { message: "Type error in src/build.ts" });
|
||||||
|
}),
|
||||||
|
dispose: vi.fn(),
|
||||||
|
},
|
||||||
|
} as any;
|
||||||
|
});
|
||||||
|
|
||||||
|
const store = createMockStore(
|
||||||
|
{ id: "FN-050", worktree: "/tmp/root/.worktrees/KB-050" },
|
||||||
|
[{ id: "FN-050", worktree: "/tmp/root/.worktrees/KB-050", column: "in-review" } as Task],
|
||||||
|
);
|
||||||
|
(store.getSettings as ReturnType<typeof vi.fn>).mockResolvedValue({
|
||||||
|
...DEFAULT_SETTINGS,
|
||||||
|
buildCommand: "pnpm build",
|
||||||
|
verificationFixRetries: 1,
|
||||||
|
buildRetryCount: 0,
|
||||||
|
});
|
||||||
|
|
||||||
|
await expect(aiMergeTask(store, "/tmp/root", "FN-050")).rejects.toThrow(
|
||||||
|
"Build verification failed for FN-050: Type error in src/build.ts",
|
||||||
|
);
|
||||||
|
|
||||||
|
const logMessages = (store.logEntry as ReturnType<typeof vi.fn>).mock.calls
|
||||||
|
.map((call: any[]) => call[1])
|
||||||
|
.filter((message: unknown): message is string => typeof message === "string");
|
||||||
|
|
||||||
|
const rerunIdx = logMessages.findIndex((message) =>
|
||||||
|
message.includes("Re-running deterministic merge verification (attempt 1)"),
|
||||||
|
);
|
||||||
|
expect(rerunIdx).toBeGreaterThan(-1);
|
||||||
|
|
||||||
|
const verificationAfterRerunIdx = logMessages.findIndex(
|
||||||
|
(message, index) =>
|
||||||
|
index > rerunIdx && message.includes("[verification] Running build command: pnpm build"),
|
||||||
|
);
|
||||||
|
expect(verificationAfterRerunIdx).toBeGreaterThan(rerunIdx);
|
||||||
|
});
|
||||||
|
|
||||||
it("verification fix is skipped when verificationFixRetries is 0", async () => {
|
it("verification fix is skipped when verificationFixRetries is 0", async () => {
|
||||||
mockedExecSync.mockImplementation((cmd: any) => {
|
mockedExecSync.mockImplementation((cmd: any) => {
|
||||||
const cmdStr = String(cmd);
|
const cmdStr = String(cmd);
|
||||||
|
|||||||
@@ -655,12 +655,22 @@ async function attemptInMergeVerificationFix(
|
|||||||
},
|
},
|
||||||
settings: Settings,
|
settings: Settings,
|
||||||
options: MergerOptions,
|
options: MergerOptions,
|
||||||
|
mergeRunContext?: Pick<EngineRunContext, "runId" | "agentId">,
|
||||||
|
fixAttemptNumber?: number,
|
||||||
_testCommand?: string,
|
_testCommand?: string,
|
||||||
_buildCommand?: string,
|
_buildCommand?: string,
|
||||||
): Promise<boolean> {
|
): Promise<boolean> {
|
||||||
try {
|
try {
|
||||||
mergerLog.log(`${taskId}: spawning in-merge verification fix agent`);
|
mergerLog.log(`${taskId}: spawning in-merge verification fix agent`);
|
||||||
|
|
||||||
|
const logger = new AgentLogger({
|
||||||
|
store,
|
||||||
|
taskId,
|
||||||
|
agent: "merger",
|
||||||
|
onAgentText: options.onAgentText,
|
||||||
|
onAgentTool: options.onAgentTool,
|
||||||
|
});
|
||||||
|
|
||||||
// Build skill selection context
|
// Build skill selection context
|
||||||
let skillContext = undefined;
|
let skillContext = undefined;
|
||||||
if (options.agentStore) {
|
if (options.agentStore) {
|
||||||
@@ -694,6 +704,10 @@ A merge has been applied and the verification command failed. Your job is to fix
|
|||||||
5. Do NOT modify files unrelated to the failure
|
5. Do NOT modify files unrelated to the failure
|
||||||
6. If you cannot fix the issue, explain why`,
|
6. If you cannot fix the issue, explain why`,
|
||||||
tools: "coding", // Agent needs read/write file access
|
tools: "coding", // Agent needs read/write file access
|
||||||
|
onText: logger.onText,
|
||||||
|
onThinking: logger.onThinking,
|
||||||
|
onToolStart: logger.onToolStart,
|
||||||
|
onToolEnd: logger.onToolEnd,
|
||||||
defaultProvider: settings.defaultProvider,
|
defaultProvider: settings.defaultProvider,
|
||||||
defaultModelId: settings.defaultModelId,
|
defaultModelId: settings.defaultModelId,
|
||||||
defaultThinkingLevel: settings.defaultThinkingLevel,
|
defaultThinkingLevel: settings.defaultThinkingLevel,
|
||||||
@@ -701,6 +715,13 @@ A merge has been applied and the verification command failed. Your job is to fix
|
|||||||
...(skillContext?.skillSelectionContext ? { skillSelection: skillContext.skillSelectionContext } : {}),
|
...(skillContext?.skillSelectionContext ? { skillSelection: skillContext.skillSelectionContext } : {}),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const runId = mergeRunContext?.runId;
|
||||||
|
const agentId = mergeRunContext?.agentId ?? "merger";
|
||||||
|
await store.logEntry(
|
||||||
|
taskId,
|
||||||
|
`In-merge verification fix agent started (model: ${describeModel(session)}, runId: ${runId ?? "unknown"}, agentId: ${agentId})`,
|
||||||
|
);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Build the fix prompt
|
// Build the fix prompt
|
||||||
const fixPrompt = `Fix the failing ${failureContext.type} verification for task ${taskId}.
|
const fixPrompt = `Fix the failing ${failureContext.type} verification for task ${taskId}.
|
||||||
@@ -729,14 +750,19 @@ ${failureContext.output.slice(0, VERIFICATION_LOG_MAX_CHARS)}
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
// Re-run the verification command that failed
|
// Re-run deterministic verification command after the fix attempt.
|
||||||
|
await store.logEntry(
|
||||||
|
taskId,
|
||||||
|
`Re-running deterministic merge verification (attempt ${fixAttemptNumber ?? "unknown"})`,
|
||||||
|
);
|
||||||
const reRunResult = await runVerificationCommand(
|
const reRunResult = await runVerificationCommand(
|
||||||
store, rootDir, taskId, failureContext.command, failureContext.type,
|
store, rootDir, taskId, failureContext.command, failureContext.type,
|
||||||
);
|
);
|
||||||
|
|
||||||
return reRunResult.success;
|
return reRunResult.success;
|
||||||
} finally {
|
} finally {
|
||||||
// Always dispose the session
|
// Flush buffered output before disposal so fix-attempt activity is visible.
|
||||||
|
await logger.flush();
|
||||||
await session.dispose();
|
await session.dispose();
|
||||||
}
|
}
|
||||||
} catch (err: unknown) {
|
} catch (err: unknown) {
|
||||||
@@ -2089,7 +2115,12 @@ export async function aiMergeTask(
|
|||||||
output: summarizeVerificationOutput(failedResult.stderr || failedResult.stdout, failedType),
|
output: summarizeVerificationOutput(failedResult.stderr || failedResult.stdout, failedType),
|
||||||
type: failedType,
|
type: failedType,
|
||||||
},
|
},
|
||||||
settings, options, effectiveTestCommand, effectiveBuildCommand,
|
settings,
|
||||||
|
options,
|
||||||
|
{ runId: mergeRunId, agentId: engineRunContext.agentId },
|
||||||
|
fixAttempt,
|
||||||
|
effectiveTestCommand,
|
||||||
|
effectiveBuildCommand,
|
||||||
);
|
);
|
||||||
|
|
||||||
const fixAttemptDurationMs = Date.now() - fixAttemptStartedAt;
|
const fixAttemptDurationMs = Date.now() - fixAttemptStartedAt;
|
||||||
@@ -2143,7 +2174,12 @@ export async function aiMergeTask(
|
|||||||
output: error.message || "Build verification failed",
|
output: error.message || "Build verification failed",
|
||||||
type: fixType,
|
type: fixType,
|
||||||
},
|
},
|
||||||
settings, options, effectiveTestCommand, effectiveBuildCommand,
|
settings,
|
||||||
|
options,
|
||||||
|
{ runId: mergeRunId, agentId: engineRunContext.agentId },
|
||||||
|
fixAttempt,
|
||||||
|
effectiveTestCommand,
|
||||||
|
effectiveBuildCommand,
|
||||||
);
|
);
|
||||||
|
|
||||||
const fixAttemptDurationMs = Date.now() - fixAttemptStartedAt;
|
const fixAttemptDurationMs = Date.now() - fixAttemptStartedAt;
|
||||||
|
|||||||
Reference in New Issue
Block a user