import { beforeEach, describe, expect, it, vi } from "vitest"; const apiGet = vi.fn(); const apiPost = vi.fn(); const apiPut = vi.fn(); const apiPatch = vi.fn(); const apiDelete = vi.fn(); vi.mock("./axios", () => ({ apiGet, apiPost, apiPut, apiPatch, apiDelete })); describe("collaboration API", () => { beforeEach(() => vi.clearAllMocks()); it("keeps editor configuration outside request dedupe and desktop cache", async () => { const { fetchCollaborationEditorConfig } = await import("./collaboration"); fetchCollaborationEditorConfig("study-1", "file-1"); expect(apiGet).toHaveBeenCalledWith( "/api/v1/studies/study-1/collaboration/files/file-1/editor-config", { cache: false, disableRequestDedupe: true, disableNetworkRetry: true, suppressErrorMessage: true, }, ); }); it("uses an independent collaboration namespace", async () => { const { createCollaborationFile, fetchCollaborationFiles } = await import("./collaboration"); createCollaborationFile("study-1", { title: "方案", file_type: "word" }); fetchCollaborationFiles("study-1", { keyword: "方案" }); expect(apiPost).toHaveBeenCalledWith( "/api/v1/studies/study-1/collaboration/files", { title: "方案", file_type: "word" }, ); expect(apiGet).toHaveBeenCalledWith( "/api/v1/studies/study-1/collaboration/files", { params: { keyword: "方案" } }, ); }); it("records a completed Save As operation for audit", async () => { const { recordCollaborationExport } = await import("./collaboration"); recordCollaborationExport("study-1", "file-1", "xlsx"); expect(apiPost).toHaveBeenCalledWith( "/api/v1/studies/study-1/collaboration/files/file-1/exports", { file_type: "xlsx" }, ); }); it("records a completed local download separately from workspace Save As", async () => { const { recordCollaborationDownload } = await import("./collaboration"); recordCollaborationDownload("study-1", "file-1", "xlsx"); expect(apiPost).toHaveBeenCalledWith( "/api/v1/studies/study-1/collaboration/files/file-1/downloads", { file_type: "xlsx" }, ); }); it("copies and downloads collaboration files through authenticated endpoints", async () => { const { copyCollaborationFile, downloadCollaborationFile } = await import("./collaboration"); copyCollaborationFile("study-1", "file-1"); downloadCollaborationFile("study-1", "file-1"); expect(apiPost).toHaveBeenCalledWith( "/api/v1/studies/study-1/collaboration/files/file-1/copy", ); expect(apiGet).toHaveBeenCalledWith( "/api/v1/studies/study-1/collaboration/files/file-1/download", { responseType: "blob", cache: false, suppressErrorMessage: true, }, ); }); it("always reloads revision history without cache or pending-request reuse", async () => { const { fetchCollaborationRevisions } = await import("./collaboration"); fetchCollaborationRevisions("study-1", "file-1"); expect(apiGet).toHaveBeenCalledWith( "/api/v1/studies/study-1/collaboration/files/file-1/revisions", { cache: false, disableRequestDedupe: true }, ); }); it("supports naming, copying, and deleting a selected historical revision", async () => { const { copyCollaborationRevision, deleteCollaborationRevision, updateCollaborationRevision } = await import("./collaboration"); updateCollaborationRevision("study-1", "file-1", "revision-2", { change_summary: "送审版" }); copyCollaborationRevision("study-1", "file-1", "revision-2", { title: "方案-R2.docx", folder_id: "folder-2" }); deleteCollaborationRevision("study-1", "file-1", "revision-2"); expect(apiPatch).toHaveBeenCalledWith( "/api/v1/studies/study-1/collaboration/files/file-1/revisions/revision-2", { change_summary: "送审版" }, ); expect(apiPost).toHaveBeenCalledWith( "/api/v1/studies/study-1/collaboration/files/file-1/revisions/revision-2/copy", { title: "方案-R2.docx", folder_id: "folder-2" }, ); expect(apiDelete).toHaveBeenCalledWith( "/api/v1/studies/study-1/collaboration/files/file-1/revisions/revision-2", ); }); it("keeps share tokens in a redacted request header and public requests outside auth recovery", async () => { const { fetchCollaborationShareLink, fetchPublicCollaborationShareMetadata, updateCollaborationShareLink, verifyPublicCollaborationSharePassword, } = await import("./collaboration"); fetchCollaborationShareLink("study-1", "file-1"); updateCollaborationShareLink("study-1", "file-1", { enabled: true, access_mode: "EDIT", expiry_policy: "SEVEN_DAYS", password_mode: "SET", password: "1234", }); fetchPublicCollaborationShareMetadata("share-secret"); verifyPublicCollaborationSharePassword("share-secret", "1234"); expect(apiGet).toHaveBeenCalledWith( "/api/v1/studies/study-1/collaboration/files/file-1/share-link", { cache: false, disableRequestDedupe: true }, ); expect(apiPut).toHaveBeenCalledWith( "/api/v1/studies/study-1/collaboration/files/file-1/share-link", expect.objectContaining({ enabled: true, expiry_policy: "SEVEN_DAYS", password: "1234" }), ); const publicConfig = expect.objectContaining({ headers: { "X-CTMS-Share-Token": "share-secret" }, publicRequest: true, cache: false, disableRequestDedupe: true, disableNetworkRetry: true, suppressErrorMessage: true, invalidateCache: false, }); expect(apiGet).toHaveBeenCalledWith("/api/v1/collaboration/shares/metadata", publicConfig); expect(apiPost).toHaveBeenCalledWith( "/api/v1/collaboration/shares/access", { password: "1234" }, publicConfig, ); }); it("supports edit-request approval and contact ownership transfer", async () => { const { createCollaborationEditRequest, fetchCollaborationEditRequests, resolveCollaborationEditRequest, transferCollaborationOwnership, } = await import("./collaboration"); createCollaborationEditRequest("study-1", "file-1"); fetchCollaborationEditRequests("study-1", "file-1"); resolveCollaborationEditRequest("study-1", "file-1", "request-1", "APPROVED"); transferCollaborationOwnership("study-1", "file-1", "user-2"); expect(apiPost).toHaveBeenCalledWith( "/api/v1/studies/study-1/collaboration/files/file-1/edit-requests", ); expect(apiGet).toHaveBeenCalledWith( "/api/v1/studies/study-1/collaboration/files/file-1/edit-requests", { cache: false, disableRequestDedupe: true }, ); expect(apiPost).toHaveBeenCalledWith( "/api/v1/studies/study-1/collaboration/files/file-1/edit-requests/request-1/resolve", { status: "APPROVED" }, ); expect(apiPost).toHaveBeenCalledWith( "/api/v1/studies/study-1/collaboration/files/file-1/transfer-ownership", { new_owner_id: "user-2" }, ); }); });