mirror of
https://github.com/kjanat/livedash-node.git
synced 2026-01-16 17:32:09 +01:00
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
This commit is contained in:
229
tests/lib/importProcessor.test.ts
Normal file
229
tests/lib/importProcessor.test.ts
Normal file
@ -0,0 +1,229 @@
|
||||
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);
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user