Skip to content

Commit f9aa95e

Browse files
committed
Merge branch 'develop' of github.com:OpenNBS/NoteBlockWorld into feature/song-search
2 parents 47409c5 + beee53c commit f9aa95e

File tree

19 files changed

+23169
-4755
lines changed

19 files changed

+23169
-4755
lines changed

apps/backend/src/user/user.controller.spec.ts

Lines changed: 240 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,22 @@
11
import type { UserDocument } from '@nbw/database';
2-
import { GetUser, PageQueryDTO } from '@nbw/database';
3-
import { HttpException } from '@nestjs/common';
2+
import {
3+
GetUser,
4+
PageQueryDTO,
5+
UpdateUsernameDto,
6+
UserDto,
7+
} from '@nbw/database';
8+
import { HttpException, HttpStatus } from '@nestjs/common';
49
import { Test, TestingModule } from '@nestjs/testing';
510

611
import { UserController } from './user.controller';
712
import { UserService } from './user.service';
813

914
const mockUserService = {
10-
getUserByEmailOrId: jest.fn(),
15+
findByEmail: jest.fn(),
16+
findByID: jest.fn(),
1117
getUserPaginated: jest.fn(),
12-
getSelfUserData: jest.fn(),
18+
normalizeUsername: jest.fn(),
19+
usernameExists: jest.fn(),
1320
};
1421

1522
describe('UserController', () => {
@@ -36,28 +43,59 @@ describe('UserController', () => {
3643
});
3744

3845
describe('getUser', () => {
39-
it('should return user data by email or ID', async () => {
46+
it('should return user data by email', async () => {
4047
const query: GetUser = {
4148
42-
username: 'test-username',
49+
};
50+
51+
const user = { email: '[email protected]' };
52+
53+
mockUserService.findByEmail.mockResolvedValueOnce(user);
54+
55+
const result = await userController.getUser(query);
56+
57+
expect(result).toEqual(user);
58+
expect(userService.findByEmail).toHaveBeenCalledWith(query.email);
59+
});
60+
61+
it('should return user data by ID', async () => {
62+
const query: GetUser = {
4363
id: 'test-id',
4464
};
4565

46-
const user = { email: 'test@example.com' };
66+
const user = { _id: 'test-id' };
4767

48-
mockUserService.getUserByEmailOrId.mockResolvedValueOnce(user);
68+
mockUserService.findByID.mockResolvedValueOnce(user);
4969

5070
const result = await userController.getUser(query);
5171

5272
expect(result).toEqual(user);
53-
expect(userService.getUserByEmailOrId).toHaveBeenCalledWith(query);
73+
expect(userService.findByID).toHaveBeenCalledWith(query.id);
74+
});
75+
76+
it('should throw an error if username is provided', async () => {
77+
const query: GetUser = {
78+
username: 'test-username',
79+
};
80+
81+
await expect(userController.getUser(query)).rejects.toThrow(
82+
HttpException,
83+
);
84+
});
85+
86+
it('should throw an error if neither email nor ID is provided', async () => {
87+
const query: GetUser = {};
88+
89+
await expect(userController.getUser(query)).rejects.toThrow(
90+
HttpException,
91+
);
5492
});
5593
});
5694

5795
describe('getUserPaginated', () => {
5896
it('should return paginated user data', async () => {
5997
const query: PageQueryDTO = { page: 1, limit: 10 };
60-
const paginatedUsers = { items: [], total: 0 };
98+
const paginatedUsers = { users: [], total: 0, page: 1, limit: 10 };
6199

62100
mockUserService.getUserPaginated.mockResolvedValueOnce(paginatedUsers);
63101

@@ -71,20 +109,209 @@ describe('UserController', () => {
71109
describe('getMe', () => {
72110
it('should return the token owner data', async () => {
73111
const user: UserDocument = { _id: 'test-user-id' } as UserDocument;
74-
const userData = { _id: 'test-user-id', email: '[email protected]' };
112+
const userData = {
113+
_id: 'test-user-id',
114+
115+
lastSeen: new Date(),
116+
loginStreak: 1,
117+
maxLoginStreak: 1,
118+
loginCount: 1,
119+
save: jest.fn().mockResolvedValue(true),
120+
} as unknown as UserDocument;
75121

76-
mockUserService.getSelfUserData.mockResolvedValueOnce(userData);
122+
mockUserService.findByID.mockResolvedValueOnce(userData);
77123

78124
const result = await userController.getMe(user);
79125

80126
expect(result).toEqual(userData);
81-
expect(userService.getSelfUserData).toHaveBeenCalledWith(user);
127+
expect(userService.findByID).toHaveBeenCalledWith(user._id.toString());
82128
});
83129

84130
it('should handle null user', async () => {
85131
const user = null;
86132

87133
await expect(userController.getMe(user)).rejects.toThrow(HttpException);
88134
});
135+
136+
it('should throw an error if user is not found', async () => {
137+
const user: UserDocument = { _id: 'test-user-id' } as UserDocument;
138+
139+
mockUserService.findByID.mockResolvedValueOnce(null);
140+
141+
await expect(userController.getMe(user)).rejects.toThrow(HttpException);
142+
});
143+
144+
it('should update lastSeen and increment loginStreak if lastSeen is before today', async () => {
145+
const user: UserDocument = { _id: 'test-user-id' } as UserDocument;
146+
const yesterday = new Date();
147+
yesterday.setDate(yesterday.getDate() - 1);
148+
yesterday.setHours(0, 0, 0, 0);
149+
150+
const userData = {
151+
_id: 'test-user-id',
152+
lastSeen: yesterday,
153+
loginStreak: 1,
154+
maxLoginStreak: 1,
155+
loginCount: 1,
156+
save: jest.fn().mockResolvedValue(true),
157+
} as unknown as UserDocument;
158+
159+
mockUserService.findByID.mockResolvedValueOnce(userData);
160+
161+
const result = await userController.getMe(user);
162+
163+
expect(result.lastSeen).toBeInstanceOf(Date);
164+
expect(result.loginStreak).toBe(2);
165+
expect(result.loginCount).toBe(2);
166+
expect(userData.save).toHaveBeenCalled();
167+
});
168+
169+
it('should not update lastSeen or increment loginStreak if lastSeen is today', async () => {
170+
const user: UserDocument = { _id: 'test-user-id' } as UserDocument;
171+
const today = new Date();
172+
today.setHours(0, 0, 0, 0);
173+
174+
const userData = {
175+
_id: 'test-user-id',
176+
lastSeen: today,
177+
loginStreak: 1,
178+
maxLoginStreak: 1,
179+
loginCount: 1,
180+
save: jest.fn().mockResolvedValue(true),
181+
} as unknown as UserDocument;
182+
183+
mockUserService.findByID.mockResolvedValueOnce(userData);
184+
185+
const result = await userController.getMe(user);
186+
187+
expect(result.lastSeen).toEqual(today);
188+
expect(result.loginStreak).toBe(1);
189+
expect(result.loginCount).toBe(1);
190+
expect(userData.save).not.toHaveBeenCalled();
191+
});
192+
193+
it('should reset loginStreak if lastSeen is not yesterday', async () => {
194+
const user: UserDocument = { _id: 'test-user-id' } as UserDocument;
195+
const twoDaysAgo = new Date();
196+
twoDaysAgo.setDate(twoDaysAgo.getDate() - 2);
197+
twoDaysAgo.setHours(0, 0, 0, 0);
198+
199+
const userData = {
200+
_id: 'test-user-id',
201+
lastSeen: twoDaysAgo,
202+
loginStreak: 5,
203+
maxLoginStreak: 5,
204+
loginCount: 1,
205+
save: jest.fn().mockResolvedValue(true),
206+
} as unknown as UserDocument;
207+
208+
mockUserService.findByID.mockResolvedValueOnce(userData);
209+
210+
const result = await userController.getMe(user);
211+
212+
expect(result.lastSeen).toBeInstanceOf(Date);
213+
expect(result.loginStreak).toBe(1);
214+
expect(userData.save).toHaveBeenCalled();
215+
});
216+
217+
it('should increment maxLoginStreak if login streak exceeds max', async () => {
218+
const user: UserDocument = { _id: 'test-user-id' } as UserDocument;
219+
const yesterday = new Date();
220+
yesterday.setDate(yesterday.getDate() - 1);
221+
yesterday.setHours(0, 0, 0, 0);
222+
223+
const userData = {
224+
_id: 'test-user-id',
225+
lastSeen: yesterday,
226+
loginStreak: 8,
227+
maxLoginStreak: 8,
228+
loginCount: 1,
229+
save: jest.fn().mockResolvedValue(true),
230+
} as unknown as UserDocument;
231+
232+
mockUserService.findByID.mockResolvedValueOnce(userData);
233+
234+
const result = await userController.getMe(user);
235+
236+
expect(result.maxLoginStreak).toBe(9);
237+
expect(userData.save).toHaveBeenCalled();
238+
});
239+
});
240+
241+
describe('updateUsername', () => {
242+
it('should update the username', async () => {
243+
const user: UserDocument = {
244+
_id: 'test-user-id',
245+
username: 'olduser',
246+
save: jest.fn().mockResolvedValue(true),
247+
} as unknown as UserDocument;
248+
const body: UpdateUsernameDto = { username: 'newuser' };
249+
const normalizedUsername = 'newuser';
250+
251+
mockUserService.normalizeUsername.mockReturnValue(normalizedUsername);
252+
mockUserService.usernameExists.mockResolvedValue(false);
253+
254+
// Mock UserDto.fromEntity
255+
jest.spyOn(UserDto, 'fromEntity').mockReturnValue({
256+
username: normalizedUsername,
257+
publicName: user.publicName,
258+
email: user.email,
259+
});
260+
261+
const result = await userController.updateUsername(user, body);
262+
263+
expect(user.username).toBe(normalizedUsername);
264+
expect(user.lastEdited).toBeInstanceOf(Date);
265+
expect(user.save).toHaveBeenCalled();
266+
expect(userService.normalizeUsername).toHaveBeenCalledWith(body.username);
267+
expect(userService.usernameExists).toHaveBeenCalledWith(
268+
normalizedUsername,
269+
);
270+
expect(result.username).toBe(normalizedUsername);
271+
});
272+
273+
it('should throw an error if username already exists', async () => {
274+
const user: UserDocument = {
275+
_id: 'test-user-id',
276+
username: 'olduser',
277+
} as unknown as UserDocument;
278+
const body: UpdateUsernameDto = { username: 'existinguser' };
279+
const normalizedUsername = 'existinguser';
280+
281+
mockUserService.normalizeUsername.mockReturnValue(normalizedUsername);
282+
mockUserService.usernameExists.mockResolvedValue(true);
283+
284+
await expect(userController.updateUsername(user, body)).rejects.toThrow(
285+
HttpException,
286+
);
287+
expect(userService.usernameExists).toHaveBeenCalledWith(
288+
normalizedUsername,
289+
);
290+
});
291+
292+
it('should throw an error if username is the same', async () => {
293+
const user: UserDocument = {
294+
_id: 'test-user-id',
295+
username: 'sameuser',
296+
} as unknown as UserDocument;
297+
const body: UpdateUsernameDto = { username: 'sameuser' };
298+
const normalizedUsername = 'sameuser';
299+
300+
mockUserService.normalizeUsername.mockReturnValue(normalizedUsername);
301+
mockUserService.usernameExists.mockResolvedValue(false);
302+
303+
await expect(userController.updateUsername(user, body)).rejects.toThrow(
304+
HttpException,
305+
);
306+
});
307+
308+
it('should handle null user', async () => {
309+
const user = null;
310+
const body: UpdateUsernameDto = { username: 'newuser' };
311+
312+
await expect(userController.updateUsername(user, body)).rejects.toThrow(
313+
HttpException,
314+
);
315+
});
89316
});
90317
});

0 commit comments

Comments
 (0)