Files
HiveGrid-Wireframes/03 Quellen und Referenzen/hivegrid-cloud/apps/web/app/services/admin/admin-sales-data.server.test.ts
T

183 lines
5.6 KiB
TypeScript

// @vitest-environment node
import { describe, expect, it, vi } from "vitest";
import {
confirmAdminSalesContactsImport,
prepareAdminSalesContactsImport,
requestAdminSalesContactsExport,
type AdminSalesDataOperatorContext,
} from "./admin-sales-data.server";
function context() {
return {
adminUserId: "admin_1",
email: "sales@hivegrid.de",
canWrite: true,
canExport: true,
actor: (reason: string) => ({
adminUserId: "admin_1",
email: "sales@hivegrid.de",
reason,
requestId: "request_1",
ip: null,
userAgent: null,
}),
service: {
createImportJob: vi.fn(async (input) => ({
id: "import_1",
fileName: input.fileName,
sourceBucket: input.sourceBucket,
sourceKey: input.sourceKey,
sourceHeaders: input.sourceHeaders,
fieldMapping: input.fieldMapping,
requestedByAdminUserId: "admin_1",
})),
getImportJobForQueue: vi.fn(async () => ({
id: "import_1",
requestedByAdminUserId: "admin_1",
})),
markImportJobQueued: vi.fn(async () => ({ id: "import_1" })),
createExportJob: vi.fn(async () => ({
id: "export_1",
requestedByAdminUserId: "admin_1",
})),
markExportJobQueued: vi.fn(async () => ({ id: "export_1" })),
},
};
}
describe("admin sales data web service", () => {
it("scans and stores CSV bytes before creating a pending preview job", async () => {
const operator = context();
const store = vi.fn(async () => "stored");
const remove = vi.fn(async () => undefined);
const result = await prepareAdminSalesContactsImport(
{
file: new File(
["Vorname,Nachname,E-Mail\nAda,Lovelace,ada@example.com\n"],
"kontakte.csv",
{ type: "text/csv" }
),
reason: "Kontaktimport prüfen",
context: operator as unknown as AdminSalesDataOperatorContext,
},
{
bucket: () => "hivegrid-files",
store,
remove,
id: () => "upload_1",
}
);
expect(store).toHaveBeenCalledWith(
expect.objectContaining({
bucket: "hivegrid-files",
id: "admin-sales/imports/admin_1/upload_1.csv",
throwOnFailure: true,
scan: expect.objectContaining({
context: "formUpload",
requireMalwareScan: true,
filename: "kontakte.csv",
}),
})
);
expect(operator.service.createImportJob).toHaveBeenCalledWith(
expect.objectContaining({
sourceHeaders: ["Vorname", "Nachname", "E-Mail"],
fieldMapping: [
"Vorname=firstName",
"Nachname=lastName",
"E-Mail=email",
],
})
);
expect(result.preview.rowCount).toBe(1);
expect(remove).not.toHaveBeenCalled();
});
it("removes the scanned object if pending-job persistence fails", async () => {
const operator = context();
operator.service.createImportJob.mockRejectedValueOnce(
new Error("database unavailable")
);
const remove = vi.fn(async () => undefined);
await expect(
prepareAdminSalesContactsImport(
{
file: new File(
["Vorname,Nachname\nAda,Lovelace\n"],
"kontakte.csv"
),
reason: "Kontaktimport prüfen",
context: operator as unknown as AdminSalesDataOperatorContext,
},
{
bucket: () => "hivegrid-files",
store: vi.fn(async () => "stored"),
remove,
id: () => "upload_1",
}
)
).rejects.toThrow("database unavailable");
expect(remove).toHaveBeenCalledWith({
bucket: "hivegrid-files",
key: "admin-sales/imports/admin_1/upload_1.csv",
});
});
it("queues only an owned reviewed import and marks it queued afterward", async () => {
const operator = context();
const add = vi.fn(async () => ({ id: "queue_1" }));
await confirmAdminSalesContactsImport({
importJobId: "import_1",
reason: "Geprüften Import starten",
context: operator as unknown as AdminSalesDataOperatorContext,
createQueue: async () => ({ add } as never),
});
expect(operator.service.getImportJobForQueue).toHaveBeenCalledWith(
expect.objectContaining({ jobId: "import_1" })
);
expect(add).toHaveBeenCalledWith(
"run-import",
expect.objectContaining({
kind: "admin-sales.contacts-import",
importJobId: "import_1",
}),
expect.objectContaining({ priority: 1 })
);
expect(
operator.service.markImportJobQueued.mock.invocationCallOrder[0]
).toBeGreaterThan(add.mock.invocationCallOrder[0]!);
});
it("uses the same worker queue for bounded contact exports", async () => {
const operator = context();
const add = vi.fn(async () => ({ id: "queue_1" }));
await requestAdminSalesContactsExport({
contactIds: ["contact_1"],
reason: "Kontakte exportieren",
requestId: "export_request_1",
context: operator as unknown as AdminSalesDataOperatorContext,
createQueue: async () => ({ add } as never),
});
expect(operator.service.createExportJob).toHaveBeenCalledWith(
expect.objectContaining({
selectedContactIds: ["contact_1"],
idempotencyKey:
"admin-sales-export:admin_1:export_request_1",
})
);
expect(add).toHaveBeenCalledWith(
"run-import",
expect.objectContaining({
kind: "admin-sales.contacts-export",
exportJobId: "export_1",
}),
expect.any(Object)
);
expect(
operator.service.markExportJobQueued.mock.invocationCallOrder[0]
).toBeGreaterThan(add.mock.invocationCallOrder[0]!);
});
});