import { describe, expect, it, vi } from "vitest"; import { ColineApiClient } from "./client"; function createJsonResponse(payload: unknown, status = 200): Response { return new Response(JSON.stringify(payload), { status, headers: { "Content-Type": "application/json" }, }); } describe("ColineApiClient", () => { it("installs a workspace app with explicit granted permissions", async () => { const fetchMock = vi.fn().mockResolvedValue( createJsonResponse({ data: { installId: "install_123", appId: "app_123", appKey: "com.coline.notes", version: "0.1.0", grantedPermissions: ["app.home.read"], }, }), ); const client = new ColineApiClient({ baseUrl: "https://api.coline.app", apiKey: "test-key", fetch: fetchMock, }); const result = await client.installWorkspaceApp("acme", { appId: "app_123", grantedPermissions: ["app.home.read"], }); expect(result.installId).toBe("install_123"); expect(fetchMock).toHaveBeenCalledWith( "https://api.coline.app/api/workspaces/acme/apps", expect.objectContaining({ method: "POST", headers: expect.any(Headers), }), ); const headers = fetchMock.mock.calls[0]?.[1]?.headers as Headers; expect(headers.get("Authorization")).toBe("Bearer test-key"); }); it("loads and updates workspace app permissions", async () => { const fetchMock = vi .fn() .mockResolvedValueOnce( createJsonResponse({ data: { install: { installId: "install_123", appId: "app_123", appKey: "com.coline.notes", name: "Notes", description: null, iconUrl: null, status: "active", installScope: "workspace", scopeSubjectId: "workspace_123", version: "0.1.0", }, availablePermissions: ["app.home.read", "files.write"], grantedPermissions: ["app.home.read"], permissions: { canManageApps: true, }, }, }), ) .mockResolvedValueOnce( createJsonResponse({ data: { install: { installId: "install_123", appId: "app_123", appKey: "com.coline.notes", name: "Notes", description: null, iconUrl: null, status: "active", installScope: "workspace", scopeSubjectId: "workspace_123", version: "0.1.0", }, availablePermissions: ["app.home.read", "files.write"], grantedPermissions: ["app.home.read", "files.write"], permissions: { canManageApps: true, }, }, }), ); const client = new ColineApiClient({ baseUrl: "https://api.coline.app", fetch: fetchMock, }); const currentPermissions = await client.getWorkspaceAppPermissions( "acme", "com.coline.notes", ); expect(currentPermissions.grantedPermissions).toEqual(["app.home.read"]); const updatedPermissions = await client.updateWorkspaceAppPermissions( "acme", "com.coline.notes", { grantedPermissions: ["app.home.read", "files.write"], }, ); expect(updatedPermissions.grantedPermissions).toEqual([ "app.home.read", "files.write", ]); }); it("loads app detail, app versions, installed app, and uninstalls it", async () => { const fetchMock = vi .fn() .mockResolvedValueOnce( createJsonResponse({ data: { app: { appId: "app_123", appKey: "com.coline.notes", name: "Notes", description: null, iconUrl: null, status: "active", ownerUserId: "user_123", latestPublishedVersion: "0.1.0", latestPublishedVersionId: "ver_123", }, }, }), ) .mockResolvedValueOnce( createJsonResponse({ data: { versions: [ { versionId: "ver_123", version: "0.1.0", status: "published", apiBaseUrl: "https://notes.example.com", permissions: ["app.home.read"], fileTypes: [], createdAt: "2026-04-01T00:00:00.000Z", updatedAt: "2026-04-01T00:00:00.000Z", }, ], }, }), ) .mockResolvedValueOnce( createJsonResponse({ data: { install: { installId: "install_123", appId: "app_123", appKey: "com.coline.notes", name: "Notes", description: null, iconUrl: null, status: "active", installScope: "workspace", scopeSubjectId: "workspace_123", version: "0.1.0", }, permissions: { canManageApps: true, }, }, }), ) .mockResolvedValueOnce(createJsonResponse({ data: { ok: true } })); const client = new ColineApiClient({ baseUrl: "https://api.coline.app", fetch: fetchMock, }); const app = await client.getApp("com.coline.notes"); expect(app.app.appKey).toBe("com.coline.notes"); const versions = await client.listAppVersions("com.coline.notes"); expect(versions.versions[0]?.version).toBe("0.1.0"); const installed = await client.getWorkspaceInstalledApp("acme", "com.coline.notes"); expect(installed.install.installId).toBe("install_123"); const uninstallResult = await client.uninstallWorkspaceApp( "acme", "com.coline.notes", ); expect(uninstallResult.ok).toBe(true); }); });