import { describe, expect, it, vi } from "vitest"; import { validateConfig } from "../src/config.js"; import { createForgejoIssue } from "../src/forgejo-client.js"; describe("forgejo client", () => { it("resolves a Paperclip-managed secret ref and sends the Forgejo token", async () => { const secretRef = "11111111-1111-4111-8111-111111111111"; const resolveSecret = vi.fn(async () => "forgejo-token"); const fetch = vi.fn(async () => ({ ok: true, json: async () => ({ id: 42, number: 7, html_url: "https://forgejo.example/acme/repo/issues/7", url: "https://forgejo.example/api/v1/repos/acme/repo/issues/7" }) })); const result = await createForgejoIssue( { config: { get: async () => ({ forgejoBaseUrl: "https://forgejo.example", forgejoTokenRef: secretRef, forgejoOwner: "acme", forgejoRepo: "repo" }) }, secrets: { resolve: resolveSecret }, http: { fetch } } as never, { title: "Example issue", body: "body" } ); expect(resolveSecret).toHaveBeenCalledWith(secretRef); expect(fetch).toHaveBeenCalledWith( "https://forgejo.example/api/v1/repos/acme/repo/issues", expect.objectContaining({ method: "POST", headers: expect.objectContaining({ authorization: "token forgejo-token" }) }) ); expect(result).toEqual({ id: 42, number: 7, url: "https://forgejo.example/acme/repo/issues/7", apiUrl: "https://forgejo.example/api/v1/repos/acme/repo/issues/7" }); }); it("rejects visible secret names with an actionable, sanitized error", async () => { const invalidRef = "forgejo-ake-paperclip-forgejo-issue-plugin-issues"; const resolveSecret = vi.fn(); const fetch = vi.fn(); expect(validateConfig({ forgejoBaseUrl: "https://forgejo.example", forgejoTokenRef: invalidRef, forgejoOwner: "acme", forgejoRepo: "repo" })).toEqual({ ok: false, errors: [ "forgejoTokenRef must be a Paperclip secret reference UUID. Re-save the field through the Paperclip secret picker or paste the secret UUID, not the visible secret name." ], warnings: [ "defaultCompanyId is not set; the reconciliation job cannot backfill unsynced issues across a company.", "syncIssueLabel is not set; the plugin will use the default label \"forgejo-sync\"." ] }); await expect(createForgejoIssue( { config: { get: async () => ({ forgejoBaseUrl: "https://forgejo.example", forgejoTokenRef: invalidRef, forgejoOwner: "acme", forgejoRepo: "repo" }) }, secrets: { resolve: resolveSecret }, http: { fetch } } as never, { title: "Example issue", body: "body" } )).rejects.toThrow( "forgejoTokenRef must be a Paperclip secret reference UUID. Re-save the field through the Paperclip secret picker or paste the secret UUID, not the visible secret name." ); expect(resolveSecret).not.toHaveBeenCalled(); expect(fetch).not.toHaveBeenCalled(); }); });