From 9f15436e05883f332680a1710faa7fa5441af7cb Mon Sep 17 00:00:00 2001 From: Chris Farhood Date: Sat, 11 Apr 2026 10:26:21 -0400 Subject: [PATCH] test(skills): add scan-projects route test for skipped/conflicts return Verifies the scan endpoint returns skipped and conflicts arrays in the response, covering the prune path where removed skills are either skipped (orphaned) or added to conflicts (still used by agents). Co-Authored-By: Claude Opus 4.6 --- .../__tests__/company-skills-routes.test.ts | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/server/src/__tests__/company-skills-routes.test.ts b/server/src/__tests__/company-skills-routes.test.ts index a75b3307..f5e7feb3 100644 --- a/server/src/__tests__/company-skills-routes.test.ts +++ b/server/src/__tests__/company-skills-routes.test.ts @@ -76,6 +76,7 @@ describe("company skill mutation permissions", () => { discovered: [], imported: [], updated: [], + skipped: [], conflicts: [], warnings: [], }); @@ -277,6 +278,53 @@ describe("company skill mutation permissions", () => { expect(mockCompanySkillService.scanProjectWorkspaces).toHaveBeenCalledWith("company-1", {}); }); + it("returns skipped and conflicts from scan including pruned skill conflicts", async () => { + mockAgentService.getById.mockResolvedValue({ + id: "agent-1", + companyId: "company-1", + permissions: { canCreateAgents: true }, + }); + + const skippedSkill = { id: "skill-1", slug: "removed-skill", name: "Removed Skill" }; + const conflictEntry = { + path: "https://github.com/vercel-labs/agent-browser", + existingSkillId: "skill-2", + existingSkillKey: "vercel-labs/agent-browser/ghost-skill", + existingSourceLocator: "https://github.com/vercel-labs/agent-browser", + reason: 'Skill "ghost-skill" was removed from https://github.com/vercel-labs/agent-browser but is still used by Builder. Detach it from those agents first.', + }; + + mockCompanySkillService.scanProjectWorkspaces.mockResolvedValueOnce({ + scannedProjects: 1, + scannedWorkspaces: 1, + discovered: [], + imported: [], + updated: [], + skipped: [skippedSkill], + conflicts: [conflictEntry], + warnings: [], + }); + + const res = await request(await createApp({ + type: "agent", + agentId: "agent-1", + companyId: "company-1", + runId: "run-1", + })) + .post("/api/companies/company-1/skills/scan-projects") + .send({}); + + expect(res.status, JSON.stringify(res.body)).toBe(200); + expect(res.body).toMatchObject({ + skipped: [{ id: "skill-1", slug: "removed-skill" }], + conflicts: [{ + path: "https://github.com/vercel-labs/agent-browser", + existingSkillId: "skill-2", + reason: expect.stringContaining("was removed from"), + }], + }); + }); + it("allows agents with canCreateAgents to mutate company skills", async () => { mockAgentService.getById.mockResolvedValue({ id: "agent-1",