Files
shockbot/utils/apiKeys.test.ts
T
Colin McDonnell 4b3c5ca905 rename agent key to opencode and add skill invocation coverage (#541)
* rename agent key to opencode and add skill invocation coverage.

add skill-invoke tests for claude and opencode with local play-based validation signals, update CI matrices, and include the current tracked refactors in this branch for review.

Made-with: Cursor

* exclude agent-specific skill-invoke tests from wrong agent in CI matrix

* address review follow-up and preserve workflow run UI tweak.

switch changed-agents ci coverage to exercise the opentoad implementation path while keeping the opencode expectation, and include the local workflow run client interaction updates requested on this branch.

Made-with: Cursor

* remove opentoad agent filename from runtime.

rename the opencode harness implementation file from opentoad.ts to opencode.ts and update ci coverage input accordingly so action code no longer carries the old filename.

Made-with: Cursor

* ensure security prompt bypass is set on every test fixture.

this keeps adversarial and permissions harnesses from being blocked by the default prompt-injection refusal path during CI security tests.

Made-with: Cursor

---------

Co-authored-by: pullfrog[bot] <226033991+pullfrog[bot]@users.noreply.github.com>
2026-04-15 19:38:36 +00:00

76 lines
2.4 KiB
TypeScript

import { afterEach, beforeEach, describe, expect, it } from "vitest";
import { validateAgentApiKey } from "./apiKeys.ts";
const base = {
agent: { name: "opencode" },
owner: "test-owner",
name: "test-repo",
};
const savedEnv = { ...process.env };
beforeEach(() => {
// strip all known provider keys so tests start clean
for (const key of Object.keys(process.env)) {
if (key.endsWith("_API_KEY") || key === "CLAUDE_CODE_OAUTH_TOKEN") delete process.env[key];
}
});
afterEach(() => {
process.env = { ...savedEnv };
});
describe("validateAgentApiKey", () => {
describe("free model (no keys required)", () => {
it("passes with zero env keys", () => {
expect(() => validateAgentApiKey({ ...base, model: "opencode/big-pickle" })).not.toThrow();
});
it("passes for other free opencode models", () => {
for (const slug of [
"opencode/gpt-5-nano",
"opencode/mimo-v2-pro-free",
"opencode/minimax-m2.5-free",
"opencode/nemotron-3-super-free",
]) {
expect(() => validateAgentApiKey({ ...base, model: slug })).not.toThrow();
}
});
});
describe("keyed model", () => {
it("passes when the required key is present", () => {
process.env.ANTHROPIC_API_KEY = "sk-test";
expect(() => validateAgentApiKey({ ...base, model: "anthropic/claude-opus" })).not.toThrow();
});
it("throws when the required key is missing", () => {
expect(() => validateAgentApiKey({ ...base, model: "anthropic/claude-opus" })).toThrow(
"no API key found"
);
});
it("passes for opencode keyed model with OPENCODE_API_KEY", () => {
process.env.OPENCODE_API_KEY = "sk-test";
expect(() => validateAgentApiKey({ ...base, model: "opencode/claude-opus" })).not.toThrow();
});
it("throws for opencode keyed model without OPENCODE_API_KEY", () => {
expect(() => validateAgentApiKey({ ...base, model: "opencode/claude-opus" })).toThrow(
"no API key found"
);
});
});
describe("no model (auto-select)", () => {
it("passes when any known provider key is present", () => {
process.env.OPENAI_API_KEY = "sk-test";
expect(() => validateAgentApiKey({ ...base, model: undefined })).not.toThrow();
});
it("throws when no provider keys are present", () => {
expect(() => validateAgentApiKey({ ...base, model: undefined })).toThrow("no API key found");
});
});
});