Skip to content

Commit 64cdca4

Browse files
authored
Fix test failures and module resolution issues (#75)
2 parents 8f6f077 + dbfd374 commit 64cdca4

File tree

6 files changed

+105
-42
lines changed

6 files changed

+105
-42
lines changed

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

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ describe('AuthController', () => {
2222
let authService: AuthService;
2323

2424
beforeEach(async () => {
25+
// Clear all mocks before each test to ensure test isolation
26+
jest.clearAllMocks();
27+
2528
const module: TestingModule = await Test.createTestingModule({
2629
controllers: [AuthController],
2730
providers: [
@@ -68,8 +71,13 @@ describe('AuthController', () => {
6871

6972
describe('githubLogin', () => {
7073
it('should call AuthService.githubLogin', async () => {
71-
await controller.githubLogin();
72-
expect(authService.githubLogin).toHaveBeenCalled();
74+
// githubLogin is just a Passport guard entry point - it doesn't call authService
75+
// The actual login is handled by the callback endpoint (githubRedirect)
76+
controller.githubLogin();
77+
// Verify the method exists and can be called without errors
78+
expect(controller.githubLogin).toBeDefined();
79+
// Verify authService was NOT called (since this is just a guard entry point)
80+
expect(authService.githubLogin).not.toHaveBeenCalled();
7381
});
7482
});
7583

@@ -97,8 +105,13 @@ describe('AuthController', () => {
97105

98106
describe('googleLogin', () => {
99107
it('should call AuthService.googleLogin', async () => {
100-
await controller.googleLogin();
101-
expect(authService.googleLogin).toHaveBeenCalled();
108+
// googleLogin is just a Passport guard entry point - it doesn't call authService
109+
// The actual login is handled by the callback endpoint (googleRedirect)
110+
controller.googleLogin();
111+
// Verify the method exists and can be called without errors
112+
expect(controller.googleLogin).toBeDefined();
113+
// Verify authService was NOT called (since this is just a guard entry point)
114+
expect(authService.googleLogin).not.toHaveBeenCalled();
102115
});
103116
});
104117

@@ -126,8 +139,13 @@ describe('AuthController', () => {
126139

127140
describe('discordLogin', () => {
128141
it('should call AuthService.discordLogin', async () => {
129-
await controller.discordLogin();
130-
expect(authService.discordLogin).toHaveBeenCalled();
142+
// discordLogin is just a Passport guard entry point - it doesn't call authService
143+
// The actual login is handled by the callback endpoint (discordRedirect)
144+
controller.discordLogin();
145+
// Verify the method exists and can be called without errors
146+
expect(controller.discordLogin).toBeDefined();
147+
// Verify authService was NOT called (since this is just a guard entry point)
148+
expect(authService.discordLogin).not.toHaveBeenCalled();
131149
});
132150
});
133151

apps/backend/src/song/my-songs/my-songs.controller.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Controller, Get, Query, UseGuards } from '@nestjs/common';
1+
import { Controller, Get, Inject, Query, UseGuards } from '@nestjs/common';
22
import { AuthGuard } from '@nestjs/passport';
33
import { ApiBearerAuth, ApiOperation, ApiTags } from '@nestjs/swagger';
44

@@ -12,7 +12,10 @@ import { SongService } from '../song.service';
1212
@Controller('my-songs')
1313
@ApiTags('song')
1414
export class MySongsController {
15-
constructor(public readonly songService: SongService) {}
15+
constructor(
16+
@Inject(SongService)
17+
public readonly songService: SongService,
18+
) {}
1619

1720
@Get('/')
1821
@ApiOperation({

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

Lines changed: 40 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ const mockSongService = {
3131
getSongEdit: jest.fn(),
3232
patchSong: jest.fn(),
3333
getSongDownloadUrl: jest.fn(),
34+
getSongFileBuffer: jest.fn(),
3435
deleteSong: jest.fn(),
3536
uploadSong: jest.fn(),
3637
getRandomSongs: jest.fn(),
@@ -46,6 +47,9 @@ describe('SongController', () => {
4647
let songService: SongService;
4748

4849
beforeEach(async () => {
50+
// Clear all mocks before each test
51+
jest.clearAllMocks();
52+
4953
const module: TestingModule = await Test.createTestingModule({
5054
controllers: [SongController],
5155
providers: [
@@ -66,8 +70,8 @@ describe('SongController', () => {
6670
songController = module.get<SongController>(SongController);
6771
songService = module.get<SongService>(SongService);
6872

69-
// Clear all mocks
70-
jest.clearAllMocks();
73+
// Verify the service is injected
74+
expect(songController.songService).toBeDefined();
7175
});
7276

7377
it('should be defined', () => {
@@ -79,7 +83,12 @@ describe('SongController', () => {
7983
const query: SongListQueryDTO = { page: 1, limit: 10 };
8084
const songList: SongPreviewDto[] = [];
8185

82-
mockSongService.getSongByPage.mockResolvedValueOnce(songList);
86+
mockSongService.querySongs.mockResolvedValueOnce({
87+
content: songList,
88+
page: 1,
89+
limit: 10,
90+
total: 0,
91+
});
8392

8493
const result = await songController.getSongList(query);
8594

@@ -88,7 +97,7 @@ describe('SongController', () => {
8897
expect(result.page).toBe(1);
8998
expect(result.limit).toBe(10);
9099
expect(result.total).toBe(0);
91-
expect(songService.getSongByPage).toHaveBeenCalled();
100+
expect(songService.querySongs).toHaveBeenCalled();
92101
});
93102

94103
it('should handle search query', async () => {
@@ -357,7 +366,7 @@ describe('SongController', () => {
357366
it('should handle errors', async () => {
358367
const query: SongListQueryDTO = { page: 1, limit: 10 };
359368

360-
mockSongService.getSongByPage.mockRejectedValueOnce(new Error('Error'));
369+
mockSongService.querySongs.mockRejectedValueOnce(new Error('Error'));
361370

362371
await expect(songController.getSongList(query)).rejects.toThrow('Error');
363372
});
@@ -437,7 +446,7 @@ describe('SongController', () => {
437446
expect(result.total).toBe(5);
438447
expect(result.page).toBe(1);
439448
expect(result.limit).toBe(10);
440-
expect(songService.querySongs).toHaveBeenCalledWith(query, q, undefined);
449+
expect(songService.querySongs).toHaveBeenCalledWith(query, q ?? '');
441450
});
442451

443452
it('should handle search with empty query string', async () => {
@@ -457,7 +466,7 @@ describe('SongController', () => {
457466
expect(result).toBeInstanceOf(PageDto);
458467
expect(result.content).toEqual(songList);
459468
expect(result.total).toBe(0);
460-
expect(songService.querySongs).toHaveBeenCalledWith(query, '', undefined);
469+
expect(songService.querySongs).toHaveBeenCalledWith(query, '');
461470
});
462471

463472
it('should handle search with null query string', async () => {
@@ -476,7 +485,7 @@ describe('SongController', () => {
476485

477486
expect(result).toBeInstanceOf(PageDto);
478487
expect(result.content).toEqual(songList);
479-
expect(songService.querySongs).toHaveBeenCalledWith(query, '', undefined);
488+
expect(songService.querySongs).toHaveBeenCalledWith(query, '');
480489
});
481490

482491
it('should handle search with multiple pages', async () => {
@@ -499,7 +508,7 @@ describe('SongController', () => {
499508
expect(result.content).toHaveLength(10);
500509
expect(result.total).toBe(25);
501510
expect(result.page).toBe(2);
502-
expect(songService.querySongs).toHaveBeenCalledWith(query, q, undefined);
511+
expect(songService.querySongs).toHaveBeenCalledWith(query, q ?? '');
503512
});
504513

505514
it('should handle search with large result set', async () => {
@@ -521,7 +530,7 @@ describe('SongController', () => {
521530
expect(result).toBeInstanceOf(PageDto);
522531
expect(result.content).toHaveLength(50);
523532
expect(result.total).toBe(500);
524-
expect(songService.querySongs).toHaveBeenCalledWith(query, q, undefined);
533+
expect(songService.querySongs).toHaveBeenCalledWith(query, q ?? '');
525534
});
526535

527536
it('should handle search on last page with partial results', async () => {
@@ -561,7 +570,7 @@ describe('SongController', () => {
561570
const result = await songController.searchSongs(query, q);
562571

563572
expect(result).toBeInstanceOf(PageDto);
564-
expect(songService.querySongs).toHaveBeenCalledWith(query, q, undefined);
573+
expect(songService.querySongs).toHaveBeenCalledWith(query, q ?? '');
565574
});
566575

567576
it('should handle search with very long query string', async () => {
@@ -579,7 +588,7 @@ describe('SongController', () => {
579588
const result = await songController.searchSongs(query, q);
580589

581590
expect(result).toBeInstanceOf(PageDto);
582-
expect(songService.querySongs).toHaveBeenCalledWith(query, q, undefined);
591+
expect(songService.querySongs).toHaveBeenCalledWith(query, q ?? '');
583592
});
584593

585594
it('should handle search with custom limit', async () => {
@@ -627,7 +636,7 @@ describe('SongController', () => {
627636

628637
expect(result).toBeInstanceOf(PageDto);
629638
expect(result.content).toHaveLength(10);
630-
expect(songService.querySongs).toHaveBeenCalledWith(query, q, undefined);
639+
expect(songService.querySongs).toHaveBeenCalledWith(query, q ?? '');
631640
});
632641

633642
it('should return correct pagination info with search results', async () => {
@@ -699,7 +708,7 @@ describe('SongController', () => {
699708
const result = await songController.searchSongs(query, q);
700709

701710
expect(result).toBeInstanceOf(PageDto);
702-
expect(songService.querySongs).toHaveBeenCalledWith(query, q, undefined);
711+
expect(songService.querySongs).toHaveBeenCalledWith(query, q ?? '');
703712
});
704713
});
705714

@@ -803,23 +812,31 @@ describe('SongController', () => {
803812

804813
const res = {
805814
set: jest.fn(),
806-
redirect: jest.fn(),
815+
send: jest.fn(),
807816
} as unknown as Response;
808817

809-
const url = 'test-url';
818+
const buffer = Buffer.from('test-song-data');
819+
const filename = 'test-song.nbs';
810820

811-
mockSongService.getSongDownloadUrl.mockResolvedValueOnce(url);
821+
mockSongService.getSongFileBuffer.mockResolvedValueOnce({
822+
buffer,
823+
filename,
824+
});
812825

813826
await songController.getSongFile(id, src, user, res);
814827

815828
expect(res.set).toHaveBeenCalledWith({
816-
'Content-Disposition': 'attachment; filename="song.nbs"',
829+
'Content-Type': 'application/octet-stream',
830+
'Content-Disposition': `attachment; filename="${filename.replace(
831+
/[/"]/g,
832+
'_',
833+
)}"`,
817834
'Access-Control-Expose-Headers': 'Content-Disposition',
818835
});
819836

820-
expect(res.redirect).toHaveBeenCalledWith(HttpStatus.FOUND, url);
837+
expect(res.send).toHaveBeenCalledWith(Buffer.from(buffer));
821838

822-
expect(songService.getSongDownloadUrl).toHaveBeenCalledWith(
839+
expect(songService.getSongFileBuffer).toHaveBeenCalledWith(
823840
id,
824841
user,
825842
src,
@@ -836,16 +853,16 @@ describe('SongController', () => {
836853

837854
const res = {
838855
set: jest.fn(),
839-
redirect: jest.fn(),
856+
send: jest.fn(),
840857
} as unknown as Response;
841858

842-
mockSongService.getSongDownloadUrl.mockRejectedValueOnce(
859+
mockSongService.getSongFileBuffer.mockRejectedValueOnce(
843860
new Error('Error'),
844861
);
845862

846863
await expect(
847864
songController.getSongFile(id, src, user, res),
848-
).rejects.toThrow('Error');
865+
).rejects.toThrow('An error occurred while retrieving the song file');
849866
});
850867
});
851868

apps/backend/src/song/song.controller.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {
88
Headers,
99
HttpException,
1010
HttpStatus,
11+
Inject,
1112
Logger,
1213
Param,
1314
Patch,
@@ -65,7 +66,9 @@ export class SongController {
6566
};
6667

6768
constructor(
69+
@Inject(SongService)
6870
public readonly songService: SongService,
71+
@Inject(FileService)
6972
public readonly fileService: FileService,
7073
) {}
7174

0 commit comments

Comments
 (0)