Files
HiveGrid-Wireframes/03 Quellen und Referenzen/hivegrid-cloud/apps/web/app/modules/emailInbox/services/EmailAttachmentService.server.test.ts
T

130 lines
4.7 KiB
TypeScript

import { beforeEach, describe, expect, it, vi } from "vitest";
const mocks = vi.hoisted(() => ({
adapterForProvider: vi.fn(),
deleteFile: vi.fn(async () => undefined),
downloadAttachment: vi.fn(),
ensureFreshMailboxCreds: vi.fn(),
fileCreate: vi.fn(async () => ({ id: "created" })),
linkCreate: vi.fn(async () => ({ id: "linked" })),
linkFindFirst: vi.fn(),
loadMailboxCreds: vi.fn(),
putBufferAsFile: vi.fn(async () => ({ publicUrl: null })),
withStorageReservation: vi.fn(),
events: [] as string[],
}));
const transaction = {
file: { create: mocks.fileCreate },
fileEntityLink: { create: mocks.linkCreate, findFirst: mocks.linkFindFirst },
};
vi.mock("@hivegrid/db/storage-capacity.server", () => ({
withTenantStorageWriteReservation: (...args: unknown[]) => mocks.withStorageReservation(...args),
}));
vi.mock("~/utils/db/.server/tenantRls.server", () => ({
withTenantRLS: async (_tenantId: string, operation: (tx: typeof transaction) => Promise<unknown>) => operation(transaction),
}));
vi.mock("~/utils/integrations/storageService", () => ({
deleteFile: (...args: unknown[]) => mocks.deleteFile(...args),
putBufferAsFile: async (...args: unknown[]) => {
mocks.events.push("upload");
return mocks.putBufferAsFile(...args);
},
resolveStorageBucket: () => "email-attachments",
}));
vi.mock("./MailboxConnectionService.server", () => ({
adapterForProvider: mocks.adapterForProvider,
ensureFreshMailboxCreds: mocks.ensureFreshMailboxCreds,
loadMailboxCreds: mocks.loadMailboxCreds,
}));
import { importAttachmentToFiles } from "./EmailAttachmentService.server";
describe("email attachment storage reservation", () => {
beforeEach(() => {
vi.clearAllMocks();
mocks.events.length = 0;
mocks.linkFindFirst.mockResolvedValue(null);
mocks.loadMailboxCreds.mockResolvedValue({ accessToken: "encrypted" });
mocks.ensureFreshMailboxCreds.mockResolvedValue({ accessToken: "fresh" });
mocks.adapterForProvider.mockReturnValue({ downloadAttachment: mocks.downloadAttachment });
mocks.downloadAttachment.mockResolvedValue({
filename: "invoice.pdf",
mimeType: "application/pdf",
contentBase64: Buffer.from("hello").toString("base64"),
});
mocks.withStorageReservation.mockImplementation(async (input) => {
mocks.events.push("reserve");
const stored = await input.upload();
return input.runTransaction(async (tx) => {
mocks.events.push("commit");
return input.commit(tx, stored);
});
});
});
it("reserves the exact decoded byte size before object upload and commits File plus link", async () => {
await expect(
importAttachmentToFiles({
tenantId: "tenant_1",
userId: "user_1",
mailboxId: "mailbox_1",
provider: "microsoft",
messageId: "message_1",
providerMessageId: "provider-message",
attachmentId: "attachment_1",
})
).resolves.toEqual({ fileId: expect.stringMatching(/^file_/u) });
expect(mocks.events).toEqual(["reserve", "upload", "commit"]);
expect(mocks.withStorageReservation).toHaveBeenCalledWith(
expect.objectContaining({
reservation: expect.objectContaining({
tenantId: "tenant_1",
actorUserId: "user_1",
originalFilename: "invoice.pdf",
mimeType: "application/pdf",
sizeBytes: 5,
}),
})
);
expect(mocks.fileCreate).toHaveBeenCalledWith({
data: expect.objectContaining({ tenantId: "tenant_1", sizeBytes: 5, storageKey: expect.stringContaining("/original") }),
});
expect(mocks.linkCreate).toHaveBeenCalledWith({
data: expect.objectContaining({ entityType: "email_message", entityId: "message_1", linkRole: "attachment:attachment_1" }),
});
});
it("exposes an object rollback callback for failed database commits", async () => {
mocks.linkCreate.mockRejectedValueOnce(new Error("link failed"));
mocks.withStorageReservation.mockImplementationOnce(async (input) => {
const stored = await input.upload();
try {
return await input.runTransaction((tx) => input.commit(tx, stored));
} catch (error) {
await input.rollbackUpload(stored);
throw error;
}
});
await expect(
importAttachmentToFiles({
tenantId: "tenant_1",
userId: "user_1",
mailboxId: "mailbox_1",
provider: "microsoft",
messageId: "message_1",
providerMessageId: "provider-message",
attachmentId: "attachment_1",
})
).rejects.toThrow("link failed");
expect(mocks.deleteFile).toHaveBeenCalledWith({
bucket: "email-attachments",
key: expect.stringMatching(/^tenants\/tenant_1\/files\/file_.+\/original$/u),
});
});
});