mirror of
https://github.com/AutoMaker-Org/automaker.git
synced 2026-02-02 08:33:36 +00:00
feat: add comprehensive integration tests for auto-mode-service
- 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>
This commit is contained in:
242
apps/server/tests/unit/providers/base-provider.test.ts
Normal file
242
apps/server/tests/unit/providers/base-provider.test.ts
Normal file
@@ -0,0 +1,242 @@
|
||||
import { describe, it, expect } from "vitest";
|
||||
import { BaseProvider } from "@/providers/base-provider.js";
|
||||
import type {
|
||||
ProviderConfig,
|
||||
ExecuteOptions,
|
||||
ProviderMessage,
|
||||
InstallationStatus,
|
||||
ModelDefinition,
|
||||
} from "@/providers/types.js";
|
||||
|
||||
// Concrete implementation for testing the abstract class
|
||||
class TestProvider extends BaseProvider {
|
||||
getName(): string {
|
||||
return "test-provider";
|
||||
}
|
||||
|
||||
async *executeQuery(
|
||||
_options: ExecuteOptions
|
||||
): AsyncGenerator<ProviderMessage> {
|
||||
yield { type: "text", text: "test response" };
|
||||
}
|
||||
|
||||
async detectInstallation(): Promise<InstallationStatus> {
|
||||
return { installed: true };
|
||||
}
|
||||
|
||||
getAvailableModels(): ModelDefinition[] {
|
||||
return [
|
||||
{ id: "test-model-1", name: "Test Model 1", description: "A test model" },
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
describe("base-provider.ts", () => {
|
||||
describe("constructor", () => {
|
||||
it("should initialize with empty config when none provided", () => {
|
||||
const provider = new TestProvider();
|
||||
expect(provider.getConfig()).toEqual({});
|
||||
});
|
||||
|
||||
it("should initialize with provided config", () => {
|
||||
const config: ProviderConfig = {
|
||||
apiKey: "test-key",
|
||||
baseUrl: "https://test.com",
|
||||
};
|
||||
const provider = new TestProvider(config);
|
||||
expect(provider.getConfig()).toEqual(config);
|
||||
});
|
||||
|
||||
it("should call getName() during initialization", () => {
|
||||
const provider = new TestProvider();
|
||||
expect(provider.getName()).toBe("test-provider");
|
||||
});
|
||||
});
|
||||
|
||||
describe("validateConfig", () => {
|
||||
it("should return valid when config exists", () => {
|
||||
const provider = new TestProvider({ apiKey: "test" });
|
||||
const result = provider.validateConfig();
|
||||
|
||||
expect(result.valid).toBe(true);
|
||||
expect(result.errors).toHaveLength(0);
|
||||
expect(result.warnings).toHaveLength(0);
|
||||
});
|
||||
|
||||
it("should return invalid when config is undefined", () => {
|
||||
// Create provider without config
|
||||
const provider = new TestProvider();
|
||||
// Manually set config to undefined to test edge case
|
||||
(provider as any).config = undefined;
|
||||
|
||||
const result = provider.validateConfig();
|
||||
|
||||
expect(result.valid).toBe(false);
|
||||
expect(result.errors).toContain("Provider config is missing");
|
||||
});
|
||||
|
||||
it("should return valid for empty config object", () => {
|
||||
const provider = new TestProvider({});
|
||||
const result = provider.validateConfig();
|
||||
|
||||
expect(result.valid).toBe(true);
|
||||
expect(result.errors).toHaveLength(0);
|
||||
});
|
||||
|
||||
it("should include warnings array in result", () => {
|
||||
const provider = new TestProvider();
|
||||
const result = provider.validateConfig();
|
||||
|
||||
expect(result).toHaveProperty("warnings");
|
||||
expect(Array.isArray(result.warnings)).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("supportsFeature", () => {
|
||||
it("should support 'tools' feature", () => {
|
||||
const provider = new TestProvider();
|
||||
expect(provider.supportsFeature("tools")).toBe(true);
|
||||
});
|
||||
|
||||
it("should support 'text' feature", () => {
|
||||
const provider = new TestProvider();
|
||||
expect(provider.supportsFeature("text")).toBe(true);
|
||||
});
|
||||
|
||||
it("should not support unknown features", () => {
|
||||
const provider = new TestProvider();
|
||||
expect(provider.supportsFeature("vision")).toBe(false);
|
||||
expect(provider.supportsFeature("mcp")).toBe(false);
|
||||
expect(provider.supportsFeature("unknown")).toBe(false);
|
||||
});
|
||||
|
||||
it("should be case-sensitive", () => {
|
||||
const provider = new TestProvider();
|
||||
expect(provider.supportsFeature("TOOLS")).toBe(false);
|
||||
expect(provider.supportsFeature("Text")).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("getConfig", () => {
|
||||
it("should return current config", () => {
|
||||
const config: ProviderConfig = {
|
||||
apiKey: "test-key",
|
||||
model: "test-model",
|
||||
};
|
||||
const provider = new TestProvider(config);
|
||||
|
||||
expect(provider.getConfig()).toEqual(config);
|
||||
});
|
||||
|
||||
it("should return same reference", () => {
|
||||
const config: ProviderConfig = { apiKey: "test" };
|
||||
const provider = new TestProvider(config);
|
||||
|
||||
const retrieved1 = provider.getConfig();
|
||||
const retrieved2 = provider.getConfig();
|
||||
|
||||
expect(retrieved1).toBe(retrieved2);
|
||||
});
|
||||
});
|
||||
|
||||
describe("setConfig", () => {
|
||||
it("should merge partial config with existing config", () => {
|
||||
const provider = new TestProvider({ apiKey: "original-key" });
|
||||
|
||||
provider.setConfig({ model: "new-model" });
|
||||
|
||||
expect(provider.getConfig()).toEqual({
|
||||
apiKey: "original-key",
|
||||
model: "new-model",
|
||||
});
|
||||
});
|
||||
|
||||
it("should override existing fields", () => {
|
||||
const provider = new TestProvider({ apiKey: "old-key", model: "old-model" });
|
||||
|
||||
provider.setConfig({ apiKey: "new-key" });
|
||||
|
||||
expect(provider.getConfig()).toEqual({
|
||||
apiKey: "new-key",
|
||||
model: "old-model",
|
||||
});
|
||||
});
|
||||
|
||||
it("should accept empty object", () => {
|
||||
const provider = new TestProvider({ apiKey: "test" });
|
||||
const originalConfig = provider.getConfig();
|
||||
|
||||
provider.setConfig({});
|
||||
|
||||
expect(provider.getConfig()).toEqual(originalConfig);
|
||||
});
|
||||
|
||||
it("should handle multiple updates", () => {
|
||||
const provider = new TestProvider();
|
||||
|
||||
provider.setConfig({ apiKey: "key1" });
|
||||
provider.setConfig({ model: "model1" });
|
||||
provider.setConfig({ baseUrl: "https://test.com" });
|
||||
|
||||
expect(provider.getConfig()).toEqual({
|
||||
apiKey: "key1",
|
||||
model: "model1",
|
||||
baseUrl: "https://test.com",
|
||||
});
|
||||
});
|
||||
|
||||
it("should preserve other fields when updating one field", () => {
|
||||
const provider = new TestProvider({
|
||||
apiKey: "key",
|
||||
model: "model",
|
||||
baseUrl: "https://test.com",
|
||||
});
|
||||
|
||||
provider.setConfig({ model: "new-model" });
|
||||
|
||||
expect(provider.getConfig()).toEqual({
|
||||
apiKey: "key",
|
||||
model: "new-model",
|
||||
baseUrl: "https://test.com",
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("abstract methods", () => {
|
||||
it("should require getName implementation", () => {
|
||||
const provider = new TestProvider();
|
||||
expect(typeof provider.getName).toBe("function");
|
||||
expect(provider.getName()).toBe("test-provider");
|
||||
});
|
||||
|
||||
it("should require executeQuery implementation", async () => {
|
||||
const provider = new TestProvider();
|
||||
expect(typeof provider.executeQuery).toBe("function");
|
||||
|
||||
const generator = provider.executeQuery({
|
||||
prompt: "test",
|
||||
projectDirectory: "/test",
|
||||
});
|
||||
const result = await generator.next();
|
||||
|
||||
expect(result.value).toEqual({ type: "text", text: "test response" });
|
||||
});
|
||||
|
||||
it("should require detectInstallation implementation", async () => {
|
||||
const provider = new TestProvider();
|
||||
expect(typeof provider.detectInstallation).toBe("function");
|
||||
|
||||
const status = await provider.detectInstallation();
|
||||
expect(status).toHaveProperty("installed");
|
||||
});
|
||||
|
||||
it("should require getAvailableModels implementation", () => {
|
||||
const provider = new TestProvider();
|
||||
expect(typeof provider.getAvailableModels).toBe("function");
|
||||
|
||||
const models = provider.getAvailableModels();
|
||||
expect(Array.isArray(models)).toBe(true);
|
||||
expect(models.length).toBeGreaterThan(0);
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user