import { afterEach, describe, expect, test, vi } from "vitest"; const agentManagerMock = vi.hoisted(() => ({ runVolumeCommand: vi.fn(), })); vi.mock("../../agents/agents-manager", () => ({ agentManager: agentManagerMock, })); import { volumeService } from "../volume.service"; import { db } from "~/server/db/db"; import { volumesTable } from "~/server/db/schema"; import { randomUUID } from "node:crypto"; import { createTestSession } from "~/test/helpers/auth"; import { withContext } from "~/server/core/request-context"; import { asShortId } from "~/server/utils/branded"; import { createTestVolume } from "~/test/helpers/volume"; import { config } from "~/server/core/config"; afterEach(() => { config.flags.enableLocalAgent = false; vi.restoreAllMocks(); agentManagerMock.runVolumeCommand.mockReset(); }); describe("volumeService.getVolume", () => { test("should find volume by shortId", async () => { const { organizationId, user } = await createTestSession(); const [volume] = await db .insert(volumesTable) .values({ shortId: asShortId(randomUUID().slice(0, 8)), name: `test-vol-${randomUUID().slice(0, 8)}`, type: "directory", status: "mounted", config: { backend: "directory", path: "/" }, autoRemount: true, organizationId, }) .returning(); await withContext({ organizationId, userId: user.id }, async () => { const result = await volumeService.getVolume(volume.shortId); expect(result.volume.id).toBe(volume.id); expect(result.volume.shortId).toBe(volume.shortId); }); }); test("should find volume by shortId from literal input", async () => { const { organizationId, user } = await createTestSession(); const [volume] = await db .insert(volumesTable) .values({ shortId: asShortId("test1234"), name: `test-vol-${randomUUID().slice(0, 8)}`, type: "directory", status: "mounted", config: { backend: "directory", path: "/" }, autoRemount: true, organizationId, }) .returning(); await withContext({ organizationId, userId: user.id }, async () => { const result = await volumeService.getVolume(volume.shortId); expect(result.volume.id).toBe(volume.id); expect(result.volume.shortId).toBe(volume.shortId); }); }); test("should find volume by numeric-looking shortId", async () => { const { organizationId, user } = await createTestSession(); const [volume] = await db .insert(volumesTable) .values({ shortId: asShortId("499780"), name: `test-vol-${randomUUID().slice(0, 8)}`, type: "directory", status: "mounted", config: { backend: "directory", path: "/" }, autoRemount: true, organizationId, }) .returning(); await withContext({ organizationId, userId: user.id }, async () => { const result = await volumeService.getVolume(asShortId("499780")); expect(result.volume.id).toBe(volume.id); expect(result.volume.shortId).toBe(asShortId("499780")); }); }); test("should throw NotFoundError for non-existent volume", async () => { const { organizationId, user } = await createTestSession(); await withContext({ organizationId, userId: user.id }, async () => { await expect(volumeService.getVolume(asShortId("nonexistent"))).rejects.toThrow("Volume not found"); }); }); }); describe("volumeService.listFiles security", () => { test("should reject traversal outside the volume root in listFiles", async () => { const { organizationId, user } = await createTestSession(); agentManagerMock.runVolumeCommand.mockRejectedValue(new Error("Invalid path")); const [volume] = await db .insert(volumesTable) .values({ shortId: asShortId(randomUUID().slice(0, 8)), name: `test-vol-${randomUUID().slice(0, 8)}`, type: "directory", status: "mounted", config: { backend: "directory", path: "/tmp/volume" }, autoRemount: true, organizationId, }) .returning(); await withContext({ organizationId, userId: user.id }, async () => { await expect(volumeService.listFiles(volume.shortId, "../volume-secret")).rejects.toThrow("Invalid path"); }); }); }); describe("volumeService.mountVolume", () => { test("routes unmount and mount to the owning agent before updating state", async () => { const { organizationId, user } = await createTestSession(); const volume = await createTestVolume({ organizationId, status: "mounted", agentId: "agent-1" }); agentManagerMock.runVolumeCommand .mockResolvedValueOnce({ name: "volume.unmount", result: { status: "unmounted" } }) .mockResolvedValueOnce({ name: "volume.mount", result: { status: "mounted" } }); await withContext({ organizationId, userId: user.id }, async () => { const result = await volumeService.mountVolume(volume.shortId); expect(result.status).toBe("mounted"); expect(agentManagerMock.runVolumeCommand).toHaveBeenNthCalledWith( 1, volume.agentId, expect.objectContaining({ name: "volume.unmount", volume: expect.objectContaining({ id: volume.id }) }), ); expect(agentManagerMock.runVolumeCommand).toHaveBeenNthCalledWith( 2, volume.agentId, expect.objectContaining({ name: "volume.mount", volume: expect.objectContaining({ id: volume.id }) }), ); }); }); }); describe("volumeService.unmountVolume", () => { test("persists the unmounted status for normal unmount requests", async () => { const { organizationId, user } = await createTestSession(); const volume = await createTestVolume({ organizationId, status: "mounted", agentId: "agent-1" }); agentManagerMock.runVolumeCommand.mockResolvedValueOnce({ name: "volume.unmount", result: { status: "unmounted" }, }); await withContext({ organizationId, userId: user.id }, async () => { const result = await volumeService.unmountVolume(volume.shortId); expect(result.status).toBe("unmounted"); expect(agentManagerMock.runVolumeCommand).toHaveBeenCalledWith( volume.agentId, expect.objectContaining({ name: "volume.unmount", volume: expect.objectContaining({ id: volume.id }) }), ); }); const updatedVolume = await db.query.volumesTable.findFirst({ where: { id: volume.id } }); expect(updatedVolume?.status).toBe("unmounted"); }); }); describe("volumeService.ensureHealthyVolume", () => { test("returns ready when the mounted volume passes its health check", async () => { const { organizationId, user } = await createTestSession(); const volume = await createTestVolume({ organizationId, status: "mounted", agentId: "agent-1" }); agentManagerMock.runVolumeCommand.mockResolvedValue({ name: "volume.checkHealth", result: { status: "mounted" }, }); await withContext({ organizationId, userId: user.id }, async () => { const result = await volumeService.ensureHealthyVolume(volume.shortId); expect(result).toEqual({ ready: true, volume: expect.objectContaining({ id: volume.id, status: "mounted", lastError: null }), remounted: false, }); expect(agentManagerMock.runVolumeCommand).toHaveBeenCalledOnce(); expect(agentManagerMock.runVolumeCommand).toHaveBeenCalledWith( volume.agentId, expect.objectContaining({ name: "volume.checkHealth", volume: expect.objectContaining({ id: volume.id }), }), ); }); }); test("auto-remounts when the mounted volume fails its health check", async () => { const { organizationId, user } = await createTestSession(); const volume = await createTestVolume({ organizationId, status: "mounted", autoRemount: true, agentId: "agent-1", }); agentManagerMock.runVolumeCommand .mockResolvedValueOnce({ name: "volume.checkHealth", result: { status: "error", error: "stale mount" } }) .mockResolvedValueOnce({ name: "volume.unmount", result: { status: "unmounted" } }) .mockResolvedValueOnce({ name: "volume.mount", result: { status: "mounted" } }); await withContext({ organizationId, userId: user.id }, async () => { const result = await volumeService.ensureHealthyVolume(volume.shortId); expect(result).toEqual({ ready: true, volume: expect.objectContaining({ id: volume.id, status: "mounted", lastError: null }), remounted: true, }); expect(agentManagerMock.runVolumeCommand).toHaveBeenCalledTimes(3); const updatedVolume = await db.query.volumesTable.findFirst({ where: { id: volume.id } }); expect(updatedVolume?.status).toBe("mounted"); expect(updatedVolume?.lastError).toBeNull(); }); }); test("returns not ready when the health check fails and auto-remount is disabled", async () => { const { organizationId, user } = await createTestSession(); const volume = await createTestVolume({ organizationId, status: "mounted", autoRemount: false, agentId: "agent-1", }); agentManagerMock.runVolumeCommand.mockResolvedValue({ name: "volume.checkHealth", result: { status: "error", error: "stale mount" }, }); await withContext({ organizationId, userId: user.id }, async () => { const result = await volumeService.ensureHealthyVolume(volume.shortId); expect(result).toEqual({ ready: false, volume: expect.objectContaining({ id: volume.id, status: "error", lastError: "stale mount" }), reason: "stale mount", }); expect(agentManagerMock.runVolumeCommand).toHaveBeenCalledOnce(); }); }); }); describe("volumeService.testConnection", () => { test("routes test connections to the local agent", async () => { config.flags.enableLocalAgent = true; agentManagerMock.runVolumeCommand.mockResolvedValue({ name: "volume.testConnection", result: { success: true, message: "Connection successful" }, }); await expect( volumeService.testConnection({ backend: "nfs", server: "127.0.0.1", exportPath: "/exports/test", version: "4", port: 2049, readOnly: false, }), ).resolves.toEqual({ success: true, message: "Connection successful", }); expect(agentManagerMock.runVolumeCommand).toHaveBeenCalledWith( "local", expect.objectContaining({ name: "volume.testConnection" }), ); }); });