import { describe, it, expect, vi, beforeEach } from "vitest"; vi.mock("../lib/config.js", () => ({ getConfig: vi.fn(() => ({ apiUrl: "https://api.skills-hub.ai" })), getAuthHeader: vi.fn(() => ({})), saveConfig: vi.fn(), })); vi.mock("../lib/api-client.js", () => ({ apiRequest: vi.fn(), })); import { feedbackCommand } from "./feedback.js"; import { apiRequest } from "../lib/api-client.js"; const mockApiRequest = vi.mocked(apiRequest); beforeEach(() => { vi.clearAllMocks(); mockApiRequest.mockResolvedValue({ ok: true, id: "fb-1" }); }); describe("feedback", () => { it("posts success feedback and prints confirmation", async () => { const log = vi.spyOn(console, "log").mockImplementation(() => {}); await feedbackCommand.parseAsync( ["node", "feedback", "my-skill", "--success", "true", "--rating", "5"], { from: "node" }, ); expect(mockApiRequest).toHaveBeenCalledWith( "/api/v1/skills/my-skill/feedback", expect.objectContaining({ method: "POST" }), ); const body = JSON.parse( (mockApiRequest.mock.calls[0][1] as any).body as string, ); expect(body.success).toBe(true); expect(body.rating).toBe(5); expect(body.source).toBe("cli"); const output = log.mock.calls.map((c) => c[0]).join("\n"); expect(output).toContain("fb-1"); log.mockRestore(); }); it("posts failure feedback without optional fields", async () => { const log = vi.spyOn(console, "log").mockImplementation(() => {}); await feedbackCommand.parseAsync( ["node", "feedback", "my-skill", "--success", "false"], { from: "node" }, ); const body = JSON.parse( (mockApiRequest.mock.calls[0][1] as any).body as string, ); expect(body.success).toBe(false); expect(body.rating).toBeUndefined(); log.mockRestore(); }); it("includes agent and time when provided", async () => { const log = vi.spyOn(console, "log").mockImplementation(() => {}); await feedbackCommand.parseAsync( [ "node", "feedback", "my-skill", "--agent", "cursor", "--time", "2000", ], { from: "node" }, ); const body = JSON.parse( (mockApiRequest.mock.calls[0][1] as any).body as string, ); expect(body.agentType).toBe("cursor"); expect(body.executionTimeMs).toBe(2000); log.mockRestore(); }); it("handles API error gracefully", async () => { mockApiRequest.mockRejectedValue(new Error("Skill not found")); const error = vi.spyOn(console, "error").mockImplementation(() => {}); const exit = vi.spyOn(process, "exit").mockImplementation(() => { throw new Error("exit"); }); await expect( feedbackCommand.parseAsync(["node", "feedback", "ghost-skill"], { from: "node", }), ).rejects.toThrow("exit"); error.mockRestore(); exit.mockRestore(); }); });