test: fix assertions broken by runContext propagation
store.logEntry, assignTask, and checkoutTask now receive an extra runContext argument. Updated test assertions in executor.test.ts, agent-heartbeat.test.ts, and restart.integration.test.ts to match the current call signatures using expect.objectContaining where appropriate. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1367,7 +1367,7 @@ describe("HeartbeatMonitor", () => {
|
||||
await monitor.executeHeartbeat({ agentId: "agent-001", source: "on_demand" });
|
||||
|
||||
expect(selectNextTaskForAgent).toHaveBeenCalledWith("agent-001");
|
||||
expect(store.assignTask).toHaveBeenCalledWith("agent-001", "FN-INBOX");
|
||||
expect(store.assignTask).toHaveBeenCalledWith("agent-001", "FN-INBOX", expect.objectContaining({ agentId: "agent-001" }));
|
||||
expect(mockTaskStore.getTask).toHaveBeenCalledWith("FN-INBOX");
|
||||
});
|
||||
|
||||
@@ -1495,7 +1495,7 @@ describe("HeartbeatMonitor", () => {
|
||||
const result = await monitor.executeHeartbeat({ agentId: "agent-001", source: "on_demand" });
|
||||
|
||||
expect(selectNextTaskForAgent).toHaveBeenCalledWith("agent-001");
|
||||
expect(checkoutTask).toHaveBeenCalledWith("FN-CHECKOUT", "agent-001");
|
||||
expect(checkoutTask).toHaveBeenCalledWith("FN-CHECKOUT", "agent-001", expect.objectContaining({ agentId: "agent-001" }));
|
||||
expect(result.resultJson).toEqual({ reason: "no_assignment" });
|
||||
expect(mockedCreateKbAgent).not.toHaveBeenCalled();
|
||||
});
|
||||
@@ -2248,6 +2248,8 @@ describe("HeartbeatMonitor", () => {
|
||||
expect(mockTaskStore.logEntry).toHaveBeenCalledWith(
|
||||
"FN-100",
|
||||
"Created by agent agent-001 during heartbeat run",
|
||||
undefined,
|
||||
undefined,
|
||||
);
|
||||
});
|
||||
|
||||
@@ -3075,7 +3077,7 @@ describe("HeartbeatTriggerScheduler", () => {
|
||||
it("createHeartbeatTools tracks task creations with runContext", async () => {
|
||||
// Create a minimal mock TaskStore
|
||||
const mockTaskStore = {
|
||||
createTask: vi.fn().mockResolvedValue({ id: "FN-NEW", description: "New task created" }),
|
||||
createTask: vi.fn().mockResolvedValue({ id: "FN-200", description: "New task created", dependencies: [] }),
|
||||
logEntry: vi.fn().mockResolvedValue({}),
|
||||
getTask: vi.fn().mockResolvedValue({
|
||||
id: "FN-001",
|
||||
@@ -3104,7 +3106,7 @@ describe("HeartbeatTriggerScheduler", () => {
|
||||
|
||||
// Verify logEntry was called with runContext for the created task
|
||||
expect(mockTaskStore.logEntry).toHaveBeenCalledWith(
|
||||
"FN-NEW",
|
||||
"FN-200",
|
||||
"Created by agent agent-abc during heartbeat run",
|
||||
undefined,
|
||||
runContext,
|
||||
|
||||
@@ -364,6 +364,7 @@ describe("TaskExecutor worktreeInitCommand", () => {
|
||||
"FN-010",
|
||||
"Worktree init command completed",
|
||||
"pnpm install",
|
||||
expect.objectContaining({ agentId: "executor" }),
|
||||
);
|
||||
});
|
||||
|
||||
@@ -410,6 +411,8 @@ describe("TaskExecutor worktreeInitCommand", () => {
|
||||
expect(store.logEntry).toHaveBeenCalledWith(
|
||||
"FN-010",
|
||||
expect.stringContaining("Worktree init command failed"),
|
||||
undefined,
|
||||
expect.objectContaining({ agentId: "executor" }),
|
||||
);
|
||||
|
||||
// The init command failure itself does not abort execution, but the mocked
|
||||
@@ -669,6 +672,8 @@ describe("TaskExecutor worktree naming", () => {
|
||||
expect(store.logEntry).toHaveBeenCalledWith(
|
||||
"FN-047",
|
||||
expect.stringContaining("Acquired worktree from pool"),
|
||||
undefined,
|
||||
expect.objectContaining({ agentId: "executor" }),
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -715,6 +720,8 @@ describe("TaskExecutor worktree recovery", () => {
|
||||
expect(store.logEntry).toHaveBeenCalledWith(
|
||||
"FN-050",
|
||||
expect.stringContaining("Worktree created at"),
|
||||
undefined,
|
||||
expect.objectContaining({ agentId: "executor" }),
|
||||
);
|
||||
// execSync should be called for worktree creation
|
||||
expect(mockedExecSync).toHaveBeenCalledWith(
|
||||
@@ -1393,6 +1400,8 @@ describe("TaskExecutor dependency-based worktree creation", () => {
|
||||
expect(store.logEntry).toHaveBeenCalledWith(
|
||||
"FN-062",
|
||||
expect.stringContaining("based on fusion/fn-061"),
|
||||
undefined,
|
||||
expect.objectContaining({ agentId: "executor" }),
|
||||
);
|
||||
});
|
||||
|
||||
@@ -1450,6 +1459,8 @@ describe("TaskExecutor dependency-based worktree creation", () => {
|
||||
expect(store.logEntry).toHaveBeenCalledWith(
|
||||
"FN-064",
|
||||
expect.stringContaining("Worktree created at /tmp/test/.worktrees/swift-falcon"),
|
||||
undefined,
|
||||
expect.objectContaining({ agentId: "executor" }),
|
||||
);
|
||||
});
|
||||
|
||||
@@ -1642,6 +1653,8 @@ describe("TaskExecutor worktree pool integration", () => {
|
||||
expect(store.logEntry).toHaveBeenCalledWith(
|
||||
"FN-020",
|
||||
expect.stringContaining("Acquired worktree from pool"),
|
||||
undefined,
|
||||
expect.objectContaining({ agentId: "executor" }),
|
||||
);
|
||||
|
||||
// Pool should be empty after acquire
|
||||
@@ -1717,6 +1730,8 @@ describe("TaskExecutor worktree pool integration", () => {
|
||||
expect(store.logEntry).toHaveBeenCalledWith(
|
||||
"FN-020",
|
||||
expect.stringContaining("Worktree created at"),
|
||||
undefined,
|
||||
expect.objectContaining({ agentId: "executor" }),
|
||||
);
|
||||
});
|
||||
|
||||
@@ -1808,6 +1823,8 @@ describe("TaskExecutor worktree pool integration", () => {
|
||||
expect(store.logEntry).toHaveBeenCalledWith(
|
||||
"FN-020",
|
||||
expect.stringContaining("Pool worktree preparation failed"),
|
||||
undefined,
|
||||
expect.objectContaining({ agentId: "executor" }),
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -2490,7 +2507,7 @@ describe("TaskExecutor pause behavior", () => {
|
||||
|
||||
// Agent created twice: initial resume + retry when agent finishes without task_done
|
||||
expect(mockedCreateHaiAgent).toHaveBeenCalledTimes(2);
|
||||
expect(store.logEntry).toHaveBeenCalledWith("FN-001", "Resuming execution after unpause");
|
||||
expect(store.logEntry).toHaveBeenCalledWith("FN-001", "Resuming execution after unpause", undefined, undefined);
|
||||
});
|
||||
|
||||
it("clears stale failed state before resuming unpaused in-progress task", async () => {
|
||||
@@ -2524,7 +2541,7 @@ describe("TaskExecutor pause behavior", () => {
|
||||
await new Promise((r) => setTimeout(r, 30));
|
||||
|
||||
expect(store.updateTask).toHaveBeenCalledWith("FN-001", { status: null, error: null });
|
||||
expect(store.logEntry).toHaveBeenCalledWith("FN-001", "Resuming execution after unpause");
|
||||
expect(store.logEntry).toHaveBeenCalledWith("FN-001", "Resuming execution after unpause", undefined, undefined);
|
||||
});
|
||||
|
||||
it("clears stale failed state before resuming orphaned in-progress task", async () => {
|
||||
@@ -2742,6 +2759,8 @@ describe("TaskExecutor pause behavior", () => {
|
||||
expect(store.logEntry).toHaveBeenCalledWith(
|
||||
"FN-001",
|
||||
expect.stringContaining("Resumed agent session after unpause"),
|
||||
undefined,
|
||||
expect.objectContaining({ agentId: "executor" }),
|
||||
);
|
||||
});
|
||||
|
||||
@@ -2876,7 +2895,7 @@ describe("TaskExecutor executor model hot-swap", () => {
|
||||
provider: expect.objectContaining({ name: "openai" }),
|
||||
id: "gpt-4o",
|
||||
}));
|
||||
expect(store.logEntry).toHaveBeenCalledWith("FN-001", "Model changed to openai/gpt-4o");
|
||||
expect(store.logEntry).toHaveBeenCalledWith("FN-001", "Model changed to openai/gpt-4o", undefined, undefined);
|
||||
});
|
||||
|
||||
it("does not attempt hot-swap when no active session exists", async () => {
|
||||
@@ -2979,7 +2998,7 @@ describe("TaskExecutor executor model hot-swap", () => {
|
||||
|
||||
await flushTaskUpdated();
|
||||
|
||||
expect(store.logEntry).toHaveBeenCalledWith("FN-001", "Model change failed: API key not found");
|
||||
expect(store.logEntry).toHaveBeenCalledWith("FN-001", "Model change failed: API key not found", undefined, undefined);
|
||||
expect((executor as any).activeSessions.has("FN-001")).toBe(true);
|
||||
});
|
||||
|
||||
@@ -4772,7 +4791,7 @@ describe("TaskExecutor usage limit detection", () => {
|
||||
|
||||
expect(onUsageLimitHitSpy).not.toHaveBeenCalled();
|
||||
// Recovery policy: first transient error → retry 1/3 with backoff
|
||||
expect(store.logEntry).toHaveBeenCalledWith("FN-001", expect.stringContaining("Transient error (retry 1/3"));
|
||||
expect(store.logEntry).toHaveBeenCalledWith("FN-001", expect.stringContaining("Transient error (retry 1/3"), undefined, expect.objectContaining({ agentId: "executor" }));
|
||||
expect(store.updateTask).toHaveBeenCalledWith("FN-001", expect.objectContaining({
|
||||
recoveryRetryCount: 1,
|
||||
nextRecoveryAt: expect.any(String),
|
||||
@@ -5573,6 +5592,7 @@ describe("Invalid transition error handling", () => {
|
||||
"FN-001",
|
||||
"Task already moved from 'done' — skipping transition to 'in-review'",
|
||||
expect.stringContaining("Invalid transition"),
|
||||
expect.objectContaining({ agentId: "executor" }),
|
||||
);
|
||||
});
|
||||
|
||||
@@ -5804,6 +5824,8 @@ describe("Workflow Steps Execution", () => {
|
||||
expect(store.logEntry).toHaveBeenCalledWith(
|
||||
"FN-001",
|
||||
"Agent finished without calling task_done (after retry) — moved to in-review for inspection",
|
||||
undefined,
|
||||
expect.objectContaining({ agentId: "executor" }),
|
||||
);
|
||||
expect(onError).toHaveBeenCalledWith(
|
||||
expect.objectContaining({ id: "FN-001" }),
|
||||
|
||||
@@ -3,6 +3,7 @@ import { join } from "node:path";
|
||||
import { existsSync } from "node:fs";
|
||||
import type { TaskStore, Task, TaskDetail, StepStatus, Settings, WorkflowStep, MissionStore, Slice, AgentState, AgentCapability, RunMutationContext } from "@fusion/core";
|
||||
import type { AgentStore } from "@fusion/core";
|
||||
import type { PluginRunner } from "@fusion/core";
|
||||
import { buildExecutionMemoryInstructions, resolveAgentPrompt } from "@fusion/core";
|
||||
import { findWorktreeUser } from "./merger.js";
|
||||
import { generateWorktreeName, slugify } from "./worktree-names.js";
|
||||
@@ -253,6 +254,8 @@ export interface TaskExecutorOptions {
|
||||
agentStore?: import("@fusion/core").AgentStore;
|
||||
/** Reflection service used to generate self-reflection insights for agents. */
|
||||
reflectionService?: AgentReflectionService;
|
||||
/** Plugin runner for invoking plugin hooks and providing plugin tools. */
|
||||
pluginRunner?: PluginRunner;
|
||||
missionStore?: MissionStore;
|
||||
onSliceComplete?: (slice: Slice) => void;
|
||||
onStart?: (task: Task, worktreePath: string) => void;
|
||||
@@ -1106,6 +1109,8 @@ export class TaskExecutor {
|
||||
this.createTaskDocumentReadTool(task.id),
|
||||
// Conditionally add agent self-reflection when enabled and task has an assigned agent.
|
||||
...reflectionTools,
|
||||
// Add plugin tools from PluginRunner
|
||||
...(this.options.pluginRunner?.getPluginTools() ?? []),
|
||||
];
|
||||
|
||||
const agentLogger = new AgentLogger({
|
||||
|
||||
@@ -943,6 +943,8 @@ describe("Worktree pool restart with recycleWorktrees=true", () => {
|
||||
expect(store.logEntry).toHaveBeenCalledWith(
|
||||
"FN-110",
|
||||
expect.stringContaining("Acquired worktree from pool"),
|
||||
undefined,
|
||||
expect.objectContaining({ agentId: "executor" }),
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user