mirror of
https://github.com/kjanat/livedash-node.git
synced 2026-01-16 12:32:10 +01:00
- Implement comprehensive tRPC setup with type-safe API - Create tRPC routers for dashboard, admin, and auth endpoints - Migrate frontend components to use tRPC client - Fix platform dashboard Settings button functionality - Add platform settings page with profile and security management - Create OpenAI API mocking infrastructure for cost-safe testing - Update tests to work with new tRPC architecture - Sync database schema to fix AIBatchRequest table errors
344 lines
10 KiB
TypeScript
344 lines
10 KiB
TypeScript
import { describe, it, expect, beforeEach, vi } from "vitest";
|
|
import { NextRequest } from "next/server";
|
|
import { GET } from "../../app/api/dashboard/metrics/route";
|
|
|
|
// Mock NextAuth
|
|
vi.mock("next-auth", () => ({
|
|
getServerSession: vi.fn(),
|
|
}));
|
|
|
|
// Mock prisma
|
|
vi.mock("../../lib/prisma", () => ({
|
|
prisma: {
|
|
user: {
|
|
findUnique: vi.fn(),
|
|
},
|
|
company: {
|
|
findUnique: vi.fn(),
|
|
},
|
|
session: {
|
|
findMany: vi.fn(),
|
|
count: vi.fn(),
|
|
},
|
|
sessionQuestion: {
|
|
findMany: vi.fn(),
|
|
},
|
|
},
|
|
}));
|
|
|
|
describe("/api/dashboard/metrics", () => {
|
|
beforeEach(() => {
|
|
vi.clearAllMocks();
|
|
});
|
|
|
|
describe("GET", () => {
|
|
it("should return 401 for unauthenticated users", async () => {
|
|
const { getServerSession } = await import("next-auth");
|
|
vi.mocked(getServerSession).mockResolvedValue(null);
|
|
|
|
const request = new NextRequest(
|
|
"http://localhost:3000/api/dashboard/metrics"
|
|
);
|
|
const response = await GET(request);
|
|
|
|
expect(response.status).toBe(401);
|
|
const data = await response.json();
|
|
expect(data.error).toBe("Unauthorized");
|
|
});
|
|
|
|
it("should return 404 when user not found", async () => {
|
|
const { getServerSession } = await import("next-auth");
|
|
const { prisma } = await import("../../lib/prisma");
|
|
|
|
vi.mocked(getServerSession).mockResolvedValue({
|
|
user: { email: "test@example.com" },
|
|
expires: "2024-12-31",
|
|
});
|
|
|
|
vi.mocked(prisma.user.findUnique).mockResolvedValue(null);
|
|
|
|
const request = new NextRequest(
|
|
"http://localhost:3000/api/dashboard/metrics"
|
|
);
|
|
const response = await GET(request);
|
|
|
|
expect(response.status).toBe(404);
|
|
const data = await response.json();
|
|
expect(data.error).toBe("User not found");
|
|
});
|
|
|
|
it("should return 404 when company not found", async () => {
|
|
const { getServerSession } = await import("next-auth");
|
|
const { prisma } = await import("../../lib/prisma");
|
|
|
|
vi.mocked(getServerSession).mockResolvedValue({
|
|
user: { email: "test@example.com" },
|
|
expires: "2024-12-31",
|
|
});
|
|
|
|
vi.mocked(prisma.user.findUnique).mockResolvedValue({
|
|
id: "user1",
|
|
name: "Test User",
|
|
email: "test@example.com",
|
|
companyId: "company1",
|
|
role: "ADMIN" as const,
|
|
password: "hashed",
|
|
resetToken: null,
|
|
resetTokenExpiry: null,
|
|
invitedAt: null,
|
|
invitedBy: null,
|
|
createdAt: new Date(),
|
|
updatedAt: new Date(),
|
|
});
|
|
|
|
vi.mocked(prisma.company.findUnique).mockResolvedValue(null);
|
|
|
|
const request = new NextRequest(
|
|
"http://localhost:3000/api/dashboard/metrics"
|
|
);
|
|
const response = await GET(request);
|
|
|
|
expect(response.status).toBe(404);
|
|
const data = await response.json();
|
|
expect(data.error).toBe("Company not found");
|
|
});
|
|
|
|
it("should return dashboard metrics successfully for admin", async () => {
|
|
const { getServerSession } = await import("next-auth");
|
|
const { prisma } = await import("../../lib/prisma");
|
|
|
|
vi.mocked(getServerSession).mockResolvedValue({
|
|
user: { email: "admin@example.com" },
|
|
expires: "2024-12-31",
|
|
});
|
|
|
|
const mockUser = {
|
|
id: "user1",
|
|
name: "Test Admin",
|
|
email: "admin@example.com",
|
|
companyId: "company1",
|
|
role: "ADMIN" as const,
|
|
password: "hashed",
|
|
resetToken: null,
|
|
resetTokenExpiry: null,
|
|
invitedAt: null,
|
|
invitedBy: null,
|
|
createdAt: new Date(),
|
|
updatedAt: new Date(),
|
|
};
|
|
|
|
const mockCompany = {
|
|
id: "company1",
|
|
name: "Test Company",
|
|
csvUrl: "http://example.com/data.csv",
|
|
csvUsername: null,
|
|
csvPassword: null,
|
|
dashboardOpts: {},
|
|
maxUsers: 10,
|
|
status: "ACTIVE" as const,
|
|
createdAt: new Date(),
|
|
updatedAt: new Date(),
|
|
};
|
|
|
|
const mockSessions = [
|
|
{
|
|
id: "session1",
|
|
companyId: "company1",
|
|
importId: "import1",
|
|
startTime: new Date("2024-01-01T10:00:00Z"),
|
|
endTime: new Date("2024-01-01T11:00:00Z"),
|
|
ipAddress: "192.168.1.1",
|
|
country: "US",
|
|
fullTranscriptUrl: null,
|
|
avgResponseTime: null,
|
|
initialMsg: null,
|
|
messagesSent: 5,
|
|
escalated: false,
|
|
forwardedHr: false,
|
|
sentiment: "POSITIVE" as const,
|
|
category: "SCHEDULE_HOURS" as const,
|
|
language: "en",
|
|
summary: "Test summary",
|
|
createdAt: new Date(),
|
|
updatedAt: new Date(),
|
|
},
|
|
];
|
|
|
|
vi.mocked(prisma.user.findUnique).mockResolvedValue(mockUser);
|
|
vi.mocked(prisma.company.findUnique).mockResolvedValue(mockCompany);
|
|
vi.mocked(prisma.session.findMany).mockResolvedValue(mockSessions);
|
|
vi.mocked(prisma.session.count).mockResolvedValue(1);
|
|
vi.mocked(prisma.sessionQuestion.findMany).mockResolvedValue([]);
|
|
|
|
const request = new NextRequest(
|
|
"http://localhost:3000/api/dashboard/metrics"
|
|
);
|
|
const response = await GET(request);
|
|
|
|
expect(response.status).toBe(200);
|
|
const data = await response.json();
|
|
expect(data).toHaveProperty("totalSessions");
|
|
expect(data).toHaveProperty("sentimentDistribution");
|
|
expect(data).toHaveProperty("countryCounts");
|
|
expect(data).toHaveProperty("topQuestions");
|
|
expect(data.totalSessions).toBe(1);
|
|
});
|
|
|
|
it("should return dashboard metrics successfully for regular user", async () => {
|
|
const { getServerSession } = await import("next-auth");
|
|
const { prisma } = await import("../../lib/prisma");
|
|
|
|
vi.mocked(getServerSession).mockResolvedValue({
|
|
user: { email: "user@example.com" },
|
|
expires: "2024-12-31",
|
|
});
|
|
|
|
const mockUser = {
|
|
id: "user1",
|
|
name: "Test User",
|
|
email: "user@example.com",
|
|
companyId: "company1",
|
|
role: "USER" as const,
|
|
password: "hashed",
|
|
resetToken: null,
|
|
resetTokenExpiry: null,
|
|
invitedAt: null,
|
|
invitedBy: null,
|
|
createdAt: new Date(),
|
|
updatedAt: new Date(),
|
|
};
|
|
|
|
const mockCompany = {
|
|
id: "company1",
|
|
name: "Test Company",
|
|
csvUrl: "http://example.com/data.csv",
|
|
csvUsername: null,
|
|
csvPassword: null,
|
|
dashboardOpts: {},
|
|
maxUsers: 10,
|
|
status: "ACTIVE" as const,
|
|
createdAt: new Date(),
|
|
updatedAt: new Date(),
|
|
};
|
|
|
|
const mockSessions = [
|
|
{
|
|
id: "session1",
|
|
companyId: "company1",
|
|
importId: "import1",
|
|
startTime: new Date("2024-01-01T10:00:00Z"),
|
|
endTime: new Date("2024-01-01T11:00:00Z"),
|
|
ipAddress: "192.168.1.1",
|
|
country: "US",
|
|
fullTranscriptUrl: null,
|
|
avgResponseTime: null,
|
|
initialMsg: null,
|
|
messagesSent: 5,
|
|
escalated: false,
|
|
forwardedHr: false,
|
|
sentiment: "POSITIVE" as const,
|
|
category: "SCHEDULE_HOURS" as const,
|
|
language: "en",
|
|
summary: "Test summary",
|
|
createdAt: new Date(),
|
|
updatedAt: new Date(),
|
|
},
|
|
];
|
|
|
|
vi.mocked(prisma.user.findUnique).mockResolvedValue(mockUser);
|
|
vi.mocked(prisma.company.findUnique).mockResolvedValue(mockCompany);
|
|
vi.mocked(prisma.session.findMany).mockResolvedValue(mockSessions);
|
|
vi.mocked(prisma.session.count).mockResolvedValue(1);
|
|
vi.mocked(prisma.sessionQuestion.findMany).mockResolvedValue([]);
|
|
|
|
const request = new NextRequest(
|
|
"http://localhost:3000/api/dashboard/metrics"
|
|
);
|
|
const response = await GET(request);
|
|
|
|
expect(response.status).toBe(200);
|
|
const data = await response.json();
|
|
expect(data).toHaveProperty("totalSessions");
|
|
expect(data).toHaveProperty("sentimentDistribution");
|
|
expect(data).toHaveProperty("countryCounts");
|
|
expect(data).toHaveProperty("topQuestions");
|
|
});
|
|
|
|
it("should handle date range filters", async () => {
|
|
const { getServerSession } = await import("next-auth");
|
|
const { prisma } = await import("../../lib/prisma");
|
|
|
|
vi.mocked(getServerSession).mockResolvedValue({
|
|
user: { email: "user@example.com" },
|
|
expires: "2024-12-31",
|
|
});
|
|
|
|
const mockUser = {
|
|
id: "user1",
|
|
name: "Test User",
|
|
email: "user@example.com",
|
|
companyId: "company1",
|
|
role: "USER" as const,
|
|
password: "hashed",
|
|
resetToken: null,
|
|
resetTokenExpiry: null,
|
|
invitedAt: null,
|
|
invitedBy: null,
|
|
createdAt: new Date(),
|
|
updatedAt: new Date(),
|
|
};
|
|
|
|
const mockCompany = {
|
|
id: "company1",
|
|
name: "Test Company",
|
|
csvUrl: "http://example.com/data.csv",
|
|
csvUsername: null,
|
|
csvPassword: null,
|
|
dashboardOpts: {},
|
|
maxUsers: 10,
|
|
status: "ACTIVE" as const,
|
|
createdAt: new Date(),
|
|
updatedAt: new Date(),
|
|
};
|
|
|
|
vi.mocked(prisma.user.findUnique).mockResolvedValue(mockUser);
|
|
vi.mocked(prisma.company.findUnique).mockResolvedValue(mockCompany);
|
|
vi.mocked(prisma.session.findMany).mockResolvedValue([]);
|
|
vi.mocked(prisma.session.count).mockResolvedValue(0);
|
|
vi.mocked(prisma.sessionQuestion.findMany).mockResolvedValue([]);
|
|
|
|
const request = new NextRequest(
|
|
"http://localhost:3000/api/dashboard/metrics?startDate=2024-01-01&endDate=2024-01-31"
|
|
);
|
|
const response = await GET(request);
|
|
|
|
expect(response.status).toBe(200);
|
|
const data = await response.json();
|
|
expect(data.totalSessions).toBe(0);
|
|
});
|
|
|
|
it("should handle database errors gracefully", async () => {
|
|
const { getServerSession } = await import("next-auth");
|
|
const { prisma } = await import("../../lib/prisma");
|
|
|
|
vi.mocked(getServerSession).mockResolvedValue({
|
|
user: { email: "user@example.com" },
|
|
expires: "2024-12-31",
|
|
});
|
|
|
|
vi.mocked(prisma.user.findUnique).mockRejectedValue(
|
|
new Error("Database connection failed")
|
|
);
|
|
|
|
const request = new NextRequest(
|
|
"http://localhost:3000/api/dashboard/metrics"
|
|
);
|
|
const response = await GET(request);
|
|
|
|
expect(response.status).toBe(500);
|
|
const data = await response.json();
|
|
expect(data.error).toBe("Internal server error");
|
|
});
|
|
});
|
|
});
|