Updated and fixed tests

This commit is contained in:
Hendrik Belitz
2025-09-16 16:07:00 +02:00
parent f575f135ab
commit 93cfa75fc7
14 changed files with 87 additions and 144 deletions
+1 -1
View File
@@ -126,7 +126,7 @@ describe("UniversalLogger Integration - Client Error Forwarding", () => {
mockFetch.mockResolvedValueOnce({
ok: false,
status: 500,
statusText: "Internal Server Error",
statusText: "Internal server error",
});
const { createLogger } = await import("../index");
@@ -1,7 +1,7 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import { describe, it, expect } from "vitest";
import { AuthorizationService } from "../authorization-service";
import { AuthenticationError } from "$lib/server/utils/errors";
import { AuthenticationError, AuthorizationError } from "$lib/server/utils/errors";
import type { JWTPayload } from "jose";
const mockGlobalAdmin: JWTPayload = {
@@ -47,7 +47,7 @@ describe("AuthorizationService", () => {
it("should deny access for incorrect role", () => {
expect(() => {
AuthorizationService.requireRole(mockTenantAdmin, "GLOBAL_ADMIN");
}).toThrow(AuthenticationError);
}).toThrow(AuthorizationError);
});
it("should deny access for null user", () => {
@@ -71,7 +71,7 @@ describe("AuthorizationService", () => {
it("should deny access for disallowed role", () => {
expect(() => {
AuthorizationService.requireAnyRole(mockStaff, ["GLOBAL_ADMIN", "TENANT_ADMIN"]);
}).toThrow(AuthenticationError);
}).toThrow(AuthorizationError);
});
});
@@ -91,7 +91,7 @@ describe("AuthorizationService", () => {
it("should deny tenant admin access to other tenants", () => {
expect(() => {
AuthorizationService.requireTenantAccess(mockTenantAdmin, "tenant-456");
}).toThrow(AuthenticationError);
}).toThrow(AuthorizationError);
});
it("should allow staff access to their own tenant", () => {
@@ -103,7 +103,7 @@ describe("AuthorizationService", () => {
it("should deny staff access to other tenants", () => {
expect(() => {
AuthorizationService.requireTenantAccess(mockStaff, "tenant-456");
}).toThrow(AuthenticationError);
}).toThrow(AuthorizationError);
});
});
@@ -117,7 +117,7 @@ describe("AuthorizationService", () => {
it("should deny non-global admin access", () => {
expect(() => {
AuthorizationService.requireGlobalAdmin(mockTenantAdmin);
}).toThrow(AuthenticationError);
}).toThrow(AuthorizationError);
});
});
@@ -137,7 +137,7 @@ describe("AuthorizationService", () => {
it("should deny staff access", () => {
expect(() => {
AuthorizationService.requireTenantAdmin(mockStaff);
}).toThrow(AuthenticationError);
}).toThrow(AuthorizationError);
});
it("should check tenant access for tenant admin", () => {
@@ -147,7 +147,7 @@ describe("AuthorizationService", () => {
expect(() => {
AuthorizationService.requireTenantAdmin(mockTenantAdmin, "tenant-456");
}).toThrow(AuthenticationError);
}).toThrow(AuthorizationError);
});
});
@@ -173,7 +173,7 @@ describe("AuthorizationService", () => {
expect(() => {
AuthorizationService.requireStaffOrAbove(mockStaff, "tenant-456");
}).toThrow(AuthenticationError);
}).toThrow(AuthorizationError);
});
});
+2 -6
View File
@@ -66,9 +66,7 @@ describe("GET /api/admin/exists", () => {
const data = await response.json();
expect(response.status).toBe(500);
expect(data).toEqual({
error: "Internal server error",
});
expect(data.error).toBe("Internal server error");
expect(UserService.adminExists).toHaveBeenCalledOnce();
expect(UserService.getAdminCount).not.toHaveBeenCalled();
});
@@ -81,9 +79,7 @@ describe("GET /api/admin/exists", () => {
const data = await response.json();
expect(response.status).toBe(500);
expect(data).toEqual({
error: "Internal server error",
});
expect(data.error).toBe("Internal server error");
expect(UserService.adminExists).toHaveBeenCalledOnce();
expect(UserService.getAdminCount).toHaveBeenCalledOnce();
});
+28 -25
View File
@@ -14,8 +14,9 @@ vi.mock("@sveltejs/kit", async () => {
(error as any).body = { message };
throw error;
}),
json: vi.fn((data: any) => {
return new Response(JSON.stringify(data), { status: 200 });
json: vi.fn((data: any, options: any = {}) => {
const status = options.status || 200;
return new Response(JSON.stringify(data), { status });
}),
};
});
@@ -83,12 +84,6 @@ vi.mock("drizzle-orm", () => ({
eq: vi.fn(() => "eq-condition"),
}));
// Mock error classes
vi.mock("$lib/server/utils/errors", () => ({
ValidationError: class ValidationError extends Error {},
NotFoundError: class NotFoundError extends Error {},
}));
// Mock permissions module
vi.mock("$lib/server/utils/permissions", () => ({
checkPermission: vi.fn(),
@@ -98,6 +93,7 @@ import { centralDb } from "$lib/server/db";
import { UserService } from "$lib/server/services/user-service";
import { generateAccessToken } from "$lib/server/auth/jwt-utils";
import { checkPermission } from "$lib/server/utils/permissions";
import { AuthenticationError, AuthorizationError } from "$lib/server/utils/errors";
describe("POST /api/admin/tenant", () => {
const mockUser = {
@@ -144,14 +140,17 @@ describe("POST /api/admin/tenant", () => {
beforeEach(() => {
vi.clearAllMocks();
// Reset checkPermission mock to not throw by default
vi.mocked(checkPermission).mockImplementation(() => {
// Default implementation that doesn't throw
});
});
it("should successfully switch to a tenant", async () => {
const tenantId = "550e8400-e29b-41d4-a716-446655440000";
const requestEvent = createRequestEvent({ tenantId });
// Mock permission check to pass
vi.mocked(checkPermission).mockReturnValue(null);
// Permission check will use default mock (pass)
// Mock tenant exists query
const mockSelectQuery = {
@@ -197,11 +196,10 @@ describe("POST /api/admin/tenant", () => {
null,
);
// Mock permission check to return 401 error
const mockErrorResponse = new Response(JSON.stringify({ error: "Authentication required" }), {
status: 401,
// Mock permission check to throw authentication error
vi.mocked(checkPermission).mockImplementationOnce(() => {
throw new AuthenticationError("Authentication required");
});
vi.mocked(checkPermission).mockReturnValue(mockErrorResponse);
const response = await POST(requestEvent);
expect(response.status).toBe(401);
@@ -216,11 +214,10 @@ describe("POST /api/admin/tenant", () => {
tenantAdminUser,
);
// Mock permission check to return 403 error
const mockErrorResponse = new Response(JSON.stringify({ error: "Insufficient permissions" }), {
status: 403,
// Mock permission check to throw authorization error
vi.mocked(checkPermission).mockImplementationOnce(() => {
throw new AuthorizationError("Insufficient permissions");
});
vi.mocked(checkPermission).mockReturnValue(mockErrorResponse);
const response = await POST(requestEvent);
expect(response.status).toBe(403);
@@ -228,20 +225,22 @@ describe("POST /api/admin/tenant", () => {
expect(result.error).toBe("Insufficient permissions");
});
it("should return 400 when request body is invalid", async () => {
it("should return 422 when request body is invalid", async () => {
const requestEvent = createRequestEvent({ tenantId: "invalid-uuid" });
// Mock permission check to pass
vi.mocked(checkPermission).mockReturnValue(null);
// Permission check will use default mock (pass)
await expect(POST(requestEvent)).rejects.toThrow();
const response = await POST(requestEvent);
const result = await response.json();
expect(response.status).toBe(422);
expect(result.error).toBe("Invalid request body");
});
it("should return 404 when tenant does not exist", async () => {
const requestEvent = createRequestEvent({ tenantId: "550e8400-e29b-41d4-a716-446655440000" });
// Mock permission check to pass
vi.mocked(checkPermission).mockReturnValue(null);
// Permission check will use default mock (pass)
// Mock tenant doesn't exist
const mockSelectQuery = {
@@ -251,6 +250,10 @@ describe("POST /api/admin/tenant", () => {
};
vi.mocked(centralDb.select).mockReturnValue(mockSelectQuery as any);
await expect(POST(requestEvent)).rejects.toThrow();
const response = await POST(requestEvent);
const result = await response.json();
expect(response.status).toBe(404);
expect(result.error).toBe("Tenant not found");
});
});
@@ -250,7 +250,7 @@ describe("Agent Detail API Routes", () => {
const response = await PUT(event);
const data = await response.json();
expect(response.status).toBe(400);
expect(response.status).toBe(422);
expect(data.error).toBe("Invalid agent data");
});
@@ -392,7 +392,7 @@ describe("Agent Absence Detail API Routes", () => {
const response = await PUT(event);
const data = await response.json();
expect(response.status).toBe(400);
expect(response.status).toBe(422);
expect(data.error).toBe("Invalid date range");
});
@@ -196,7 +196,7 @@ describe("Agent Absence API Routes", () => {
const response = await POST(event);
const data = await response.json();
expect(response.status).toBe(400);
expect(response.status).toBe(422);
expect(data.error).toBe("Invalid date range");
});
@@ -243,7 +243,7 @@ describe("Agent Absence API Routes", () => {
const response = await POST(event);
const data = await response.json();
expect(response.status).toBe(400);
expect(response.status).toBe(422);
expect(data.error).toBe("Missing tenant or agent ID");
});
});
@@ -422,7 +422,7 @@ describe("Agent Absence API Routes", () => {
const response = await GET(event);
const data = await response.json();
expect(response.status).toBe(400);
expect(response.status).toBe(422);
expect(data.error).toBe("Invalid date format");
});
@@ -134,19 +134,16 @@ describe("Agent API Routes", () => {
expect(data.agents).toEqual(mockAgents);
});
it("should reject unauthenticated requests", async () => {
const event = createMockRequestEvent({
locals: { user: null } as any,
});
it("should return 401 for unauthenticated requests", async () => {
const event = createMockRequestEvent({ locals: { user: null } as any });
const response = await GET(event);
const data = await response.json();
const result = await response.json();
expect(response.status).toBe(401);
expect(data.error).toBe("Authentication required");
expect(result.error).toBe("Authentication required");
});
it("should reject insufficient permissions", async () => {
it("should return 403 for insufficient permissions", async () => {
const event = createMockRequestEvent({
locals: {
user: {
@@ -156,12 +153,11 @@ describe("Agent API Routes", () => {
} as any,
},
});
const response = await GET(event);
const data = await response.json();
const result = await response.json();
expect(response.status).toBe(403);
expect(data.error).toBe("Insufficient permissions");
expect(result.error).toBe("Insufficient permissions");
});
it("should handle missing tenant ID", async () => {
@@ -172,7 +168,7 @@ describe("Agent API Routes", () => {
const response = await GET(event);
const data = await response.json();
expect(response.status).toBe(400);
expect(response.status).toBe(422);
expect(data.error).toBe("No tenant id given");
});
@@ -251,7 +247,7 @@ describe("Agent API Routes", () => {
expect(data.agent).toEqual(mockAgent);
});
it("should reject staff users from creating agents", async () => {
it("should return 403 for staff users creating agents", async () => {
const event = createMockRequestEvent({
locals: {
user: {
@@ -261,25 +257,21 @@ describe("Agent API Routes", () => {
} as any,
},
});
const response = await POST(event);
const data = await response.json();
const result = await response.json();
expect(response.status).toBe(403);
expect(data.error).toBe("Insufficient permissions");
expect(result.error).toBe("Insufficient permissions");
expect(mockAgentService.createAgent).not.toHaveBeenCalled();
});
it("should reject unauthenticated requests", async () => {
const event = createMockRequestEvent({
locals: { user: null } as any,
});
it("should return 401 for unauthenticated requests", async () => {
const event = createMockRequestEvent({ locals: { user: null } as any });
const response = await POST(event);
const data = await response.json();
const result = await response.json();
expect(response.status).toBe(401);
expect(data.error).toBe("Authentication required");
expect(result.error).toBe("Authentication required");
});
it("should handle validation errors", async () => {
@@ -289,7 +281,7 @@ describe("Agent API Routes", () => {
const response = await POST(event);
const data = await response.json();
expect(response.status).toBe(400);
expect(response.status).toBe(422);
expect(data.error).toBe("Invalid agent name");
});
@@ -323,7 +315,7 @@ describe("Agent API Routes", () => {
const response = await POST(event);
const data = await response.json();
expect(response.status).toBe(400);
expect(response.status).toBe(422);
expect(data.error).toBe("No tenant id given");
});
});
@@ -77,7 +77,6 @@ describe("Appointment Detail API Routes", () => {
it("should return 404 if appointment not found", async () => {
mockAppointmentService.getAppointmentById.mockResolvedValue(null);
const event = createMockRequestEvent();
const response = await GET(event);
const result = await response.json();
@@ -87,10 +86,7 @@ describe("Appointment Detail API Routes", () => {
});
it("should return 401 if user is not authenticated", async () => {
const event = createMockRequestEvent({
locals: {},
});
const event = createMockRequestEvent({ locals: {} });
const response = await GET(event);
const result = await response.json();
@@ -109,7 +105,6 @@ describe("Appointment Detail API Routes", () => {
} as any,
},
});
const response = await GET(event);
const result = await response.json();
@@ -265,7 +260,7 @@ describe("Appointment Detail API Routes", () => {
expect(response.status).toBe(200);
});
it("should return 400 if tenant ID or appointment ID is missing", async () => {
it("should return 422 if tenant ID or appointment ID is missing", async () => {
const event = createMockRequestEvent({
params: { id: mockTenantId },
});
@@ -273,7 +268,7 @@ describe("Appointment Detail API Routes", () => {
const response = await DELETE(event);
const result = await response.json();
expect(response.status).toBe(400);
expect(response.status).toBe(422);
expect(result.error).toBe("Tenant ID and appointment ID are required");
});
@@ -74,10 +74,7 @@ describe("Appointment Cancel API", () => {
});
it("should return 401 if user is not authenticated", async () => {
const event = createMockRequestEvent({
locals: {},
});
const event = createMockRequestEvent({ locals: {} });
const response = await PUT(event);
const result = await response.json();
@@ -96,7 +93,6 @@ describe("Appointment Cancel API", () => {
} as any,
},
});
const response = await PUT(event);
const result = await response.json();
@@ -74,10 +74,7 @@ describe("Appointment Confirm API", () => {
});
it("should return 401 if user is not authenticated", async () => {
const event = createMockRequestEvent({
locals: {},
});
const event = createMockRequestEvent({ locals: {} });
const response = await PUT(event);
const result = await response.json();
@@ -96,7 +93,6 @@ describe("Appointment Confirm API", () => {
} as any,
},
});
const response = await PUT(event);
const result = await response.json();
@@ -150,7 +146,7 @@ describe("Appointment Confirm API", () => {
expect(response.status).toBe(200);
});
it("should return 400 if tenant ID or appointment ID is missing", async () => {
it("should return 422 if tenant ID or appointment ID is missing", async () => {
const event = createMockRequestEvent({
params: { id: mockTenantId },
});
@@ -158,7 +154,7 @@ describe("Appointment Confirm API", () => {
const response = await PUT(event);
const result = await response.json();
expect(response.status).toBe(400);
expect(response.status).toBe(422);
expect(result.error).toBe("Tenant ID and appointment ID are required");
});
@@ -92,10 +92,7 @@ describe("Appointment API Routes", () => {
});
it("should return 401 if user is not authenticated", async () => {
const event = createMockRequestEvent({
locals: {},
});
const event = createMockRequestEvent({ locals: {} });
const response = await POST(event);
const result = await response.json();
@@ -114,7 +111,6 @@ describe("Appointment API Routes", () => {
} as any,
},
});
const response = await POST(event);
const result = await response.json();
@@ -178,7 +174,7 @@ describe("Appointment API Routes", () => {
expect(response.status).toBe(201);
});
it("should return 400 for validation errors", async () => {
it("should return 422 for validation errors", async () => {
mockAppointmentService.createAppointment.mockRejectedValue(
new ValidationError("Invalid data"),
);
@@ -187,7 +183,7 @@ describe("Appointment API Routes", () => {
const response = await POST(event);
const result = await response.json();
expect(response.status).toBe(400);
expect(response.status).toBe(422);
expect(result.error).toBe("Invalid data");
});
@@ -277,7 +273,7 @@ describe("Appointment API Routes", () => {
expect(result.error).toBe("Insufficient permissions");
});
it("should return 400 if startDate or endDate is missing", async () => {
it("should return 422 if startDate or endDate is missing", async () => {
const event = createMockRequestEvent({
url: new URL("http://localhost?startDate=2024-12-01T00:00:00Z"),
});
@@ -285,7 +281,7 @@ describe("Appointment API Routes", () => {
const response = await GET(event);
const result = await response.json();
expect(response.status).toBe(400);
expect(response.status).toBe(422);
expect(result.error).toBe("startDate and endDate are required");
});
@@ -310,7 +306,7 @@ describe("Appointment API Routes", () => {
});
});
it("should return 400 for validation errors", async () => {
it("should return 422 for validation errors", async () => {
mockAppointmentService.queryAppointments.mockRejectedValue(
new ValidationError("Invalid query"),
);
@@ -319,7 +315,7 @@ describe("Appointment API Routes", () => {
const response = await GET(event);
const result = await response.json();
expect(response.status).toBe(400);
expect(response.status).toBe(422);
expect(result.error).toBe("Invalid query");
});
});
+7 -30
View File
@@ -43,21 +43,6 @@ vi.mock("$lib/logger", () => ({
})),
}));
vi.mock("$lib/server/utils/errors", () => ({
ValidationError: class ValidationError extends Error {
constructor(message: string) {
super(message);
this.name = "ValidationError";
}
},
NotFoundError: class NotFoundError extends Error {
constructor(message: string) {
super(message);
this.name = "NotFoundError";
}
},
}));
vi.mock("$lib/server/auth/authorization-service", () => ({
AuthorizationService: {
requireGlobalAdmin: vi.fn(),
@@ -154,10 +139,8 @@ describe("Tenant API Routes", () => {
} as any);
const data = await response.json();
expect(data).toEqual({
error: "No tenant id given",
});
expect(response.status).toBe(400);
expect(response.status).toBe(422);
expect(data.error).toBe("No tenant id given");
});
});
@@ -210,10 +193,8 @@ describe("Tenant API Routes", () => {
} as any);
const data = await response.json();
expect(data).toEqual({
error: "No tenant id given",
});
expect(response.status).toBe(400);
expect(response.status).toBe(422);
expect(data.error).toBe("No tenant id given");
});
});
@@ -295,10 +276,8 @@ describe("Tenant API Routes", () => {
} as any);
const data = await response.json();
expect(data).toEqual({
error: "Invalid configuration data",
});
expect(response.status).toBe(400);
expect(response.status).toBe(422);
expect(data.error).toBe("Invalid configuration data");
});
it("should handle tenant not found", async () => {
@@ -330,10 +309,8 @@ describe("Tenant API Routes", () => {
} as any);
const data = await response.json();
expect(data).toEqual({
error: "Tenant not found",
});
expect(response.status).toBe(404);
expect(data.error).toBe("Tenant not found");
});
});
});
+4 -12
View File
@@ -184,9 +184,7 @@ describe("/api/tenants", () => {
} as any);
const data = await response.json();
expect(data).toEqual({
message: "Invalid tenant creation request",
});
expect(data.error).toEqual("Invalid tenant creation request");
expect(response.status).toBe(422);
});
@@ -226,9 +224,7 @@ describe("/api/tenants", () => {
} as any);
const data = await response.json();
expect(data).toEqual({
error: ERRORS.TENANTS.NAME_EXISTS,
});
expect(data.error).toEqual(ERRORS.TENANTS.NAME_EXISTS);
expect(response.status).toBe(409);
});
@@ -268,9 +264,7 @@ describe("/api/tenants", () => {
} as any);
const data = await response.json();
expect(data).toEqual({
error: "Internal server error",
});
expect(data.error).toEqual("Internal server error");
expect(response.status).toBe(500);
});
});
@@ -339,9 +333,7 @@ describe("/api/tenants", () => {
} as any);
const data = await response.json();
expect(data).toEqual({
error: "Internal server error",
});
expect(data.error).toEqual("Internal server error");
expect(response.status).toBe(500);
});
});