// The Lucia project. // Copyright 2026-2026 DSP, inc. All rights reserved. // Authors: // imacat.yang@dsp.im (imacat), 2026/03/06 import { describe, it, expect, beforeEach, vi } from "vitest"; import { setActivePinia, createPinia } from "pinia"; import { http, HttpResponse } from "msw"; import { server } from "@/mocks/node.js"; import { findRequest, captureRequest } from "@/mocks/request-log.js"; // Mock modules that have deep import chains (router, Swal, pinia, toast) vi.mock("@/module/apiError.js", () => ({ default: vi.fn(), })); vi.mock("@/module/alertModal.js", () => ({ uploadFailedFirst: vi.fn(), uploadFailedSecond: vi.fn(), uploadloader: vi.fn(), uploadSuccess: vi.fn(), deleteSuccess: vi.fn(), })); vi.mock("sweetalert2", () => ({ default: { close: vi.fn(), fire: vi.fn() }, })); // Prevent module-level store init in cytoscapeMap.js (loaded via router → Map.vue) vi.mock("@/module/cytoscapeMap.js", () => ({})); vi.mock("@/router/index.ts", () => ({ default: { push: vi.fn(), currentRoute: { value: { path: "/" } } }, })); import { useFilesStore } from "@/stores/files"; describe("filesStore", () => { let store; beforeEach(() => { setActivePinia(createPinia()); store = useFilesStore(); store.$router = { push: vi.fn() }; document.cookie = "luciaToken=fake-test-token"; }); it("has correct default state", () => { expect(store.filesTag).toBe("ALL"); expect(store.httpStatus).toBe(200); expect(store.uploadId).toBeNull(); }); describe("allFiles getter", () => { it("filters files by current filesTag", () => { store.allEventFiles = [ { fileType: "Log", name: "a.xes" }, { fileType: "Filter", name: "b" }, { fileType: "Design", name: "c" }, ]; store.filesTag = "COMPARE"; expect(store.allFiles.map((f) => f.name)).toEqual(["a.xes", "b"]); store.filesTag = "ALL"; expect(store.allFiles).toHaveLength(3); }); }); describe("fetchAllFiles", () => { it("fetches and transforms file data", async () => { server.use( http.get("/api/files", ({ request }) => { captureRequest("GET", new URL(request.url).pathname); return HttpResponse.json([ { type: "log", name: "test.xes", owner: { name: "Alice" }, updated_at: "2024-01-15T10:00:00Z", accessed_at: "2024-01-15T11:00:00Z", }, { type: "filter", name: "filter1", parent: { name: "test.xes" }, owner: { name: "Bob" }, updated_at: "2024-01-16T10:00:00Z", accessed_at: null, }, ]); }), ); await store.fetchAllFiles(); const reqs = findRequest("GET", "/api/files"); expect(reqs).toHaveLength(1); expect(store.allEventFiles).toHaveLength(2); expect(store.allEventFiles[0].fileType).toBe("Log"); expect(store.allEventFiles[0].icon).toBe("work_history"); expect(store.allEventFiles[0].ownerName).toBe("Alice"); expect(store.allEventFiles[1].fileType).toBe("Filter"); expect(store.allEventFiles[1].parentLog).toBe("test.xes"); expect(store.allEventFiles[1].accessed_at).toBeNull(); }); it("does not throw on API failure", async () => { server.use( http.get("/api/files", () => new HttpResponse(null, { status: 500 }), ), ); await expect(store.fetchAllFiles()).resolves.toBeUndefined(); }); it("maps design files without leaking metadata from previous file items", async () => { server.use( http.get("/api/files", () => HttpResponse.json([ { type: "log", name: "order-log", owner: { name: "Alice" }, updated_at: "2024-01-15T10:00:00Z", accessed_at: null, }, { type: "design", name: "diagram-a", owner: { name: "Bob" }, updated_at: "2024-01-16T10:00:00Z", accessed_at: null, }, ]), ), ); await store.fetchAllFiles(); expect(store.allEventFiles[1].icon).toBe("shape_line"); expect(store.allEventFiles[1].fileType).toBe("Design"); expect(store.allEventFiles[1].parentLog).toBe("diagram-a"); }); }); describe("upload", () => { it("uploads file and navigates to Upload page", async () => { server.use( http.post("/api/logs/csv-uploads", async ({ request }) => { captureRequest("POST", new URL(request.url).pathname); return HttpResponse.json({ id: 42 }); }), ); const formData = new FormData(); await store.upload(formData); const reqs = findRequest("POST", "/api/logs/csv-uploads"); expect(reqs).toHaveLength(1); expect(store.uploadId).toBe(42); expect(store.$router.push).toHaveBeenCalledWith({ name: "Upload" }); }); }); describe("getUploadDetail", () => { it("fetches upload preview", async () => { store.uploadId = 10; server.use( http.get("/api/logs/csv-uploads/:id", ({ request }) => { captureRequest("GET", new URL(request.url).pathname); return HttpResponse.json({ preview: { columns: ["a", "b"] } }); }), ); await store.getUploadDetail(); const reqs = findRequest("GET", "/api/logs/csv-uploads/10"); expect(reqs).toHaveLength(1); expect(store.allUploadDetail).toEqual({ columns: ["a", "b"] }); }); }); describe("rename", () => { it("renames a log file", async () => { server.use( http.put("/api/logs/:id/name", async ({ request, params }) => { const body = await request.json(); captureRequest("PUT", `/api/logs/${params.id}/name`, body); return HttpResponse.json({}); }), http.get("/api/files", () => HttpResponse.json([])), ); await store.rename("log", 5, "new-name"); const reqs = findRequest("PUT", "/api/logs/5/name"); expect(reqs).toHaveLength(1); expect(reqs[0].body).toEqual({ name: "new-name" }); }); }); describe("getDependents", () => { it("fetches dependents for a log", async () => { server.use( http.get("/api/logs/:id/dependents", ({ request }) => { captureRequest("GET", new URL(request.url).pathname); return HttpResponse.json([{ id: 1 }, { id: 2 }]); }), ); await store.getDependents("log", 7); const reqs = findRequest("GET", "/api/logs/7/dependents"); expect(reqs).toHaveLength(1); expect(store.allDependentsData).toEqual([{ id: 1 }, { id: 2 }]); }); }); describe("deleteFile", () => { it("calls delete before fetchAllFiles", async () => { const callOrder = []; server.use( http.delete("/api/logs/:id", ({ params }) => { callOrder.push("delete"); captureRequest("DELETE", `/api/logs/${params.id}`); return HttpResponse.json({}); }), http.get("/api/files", () => { callOrder.push("get"); return HttpResponse.json([]); }), ); await store.deleteFile("log", 1); const reqs = findRequest("DELETE", "/api/logs/1"); expect(reqs).toHaveLength(1); expect(callOrder.indexOf("delete")).toBeLessThan( callOrder.indexOf("get"), ); }); it("returns early for invalid id without throwing", async () => { const spy = vi.spyOn(console, "error").mockImplementation(() => {}); await expect(store.deleteFile("log", null)).resolves.toBeUndefined(); expect(spy).toHaveBeenCalledWith("Delete File API Error: invalid id"); spy.mockRestore(); }); }); describe("deletionRecord", () => { it("deletes a deletion record", async () => { server.use( http.delete("/api/deletion/:id", ({ params }) => { captureRequest("DELETE", `/api/deletion/${params.id}`); return HttpResponse.json({}); }), ); await store.deletionRecord(5); const reqs = findRequest("DELETE", "/api/deletion/5"); expect(reqs).toHaveLength(1); }); }); describe("downloadFileCSV", () => { it("downloads CSV for a log", async () => { server.use( http.get("/api/logs/:id/csv", ({ request }) => { captureRequest("GET", new URL(request.url).pathname); return new HttpResponse("col1,col2\na,b"); }), ); globalThis.URL.createObjectURL = vi.fn().mockReturnValue("blob:test"); globalThis.URL.revokeObjectURL = vi.fn(); await store.downloadFileCSV("log", 3, "my-file"); const reqs = findRequest("GET", "/api/logs/3/csv"); expect(reqs).toHaveLength(1); expect(globalThis.URL.revokeObjectURL).toHaveBeenCalledWith("blob:test"); }); it("returns early for unsupported type", async () => { await store.downloadFileCSV("log-check", 3, "file"); const reqs = findRequest("GET", /\/api\/.*\/csv/); expect(reqs).toHaveLength(0); }); }); });