Files
ai_ops/tests/provider-executor.test.ts
2026-02-24 18:57:20 -05:00

146 lines
4.1 KiB
TypeScript

import test from "node:test";
import assert from "node:assert/strict";
import { mkdtemp } from "node:fs/promises";
import { tmpdir } from "node:os";
import { resolve } from "node:path";
import { loadConfig } from "../src/config.js";
import type { ActorExecutionInput } from "../src/agents/pipeline.js";
import {
buildProviderRuntimeEnv,
createProviderRunRuntime,
parseActorExecutionResultFromModelOutput,
resolveProviderWorkingDirectory,
type ProviderRunRuntime,
} from "../src/ui/provider-executor.js";
test("parseActorExecutionResultFromModelOutput parses strict JSON payload", () => {
const parsed = parseActorExecutionResultFromModelOutput({
rawText: JSON.stringify({
status: "validation_fail",
payload: {
summary: "missing test",
},
stateFlags: {
needs_fix: true,
},
stateMetadata: {
stage: "qa",
},
events: [
{
type: "validation_failed",
payload: {
summary: "failed",
},
},
],
failureKind: "soft",
failureCode: "missing_test",
}),
});
assert.equal(parsed.status, "validation_fail");
assert.equal(parsed.payload?.summary, "missing test");
assert.equal(parsed.stateFlags?.needs_fix, true);
assert.equal(parsed.stateMetadata?.stage, "qa");
assert.equal(parsed.events?.[0]?.type, "validation_failed");
assert.equal(parsed.failureKind, "soft");
assert.equal(parsed.failureCode, "missing_test");
});
test("parseActorExecutionResultFromModelOutput parses fenced JSON", () => {
const parsed = parseActorExecutionResultFromModelOutput({
rawText: [
"Here is the result",
"```json",
JSON.stringify({
status: "success",
payload: {
code: "done",
},
}),
"```",
].join("\n"),
});
assert.equal(parsed.status, "success");
assert.equal(parsed.payload?.code, "done");
});
test("parseActorExecutionResultFromModelOutput falls back when response is not JSON", () => {
const parsed = parseActorExecutionResultFromModelOutput({
rawText: "Implemented update successfully.",
});
assert.equal(parsed.status, "success");
assert.equal(parsed.payload?.assistantResponse, "Implemented update successfully.");
});
test("resolveProviderWorkingDirectory reads cwd from actor execution context", () => {
const actorInput = {
executionContext: {
security: {
worktreePath: "/tmp/session/tasks/product-intake",
},
},
} as unknown as ActorExecutionInput;
assert.equal(
resolveProviderWorkingDirectory(actorInput),
"/tmp/session/tasks/product-intake",
);
});
test("buildProviderRuntimeEnv scopes AGENT_WORKTREE_PATH to actor worktree and filters undefined auth", () => {
const config = loadConfig({
CLAUDE_CODE_OAUTH_TOKEN: "oauth-token",
});
const runtime = {
provider: "claude",
config,
sharedEnv: {
PATH: "/usr/bin",
KEEP_ME: "1",
},
claudeObservability: {} as ProviderRunRuntime["claudeObservability"],
close: async () => {},
} satisfies ProviderRunRuntime;
const actorInput = {
executionContext: {
security: {
worktreePath: "/tmp/session/tasks/product-intake",
},
},
} as unknown as ActorExecutionInput;
const env = buildProviderRuntimeEnv({
runtime,
actorInput,
includeClaudeAuth: true,
});
assert.equal(env.AGENT_WORKTREE_PATH, "/tmp/session/tasks/product-intake");
assert.equal(env.CLAUDE_CODE_OAUTH_TOKEN, "oauth-token");
assert.equal("ANTHROPIC_API_KEY" in env, false);
assert.equal(env.KEEP_ME, "1");
});
test("createProviderRunRuntime does not require session context provisioning", async () => {
const observabilityRoot = await mkdtemp(resolve(tmpdir(), "ai-ops-provider-runtime-"));
const runtime = await createProviderRunRuntime({
provider: "claude",
config: loadConfig({}),
observabilityRootPath: observabilityRoot,
baseEnv: {
PATH: "/usr/bin",
},
});
try {
assert.equal(runtime.provider, "claude");
assert.equal(runtime.sharedEnv.PATH, "/usr/bin");
} finally {
await runtime.close();
}
});