Add tests, free-text model field, and K8s job improvements

- Add vitest with 26 passing tests for parse and job-manifest
- Set models to undefined for free-text model input
- Add fsGroupChangePolicy: "OnRootMismatch" to reduce volume chown delays
- Change job name prefix to agent-opencode- for adapter identification
- Add .npmrc to .gitignore

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-12 08:14:57 -04:00
parent 75f54b1edc
commit 6866da42bf
8 changed files with 1556 additions and 17 deletions
+140
View File
@@ -0,0 +1,140 @@
import { describe, it, expect } from "vitest";
import { parseOpenCodeJsonl, isOpenCodeUnknownSessionError } from "./parse.js";
describe("parseOpenCodeJsonl", () => {
it("parses text messages", () => {
const stdout = [
JSON.stringify({ type: "text", part: { text: "Hello" }, sessionID: "ses_123" }),
JSON.stringify({ type: "text", part: { text: "World" }, sessionID: "ses_123" }),
].join("\n");
const result = parseOpenCodeJsonl(stdout);
expect(result.sessionId).toBe("ses_123");
expect(result.summary).toBe("Hello\n\nWorld");
expect(result.errorMessage).toBeNull();
});
it("accumulates usage from step_finish events", () => {
const stdout = [
JSON.stringify({
type: "step_finish",
part: { tokens: { input: 100, output: 50, reasoning: 20, cache: { read: 80 } }, cost: 0.001 },
}),
].join("\n");
const result = parseOpenCodeJsonl(stdout);
expect(result.usage.inputTokens).toBe(100);
expect(result.usage.cachedInputTokens).toBe(80);
expect(result.usage.outputTokens).toBe(70);
expect(result.costUsd).toBeCloseTo(0.001);
});
it("captures errors from error type events", () => {
const stdout = [
JSON.stringify({ type: "error", error: { message: "Something went wrong" } }),
].join("\n");
const result = parseOpenCodeJsonl(stdout);
expect(result.errorMessage).toBe("Something went wrong");
});
it("captures tool_use errors with error state", () => {
const stdout = [
JSON.stringify({
type: "tool_use",
part: { state: { status: "error", error: "Tool failed" } },
}),
].join("\n");
const result = parseOpenCodeJsonl(stdout);
expect(result.errorMessage).toBe("Tool failed");
});
it("extracts sessionId from any event", () => {
const stdout = [
JSON.stringify({ type: "text", part: { text: "Hi" }, sessionID: "ses_abc" }),
].join("\n");
const result = parseOpenCodeJsonl(stdout);
expect(result.sessionId).toBe("ses_abc");
});
it("handles empty stdout", () => {
const result = parseOpenCodeJsonl("");
expect(result.sessionId).toBeNull();
expect(result.summary).toBe("");
expect(result.errorMessage).toBeNull();
});
it("skips malformed JSON lines", () => {
const stdout = [
"not json at all",
JSON.stringify({ type: "text", part: { text: "Valid" }, sessionID: "ses_1" }),
"",
].join("\n");
const result = parseOpenCodeJsonl(stdout);
expect(result.summary).toBe("Valid");
});
it("combines multiple errors", () => {
const stdout = [
JSON.stringify({ type: "error", error: { message: "Error 1" } }),
JSON.stringify({ type: "error", error: { message: "Error 2" } }),
].join("\n");
const result = parseOpenCodeJsonl(stdout);
expect(result.errorMessage).toBe("Error 1\nError 2");
});
it("parses nested error message in data field", () => {
const stdout = [
JSON.stringify({ type: "error", error: { data: { message: "Nested error" } } }),
].join("\n");
const result = parseOpenCodeJsonl(stdout);
expect(result.errorMessage).toBe("Nested error");
});
});
describe("isOpenCodeUnknownSessionError", () => {
it("detects 'unknown session' in stdout", () => {
const stdout = "Error: unknown session";
expect(isOpenCodeUnknownSessionError(stdout, "")).toBe(true);
});
it("detects 'session not found' in stdout", () => {
const stdout = "session not found";
expect(isOpenCodeUnknownSessionError(stdout, "")).toBe(true);
});
it("detects 'resource not found' with session path in stdout", () => {
const stdout = "resource not found: /session/abc.json";
expect(isOpenCodeUnknownSessionError(stdout, "")).toBe(true);
});
it("detects 'no session' in combined output", () => {
const stdout = "";
const stderr = "no session available";
expect(isOpenCodeUnknownSessionError(stdout, stderr)).toBe(true);
});
it("returns false for normal errors", () => {
const stdout = "Something went wrong";
expect(isOpenCodeUnknownSessionError(stdout, "")).toBe(false);
});
it("handles case insensitivity", () => {
const stdout = "UNKNOWN SESSION";
expect(isOpenCodeUnknownSessionError(stdout, "")).toBe(true);
});
});