import { runAll, parseTestFile, Reporter } from "../src/core"; import { AgentRailsConfig } from "../src/types"; import * as fs from "fs"; import * as path from "path"; // Mock the evaluator const mockEvaluate = jest.fn(); // Mock the evaluator jest.mock("../src/evaluator", () => ({ createEvaluator: jest.fn(() => ({ evaluate: mockEvaluate, })), })); // Mock glob jest.mock("glob", () => ({ sync: jest.fn(), })); describe("Core", () => { const mockAgent = jest.fn(); const mockConfig: AgentRailsConfig = { llm: { provider: "openai", apiKey: "test-api-key", }, agent: mockAgent, testMatch: ["**/*.test.yaml"], timeout: 5000, }; beforeEach(() => { mockAgent.mockClear(); mockEvaluate.mockClear(); }); describe("parseTestFile", () => { const fixturesDir = path.join(__dirname, "fixtures"); const validYamlPath = path.join(fixturesDir, "valid.test.yaml"); beforeAll(() => { if (!fs.existsSync(fixturesDir)) { fs.mkdirSync(fixturesDir, { recursive: true }); } fs.writeFileSync( validYamlPath, ` suite: "Test Suite" description: "Test description" tests: - name: "Test 1" input: "Hello" expectedBehavior: "Should respond with greeting" exampleResponses: - "Hi there!" - "Hello!" - name: "Test 2" description: "Complex test" input: type: "query" data: "test data" timeout: 5000 metadata: priority: high ` ); }); afterAll(() => { if (fs.existsSync(validYamlPath)) fs.unlinkSync(validYamlPath); if (fs.existsSync(fixturesDir)) fs.rmSync(fixturesDir, { recursive: true, force: true }); }); it("should parse a valid YAML file", () => { const suite = parseTestFile(validYamlPath); expect(suite.suite).toBe("Test Suite"); expect(suite.description).toBe("Test description"); expect(suite.tests).toHaveLength(2); }); it("should parse test cases correctly", () => { const suite = parseTestFile(validYamlPath); const test1 = suite.tests[0]; const test2 = suite.tests[1]; expect(test1.name).toBe("Test 1"); expect(test1.input).toBe("Hello"); expect(test1.expectedBehavior).toBe("Should respond with greeting"); expect(test1.exampleResponses).toEqual(["Hi there!", "Hello!"]); expect(test2.name).toBe("Test 2"); expect(test2.description).toBe("Complex test"); expect(test2.input).toEqual({ type: "query", data: "test data" }); expect(test2.timeout).toBe(5000); expect(test2.metadata).toEqual({ priority: "high" }); }); }); describe("runAll", () => { it("should run inline tests", async () => { const configWithTests: AgentRailsConfig = { ...mockConfig, tests: [ { suite: "Test Suite", tests: [ { name: "Test 1", input: "Hello" }, { name: "Test 2", input: "World" }, ], }, ], }; mockAgent.mockResolvedValue("response"); mockEvaluate.mockResolvedValue({ passed: true, reasoning: "Good" }); const results = await runAll(configWithTests); expect(results).toHaveLength(1); expect(results[0].suiteName).toBe("Test Suite"); expect(results[0].totalTests).toBe(2); expect(results[0].passed).toBe(2); }); it("should run file-based tests", async () => { // Create a real test file for this test const fixturesDir = path.join(__dirname, "fixtures"); const testFile = path.join(fixturesDir, "file-test.test.yaml"); if (!fs.existsSync(fixturesDir)) { fs.mkdirSync(fixturesDir, { recursive: true }); } fs.writeFileSync( testFile, ` suite: "File Suite" tests: - name: "File Test" input: "test" ` ); const glob = require("glob"); glob.sync.mockReturnValue([testFile]); mockAgent.mockResolvedValue("response"); mockEvaluate.mockResolvedValue({ passed: true, reasoning: "Good" }); const results = await runAll(mockConfig); expect(results).toHaveLength(1); expect(results[0].suiteName).toBe("File Suite"); // Cleanup fs.unlinkSync(testFile); }); it("should throw error if no tests found", async () => { const configNoTests: AgentRailsConfig = { ...mockConfig, tests: undefined, testMatch: undefined, }; await expect(runAll(configNoTests)).rejects.toThrow( "No tests found. Define tests inline in config or specify testMatch patterns." ); }); }); // Reporter tests skipped due to chalk mocking complexity // The Reporter class is simple and works correctly in practice });