fix most tests

This commit is contained in:
StarAppeal
2025-09-27 01:21:57 +02:00
parent 67ca195c15
commit a1b02c3088
9 changed files with 162 additions and 105 deletions
+2 -1
View File
@@ -1,5 +1,6 @@
import jwt from "jsonwebtoken";
import { DecodedToken } from "../interfaces/decodedToken";
import logger from "./logger";
export class JwtAuthenticator {
constructor(private secret: string) {}
@@ -12,7 +13,7 @@ export class JwtAuthenticator {
try {
return jwt.verify(token, this.secret) as DecodedToken;
} catch (error) {
console.error("Error while verifying token:", error);
logger.error("Error while verifying token:", error);
}
return null;
@@ -12,9 +12,7 @@ export class GetStateEvent extends CustomWebsocketEvent {
this.ws.send(
JSON.stringify({
type: "STATE",
payload: {
state: this.ws.user.lastState,
},
payload: this.ws.user.lastState,
}),
{ binary: false }
);
+11 -6
View File
@@ -1,11 +1,21 @@
import { describe, it, expect, vi, beforeEach } from "vitest";
import { FileModel } from "../../../src/db/models/file";
import { FileService } from "../../../src/services/db/fileService";
import logger from "../../../src/utils/logger";
vi.mock("../../../src/db/models/file");
const mockedFileModel = vi.mocked(FileModel);
vi.mock("../../../src/utils/logger", () => ({
default: {
warn: vi.fn(),
info: vi.fn(),
error: vi.fn(),
debug: vi.fn(),
},
}));
describe("FileService", () => {
let fileService: FileService;
@@ -165,15 +175,10 @@ describe("FileService", () => {
it("should return false if an error occurs", async () => {
mockedFileModel.countDocuments.mockRejectedValue(new Error("Database error"));
// Mock console.error to prevent test output cluttering
const consoleSpy = vi.spyOn(console, "error").mockImplementation(() => {});
const result = await fileService.isFileDuplicate("error-file.txt", "user123");
expect(result).toBe(false);
expect(consoleSpy).toHaveBeenCalledWith(expect.stringContaining("Error in isFileDuplicate"));
consoleSpy.mockRestore();
expect(logger.error).toHaveBeenCalledWith(expect.stringContaining("Error checking file duplicate"));
});
});
+23 -18
View File
@@ -1,9 +1,8 @@
import {describe, it, expect, vi, beforeEach, afterEach, Mocked} from "vitest";
import {appEventBus, USER_UPDATED_EVENT, WEATHER_STATE_UPDATED_EVENT} from "../../src/utils/eventBus";
import {WeatherPollingService} from "../../src/services/weatherPollingService";
import {IUser} from "../../src/db/models/user";
import {getCurrentWeather} from "../../src/services/owmApiService";
import { describe, it, expect, vi, beforeEach, afterEach, Mocked } from "vitest";
import { appEventBus, USER_UPDATED_EVENT, WEATHER_STATE_UPDATED_EVENT } from "../../src/utils/eventBus";
import { WeatherPollingService } from "../../src/services/weatherPollingService";
import { IUser } from "../../src/db/models/user";
import { getCurrentWeather } from "../../src/services/owmApiService";
vi.mock("../../src/services/owmApiService");
@@ -12,8 +11,8 @@ vi.mock("../../src/utils/eventBus", () => ({
on: vi.fn(),
emit: vi.fn(),
},
WEATHER_STATE_UPDATED_EVENT: 'weather:state-updated',
USER_UPDATED_EVENT: 'user:updated',
WEATHER_STATE_UPDATED_EVENT: "weather:state-updated",
USER_UPDATED_EVENT: "user:updated",
}));
vi.mock("../../src/services/owmApiService", () => ({
@@ -67,18 +66,26 @@ describe("WeatherPollingService", () => {
});
it("should stop the poll when the last user unsubscribes from a location", async () => {
// @ts-ignore - access to private property for test purposes
const activePolls = (pollingService as any).activeLocationPolls;
pollingService.subscribeUser("user-1", "Berlin");
pollingService.subscribeUser("user-2", "Berlin");
await vi.advanceTimersByTimeAsync(0);
expect(vi.getTimerCount()).toBe(1);
expect(activePolls.has("Berlin")).toBe(true);
pollingService.unsubscribeUser("user-1", "Berlin");
expect(vi.getTimerCount()).toBe(1);
expect(activePolls.has("Berlin")).toBe(true);
// @ts-ignore - access to private property for test purposes
const stopPollingSpy = vi.spyOn(pollingService as any, "_stopPollingForLocation");
pollingService.unsubscribeUser("user-2", "Berlin");
expect(vi.getTimerCount()).toBe(0);
expect(stopPollingSpy).toHaveBeenCalledWith("Berlin");
expect(activePolls.has("Berlin")).toBe(false);
});
});
@@ -109,9 +116,7 @@ describe("WeatherPollingService", () => {
let userUpdateListener: (user: IUser) => void;
beforeEach(() => {
const onCall = mockedAppEventBus.on.mock.calls.find(
call => call[0] === USER_UPDATED_EVENT
);
const onCall = mockedAppEventBus.on.mock.calls.find((call) => call[0] === USER_UPDATED_EVENT);
if (onCall) {
userUpdateListener = onCall[1];
}
@@ -123,8 +128,8 @@ describe("WeatherPollingService", () => {
});
it("should automatically move a user's subscription when their location changes", () => {
const unsubscribeSpy = vi.spyOn(pollingService, 'unsubscribeUser');
const subscribeSpy = vi.spyOn(pollingService, 'subscribeUser');
const unsubscribeSpy = vi.spyOn(pollingService, "unsubscribeUser");
const subscribeSpy = vi.spyOn(pollingService, "subscribeUser");
pollingService.subscribeUser("user-moving", "Berlin");
@@ -139,7 +144,7 @@ describe("WeatherPollingService", () => {
});
it("should do nothing if the user's location has not changed", () => {
const unsubscribeSpy = vi.spyOn(pollingService, 'unsubscribeUser');
const unsubscribeSpy = vi.spyOn(pollingService, "unsubscribeUser");
pollingService.subscribeUser("user-staying", "Berlin");
@@ -149,4 +154,4 @@ describe("WeatherPollingService", () => {
expect(unsubscribeSpy).not.toHaveBeenCalled();
});
});
});
});
+12 -4
View File
@@ -9,8 +9,18 @@ vi.mock("jsonwebtoken", () => {
};
});
vi.mock("../../src/utils/logger", () => ({
default: {
warn: vi.fn(),
info: vi.fn(),
error: vi.fn(),
debug: vi.fn(),
},
}));
import jwt from "jsonwebtoken";
import { JwtAuthenticator } from "../../src/utils/jwtAuthenticator";
import logger from "../../src/utils/logger";
describe("JwtAuthenticator", () => {
const secret = "test-secret";
@@ -40,15 +50,13 @@ describe("JwtAuthenticator", () => {
});
it("verifyToken returns null when verify throws error", () => {
const spy = vi.spyOn(console, "error").mockImplementation(() => {});
(jwt.verify as any).mockImplementation(() => {
throw new Error("invalid");
});
const res = auth.verifyToken("broken.token");
expect(res).toBeNull();
expect(spy).toHaveBeenCalled();
spy.mockRestore();
expect(logger.error).toHaveBeenCalled();
});
it("generateToken signs payload with secret", () => {
@@ -59,4 +67,4 @@ describe("JwtAuthenticator", () => {
expect(jwt.sign).toHaveBeenCalledWith(payload, secret);
expect(token).toBe("signed.jwt");
});
});
});
+19 -15
View File
@@ -1,17 +1,26 @@
import {describe, it, expect, vi, beforeEach, afterEach, Mocked} from "vitest";
import { describe, it, expect, vi, beforeEach, afterEach, Mocked } from "vitest";
import type { IncomingMessage } from "node:http";
import { verifyClient } from "../../src/utils/verifyClient";
import {JwtAuthenticator} from "../../src/utils/jwtAuthenticator";
import { JwtAuthenticator } from "../../src/utils/jwtAuthenticator";
// @ts-ignore
import {createMockJwtAuthenticator} from "../helpers/testSetup";
import { createMockJwtAuthenticator } from "../helpers/testSetup";
import logger from "../../src/utils/logger";
vi.mock("../../src/utils/logger", () => ({
default: {
warn: vi.fn(),
info: vi.fn(),
error: vi.fn(),
debug: vi.fn(),
},
}));
describe("verifyClient", () => {
const payload = { id: "user-1", username: "hi", uuid: "1234" }
const payload = { id: "user-1", username: "hi", uuid: "1234" };
const cb = vi.fn();
let mockJwtAuthenticator: Mocked<JwtAuthenticator>
let consoleSpy: ReturnType<typeof vi.spyOn>;
let mockJwtAuthenticator: Mocked<JwtAuthenticator>;
function makeReq(authHeader?: string) {
const headers: Record<string, string> = {};
@@ -24,18 +33,13 @@ describe("verifyClient", () => {
beforeEach(() => {
cb.mockReset();
mockJwtAuthenticator = createMockJwtAuthenticator() as any;
consoleSpy = vi.spyOn(console, "log").mockImplementation(() => {});
});
afterEach(() => {
consoleSpy.mockRestore();
});
it("accepts connections with valid token and sets payload", () => {
const req = makeReq("Bearer valid.jwt");
mockJwtAuthenticator.verifyToken.mockReturnValue(payload);
verifyClient(req, mockJwtAuthenticator ,cb);
verifyClient(req, mockJwtAuthenticator, cb);
expect(mockJwtAuthenticator.verifyToken).toHaveBeenCalledWith("valid.jwt");
expect(cb).toHaveBeenCalledWith(true);
@@ -49,7 +53,7 @@ describe("verifyClient", () => {
verifyClient(req, mockJwtAuthenticator, cb);
expect(cb).toHaveBeenCalledWith(false, 401, "Unauthorized");
expect(consoleSpy).toHaveBeenCalled();
expect(logger.warn).toHaveBeenCalled();
});
it("rejects connection, if token is invalid", () => {
@@ -67,9 +71,9 @@ describe("verifyClient", () => {
const req = makeReq(`Bearer ${expectedToken}`);
mockJwtAuthenticator.verifyToken.mockReturnValue(payload);
verifyClient(req,mockJwtAuthenticator, cb);
verifyClient(req, mockJwtAuthenticator, cb);
expect(mockJwtAuthenticator.verifyToken).toHaveBeenCalledWith(expectedToken);
expect(cb).toHaveBeenCalledWith(true);
});
});
});
@@ -1,18 +1,18 @@
import {describe, it, expect, vi, beforeEach, type Mocked, afterEach} from "vitest";
import {ExtendedWebSocket} from "../../../../src/interfaces/extendedWebsocket";
import {GetStateEvent} from "../../../../src/utils/websocket/websocketCustomEvents/getStateEvent";
import {GetSettingsEvent} from "../../../../src/utils/websocket/websocketCustomEvents/getSettingsEvent";
import {GetSpotifyUpdatesEvent} from "../../../../src/utils/websocket/websocketCustomEvents/getSpotifyUpdatesEvent";
import {SpotifyPollingService} from "../../../../src/services/spotifyPollingService";
import { describe, it, expect, vi, beforeEach, type Mocked, afterEach } from "vitest";
import { ExtendedWebSocket } from "../../../../src/interfaces/extendedWebsocket";
import { GetStateEvent } from "../../../../src/utils/websocket/websocketCustomEvents/getStateEvent";
import { GetSettingsEvent } from "../../../../src/utils/websocket/websocketCustomEvents/getSettingsEvent";
import { GetSpotifyUpdatesEvent } from "../../../../src/utils/websocket/websocketCustomEvents/getSpotifyUpdatesEvent";
import { SpotifyPollingService } from "../../../../src/services/spotifyPollingService";
// @ts-ignore
import {createMockSpotifyPollingService,} from "../../../helpers/testSetup";
import {StopSpotifyUpdatesEvent} from "../../../../src/utils/websocket/websocketCustomEvents/stopSpotifyUpdatesEvent";
import {GetWeatherUpdatesEvent
} from "../../../../src/utils/websocket/websocketCustomEvents/getWeatherUpdatesEvent";
import {ErrorEvent} from "../../../../src/utils/websocket/websocketCustomEvents/errorEvent";
import {UpdateUserSingleEvent} from "../../../../src/utils/websocket/websocketCustomEvents/updateUserEvent";
import {StopWeatherUpdatesEvent} from "../../../../src/utils/websocket/websocketCustomEvents/stopWeatherUpdatesEvent";
import {WeatherPollingService} from "../../../../src/services/weatherPollingService";
import { createMockSpotifyPollingService } from "../../../helpers/testSetup";
import { StopSpotifyUpdatesEvent } from "../../../../src/utils/websocket/websocketCustomEvents/stopSpotifyUpdatesEvent";
import { GetWeatherUpdatesEvent } from "../../../../src/utils/websocket/websocketCustomEvents/getWeatherUpdatesEvent";
import { ErrorEvent } from "../../../../src/utils/websocket/websocketCustomEvents/errorEvent";
import { UpdateUserSingleEvent } from "../../../../src/utils/websocket/websocketCustomEvents/updateUserEvent";
import { StopWeatherUpdatesEvent } from "../../../../src/utils/websocket/websocketCustomEvents/stopWeatherUpdatesEvent";
import { WeatherPollingService } from "../../../../src/services/weatherPollingService";
import logger from "../../../../src/utils/logger";
const createMockWebSocket = (userPayload: any = {}): ExtendedWebSocket => {
return {
@@ -20,11 +20,11 @@ const createMockWebSocket = (userPayload: any = {}): ExtendedWebSocket => {
emit: vi.fn(),
user: {
timezone: "Europe/Berlin",
lastState: {global: {mode: "idle", brightness: 42}},
lastState: { global: { mode: "idle", brightness: 42 } },
...userPayload,
},
payload: {uuid: "test-uuid-123"},
asyncUpdates: new Map
payload: { uuid: "test-uuid-123" },
asyncUpdates: new Map(),
} as unknown as ExtendedWebSocket;
};
@@ -34,8 +34,16 @@ vi.mock("../../../../src/services/owmApiService", () => ({
vi.mock("../../../../src/services/weatherPollingService");
describe("WebSocket Custom Event Handlers", () => {
vi.mock("../../../../src/utils/logger", () => ({
default: {
warn: vi.fn(),
info: vi.fn(),
error: vi.fn(),
debug: vi.fn(),
},
}));
describe("WebSocket Custom Event Handlers", () => {
let mockSpotifyPollingService: Mocked<SpotifyPollingService>;
let mockWeatherPollingService: Mocked<WeatherPollingService>;
@@ -44,41 +52,39 @@ describe("WebSocket Custom Event Handlers", () => {
vi.useFakeTimers();
mockSpotifyPollingService = createMockSpotifyPollingService() as any;
mockWeatherPollingService = new WeatherPollingService() as Mocked<WeatherPollingService>;
})
});
afterEach(() => {
vi.useRealTimers();
});
describe("GetStateEvent", () => {
it("should send the user's lastState when its handler is called", async () => {
const mockLastState = {global: {mode: "music", brightness: 100}};
const mockWs = createMockWebSocket({lastState: mockLastState});
const mockLastState = { global: { mode: "music", brightness: 100 } };
const mockWs = createMockWebSocket({ lastState: mockLastState });
const event = new GetStateEvent(mockWs);
await event.handler();
expect(mockWs.send).toHaveBeenCalledOnce();
expect(mockWs.send).toHaveBeenCalledWith(
JSON.stringify({type: "STATE", payload: mockLastState}),
{binary: false}
);
expect(mockWs.send).toHaveBeenCalledWith(JSON.stringify({ type: "STATE", payload: mockLastState }), {
binary: false,
});
});
});
describe("GetSettingsEvent", () => {
it("should send the user's settings when its handler is called", async () => {
const mockTimezone = "America/New_York";
const mockWs = createMockWebSocket({timezone: mockTimezone});
const mockWs = createMockWebSocket({ timezone: mockTimezone });
const event = new GetSettingsEvent(mockWs);
await event.handler();
expect(mockWs.send).toHaveBeenCalledOnce();
expect(mockWs.send).toHaveBeenCalledWith(
JSON.stringify({type: "SETTINGS", payload: {timezone: mockTimezone}}),
{binary: false}
JSON.stringify({ type: "SETTINGS", payload: { timezone: mockTimezone } }),
{ binary: false }
);
});
});
@@ -111,7 +117,7 @@ describe("WebSocket Custom Event Handlers", () => {
it("should subscribe the user to the WeatherPollingService using their location", async () => {
const mockWs = createMockWebSocket({
uuid: "user-uuid-weather",
location: "London"
location: "London",
});
const event = new GetWeatherUpdatesEvent(mockWs, mockWeatherPollingService);
@@ -131,12 +137,11 @@ describe("WebSocket Custom Event Handlers", () => {
});
});
describe("StopWeatherUpdatesEvent", () => {
it("should unsubscribe the user from the WeatherPollingService using their location", async () => {
const mockWs = createMockWebSocket({
uuid: "user-uuid-weather",
location: "Paris"
location: "Paris",
});
const event = new StopWeatherUpdatesEvent(mockWs, mockWeatherPollingService);
@@ -156,7 +161,6 @@ describe("WebSocket Custom Event Handlers", () => {
});
});
describe("UpdateUserSingleEvent", () => {
it("should update the user property on the websocket object", async () => {
const mockWs = createMockWebSocket();
@@ -172,14 +176,13 @@ describe("WebSocket Custom Event Handlers", () => {
describe("ErrorEvent", () => {
it("should log the received error message and traceback", async () => {
const mockWs = createMockWebSocket();
const consoleWarnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
const event = new ErrorEvent(mockWs);
const errorData = { message: "Client-Side Error", traceback: "Component > render > error" };
await event.handler(errorData);
expect(consoleWarnSpy).toHaveBeenCalledWith("Error message received", errorData.message);
expect(consoleWarnSpy).toHaveBeenCalledWith("Traceback", errorData.traceback);
expect(logger.warn).toHaveBeenCalledWith("Error message received", errorData.message);
expect(logger.warn).toHaveBeenCalledWith("Traceback", errorData.traceback);
});
});
});
});
@@ -2,22 +2,29 @@ import { describe, it, expect, vi, beforeEach, afterEach, type Mocked } from "vi
import { WebsocketEventHandler } from "../../../src/utils/websocket/websocketEventHandler";
import { ExtendedWebSocket } from "../../../src/interfaces/extendedWebsocket";
import { CustomWebsocketEvent } from "../../../src/utils/websocket/websocketCustomEvents/customWebsocketEvent";
import {SpotifyPollingService} from "../../../src/services/spotifyPollingService";
import {WeatherPollingService} from "../../../src/services/weatherPollingService";
import { SpotifyPollingService } from "../../../src/services/spotifyPollingService";
import { WeatherPollingService } from "../../../src/services/weatherPollingService";
import logger from "../../../src/utils/logger";
vi.mock("../../../src/utils/logger", () => ({
default: {
warn: vi.fn(),
info: vi.fn(),
error: vi.fn(),
debug: vi.fn(),
},
}));
describe("WebsocketEventHandler", () => {
let mockWebSocket: Mocked<ExtendedWebSocket>;
let websocketEventHandler: WebsocketEventHandler;
let mockSpotifyPollingService: Mocked<SpotifyPollingService>
let mockWeatherPollingService: Mocked<WeatherPollingService>
let mockSpotifyPollingService: Mocked<SpotifyPollingService>;
let mockWeatherPollingService: Mocked<WeatherPollingService>;
let registeredHandlers: Map<string, (...args: any[]) => void>;
beforeEach(() => {
vi.clearAllMocks();
vi.spyOn(console, "error").mockImplementation(() => {});
vi.spyOn(console, "log").mockImplementation(() => {});
registeredHandlers = new Map();
mockWebSocket = {
@@ -34,7 +41,11 @@ describe("WebsocketEventHandler", () => {
mockSpotifyPollingService = {} as Mocked<SpotifyPollingService>;
mockWeatherPollingService = {} as Mocked<WeatherPollingService>;
websocketEventHandler = new WebsocketEventHandler(mockWebSocket, mockSpotifyPollingService, mockWeatherPollingService);
websocketEventHandler = new WebsocketEventHandler(
mockWebSocket,
mockSpotifyPollingService,
mockWeatherPollingService
);
});
afterEach(() => {
@@ -43,7 +54,16 @@ describe("WebsocketEventHandler", () => {
it("should register an error event handler", () => {
websocketEventHandler.enableErrorEvent();
expect(mockWebSocket.on).toHaveBeenCalledWith("error", console.error);
expect(mockWebSocket.on).toHaveBeenCalledWith("error", expect.any(Function));
const errorHandler = registeredHandlers.get("error");
expect(errorHandler).toBeDefined();
const testError = new Error("Test error");
errorHandler!(testError);
expect(logger.error).toHaveBeenCalledWith("WebSocket error:", testError);
});
it("should register a pong event handler that sets isAlive to true", () => {
@@ -55,7 +75,7 @@ describe("WebsocketEventHandler", () => {
pongHandler!();
expect(mockWebSocket.isAlive).toBe(true);
expect(console.log).toHaveBeenCalledWith("Pong received");
expect(logger.debug).toHaveBeenCalledWith("Pong received from client");
});
describe("enableDisconnectEvent", () => {
@@ -65,13 +85,16 @@ describe("WebsocketEventHandler", () => {
websocketEventHandler.enableDisconnectEvent(mockCallback);
expect(mockWebSocket.onclose).toBeInstanceOf(Function);
mockWebSocket.onclose!({ code: 1000, reason: "Normal" } as any);
mockWebSocket.onclose!({ code: 1000, reason: "Normal", wasClean: true, type: "close" } as any);
expect(console.log).toHaveBeenCalledWith("User: testuser disconnected");
expect(logger.info).toHaveBeenCalledWith(
"WebSocket closed: code=1000, reason=Normal, wasClean=true, type=close"
);
expect(logger.info).toHaveBeenCalledWith(`User: ${mockWebSocket.payload.username} disconnected`);
expect(mockCallback).toHaveBeenCalledOnce();
});
it("should handle disconnect with", () => {
it("should handle disconnect without calling clearInterval", () => {
const mockCallback = vi.fn();
const clearIntervalSpy = vi.spyOn(global, "clearInterval");
@@ -95,7 +118,9 @@ describe("WebsocketEventHandler", () => {
messageHandler!(rawData);
expect(console.log).toHaveBeenCalledWith("Received message:", JSON.stringify(message));
expect(logger.debug).toHaveBeenCalledWith(`Received WebSocket message of type "test_event"`, {
messageData: message,
});
expect(mockWebSocket.emit).toHaveBeenCalledWith("test_event", message);
});
});
@@ -118,4 +143,4 @@ describe("WebsocketEventHandler", () => {
expect(bindSpy).toHaveBeenCalledWith(customEvent);
});
});
});
});
@@ -1,12 +1,23 @@
import { describe, it, expect, vi, beforeEach, afterEach, type Mocked } from "vitest";
import { WebsocketServerEventHandler } from "../../../src/utils/websocket/websocketServerEventHandler";
import {UserService} from "../../../src/services/db/UserService";
import { UserService } from "../../../src/services/db/UserService";
import logger from "../../../src/utils/logger";
const heartbeatSpy = vi.fn();
vi.mock("../../../src/utils/websocket/websocketServerHeartbeatInterval", () => ({
heartbeat: () => heartbeatSpy,
}));
vi.mock("../../../src/utils/logger", () => ({
default: {
warn: vi.fn(),
info: vi.fn(),
error: vi.fn(),
debug: vi.fn(),
},
}));
const userObj = {
name: "tester",
uuid: "uuid-1",
@@ -81,14 +92,11 @@ describe("WebsocketServerEventHandler", () => {
it("enableCloseEvent registers Listener and calls callback on close", () => {
const handler = new WebsocketServerEventHandler(wss as any, mockUserService);
const cb = vi.fn();
const logSpy = vi.spyOn(console, "log").mockImplementation(() => {});
handler.enableCloseEvent(cb);
wss.emit("close");
expect(cb).toHaveBeenCalledTimes(1);
expect(logSpy).toHaveBeenCalledWith("WebSocket server closed");
logSpy.mockRestore();
expect(logger.info).toHaveBeenCalledWith("WebSocket server closed");
});
});
});