import { describe, it, expect, vi, beforeEach, afterEach } from "vitest"; import { mkdirSync, writeFileSync, readFileSync, existsSync, rmSync, } from "node:fs"; import { join } from "node:path"; import { tmpdir } from "node:os"; // We test the generation logic by writing a spec to a temp dir and running the command's action vi.mock("ora", () => ({ default: () => ({ start: vi.fn().mockReturnThis(), succeed: vi.fn().mockReturnThis(), fail: vi.fn().mockReturnThis(), info: vi.fn().mockReturnThis(), text: "", }), })); vi.mock("chalk", () => ({ default: { bold: (s: string) => s, cyan: (s: string) => s, red: (s: string) => s, yellow: (s: string) => s, dim: (s: string) => s, }, })); describe("generate command", () => { let tempDir: string; let specFile: string; let outputDir: string; beforeEach(() => { tempDir = join(tmpdir(), `skills-gen-test-${Date.now()}`); mkdirSync(tempDir, { recursive: true }); specFile = join(tempDir, "petstore.json"); outputDir = join(tempDir, "skills"); }); afterEach(() => { rmSync(tempDir, { recursive: true, force: true }); }); it("generates skill files from a minimal OpenAPI spec", async () => { const spec = { openapi: "3.0.0", info: { title: "Pet Store", version: "1.0.0" }, servers: [{ url: "https://api.petstore.com" }], paths: { "/pets": { get: { operationId: "listPets", summary: "List all pets", parameters: [ { name: "limit", in: "query", schema: { type: "integer" } }, ], }, post: { operationId: "createPet", summary: "Create a pet", requestBody: { required: true, content: { "application/json": { schema: {} } }, }, }, }, }, }; writeFileSync(specFile, JSON.stringify(spec)); // Import and call the action directly const { generateCommand } = await import("./generate.js"); // Override process.exit to not actually exit const exitSpy = vi.spyOn(process, "exit").mockImplementation(() => { throw new Error("exit"); }); try { await generateCommand.parseAsync( ["--openapi", specFile, "-o", outputDir], { from: "user" }, ); } catch { // parseAsync may throw in test env } exitSpy.mockRestore(); // Check that files were created expect(existsSync(outputDir)).toBe(true); const files = require("node:fs").readdirSync(outputDir); expect(files.length).toBe(2); expect(files.some((f: string) => f.includes("listpets"))).toBe(true); expect(files.some((f: string) => f.includes("createpet"))).toBe(true); // Check content of one file const listPetsFile = files.find((f: string) => f.includes("listpets")); const content = readFileSync(join(outputDir, listPetsFile!), "utf-8"); expect(content).toContain("name:"); expect(content).toContain("List all pets"); expect(content).toContain("GET https://api.petstore.com/pets"); expect(content).toContain("permissions:"); expect(content).toContain("network"); }); it("handles spec with security schemes", async () => { const spec = { openapi: "3.0.0", info: { title: "Secure API", version: "2.0.0" }, servers: [{ url: "https://api.example.com" }], security: [{ bearerAuth: [] }], components: { securitySchemes: { bearerAuth: { type: "http", scheme: "bearer" }, }, }, paths: { "/data": { get: { operationId: "getData", summary: "Get secure data", }, }, }, }; writeFileSync(specFile, JSON.stringify(spec)); const { generateCommand } = await import("./generate.js"); const exitSpy = vi.spyOn(process, "exit").mockImplementation(() => { throw new Error("exit"); }); try { await generateCommand.parseAsync( ["--openapi", specFile, "-o", outputDir], { from: "user" }, ); } catch { // parseAsync may throw } exitSpy.mockRestore(); const files = require("node:fs").readdirSync(outputDir); expect(files.length).toBe(1); const content = readFileSync(join(outputDir, files[0]), "utf-8"); expect(content).toContain("api"); expect(content).toContain("Bearer token authentication"); }); });