Files
livedash-node/tests/lib/importProcessor.test.ts
Kaj Kowalski a0ac60cf04 feat: implement comprehensive email system with rate limiting and extensive test suite
- Add robust email service with rate limiting and configuration management
- Implement shared rate limiter utility for consistent API protection
- Create comprehensive test suite for core processing pipeline
- Add API tests for dashboard metrics and authentication routes
- Fix date range picker infinite loop issue
- Improve session lookup in refresh sessions API
- Refactor session API routing with better code organization
- Update processing pipeline status monitoring
- Clean up leftover files and improve code formatting
2025-07-12 00:26:30 +02:00

230 lines
6.3 KiB
TypeScript

import { describe, it, expect, beforeEach, afterEach, vi } from "vitest";
import { PrismaClient } from "@prisma/client";
import { processQueuedImports } from "../../lib/importProcessor";
import { ProcessingStatusManager } from "../../lib/processingStatusManager";
vi.mock("../../lib/prisma", () => ({
prisma: new PrismaClient(),
}));
vi.mock("../../lib/processingStatusManager", () => ({
ProcessingStatusManager: {
initializeStage: vi.fn(),
startStage: vi.fn(),
completeStage: vi.fn(),
failStage: vi.fn(),
skipStage: vi.fn(),
},
}));
describe("Import Processor", () => {
beforeEach(() => {
vi.clearAllMocks();
});
afterEach(() => {
vi.restoreAllMocks();
});
describe("processQueuedImports", () => {
it("should process imports within specified limit", async () => {
const mockSessionImports = [
{
id: "import1",
companyId: "company1",
externalSessionId: "session1",
startTimeRaw: "2024-01-01 10:00:00",
endTimeRaw: "2024-01-01 11:00:00",
ipAddress: "192.168.1.1",
countryCode: "US",
language: "en",
messagesSent: 5,
sentimentRaw: "positive",
escalatedRaw: "false",
forwardedHrRaw: "false",
fullTranscriptUrl: "http://example.com/transcript1",
avgResponseTimeSeconds: 2.5,
tokens: 100,
tokensEur: 0.002,
category: "SUPPORT",
initialMessage: "Hello, I need help",
},
];
// Mock the prisma queries
const prismaMock = {
sessionImport: {
findMany: vi.fn().mockResolvedValue(mockSessionImports),
},
session: {
create: vi.fn().mockResolvedValue({
id: "new-session-id",
companyId: "company1",
sessionId: "session1",
}),
},
};
// Replace the prisma import with our mock
vi.doMock("../../lib/prisma", () => ({
prisma: prismaMock,
}));
const result = await processQueuedImports(10);
expect(prismaMock.sessionImport.findMany).toHaveBeenCalledWith({
where: {
processingStatus: {
some: {
stage: "CSV_IMPORT",
status: "COMPLETED",
},
none: {
stage: "SESSION_CREATION",
status: "COMPLETED",
},
},
},
take: 10,
orderBy: { createdAt: "asc" },
});
expect(result.processed).toBe(1);
expect(result.total).toBe(1);
});
it("should handle processing errors gracefully", async () => {
const mockSessionImports = [
{
id: "import1",
companyId: "company1",
externalSessionId: "session1",
startTimeRaw: "invalid-date",
endTimeRaw: "2024-01-01 11:00:00",
},
];
const prismaMock = {
sessionImport: {
findMany: vi.fn().mockResolvedValue(mockSessionImports),
},
session: {
create: vi.fn().mockRejectedValue(new Error("Database error")),
},
};
vi.doMock("../../lib/prisma", () => ({
prisma: prismaMock,
}));
const result = await processQueuedImports(10);
expect(ProcessingStatusManager.failStage).toHaveBeenCalled();
expect(result.processed).toBe(0);
expect(result.errors).toBe(1);
});
it("should correctly parse sentiment values", async () => {
const testCases = [
{ sentimentRaw: "positive", expected: "POSITIVE" },
{ sentimentRaw: "negative", expected: "NEGATIVE" },
{ sentimentRaw: "neutral", expected: "NEUTRAL" },
{ sentimentRaw: "unknown", expected: "NEUTRAL" },
{ sentimentRaw: null, expected: "NEUTRAL" },
];
for (const testCase of testCases) {
const mockImport = {
id: "import1",
companyId: "company1",
externalSessionId: "session1",
sentimentRaw: testCase.sentimentRaw,
startTimeRaw: "2024-01-01 10:00:00",
endTimeRaw: "2024-01-01 11:00:00",
};
const prismaMock = {
sessionImport: {
findMany: vi.fn().mockResolvedValue([mockImport]),
},
session: {
create: vi.fn().mockImplementation((data) => {
expect(data.data.sentiment).toBe(testCase.expected);
return Promise.resolve({ id: "session-id" });
}),
},
};
vi.doMock("../../lib/prisma", () => ({
prisma: prismaMock,
}));
await processQueuedImports(1);
}
});
it("should handle boolean string conversions", async () => {
const mockImport = {
id: "import1",
companyId: "company1",
externalSessionId: "session1",
escalatedRaw: "true",
forwardedHrRaw: "false",
startTimeRaw: "2024-01-01 10:00:00",
endTimeRaw: "2024-01-01 11:00:00",
};
const prismaMock = {
sessionImport: {
findMany: vi.fn().mockResolvedValue([mockImport]),
},
session: {
create: vi.fn().mockImplementation((data) => {
expect(data.data.escalated).toBe(true);
expect(data.data.forwardedHr).toBe(false);
return Promise.resolve({ id: "session-id" });
}),
},
};
vi.doMock("../../lib/prisma", () => ({
prisma: prismaMock,
}));
await processQueuedImports(1);
});
it("should validate required fields", async () => {
const mockImport = {
id: "import1",
companyId: null, // Invalid - missing required field
externalSessionId: "session1",
startTimeRaw: "2024-01-01 10:00:00",
endTimeRaw: "2024-01-01 11:00:00",
};
const prismaMock = {
sessionImport: {
findMany: vi.fn().mockResolvedValue([mockImport]),
},
session: {
create: vi.fn(),
},
};
vi.doMock("../../lib/prisma", () => ({
prisma: prismaMock,
}));
const result = await processQueuedImports(1);
expect(ProcessingStatusManager.failStage).toHaveBeenCalledWith(
expect.any(String),
"SESSION_CREATION",
expect.stringContaining("Missing required field")
);
expect(result.errors).toBe(1);
});
});
});