a8b50f5109
---ci---
project: ci
phase: 6
milestone: v0.9
status: complete
artifacts:
tags: [v0.9.0]
decisions:
- id: D-047
decision: v0.9 theme = Distribution & Expansion
rationale: npm publish + OpenAI/Anthropic backends + agent flesh + parallel execution
confidence: 0.92
- id: D-049
decision: Feature milestone — patch tags v0.8.1-v0.8.6 then v0.9.0
rationale: OpenAI backend, agent flesh, npm publish all feat
confidence: 0.95
- id: D-059
decision: Rename OllamaBaseBackend to LLMBaseBackend + thin OllamaBaseBackend subclass
rationale: 15 of 17 methods backend-agnostic
confidence: 0.92
- id: D-060
decision: OpenAI/Anthropic backends use native fetch() not SDK packages
rationale: No dependency bloat; fetch native in Node 18+
confidence: 0.85
- id: D-066
decision: Concurrency limiter internal (no p-limit dependency)
rationale: 15 lines; avoids dependency for trivial feature
confidence: 0.90
- id: D-067
decision: Promise.allSettled for review agents at orchestrator lines 373-400
rationale: Current sequential loop replaced with parallel execution
confidence: 0.88
requirements:
covered: [PUBLISH-01, PUBLISH-02, PUBLISH-03, PUBLISH-04, OPENAI-01, OPENAI-02, OPENAI-03, OPENAI-04, OPENAI-05, FLESH-01, FLESH-02, FLESH-03, FLESH-04, FLESH-05, ANTHROPIC-01, ANTHROPIC-02, FLESH-06, FLESH-07, NPM-01, NPM-02, PARALLEL-01, PARALLEL-02, PARALLEL-03, INTEG-01, INTEG-02, INTEG-03, INTEG-04, INTEG-05]
---/ci---
6 phases, 28 tasks, 4077 net lines added, 57 test suites, 527 tests, zero stub agents
104 lines
3.7 KiB
TypeScript
104 lines
3.7 KiB
TypeScript
import { OllamaLocalBackend } from "../backends/ollama-local.js";
|
|
import { OllamaCloudBackend } from "../backends/ollama-cloud.js";
|
|
import { OpencodeBackend } from "../backends/opencode.js";
|
|
import { resolveBackend, createBackend } from "../backends/index.js";
|
|
import { DEFAULT_BACKEND_CONFIG, BackendUnavailableError } from "../backends/types.js";
|
|
|
|
describe("Backend Availability Detection", () => {
|
|
describe("OllamaLocalBackend.isAvailable", () => {
|
|
it("returns false for unreachable host", async () => {
|
|
const backend = new OllamaLocalBackend({
|
|
base_url: "http://localhost:1",
|
|
model_profile: "balanced",
|
|
});
|
|
expect(await backend.isAvailable()).toBe(false);
|
|
});
|
|
|
|
it("returns false for invalid URL", async () => {
|
|
const backend = new OllamaLocalBackend({
|
|
base_url: "not-a-url",
|
|
model_profile: "balanced",
|
|
});
|
|
expect(await backend.isAvailable()).toBe(false);
|
|
});
|
|
|
|
it("returns false for timeout", async () => {
|
|
const backend = new OllamaLocalBackend({
|
|
base_url: "http://192.0.2.1",
|
|
model_profile: "balanced",
|
|
});
|
|
expect(await backend.isAvailable()).toBe(false);
|
|
}, 10000);
|
|
});
|
|
|
|
describe("OllamaCloudBackend.isAvailable", () => {
|
|
it("returns false when base_url is empty", async () => {
|
|
const backend = new OllamaCloudBackend({
|
|
base_url: "",
|
|
api_key_env: "OLLAMA_CLOUD_API_KEY",
|
|
model_profile: "quality",
|
|
});
|
|
expect(await backend.isAvailable()).toBe(false);
|
|
});
|
|
|
|
it("returns false when no API key in env", async () => {
|
|
const backend = new OllamaCloudBackend({
|
|
base_url: "https://api.example.com",
|
|
api_key_env: "NONEXISTENT_ENV_VAR_12345",
|
|
model_profile: "quality",
|
|
timeout_ms: 5000,
|
|
});
|
|
expect(await backend.isAvailable()).toBe(false);
|
|
});
|
|
});
|
|
|
|
describe("OpencodeBackend.isAvailable", () => {
|
|
it("returns false when executable not found", async () => {
|
|
const backend = new OpencodeBackend({
|
|
enabled: true,
|
|
executable: "nonexistent-opencode-binary-xyz",
|
|
});
|
|
expect(await backend.isAvailable()).toBe(false);
|
|
});
|
|
|
|
it("returns false when disabled", async () => {
|
|
const backend = new OpencodeBackend({ enabled: false });
|
|
expect(await backend.isAvailable()).toBe(false);
|
|
});
|
|
});
|
|
|
|
describe("resolveBackend auto-detection", () => {
|
|
it("throws BackendUnavailableError when no backends available", async () => {
|
|
const config = {
|
|
...DEFAULT_BACKEND_CONFIG,
|
|
llm_backends: {
|
|
"ollama-local": { base_url: "http://localhost:1", model_profile: "balanced" as const },
|
|
"ollama-cloud": { base_url: "", api_key_env: "NONEXISTENT_12345", model_profile: "quality" as const },
|
|
},
|
|
agent_backends: {
|
|
opencode: { enabled: true, executable: "nonexistent-opencode-binary-xyz" },
|
|
},
|
|
};
|
|
|
|
await expect(resolveBackend(config)).rejects.toThrow(BackendUnavailableError);
|
|
});
|
|
|
|
it("tries opencode before ollama-local", async () => {
|
|
expect(DEFAULT_BACKEND_CONFIG.provider).toBe("auto");
|
|
});
|
|
|
|
it("createBackend throws for unknown provider", () => {
|
|
expect(() => createBackend("unknown-provider" as "opencode", DEFAULT_BACKEND_CONFIG)).toThrow(BackendUnavailableError);
|
|
});
|
|
});
|
|
|
|
describe("BackendUnavailableError", () => {
|
|
it("contains installation hints", () => {
|
|
const err = new BackendUnavailableError("auto");
|
|
expect(err.message).toContain("opencode");
|
|
expect(err.message).toContain("OpenAI");
|
|
expect(err.message).toContain("Ollama");
|
|
expect(err.message).toContain("OLLAMA_CLOUD_API_KEY");
|
|
});
|
|
});
|
|
}); |