mirror of
https://github.com/AutoMaker-Org/automaker.git
synced 2026-02-02 08:33:36 +00:00
- Created git-test-repo helper for managing test git repositories - Added 13 integration tests covering: - Worktree operations (create, error handling, non-worktree mode) - Feature execution (status updates, model selection, duplicate prevention) - Auto loop (start/stop, pending features, max concurrency, events) - Error handling (provider errors, continue after failures) - Integration tests use real git operations with temporary repos - All 416 tests passing with 72.65% overall coverage - Service coverage improved: agent-service 58%, auto-mode-service 44%, feature-loader 66% 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
294 lines
10 KiB
TypeScript
294 lines
10 KiB
TypeScript
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
|
|
import { ProviderFactory } from "@/providers/provider-factory.js";
|
|
import { ClaudeProvider } from "@/providers/claude-provider.js";
|
|
import { CodexProvider } from "@/providers/codex-provider.js";
|
|
|
|
describe("provider-factory.ts", () => {
|
|
let consoleSpy: any;
|
|
|
|
beforeEach(() => {
|
|
consoleSpy = {
|
|
warn: vi.spyOn(console, "warn").mockImplementation(() => {}),
|
|
};
|
|
});
|
|
|
|
afterEach(() => {
|
|
consoleSpy.warn.mockRestore();
|
|
});
|
|
|
|
describe("getProviderForModel", () => {
|
|
describe("OpenAI/Codex models (gpt-*)", () => {
|
|
it("should return CodexProvider for gpt-5.2", () => {
|
|
const provider = ProviderFactory.getProviderForModel("gpt-5.2");
|
|
expect(provider).toBeInstanceOf(CodexProvider);
|
|
});
|
|
|
|
it("should return CodexProvider for gpt-5.1-codex", () => {
|
|
const provider = ProviderFactory.getProviderForModel("gpt-5.1-codex");
|
|
expect(provider).toBeInstanceOf(CodexProvider);
|
|
});
|
|
|
|
it("should return CodexProvider for gpt-4", () => {
|
|
const provider = ProviderFactory.getProviderForModel("gpt-4");
|
|
expect(provider).toBeInstanceOf(CodexProvider);
|
|
});
|
|
|
|
it("should be case-insensitive for gpt models", () => {
|
|
const provider1 = ProviderFactory.getProviderForModel("GPT-5.2");
|
|
const provider2 = ProviderFactory.getProviderForModel("Gpt-5.1");
|
|
expect(provider1).toBeInstanceOf(CodexProvider);
|
|
expect(provider2).toBeInstanceOf(CodexProvider);
|
|
});
|
|
});
|
|
|
|
describe("Unsupported o-series models", () => {
|
|
it("should default to ClaudeProvider for o1 (not supported by Codex CLI)", () => {
|
|
const provider = ProviderFactory.getProviderForModel("o1");
|
|
expect(provider).toBeInstanceOf(ClaudeProvider);
|
|
expect(consoleSpy.warn).toHaveBeenCalled();
|
|
});
|
|
|
|
it("should default to ClaudeProvider for o3", () => {
|
|
const provider = ProviderFactory.getProviderForModel("o3");
|
|
expect(provider).toBeInstanceOf(ClaudeProvider);
|
|
});
|
|
|
|
it("should default to ClaudeProvider for o1-mini", () => {
|
|
const provider = ProviderFactory.getProviderForModel("o1-mini");
|
|
expect(provider).toBeInstanceOf(ClaudeProvider);
|
|
});
|
|
});
|
|
|
|
describe("Claude models (claude-* prefix)", () => {
|
|
it("should return ClaudeProvider for claude-opus-4-5-20251101", () => {
|
|
const provider = ProviderFactory.getProviderForModel(
|
|
"claude-opus-4-5-20251101"
|
|
);
|
|
expect(provider).toBeInstanceOf(ClaudeProvider);
|
|
});
|
|
|
|
it("should return ClaudeProvider for claude-sonnet-4-20250514", () => {
|
|
const provider = ProviderFactory.getProviderForModel(
|
|
"claude-sonnet-4-20250514"
|
|
);
|
|
expect(provider).toBeInstanceOf(ClaudeProvider);
|
|
});
|
|
|
|
it("should return ClaudeProvider for claude-haiku-4-5", () => {
|
|
const provider = ProviderFactory.getProviderForModel("claude-haiku-4-5");
|
|
expect(provider).toBeInstanceOf(ClaudeProvider);
|
|
});
|
|
|
|
it("should be case-insensitive for claude models", () => {
|
|
const provider = ProviderFactory.getProviderForModel(
|
|
"CLAUDE-OPUS-4-5-20251101"
|
|
);
|
|
expect(provider).toBeInstanceOf(ClaudeProvider);
|
|
});
|
|
});
|
|
|
|
describe("Claude aliases", () => {
|
|
it("should return ClaudeProvider for 'haiku'", () => {
|
|
const provider = ProviderFactory.getProviderForModel("haiku");
|
|
expect(provider).toBeInstanceOf(ClaudeProvider);
|
|
});
|
|
|
|
it("should return ClaudeProvider for 'sonnet'", () => {
|
|
const provider = ProviderFactory.getProviderForModel("sonnet");
|
|
expect(provider).toBeInstanceOf(ClaudeProvider);
|
|
});
|
|
|
|
it("should return ClaudeProvider for 'opus'", () => {
|
|
const provider = ProviderFactory.getProviderForModel("opus");
|
|
expect(provider).toBeInstanceOf(ClaudeProvider);
|
|
});
|
|
|
|
it("should be case-insensitive for aliases", () => {
|
|
const provider1 = ProviderFactory.getProviderForModel("HAIKU");
|
|
const provider2 = ProviderFactory.getProviderForModel("Sonnet");
|
|
const provider3 = ProviderFactory.getProviderForModel("Opus");
|
|
|
|
expect(provider1).toBeInstanceOf(ClaudeProvider);
|
|
expect(provider2).toBeInstanceOf(ClaudeProvider);
|
|
expect(provider3).toBeInstanceOf(ClaudeProvider);
|
|
});
|
|
});
|
|
|
|
describe("Unknown models", () => {
|
|
it("should default to ClaudeProvider for unknown model", () => {
|
|
const provider = ProviderFactory.getProviderForModel("unknown-model-123");
|
|
expect(provider).toBeInstanceOf(ClaudeProvider);
|
|
});
|
|
|
|
it("should warn when defaulting to Claude", () => {
|
|
ProviderFactory.getProviderForModel("random-model");
|
|
expect(consoleSpy.warn).toHaveBeenCalledWith(
|
|
expect.stringContaining("Unknown model prefix")
|
|
);
|
|
expect(consoleSpy.warn).toHaveBeenCalledWith(
|
|
expect.stringContaining("random-model")
|
|
);
|
|
expect(consoleSpy.warn).toHaveBeenCalledWith(
|
|
expect.stringContaining("defaulting to Claude")
|
|
);
|
|
});
|
|
|
|
it("should handle empty string", () => {
|
|
const provider = ProviderFactory.getProviderForModel("");
|
|
expect(provider).toBeInstanceOf(ClaudeProvider);
|
|
expect(consoleSpy.warn).toHaveBeenCalled();
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("getAllProviders", () => {
|
|
it("should return array of all providers", () => {
|
|
const providers = ProviderFactory.getAllProviders();
|
|
expect(Array.isArray(providers)).toBe(true);
|
|
});
|
|
|
|
it("should include ClaudeProvider", () => {
|
|
const providers = ProviderFactory.getAllProviders();
|
|
const hasClaudeProvider = providers.some(
|
|
(p) => p instanceof ClaudeProvider
|
|
);
|
|
expect(hasClaudeProvider).toBe(true);
|
|
});
|
|
|
|
it("should include CodexProvider", () => {
|
|
const providers = ProviderFactory.getAllProviders();
|
|
const hasCodexProvider = providers.some((p) => p instanceof CodexProvider);
|
|
expect(hasCodexProvider).toBe(true);
|
|
});
|
|
|
|
it("should return exactly 2 providers", () => {
|
|
const providers = ProviderFactory.getAllProviders();
|
|
expect(providers).toHaveLength(2);
|
|
});
|
|
|
|
it("should create new instances each time", () => {
|
|
const providers1 = ProviderFactory.getAllProviders();
|
|
const providers2 = ProviderFactory.getAllProviders();
|
|
|
|
expect(providers1[0]).not.toBe(providers2[0]);
|
|
expect(providers1[1]).not.toBe(providers2[1]);
|
|
});
|
|
});
|
|
|
|
describe("checkAllProviders", () => {
|
|
it("should return installation status for all providers", async () => {
|
|
const statuses = await ProviderFactory.checkAllProviders();
|
|
|
|
expect(statuses).toHaveProperty("claude");
|
|
expect(statuses).toHaveProperty("codex");
|
|
});
|
|
|
|
it("should call detectInstallation on each provider", async () => {
|
|
const statuses = await ProviderFactory.checkAllProviders();
|
|
|
|
expect(statuses.claude).toHaveProperty("installed");
|
|
expect(statuses.codex).toHaveProperty("installed");
|
|
});
|
|
|
|
it("should return correct provider names as keys", async () => {
|
|
const statuses = await ProviderFactory.checkAllProviders();
|
|
const keys = Object.keys(statuses);
|
|
|
|
expect(keys).toContain("claude");
|
|
expect(keys).toContain("codex");
|
|
expect(keys).toHaveLength(2);
|
|
});
|
|
});
|
|
|
|
describe("getProviderByName", () => {
|
|
it("should return ClaudeProvider for 'claude'", () => {
|
|
const provider = ProviderFactory.getProviderByName("claude");
|
|
expect(provider).toBeInstanceOf(ClaudeProvider);
|
|
});
|
|
|
|
it("should return ClaudeProvider for 'anthropic'", () => {
|
|
const provider = ProviderFactory.getProviderByName("anthropic");
|
|
expect(provider).toBeInstanceOf(ClaudeProvider);
|
|
});
|
|
|
|
it("should return CodexProvider for 'codex'", () => {
|
|
const provider = ProviderFactory.getProviderByName("codex");
|
|
expect(provider).toBeInstanceOf(CodexProvider);
|
|
});
|
|
|
|
it("should return CodexProvider for 'openai'", () => {
|
|
const provider = ProviderFactory.getProviderByName("openai");
|
|
expect(provider).toBeInstanceOf(CodexProvider);
|
|
});
|
|
|
|
it("should be case-insensitive", () => {
|
|
const provider1 = ProviderFactory.getProviderByName("CLAUDE");
|
|
const provider2 = ProviderFactory.getProviderByName("Codex");
|
|
const provider3 = ProviderFactory.getProviderByName("ANTHROPIC");
|
|
|
|
expect(provider1).toBeInstanceOf(ClaudeProvider);
|
|
expect(provider2).toBeInstanceOf(CodexProvider);
|
|
expect(provider3).toBeInstanceOf(ClaudeProvider);
|
|
});
|
|
|
|
it("should return null for unknown provider", () => {
|
|
const provider = ProviderFactory.getProviderByName("unknown");
|
|
expect(provider).toBeNull();
|
|
});
|
|
|
|
it("should return null for empty string", () => {
|
|
const provider = ProviderFactory.getProviderByName("");
|
|
expect(provider).toBeNull();
|
|
});
|
|
|
|
it("should create new instance each time", () => {
|
|
const provider1 = ProviderFactory.getProviderByName("claude");
|
|
const provider2 = ProviderFactory.getProviderByName("claude");
|
|
|
|
expect(provider1).not.toBe(provider2);
|
|
expect(provider1).toBeInstanceOf(ClaudeProvider);
|
|
expect(provider2).toBeInstanceOf(ClaudeProvider);
|
|
});
|
|
});
|
|
|
|
describe("getAllAvailableModels", () => {
|
|
it("should return array of models", () => {
|
|
const models = ProviderFactory.getAllAvailableModels();
|
|
expect(Array.isArray(models)).toBe(true);
|
|
});
|
|
|
|
it("should include models from all providers", () => {
|
|
const models = ProviderFactory.getAllAvailableModels();
|
|
expect(models.length).toBeGreaterThan(0);
|
|
});
|
|
|
|
it("should return models with required fields", () => {
|
|
const models = ProviderFactory.getAllAvailableModels();
|
|
|
|
models.forEach((model) => {
|
|
expect(model).toHaveProperty("id");
|
|
expect(model).toHaveProperty("name");
|
|
expect(typeof model.id).toBe("string");
|
|
expect(typeof model.name).toBe("string");
|
|
});
|
|
});
|
|
|
|
it("should aggregate models from both Claude and Codex", () => {
|
|
const models = ProviderFactory.getAllAvailableModels();
|
|
|
|
// Claude models should include claude-* in their IDs
|
|
const hasClaudeModels = models.some((m) =>
|
|
m.id.toLowerCase().includes("claude")
|
|
);
|
|
|
|
// Codex models should include gpt-* in their IDs
|
|
const hasCodexModels = models.some((m) =>
|
|
m.id.toLowerCase().includes("gpt")
|
|
);
|
|
|
|
expect(hasClaudeModels).toBe(true);
|
|
expect(hasCodexModels).toBe(true);
|
|
});
|
|
});
|
|
});
|