import { describe, expect, it, vi } from "vitest"; import { parseServicePushRepos, resolveAgentPushCredential, type ResolveAgentPushCredentialOptions, } from "./AgentPushAuth"; const target = { repoId: "service-owner/new-agent-app", repoType: "space" as const, branch: "main", create: true, }; const configValues = { AGENT_HF_TOKEN: "hf_service_secret", HF_TOKEN: "hf_fallback_secret", AGENT_SANDBOX_NAMESPACE: "service-owner", AGENT_SERVICE_PUSH_REPOS: "", }; const defaultConfigValue: NonNullable = (name) => configValues[name]; function options( overrides: Partial = {} ): ResolveAgentPushCredentialOptions { return { workspace: { repo: { kind: "blank" } }, target, configValue: defaultConfigValue, resolveServiceOwner: vi.fn().mockResolvedValue("service-owner"), ...overrides, }; } describe("resolveAgentPushCredential", () => { it("always prefers the signed-in user's OAuth token", async () => { const resolveServiceOwner = vi.fn(); await expect( resolveAgentPushCredential( options({ userToken: "hf_user_token", workspace: { repo: { kind: "hf", ref: "someone/existing", repoType: "space" } }, target: { ...target, repoId: "arbitrary-owner/existing", create: false }, resolveServiceOwner, }) ) ).resolves.toBe("hf_user_token"); expect(resolveServiceOwner).not.toHaveBeenCalled(); }); it("allows service-token creation only in the configured namespace", async () => { await expect(resolveAgentPushCredential(options())).resolves.toBe("hf_service_secret"); await expect( resolveAgentPushCredential(options({ target: { ...target, repoId: "attacker/new-app" } })) ).rejects.toMatchObject({ status: 403 }); }); it("never uses the service token to update a pre-existing or imported repository", async () => { await expect( resolveAgentPushCredential( options({ workspace: { repo: { kind: "hf", ref: "service-owner/important", repoType: "space" }, }, target: { ...target, repoId: "service-owner/important", create: false }, }) ) ).rejects.toMatchObject({ status: 403 }); await expect( resolveAgentPushCredential(options({ target: { ...target, create: false } })) ).rejects.toMatchObject({ status: 403 }); await expect( resolveAgentPushCredential( options({ workspace: { repo: { kind: "git", ref: "https://example.com/source.git" } }, target: { ...target, create: false }, configValue: (name) => name === "AGENT_SERVICE_PUSH_REPOS" ? "space:service-owner/new-agent-app" : defaultConfigValue(name), }) ) ).rejects.toMatchObject({ status: 403 }); }); it("allows a delta update only for the exact current HF source and allowlist entry", async () => { const allowlisted = options({ workspace: { repo: { kind: "hf", ref: "service-owner/live-fixture", repoType: "space" }, }, target: { ...target, repoId: "service-owner/live-fixture", create: false }, configValue: (name) => { if (name === "AGENT_SERVICE_PUSH_REPOS") { return "model:service-owner/other, space:service-owner/live-fixture"; } return defaultConfigValue(name); }, }); await expect(resolveAgentPushCredential(allowlisted)).resolves.toBe("hf_service_secret"); await expect( resolveAgentPushCredential({ ...allowlisted, target: { ...allowlisted.target, repoId: "service-owner/different" }, }) ).rejects.toMatchObject({ status: 403 }); await expect( resolveAgentPushCredential({ ...allowlisted, target: { ...allowlisted.target, repoType: "model" }, }) ).rejects.toMatchObject({ status: 403 }); await expect( resolveAgentPushCredential({ ...allowlisted, target: { ...allowlisted.target, create: true }, }) ).rejects.toMatchObject({ status: 403 }); }); it("retains the namespace gate even for an exact allowlist entry", async () => { await expect( resolveAgentPushCredential( options({ workspace: { repo: { kind: "hf", ref: "other-owner/live-fixture", repoType: "space" }, }, target: { ...target, repoId: "other-owner/live-fixture", create: false }, configValue: (name) => { if (name === "AGENT_SERVICE_PUSH_REPOS") { return "space:other-owner/live-fixture"; } return defaultConfigValue(name); }, }) ) ).rejects.toMatchObject({ status: 403 }); }); it("parses only exact typed repository entries", () => { expect([ ...parseServicePushRepos(" space:owner/app, model:owner/m, bad, SPACE:owner/nope "), ]).toEqual(["space:owner/app", "model:owner/m"]); }); it("falls back to the authenticated service owner when no namespace is configured", async () => { const resolveServiceOwner = vi.fn().mockResolvedValue("service-owner"); await expect( resolveAgentPushCredential( options({ configValue: (name) => (name === "AGENT_HF_TOKEN" ? "hf_service_secret" : ""), resolveServiceOwner, }) ) ).resolves.toBe("hf_service_secret"); expect(resolveServiceOwner).toHaveBeenCalledWith("hf_service_secret"); }); it("requires either a user credential or configured service credential", async () => { await expect( resolveAgentPushCredential(options({ configValue: () => "" })) ).rejects.toMatchObject({ status: 401 }); }); });