fix tests

This commit is contained in:
StarAppeal
2025-09-20 22:01:11 +02:00
parent 2a62fdfab3
commit 3621dcb87b
5 changed files with 292 additions and 260 deletions
+2 -2
View File
@@ -48,7 +48,7 @@ export class RestUser {
};
await this.userService.updateUserByUUID(req.payload.uuid, {spotifyConfig: spotifyConfig});
return ok(res, {message: "Spotify Config erfolgreich geändert"});
return ok(res, {message: "Spotify config changed successfully."});
})
);
@@ -78,7 +78,7 @@ export class RestUser {
const newPassword = await PasswordUtils.hashPassword(password);
await this.userService.updateUserByUUID(req.payload.uuid, {password: newPassword});
return ok(res, {message: "Passwort erfolgreich geändert"});
return ok(res, {message: "Password changed successfully"});
})
);
+213 -250
View File
@@ -22,8 +22,8 @@ describe("RestUser", () => {
let testEnv: TestEnvironment;
const requestingUserUUID = "test-user-uuid";
const adminUser = { uuid: requestingUserUUID, config: { isAdmin: true } };
const nonAdminUser = { uuid: requestingUserUUID, config: { isAdmin: false } };
const adminUser = {uuid: requestingUserUUID, config: {isAdmin: true}};
const nonAdminUser = {uuid: requestingUserUUID, config: {isAdmin: false}};
const mockedUserService = createMockUserService();
beforeEach(() => {
@@ -72,36 +72,23 @@ describe("RestUser", () => {
spotifyConfig: null
};
mockedUserService.getUserByUUID.mockResolvedValue(mockUser);
mockedUserService.updateUserById.mockResolvedValue(mockUser);
mockedUserService.updateUserByUUID.mockResolvedValue(mockUser);
const response = await request(testEnv.app)
.put("/user/me/spotify")
.send(validSpotifyData)
.expect(200);
expect(response.body.data.message).toBe("Spotify Config erfolgreich geändert");
expect(mockedUserService.getUserByUUID).toHaveBeenCalledWith("test-user-uuid");
expect(mockedUserService.updateUserById).toHaveBeenCalledWith(
mockUser.id, {
spotifyConfig: {
accessToken: "access-token-123",
refreshToken: "refresh-token-123",
scope: "user-read-playback-state",
expirationDate: new Date("2024-12-31T23:59:59.000Z")
}
});
});
it("should return bad request when user not found", async () => {
mockedUserService.getUserByUUID.mockResolvedValue(null);
const response = await request(testEnv.app)
.put("/user/me/spotify")
.send(validSpotifyData)
.expect(400);
expect(response.body.data.message).toBe("User not found");
expect(response.body.data.message).toBe("Spotify config changed successfully.");
expect(mockedUserService.updateUserByUUID).toHaveBeenCalledWith(
mockUser.uuid, {
spotifyConfig: {
accessToken: "access-token-123",
refreshToken: "refresh-token-123",
scope: "user-read-playback-state",
expirationDate: new Date("2024-12-31T23:59:59.000Z")
}
});
});
it("should return bad request for missing accessToken", async () => {
@@ -177,7 +164,6 @@ describe("RestUser", () => {
spotifyConfig: null
};
mockedUserService.getUserByUUID.mockResolvedValue(mockUser);
mockedUserService.clearSpotifyConfigByUUID.mockResolvedValue(updatedUser);
const response = await request(testEnv.app)
@@ -185,255 +171,232 @@ describe("RestUser", () => {
.expect(200);
expect(response.body.data.user).toEqual(updatedUser);
expect(mockedUserService.getUserByUUID).toHaveBeenCalledWith("test-user-uuid");
expect(mockedUserService.clearSpotifyConfigByUUID).toHaveBeenCalledWith("test-user-uuid");
});
it("should return bad request when user not found", async () => {
mockedUserService.getUserByUUID.mockResolvedValue(null);
const response = await request(testEnv.app)
.delete("/user/me/spotify")
.expect(400);
expect(response.body.data.message).toBe("User not found");
});
});
describe("PUT /me/password", () => {
const validPasswordData = {
password: "newpassword123",
passwordConfirmation: "newpassword123"
};
it("should update password successfully", async () => {
const {PasswordUtils} = await import("../../src/utils/passwordUtils");
const mockUser = {
id: "test-user-id",
name: "testuser",
uuid: "test-user-uuid",
password: "old-hashed-password"
};
mockedUserService.getUserByUUID.mockResolvedValue(mockUser);
vi.mocked(PasswordUtils.validatePassword).mockReturnValue({valid: true});
vi.mocked(PasswordUtils.hashPassword).mockResolvedValue("new-hashed-password");
mockedUserService.updateUserById.mockResolvedValue(mockUser);
const response = await request(testEnv.app)
.put("/user/me/password")
.send(validPasswordData)
.expect(200);
expect(response.body.data.message).toBe("Passwort erfolgreich geändert");
expect(PasswordUtils.validatePassword).toHaveBeenCalledWith("newpassword123");
expect(PasswordUtils.hashPassword).toHaveBeenCalledWith("newpassword123");
expect(mockedUserService.updateUserById).toHaveBeenCalledWith(
mockUser.id, {
password: "new-hashed-password"
});
});
it("should return bad request when user not found", async () => {
mockedUserService.getUserByUUID.mockResolvedValue(null);
const response = await request(testEnv.app)
.put("/user/me/password")
.send(validPasswordData)
.expect(400);
expect(response.body.data.message).toBe("User not found");
});
it("should return bad request when passwords don't match", async () => {
const mockUser = {
id: "test-user-id",
name: "testuser",
uuid: "test-user-uuid"
};
mockedUserService.getUserByUUID.mockResolvedValue(mockUser);
const invalidData = {
describe("PUT /me/password", () => {
const validPasswordData = {
password: "newpassword123",
passwordConfirmation: "differentpassword"
passwordConfirmation: "newpassword123"
};
const response = await request(testEnv.app)
.put("/user/me/password")
.send(invalidData)
.expect(400);
it("should update password successfully", async () => {
const {PasswordUtils} = await import("../../src/utils/passwordUtils");
expect(response.body.data.message).toBe("Passwörter stimmen nicht überein");
});
const mockUser = {
id: "test-user-id",
name: "testuser",
uuid: "test-user-uuid",
password: "old-hashed-password"
};
it("should return bad request for invalid password", async () => {
const {PasswordUtils} = await import("../../src/utils/passwordUtils");
const mockUser = {
id: "test-user-id",
name: "testuser",
uuid: "test-user-uuid"
};
mockedUserService.getUserByUUID.mockResolvedValue(mockUser);
vi.mocked(PasswordUtils.validatePassword).mockReturnValue({
valid: false,
message: "Password too weak"
});
const response = await request(testEnv.app)
.put("/user/me/password")
.send(validPasswordData)
.expect(400);
expect(response.body.data.message).toBe("Password too weak");
});
it("should return bad request for missing password", async () => {
const invalidData = {passwordConfirmation: "newpassword123"};
const response = await request(testEnv.app)
.put("/user/me/password")
.send(invalidData)
.expect(400);
expect(response.body.data.details[0]).toContain("password");
});
it("should return bad request for missing passwordConfirmation", async () => {
const invalidData = {password: "newpassword123"};
const response = await request(testEnv.app)
.put("/user/me/password")
.send(invalidData)
.expect(400);
expect(response.body.data.details[0]).toContain("passwordConfirmation");
});
it("should return bad request for short password", async () => {
const invalidData = {
password: "short",
passwordConfirmation: "short"
};
const response = await request(testEnv.app)
.put("/user/me/password")
.send(invalidData)
.expect(400);
expect(response.body.data.details[0]).toContain("password");
});
});
describe("GET / (Admin only)", () => {
describe("when user is an admin", () => {
beforeEach(() => {
mockedUserService.getUserByUUID.mockResolvedValue(adminUser);
});
it("should return all users", async () => {
const mockUsers = [
{id: "1", name: "user1", uuid: "uuid1"},
{id: "2", name: "user2", uuid: "uuid2"}
];
mockedUserService.getAllUsers.mockResolvedValue(mockUsers);
vi.mocked(PasswordUtils.validatePassword).mockReturnValue({valid: true});
vi.mocked(PasswordUtils.hashPassword).mockResolvedValue("new-hashed-password");
mockedUserService.updateUserByUUID.mockResolvedValue(mockUser);
const response = await request(testEnv.app)
.get("/user/")
.put("/user/me/password")
.send(validPasswordData)
.expect(200);
expect(response.body.data.users).toEqual(mockUsers);
expect(mockedUserService.getUserByUUID).toHaveBeenCalledWith(requestingUserUUID);
expect(mockedUserService.getAllUsers).toHaveBeenCalled();
expect(response.body.data.message).toBe("Password changed successfully");
expect(PasswordUtils.validatePassword).toHaveBeenCalledWith("newpassword123");
expect(PasswordUtils.hashPassword).toHaveBeenCalledWith("newpassword123");
expect(mockedUserService.updateUserByUUID).toHaveBeenCalledWith(
mockUser.uuid, {
password: "new-hashed-password"
});
});
it("should handle empty user list", async () => {
mockedUserService.getAllUsers.mockResolvedValue([]);
it("should return bad request when passwords don't match", async () => {
const mockUser = {
id: "test-user-id",
name: "testuser",
uuid: "test-user-uuid"
};
mockedUserService.getUserByUUID.mockResolvedValue(mockUser);
const invalidData = {
password: "newpassword123",
passwordConfirmation: "differentpassword"
};
const response = await request(testEnv.app)
.get("/user/")
.expect(200);
expect(response.body.data.users).toEqual([]);
});
});
describe("when user is not an admin", () => {
it("should return 404 Not Found if user is not an admin", async () => {
mockedUserService.getUserByUUID.mockResolvedValue(nonAdminUser);
await request(testEnv.app)
.get("/user/")
.expect(404);
});
it("should return 404 Not Found if user does not exist", async () => {
mockedUserService.getUserByUUID.mockResolvedValue(null);
await request(testEnv.app)
.get("/user/")
.expect(404);
});
});
});
describe("GET /:id (Admin only)", () => {
const specificUserId = "specific-user-id";
const mockUser = {
id: specificUserId,
name: "specificuser",
uuid: "specific-uuid"
};
describe("when user is an admin", () => {
beforeEach(() => {
mockedUserService.getUserByUUID.mockResolvedValue(adminUser);
});
it("should return user by id", async () => {
mockedUserService.getUserById.mockResolvedValue(mockUser);
const response = await request(testEnv.app)
.get(`/user/${specificUserId}`)
.expect(200);
expect(response.body.data).toEqual(mockUser);
expect(mockedUserService.getUserByUUID).toHaveBeenCalledWith(requestingUserUUID);
expect(mockedUserService.getUserById).toHaveBeenCalledWith(specificUserId);
});
it("should return bad request when target user is not found", async () => {
mockedUserService.getUserById.mockResolvedValue(null);
const response = await request(testEnv.app)
.get(`/user/nonexistent-id`)
.put("/user/me/password")
.send(invalidData)
.expect(400);
expect(response.body.data.message).toBe("Unable to find matching document with id: nonexistent-id");
expect(response.body.data.message).toBe("Passwörter stimmen nicht überein");
});
it("should return bad request for invalid password", async () => {
const {PasswordUtils} = await import("../../src/utils/passwordUtils");
const mockUser = {
id: "test-user-id",
name: "testuser",
uuid: "test-user-uuid"
};
mockedUserService.getUserByUUID.mockResolvedValue(mockUser);
vi.mocked(PasswordUtils.validatePassword).mockReturnValue({
valid: false,
message: "Password too weak"
});
const response = await request(testEnv.app)
.put("/user/me/password")
.send(validPasswordData)
.expect(400);
expect(response.body.data.message).toBe("Password too weak");
});
it("should return bad request for missing password", async () => {
const invalidData = {passwordConfirmation: "newpassword123"};
const response = await request(testEnv.app)
.put("/user/me/password")
.send(invalidData)
.expect(400);
expect(response.body.data.details[0]).toContain("password");
});
it("should return bad request for missing passwordConfirmation", async () => {
const invalidData = {password: "newpassword123"};
const response = await request(testEnv.app)
.put("/user/me/password")
.send(invalidData)
.expect(400);
expect(response.body.data.details[0]).toContain("passwordConfirmation");
});
it("should return bad request for short password", async () => {
const invalidData = {
password: "short",
passwordConfirmation: "short"
};
const response = await request(testEnv.app)
.put("/user/me/password")
.send(invalidData)
.expect(400);
expect(response.body.data.details[0]).toContain("password");
});
});
describe("when user is not an admin", () => {
it("should return 404 Not Found if user is not an admin", async () => {
mockedUserService.getUserByUUID.mockResolvedValue(nonAdminUser);
describe("GET / (Admin only)", () => {
await request(testEnv.app)
.get(`/user/${specificUserId}`)
.expect(404);
describe("when user is an admin", () => {
beforeEach(() => {
mockedUserService.getUserByUUID.mockResolvedValue(adminUser);
});
it("should return all users", async () => {
const mockUsers = [
{id: "1", name: "user1", uuid: "uuid1"},
{id: "2", name: "user2", uuid: "uuid2"}
];
mockedUserService.getAllUsers.mockResolvedValue(mockUsers);
const response = await request(testEnv.app)
.get("/user/")
.expect(200);
expect(response.body.data.users).toEqual(mockUsers);
expect(mockedUserService.getUserByUUID).toHaveBeenCalledWith(requestingUserUUID);
expect(mockedUserService.getAllUsers).toHaveBeenCalled();
});
it("should handle empty user list", async () => {
mockedUserService.getAllUsers.mockResolvedValue([]);
const response = await request(testEnv.app)
.get("/user/")
.expect(200);
expect(response.body.data.users).toEqual([]);
});
});
it("should return 404 Not Found if user does not exist", async () => {
mockedUserService.getUserByUUID.mockResolvedValue(null);
describe("when user is not an admin", () => {
it("should return 404 Not Found if user is not an admin", async () => {
mockedUserService.getUserByUUID.mockResolvedValue(nonAdminUser);
await request(testEnv.app)
.get(`/user/${specificUserId}`)
.expect(404);
await request(testEnv.app)
.get("/user/")
.expect(404);
});
it("should return 404 Not Found if user does not exist", async () => {
mockedUserService.getUserByUUID.mockResolvedValue(null);
await request(testEnv.app)
.get("/user/")
.expect(404);
});
});
});
describe("GET /:id (Admin only)", () => {
const specificUserId = "specific-user-id";
const mockUser = {
id: specificUserId,
name: "specificuser",
uuid: "specific-uuid"
};
describe("when user is an admin", () => {
beforeEach(() => {
mockedUserService.getUserByUUID.mockResolvedValue(adminUser);
});
it("should return user by id", async () => {
mockedUserService.getUserById.mockResolvedValue(mockUser);
const response = await request(testEnv.app)
.get(`/user/${specificUserId}`)
.expect(200);
expect(response.body.data).toEqual(mockUser);
expect(mockedUserService.getUserByUUID).toHaveBeenCalledWith(requestingUserUUID);
expect(mockedUserService.getUserById).toHaveBeenCalledWith(specificUserId);
});
it("should return bad request when target user is not found", async () => {
mockedUserService.getUserById.mockResolvedValue(null);
const response = await request(testEnv.app)
.get(`/user/nonexistent-id`)
.expect(400);
expect(response.body.data.message).toBe("Unable to find matching document with id: nonexistent-id");
});
});
describe("when user is not an admin", () => {
it("should return 404 Not Found if user is not an admin", async () => {
mockedUserService.getUserByUUID.mockResolvedValue(nonAdminUser);
await request(testEnv.app)
.get(`/user/${specificUserId}`)
.expect(404);
});
it("should return 404 Not Found if user does not exist", async () => {
mockedUserService.getUserByUUID.mockResolvedValue(null);
await request(testEnv.app)
.get(`/user/${specificUserId}`)
.expect(404);
});
});
});
});
});
+2 -2
View File
@@ -3,11 +3,11 @@ import {UserModel} from "../../../src/db/models/user";
import {UserService} from "../../../src/services/db/UserService";
import {connectToDatabase} from "../../../src/services/db/database.service";
vi.mock("../../../../src/services/db/database.service", () => ({
vi.mock("../../../src/services/db/database.service", () => ({
connectToDatabase: vi.fn(),
}));
vi.mock("../../../../src/db/models/user");
vi.mock("../../../src/db/models/user");
const mockedUserModel = vi.mocked(UserModel);
const mockedConnectToDatabase = vi.mocked(connectToDatabase);
+1 -1
View File
@@ -1,7 +1,7 @@
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
import mongoose from "mongoose";
const MODULE_PATH = "../../../../src/services/db/database.service";
const MODULE_PATH = "../../../src/services/db/database.service";
type SpyInstance<T extends (...args: any) => any> = ReturnType<typeof vi.spyOn<any, Parameters<T>[0]>>;
+74 -5
View File
@@ -8,10 +8,25 @@ import { getEventListeners } from "../src/utils/websocket/websocketCustomEvents/
import { createMockUserService} from "./helpers/testSetup";
import {UserService} from "../src/services/db/UserService";
import {SpotifyPollingService} from "../src/services/spotifyPollingService";
import { USER_UPDATED_EVENT, SPOTIFY_STATE_UPDATED_EVENT } from "../src/utils/eventBus";
import { WebsocketEventType } from "../src/utils/websocket/websocketCustomEvents/websocketEventType";
let mockWssInstance: Mocked<WebSocketServer>;
let mockServerEventHandler: Mocked<WebsocketServerEventHandler>;
const eventBusListeners = new Map<string, (...args: any[]) => void>();
vi.mock("../src/utils/eventBus", () => ({
appEventBus: {
on: vi.fn((event, listener) => {
eventBusListeners.set(event, listener);
}),
emit: vi.fn(),
},
USER_UPDATED_EVENT: 'user:updated',
SPOTIFY_STATE_UPDATED_EVENT: 'spotify:state-updated',
}));
vi.mock("ws", () => ({
Server: vi.fn().mockImplementation(() => mockWssInstance),
WebSocket: { OPEN: 1, CLOSED: 3 },
@@ -134,11 +149,65 @@ describe("ExtendedWebSocketServer", () => {
expect(mockWsClient.emit).toHaveBeenCalledWith("GET_STATE", {});
expect(mockWsClient.emit).toHaveBeenCalledWith("GET_SETTINGS", {});
});
});
it("should emit GET_SPOTIFY_UPDATES if last state was 'music'", () => {
mockWsClient.user.lastState.global.mode = "music";
connectionHandler(mockWsClient, {});
expect(mockWsClient.emit).toHaveBeenCalledWith("GET_SPOTIFY_UPDATES", {});
describe("_listenForAppEvents", () => {
let mockClient: any;
beforeEach(() => {
mockClient = {
readyState: WebSocket.OPEN,
payload: { uuid: "user-123" },
send: vi.fn(),
emit: vi.fn(),
};
mockWssInstance.clients.add(mockClient);
});
it("should listen for USER_UPDATED_EVENT and emit to the correct client", () => {
const userUpdateListener = eventBusListeners.get(USER_UPDATED_EVENT);
expect(userUpdateListener).toBeDefined();
const updatedUserPayload = { uuid: "user-123", name: "Neuer Name" };
userUpdateListener!(updatedUserPayload);
expect(mockClient.emit).toHaveBeenCalledOnce();
expect(mockClient.emit).toHaveBeenCalledWith(
WebsocketEventType.UPDATE_USER_SINGLE,
updatedUserPayload
);
});
it("should listen for SPOTIFY_STATE_UPDATED_EVENT and send to the correct client", () => {
const spotifyStateListener = eventBusListeners.get(SPOTIFY_STATE_UPDATED_EVENT);
expect(spotifyStateListener).toBeDefined();
const spotifyUpdatePayload = { state: { item: { name: "Neuer Song" } } };
const eventPayload = { uuid: "user-123", ...spotifyUpdatePayload };
spotifyStateListener!(eventPayload);
expect(mockClient.send).toHaveBeenCalledOnce();
const expectedMessage = JSON.stringify({
type: "SPOTIFY_UPDATE",
payload: spotifyUpdatePayload.state,
});
expect(mockClient.send).toHaveBeenCalledWith(expectedMessage, { binary: false });
});
it("should not send a message if the target client is not connected", () => {
const userUpdateListener = eventBusListeners.get(USER_UPDATED_EVENT);
const spotifyStateListener = eventBusListeners.get(SPOTIFY_STATE_UPDATED_EVENT);
const eventPayload = { uuid: "user-unknown", name: "some data" };
userUpdateListener!(eventPayload);
spotifyStateListener!(eventPayload);
expect(mockClient.send).not.toHaveBeenCalled();
expect(mockClient.emit).not.toHaveBeenCalled();
});
});
});
});