mirror of
https://github.com/open-reception/appointment-booking-software.git
synced 2026-09-07 01:37:38 +02:00
Fix lint, check and test
This commit is contained in:
@@ -38,7 +38,7 @@ vi.mock("../notification-service", () => ({
|
||||
vi.mock("../../auth/webauthn-service", () => ({
|
||||
WebAuthnService: {
|
||||
// getClientTunnels scopes staff key shares to the caller's most recently used passkey.
|
||||
getMostRecentPasskey: vi.fn().mockResolvedValue({ id: "passkey-123", lastUsedAt: new Date() }),
|
||||
getCurrentPasskey: vi.fn().mockResolvedValue({ id: "passkey-123", lastUsedAt: new Date() }),
|
||||
},
|
||||
}));
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ vi.mock("$lib/logger", () => ({
|
||||
vi.mock("../../auth/webauthn-service", () => ({
|
||||
WebAuthnService: {
|
||||
// Staff key shares are scoped to the caller's most recently used passkey.
|
||||
getMostRecentPasskey: vi.fn().mockResolvedValue({ id: "passkey-123", lastUsedAt: new Date() }),
|
||||
getCurrentPasskey: vi.fn().mockResolvedValue({ id: "passkey-123", lastUsedAt: new Date() }),
|
||||
},
|
||||
}));
|
||||
|
||||
@@ -146,9 +146,9 @@ describe("ScheduleService", () => {
|
||||
tenantId: "invalid-uuid",
|
||||
};
|
||||
|
||||
await expect(service.getSchedule(invalidRequest as ScheduleRequest)).rejects.toThrow(
|
||||
ValidationError,
|
||||
);
|
||||
await expect(
|
||||
service.getSchedule(invalidRequest as ScheduleRequest, "passkeyId"),
|
||||
).rejects.toThrow(ValidationError);
|
||||
});
|
||||
|
||||
it("should generate schedule for valid date range", async () => {
|
||||
@@ -206,7 +206,7 @@ describe("ScheduleService", () => {
|
||||
channelAgents: mockChannelAgents,
|
||||
});
|
||||
|
||||
const result = await service.getSchedule(validRequest);
|
||||
const result = await service.getSchedule(validRequest, "passkeyId");
|
||||
|
||||
// Validate basic structure
|
||||
expect(result).toHaveProperty("period");
|
||||
@@ -265,7 +265,7 @@ describe("ScheduleService", () => {
|
||||
channelAgents: [],
|
||||
});
|
||||
|
||||
const result = await service.getSchedule(validRequest);
|
||||
const result = await service.getSchedule(validRequest, "passkeyId");
|
||||
|
||||
expect(result.schedule).toHaveLength(1); // One day
|
||||
expect(result.schedule[0].channels).toEqual({});
|
||||
@@ -283,7 +283,9 @@ describe("ScheduleService", () => {
|
||||
throw new Error("Database error");
|
||||
});
|
||||
|
||||
await expect(service.getSchedule(validRequest)).rejects.toThrow("Database error");
|
||||
await expect(service.getSchedule(validRequest, "passkeyId")).rejects.toThrow(
|
||||
"Database error",
|
||||
);
|
||||
});
|
||||
|
||||
it("should generate multiple days for date range", async () => {
|
||||
@@ -303,7 +305,7 @@ describe("ScheduleService", () => {
|
||||
channelAgents: [],
|
||||
});
|
||||
|
||||
const result = await service.getSchedule(validRequest);
|
||||
const result = await service.getSchedule(validRequest, "passkeyId");
|
||||
|
||||
expect(result.schedule).toHaveLength(3); // Three days
|
||||
expect(result.schedule[0].date).toBe("2024-01-01");
|
||||
@@ -384,7 +386,7 @@ describe("ScheduleService", () => {
|
||||
channelAgents: mockChannelAgents,
|
||||
});
|
||||
|
||||
const result = await service.getSchedule(validRequest);
|
||||
const result = await service.getSchedule(validRequest, "passkeyId");
|
||||
|
||||
// Should only have Monday slot (09:00-10:00), not Tuesday slot
|
||||
const channelSchedule = result.schedule[0].channels["channel1"];
|
||||
@@ -459,7 +461,7 @@ describe("ScheduleService", () => {
|
||||
channelAgents: mockChannelAgents,
|
||||
});
|
||||
|
||||
const result = await service.getSchedule(validRequest);
|
||||
const result = await service.getSchedule(validRequest, "passkeyId");
|
||||
|
||||
const channelSchedule = result.schedule[0].channels["channel1"];
|
||||
// Should only have 10:00-11:00 slot, not 09:00-10:00 (has appointment)
|
||||
@@ -544,7 +546,7 @@ describe("ScheduleService", () => {
|
||||
channelAgents: mockChannelAgents,
|
||||
});
|
||||
|
||||
const result = await service.getSchedule(validRequest);
|
||||
const result = await service.getSchedule(validRequest, "passkeyId");
|
||||
|
||||
// Validate appointments are returned
|
||||
const channelSchedule = result.schedule[0].channels["channel1"];
|
||||
@@ -642,7 +644,7 @@ describe("ScheduleService", () => {
|
||||
channelAgents: mockChannelAgents,
|
||||
});
|
||||
|
||||
const result = await service.getSchedule(validRequest);
|
||||
const result = await service.getSchedule(validRequest, "passkeyId");
|
||||
|
||||
const channelSchedule = result.schedule[0].channels["channel1"];
|
||||
// Should have no available slots since only agent is absent
|
||||
@@ -724,7 +726,7 @@ describe("ScheduleService", () => {
|
||||
channelAgents: mockChannelAgents,
|
||||
});
|
||||
|
||||
const result = await service.getSchedule(validRequest);
|
||||
const result = await service.getSchedule(validRequest, "passkeyId");
|
||||
|
||||
const channelSchedule = result.schedule[0].channels["channel1"];
|
||||
expect(channelSchedule.availableSlots).toHaveLength(1);
|
||||
@@ -828,7 +830,7 @@ describe("ScheduleService", () => {
|
||||
channelAgents: mockChannelAgents,
|
||||
});
|
||||
|
||||
const result = await service.getSchedule(validRequest);
|
||||
const result = await service.getSchedule(validRequest, "passkeyId");
|
||||
|
||||
const channel1Schedule = result.schedule[0].channels["channel1"];
|
||||
const channel2Schedule = result.schedule[0].channels["channel2"];
|
||||
@@ -910,7 +912,7 @@ describe("ScheduleService", () => {
|
||||
channelAgents: mockChannelAgents,
|
||||
});
|
||||
|
||||
const result = await service.getSchedule(validRequest);
|
||||
const result = await service.getSchedule(validRequest, "passkeyId");
|
||||
|
||||
const channelSchedule = result.schedule[0].channels["channel1"];
|
||||
expect(channelSchedule.availableSlots).toHaveLength(2);
|
||||
|
||||
@@ -95,7 +95,7 @@ export class ScheduleService {
|
||||
*/
|
||||
async getSchedule(
|
||||
request: ScheduleRequest,
|
||||
passkeyId: string | undefined,
|
||||
passkeyId?: string | undefined,
|
||||
): Promise<ScheduleResult> {
|
||||
const log = logger.setContext("ScheduleService");
|
||||
|
||||
|
||||
@@ -358,12 +358,10 @@ export const getPasskeyFormData = async ({
|
||||
email,
|
||||
userId,
|
||||
setPasskeyFieldState,
|
||||
isAdditionalPasskey,
|
||||
}: {
|
||||
email: string;
|
||||
userId: string;
|
||||
setPasskeyFieldState: (newState: PasskeyState) => void;
|
||||
isAdditionalPasskey: boolean;
|
||||
}) => {
|
||||
setPasskeyFieldState("loading");
|
||||
|
||||
|
||||
@@ -71,7 +71,6 @@
|
||||
email: $formData.email,
|
||||
userId: $formData.userId,
|
||||
setPasskeyFieldState: (v) => ($passkeyLoading = v),
|
||||
isAdditionalPasskey: false,
|
||||
});
|
||||
|
||||
if (!data) {
|
||||
|
||||
-1
@@ -63,7 +63,6 @@
|
||||
email: $formData.email,
|
||||
userId: $formData.userId,
|
||||
setPasskeyFieldState: (v) => ($passkeyLoading = v),
|
||||
isAdditionalPasskey: true,
|
||||
});
|
||||
|
||||
if (!data) {
|
||||
|
||||
@@ -54,6 +54,7 @@ describe("Calendar API", () => {
|
||||
locals: {
|
||||
user: {
|
||||
id: "user-123",
|
||||
passkeyId: "passkey-123",
|
||||
tenantId,
|
||||
role: "GUEST",
|
||||
},
|
||||
@@ -150,13 +151,16 @@ describe("Calendar API", () => {
|
||||
});
|
||||
|
||||
expect(ScheduleService.forTenant).toHaveBeenCalledWith("tenant-123");
|
||||
expect(mockGetSchedule).toHaveBeenCalledWith({
|
||||
tenantId: "tenant-123",
|
||||
startDate: "2024-01-01T00:00:00.000Z",
|
||||
endDate: "2024-01-02T00:00:00.000Z",
|
||||
timeZone: "UTC",
|
||||
staffUserId: "user-123",
|
||||
});
|
||||
expect(mockGetSchedule).toHaveBeenCalledWith(
|
||||
{
|
||||
tenantId: "tenant-123",
|
||||
startDate: "2024-01-01T00:00:00.000Z",
|
||||
endDate: "2024-01-02T00:00:00.000Z",
|
||||
timeZone: "UTC",
|
||||
staffUserId: "user-123",
|
||||
},
|
||||
"passkey-123",
|
||||
);
|
||||
});
|
||||
|
||||
it("should include full appointment and agent information in calendar response", async () => {
|
||||
|
||||
+30
-15
@@ -37,7 +37,7 @@ vi.mock("$lib/server/services/staff-crypto.service", () => ({
|
||||
|
||||
vi.mock("$lib/server/auth/webauthn-service", () => ({
|
||||
WebAuthnService: {
|
||||
getMostRecentPasskey: vi.fn(async () => undefined),
|
||||
getCurrentPasskey: vi.fn(async () => undefined),
|
||||
},
|
||||
}));
|
||||
|
||||
@@ -108,6 +108,7 @@ describe("Staff Key Shard API Route", () => {
|
||||
locals: {
|
||||
user: {
|
||||
id: mockUserId,
|
||||
passkeyId: mockPasskeyId,
|
||||
session: {
|
||||
sessionId: "session-123",
|
||||
},
|
||||
@@ -130,7 +131,7 @@ describe("Staff Key Shard API Route", () => {
|
||||
passkeyId: mockPasskeyId,
|
||||
};
|
||||
|
||||
(WebAuthnService.getMostRecentPasskey as any).mockResolvedValue(mockRecentPasskey);
|
||||
(WebAuthnService.getCurrentPasskey as any).mockResolvedValue(mockRecentPasskey);
|
||||
mockStaffCryptoService.getStaffCryptoForPasskey.mockResolvedValue(mockStaffCrypto);
|
||||
|
||||
const event = createMockRequestEvent();
|
||||
@@ -144,7 +145,7 @@ describe("Staff Key Shard API Route", () => {
|
||||
passkeyId: mockStaffCrypto.passkeyId,
|
||||
});
|
||||
|
||||
expect(WebAuthnService.getMostRecentPasskey).toHaveBeenCalledWith(mockUserId);
|
||||
expect(WebAuthnService.getCurrentPasskey).toHaveBeenCalledWith(mockUserId, mockPasskeyId);
|
||||
expect(mockStaffCryptoService.getStaffCryptoForPasskey).toHaveBeenCalledWith(
|
||||
mockTenantId,
|
||||
mockStaffId,
|
||||
@@ -157,6 +158,7 @@ describe("Staff Key Shard API Route", () => {
|
||||
locals: {
|
||||
user: {
|
||||
id: "different-user-id",
|
||||
passkeyId: mockPasskeyId,
|
||||
session: { sessionId: "session-123" },
|
||||
},
|
||||
} as any,
|
||||
@@ -168,7 +170,7 @@ describe("Staff Key Shard API Route", () => {
|
||||
expect(response.status).toBe(403);
|
||||
expect(data.error).toBe("You can only access your own key shard");
|
||||
|
||||
expect(WebAuthnService.getMostRecentPasskey).not.toHaveBeenCalled();
|
||||
expect(WebAuthnService.getCurrentPasskey).not.toHaveBeenCalled();
|
||||
expect(mockStaffCryptoService.getStaffCryptoForPasskey).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
@@ -187,7 +189,7 @@ describe("Staff Key Shard API Route", () => {
|
||||
});
|
||||
|
||||
it("should reject access when no recent passkey is found", async () => {
|
||||
(WebAuthnService.getMostRecentPasskey as any).mockResolvedValue(null);
|
||||
(WebAuthnService.getCurrentPasskey as any).mockResolvedValue(null);
|
||||
|
||||
const event = createMockRequestEvent();
|
||||
const response = await GET(event);
|
||||
@@ -196,7 +198,7 @@ describe("Staff Key Shard API Route", () => {
|
||||
expect(response.status).toBe(403);
|
||||
expect(data.error).toBe("No valid passkey found for authenticated user");
|
||||
|
||||
expect(WebAuthnService.getMostRecentPasskey).toHaveBeenCalledWith(mockUserId);
|
||||
expect(WebAuthnService.getCurrentPasskey).toHaveBeenCalledWith(mockUserId, mockPasskeyId);
|
||||
expect(mockStaffCryptoService.getStaffCryptoForPasskey).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
@@ -206,7 +208,7 @@ describe("Staff Key Shard API Route", () => {
|
||||
lastUsedAt: new Date("2024-01-15T10:00:00Z"),
|
||||
};
|
||||
|
||||
(WebAuthnService.getMostRecentPasskey as any).mockResolvedValue(mockRecentPasskey);
|
||||
(WebAuthnService.getCurrentPasskey as any).mockResolvedValue(mockRecentPasskey);
|
||||
mockStaffCryptoService.getStaffCryptoForPasskey.mockResolvedValue(null);
|
||||
|
||||
const event = createMockRequestEvent();
|
||||
@@ -216,7 +218,7 @@ describe("Staff Key Shard API Route", () => {
|
||||
expect(response.status).toBe(404);
|
||||
expect(data.error).toBe("Staff crypto data not found for the authenticated passkey");
|
||||
|
||||
expect(WebAuthnService.getMostRecentPasskey).toHaveBeenCalledWith(mockUserId);
|
||||
expect(WebAuthnService.getCurrentPasskey).toHaveBeenCalledWith(mockUserId, mockPasskeyId);
|
||||
expect(mockStaffCryptoService.getStaffCryptoForPasskey).toHaveBeenCalledWith(
|
||||
mockTenantId,
|
||||
mockStaffId,
|
||||
@@ -249,7 +251,7 @@ describe("Staff Key Shard API Route", () => {
|
||||
});
|
||||
|
||||
it("should handle WebAuthn service errors gracefully", async () => {
|
||||
(WebAuthnService.getMostRecentPasskey as any).mockRejectedValue(
|
||||
(WebAuthnService.getCurrentPasskey as any).mockRejectedValue(
|
||||
new Error("Database connection failed"),
|
||||
);
|
||||
|
||||
@@ -260,7 +262,7 @@ describe("Staff Key Shard API Route", () => {
|
||||
expect(response.status).toBe(500);
|
||||
expect(data.error).toBe("Failed to determine passkey ID");
|
||||
|
||||
expect(WebAuthnService.getMostRecentPasskey).toHaveBeenCalledWith(mockUserId);
|
||||
expect(WebAuthnService.getCurrentPasskey).toHaveBeenCalledWith(mockUserId, mockPasskeyId);
|
||||
expect(mockStaffCryptoService.getStaffCryptoForPasskey).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
@@ -270,7 +272,7 @@ describe("Staff Key Shard API Route", () => {
|
||||
lastUsedAt: new Date("2024-01-15T10:00:00Z"),
|
||||
};
|
||||
|
||||
(WebAuthnService.getMostRecentPasskey as any).mockResolvedValue(mockRecentPasskey);
|
||||
(WebAuthnService.getCurrentPasskey as any).mockResolvedValue(mockRecentPasskey);
|
||||
mockStaffCryptoService.getStaffCryptoForPasskey.mockRejectedValue(
|
||||
new Error("Database connection failed"),
|
||||
);
|
||||
@@ -282,7 +284,7 @@ describe("Staff Key Shard API Route", () => {
|
||||
expect(response.status).toBe(500);
|
||||
expect(data.error).toBe("Internal server error");
|
||||
|
||||
expect(WebAuthnService.getMostRecentPasskey).toHaveBeenCalledWith(mockUserId);
|
||||
expect(WebAuthnService.getCurrentPasskey).toHaveBeenCalledWith(mockUserId, mockPasskeyId);
|
||||
expect(mockStaffCryptoService.getStaffCryptoForPasskey).toHaveBeenCalledWith(
|
||||
mockTenantId,
|
||||
mockStaffId,
|
||||
@@ -302,7 +304,7 @@ describe("Staff Key Shard API Route", () => {
|
||||
passkeyId: mockPasskeyId,
|
||||
};
|
||||
|
||||
(WebAuthnService.getMostRecentPasskey as any).mockResolvedValue(mockRecentPasskey);
|
||||
(WebAuthnService.getCurrentPasskey as any).mockResolvedValue(mockRecentPasskey);
|
||||
mockStaffCryptoService.getStaffCryptoForPasskey.mockResolvedValue(mockStaffCrypto);
|
||||
|
||||
const event = createMockRequestEvent();
|
||||
@@ -352,11 +354,24 @@ describe("Staff Key Shard API Route", () => {
|
||||
locals: {
|
||||
user: {
|
||||
id: testCase.requestingUserId,
|
||||
passkeyId: mockPasskeyId,
|
||||
session: { sessionId: "session-123" },
|
||||
},
|
||||
} as any,
|
||||
});
|
||||
|
||||
if (testCase.shouldAllow) {
|
||||
(WebAuthnService.getCurrentPasskey as any).mockResolvedValue({
|
||||
id: mockPasskeyId,
|
||||
lastUsedAt: new Date("2024-01-15T10:00:00Z"),
|
||||
});
|
||||
mockStaffCryptoService.getStaffCryptoForPasskey.mockResolvedValue({
|
||||
publicKey: "base64-encoded-ml-kem-768-public-key==",
|
||||
privateKeyShare: "base64-encoded-private-key-shard==",
|
||||
passkeyId: mockPasskeyId,
|
||||
});
|
||||
}
|
||||
|
||||
const response = await GET(event);
|
||||
|
||||
if (testCase.shouldAllow) {
|
||||
@@ -371,7 +386,7 @@ describe("Staff Key Shard API Route", () => {
|
||||
|
||||
it("should require valid passkey authentication", async () => {
|
||||
// Mock no recent passkey (security violation)
|
||||
(WebAuthnService.getMostRecentPasskey as any).mockResolvedValue(null);
|
||||
(WebAuthnService.getCurrentPasskey as any).mockResolvedValue(null);
|
||||
|
||||
const event = createMockRequestEvent();
|
||||
const response = await GET(event);
|
||||
@@ -387,7 +402,7 @@ describe("Staff Key Shard API Route", () => {
|
||||
lastUsedAt: new Date("2024-01-15T10:00:00Z"),
|
||||
};
|
||||
|
||||
(WebAuthnService.getMostRecentPasskey as any).mockResolvedValue(mockRecentPasskey);
|
||||
(WebAuthnService.getCurrentPasskey as any).mockResolvedValue(mockRecentPasskey);
|
||||
// Mock no crypto data for this passkey (security violation)
|
||||
mockStaffCryptoService.getStaffCryptoForPasskey.mockResolvedValue(null);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user