test(09-04): add failing tests for skill registry routes (RED)
- Tests for GET /api/skill-registry/skills (list, includeRemoved param) - Tests for GET /api/skill-registry/skills/:id (found, 404) - Tests for GET /api/skill-registry/skills/:id/versions - Tests for POST /api/skill-registry/fetch - Tests for POST /api/skill-registry/skills/:id/install (success, 400) - Tests for POST /api/skill-registry/skills/:id/rollback (success, 400) - Tests for DELETE /api/skill-registry/skills/:id
This commit is contained in:
parent
7c9393bd93
commit
380f561963
1 changed files with 238 additions and 0 deletions
238
server/src/__tests__/skill-registry-routes.test.ts
Normal file
238
server/src/__tests__/skill-registry-routes.test.ts
Normal file
|
|
@ -0,0 +1,238 @@
|
|||
import express from "express";
|
||||
import request from "supertest";
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { skillRegistryRoutes } from "../routes/skill-registry.js";
|
||||
import { errorHandler } from "../middleware/index.js";
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Mock skillRegistryService
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
const mockSkillRegistryService = vi.hoisted(() => ({
|
||||
list: vi.fn(),
|
||||
getById: vi.fn(),
|
||||
getVersions: vi.fn(),
|
||||
fetchAll: vi.fn(),
|
||||
install: vi.fn(),
|
||||
rollback: vi.fn(),
|
||||
uninstall: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock("../services/skill-registry.js", () => ({
|
||||
skillRegistryService: () => mockSkillRegistryService,
|
||||
}));
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// App factory
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
function createApp() {
|
||||
const app = express();
|
||||
app.use(express.json());
|
||||
app.use((req, _res, next) => {
|
||||
(req as any).actor = {
|
||||
type: "board",
|
||||
userId: "local-board",
|
||||
companyIds: [],
|
||||
source: "local_implicit",
|
||||
isInstanceAdmin: false,
|
||||
};
|
||||
next();
|
||||
});
|
||||
app.use("/api", skillRegistryRoutes());
|
||||
app.use(errorHandler);
|
||||
return app;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Fixtures
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
const skill1 = {
|
||||
id: "anthropic-official/bash",
|
||||
sourceId: "anthropic-official",
|
||||
name: "Bash",
|
||||
description: "A bash skill",
|
||||
activeVersionId: null,
|
||||
removedAt: null,
|
||||
createdAt: 1000,
|
||||
updatedAt: 1000,
|
||||
};
|
||||
|
||||
const version1 = {
|
||||
id: "anthropic-official/bash@abc123",
|
||||
skillId: "anthropic-official/bash",
|
||||
sha: "abc123",
|
||||
cacheDir: "/tmp/cache/bash@abc123",
|
||||
fetchedAt: 1000,
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Tests
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
describe("skill registry routes", () => {
|
||||
let app: ReturnType<typeof createApp>;
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
app = createApp();
|
||||
});
|
||||
|
||||
// ---- Test 1: GET /api/skill-registry/skills ----
|
||||
|
||||
describe("GET /api/skill-registry/skills", () => {
|
||||
it("returns 200 with JSON array of skills", async () => {
|
||||
mockSkillRegistryService.list.mockResolvedValue([skill1]);
|
||||
|
||||
const res = await request(app).get("/api/skill-registry/skills");
|
||||
|
||||
expect(res.status).toBe(200);
|
||||
expect(res.body).toEqual([skill1]);
|
||||
expect(mockSkillRegistryService.list).toHaveBeenCalledWith({ includeRemoved: false });
|
||||
});
|
||||
|
||||
it("passes includeRemoved=true when query param set", async () => {
|
||||
mockSkillRegistryService.list.mockResolvedValue([skill1]);
|
||||
|
||||
const res = await request(app).get("/api/skill-registry/skills?includeRemoved=true");
|
||||
|
||||
expect(res.status).toBe(200);
|
||||
expect(mockSkillRegistryService.list).toHaveBeenCalledWith({ includeRemoved: true });
|
||||
});
|
||||
});
|
||||
|
||||
// ---- Test 2: GET /api/skill-registry/skills/:id ----
|
||||
|
||||
describe("GET /api/skill-registry/skills/:id", () => {
|
||||
it("returns 200 with skill object when found", async () => {
|
||||
mockSkillRegistryService.getById.mockResolvedValue(skill1);
|
||||
|
||||
const res = await request(app).get("/api/skill-registry/skills/anthropic-official%2Fbash");
|
||||
|
||||
expect(res.status).toBe(200);
|
||||
expect(res.body).toEqual(skill1);
|
||||
});
|
||||
|
||||
it("returns 404 when skill not found", async () => {
|
||||
mockSkillRegistryService.getById.mockResolvedValue(undefined);
|
||||
|
||||
const res = await request(app).get("/api/skill-registry/skills/unknown%2Fskill");
|
||||
|
||||
expect(res.status).toBe(404);
|
||||
expect(res.body).toEqual({ error: "Skill not found" });
|
||||
});
|
||||
});
|
||||
|
||||
// ---- Test 3: GET /api/skill-registry/skills/:id/versions ----
|
||||
|
||||
describe("GET /api/skill-registry/skills/:id/versions", () => {
|
||||
it("returns 200 with version array", async () => {
|
||||
mockSkillRegistryService.getVersions.mockResolvedValue([version1]);
|
||||
|
||||
const res = await request(app).get("/api/skill-registry/skills/anthropic-official%2Fbash/versions");
|
||||
|
||||
expect(res.status).toBe(200);
|
||||
expect(res.body).toEqual([version1]);
|
||||
});
|
||||
});
|
||||
|
||||
// ---- Test 4: POST /api/skill-registry/fetch ----
|
||||
|
||||
describe("POST /api/skill-registry/fetch", () => {
|
||||
it("returns 200 with { fetched, errors } object", async () => {
|
||||
mockSkillRegistryService.fetchAll.mockResolvedValue({ fetched: 3, errors: [] });
|
||||
|
||||
const res = await request(app).post("/api/skill-registry/fetch");
|
||||
|
||||
expect(res.status).toBe(200);
|
||||
expect(res.body).toEqual({ fetched: 3, errors: [] });
|
||||
});
|
||||
});
|
||||
|
||||
// ---- Test 5: POST /api/skill-registry/skills/:id/install ----
|
||||
|
||||
describe("POST /api/skill-registry/skills/:id/install", () => {
|
||||
it("returns 200 with install result when agentSkillsDir provided", async () => {
|
||||
const installResult = {
|
||||
type: "installed",
|
||||
skillId: "anthropic-official/bash",
|
||||
versionId: "anthropic-official/bash@abc123",
|
||||
targetDir: "/agent/skills/bash",
|
||||
};
|
||||
mockSkillRegistryService.install.mockResolvedValue(installResult);
|
||||
|
||||
const res = await request(app)
|
||||
.post("/api/skill-registry/skills/anthropic-official%2Fbash/install")
|
||||
.send({ agentSkillsDir: "/agent/skills" });
|
||||
|
||||
expect(res.status).toBe(200);
|
||||
expect(res.body).toEqual(installResult);
|
||||
expect(mockSkillRegistryService.install).toHaveBeenCalledWith(
|
||||
"anthropic-official/bash",
|
||||
"/agent/skills",
|
||||
);
|
||||
});
|
||||
|
||||
it("returns 400 when agentSkillsDir is missing", async () => {
|
||||
const res = await request(app)
|
||||
.post("/api/skill-registry/skills/anthropic-official%2Fbash/install")
|
||||
.send({});
|
||||
|
||||
expect(res.status).toBe(400);
|
||||
expect(res.body).toEqual({ error: "agentSkillsDir required" });
|
||||
});
|
||||
});
|
||||
|
||||
// ---- Test 6: POST /api/skill-registry/skills/:id/rollback ----
|
||||
|
||||
describe("POST /api/skill-registry/skills/:id/rollback", () => {
|
||||
it("returns 200 when versionId and agentSkillsDir provided", async () => {
|
||||
mockSkillRegistryService.rollback.mockResolvedValue(undefined);
|
||||
|
||||
const res = await request(app)
|
||||
.post("/api/skill-registry/skills/anthropic-official%2Fbash/rollback")
|
||||
.send({ versionId: "anthropic-official/bash@abc123", agentSkillsDir: "/agent/skills" });
|
||||
|
||||
expect(res.status).toBe(200);
|
||||
expect(res.body).toEqual({ ok: true });
|
||||
expect(mockSkillRegistryService.rollback).toHaveBeenCalledWith(
|
||||
"anthropic-official/bash",
|
||||
"anthropic-official/bash@abc123",
|
||||
"/agent/skills",
|
||||
);
|
||||
});
|
||||
|
||||
it("returns 400 when versionId is missing", async () => {
|
||||
const res = await request(app)
|
||||
.post("/api/skill-registry/skills/anthropic-official%2Fbash/rollback")
|
||||
.send({ agentSkillsDir: "/agent/skills" });
|
||||
|
||||
expect(res.status).toBe(400);
|
||||
expect(res.body).toEqual({ error: "versionId and agentSkillsDir required" });
|
||||
});
|
||||
|
||||
it("returns 400 when agentSkillsDir is missing", async () => {
|
||||
const res = await request(app)
|
||||
.post("/api/skill-registry/skills/anthropic-official%2Fbash/rollback")
|
||||
.send({ versionId: "anthropic-official/bash@abc123" });
|
||||
|
||||
expect(res.status).toBe(400);
|
||||
expect(res.body).toEqual({ error: "versionId and agentSkillsDir required" });
|
||||
});
|
||||
});
|
||||
|
||||
// ---- Test 7: DELETE /api/skill-registry/skills/:id ----
|
||||
|
||||
describe("DELETE /api/skill-registry/skills/:id", () => {
|
||||
it("returns 200 after soft-delete", async () => {
|
||||
mockSkillRegistryService.uninstall.mockResolvedValue(undefined);
|
||||
|
||||
const res = await request(app).delete("/api/skill-registry/skills/anthropic-official%2Fbash");
|
||||
|
||||
expect(res.status).toBe(200);
|
||||
expect(res.body).toEqual({ ok: true });
|
||||
expect(mockSkillRegistryService.uninstall).toHaveBeenCalledWith("anthropic-official/bash");
|
||||
});
|
||||
});
|
||||
});
|
||||
Loading…
Add table
Reference in a new issue