Skip to content

Commit 538982b

Browse files
committed
tests: adds unit tests for media items controller
Signed-off-by: Anthony D. Mays <[email protected]>
1 parent 744540d commit 538982b

File tree

2 files changed

+260
-2
lines changed

2 files changed

+260
-2
lines changed

lesson_26/api/javascript/api_app/src/app.controller.spec.ts renamed to lesson_26/api/javascript/api_app/src/__tests__/app.controller.spec.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { Test, TestingModule } from '@nestjs/testing';
2-
import { AppController } from './app.controller';
3-
import { AppService } from './app.service';
2+
import { AppController } from '../app.controller';
3+
import { AppService } from '../app.service';
44

55
describe('AppController', () => {
66
let appController: AppController;
Lines changed: 258 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,258 @@
1+
import { HttpStatus } from '@nestjs/common';
2+
import { Test, TestingModule } from '@nestjs/testing';
3+
import { Librarian, LibraryService, MediaItem } from '../../library';
4+
import { CreateMediaItemRequest } from '../create_media_item_request';
5+
import { fromMediaItemRequest } from '../media_item_request';
6+
import { toMediaItemResponse } from '../media_item_response';
7+
import { MediaItemsController } from '../media_items.controller';
8+
9+
// Mock dependencies and utils
10+
jest.mock('../media_item_request', () => ({
11+
fromMediaItemRequest: jest.fn(),
12+
}));
13+
14+
jest.mock('../media_item_response', () => ({
15+
toMediaItemResponse: jest.fn(),
16+
}));
17+
18+
describe('MediaItemsController', () => {
19+
let controller: MediaItemsController;
20+
let libraryService: LibraryService;
21+
let mockLibrarian: Librarian;
22+
let mockMediaItem1: MediaItem;
23+
let mockMediaItem2: MediaItem;
24+
25+
beforeEach(async () => {
26+
// Create mock objects
27+
mockLibrarian = new Librarian('Test Librarian', '[email protected]');
28+
29+
mockMediaItem1 = {
30+
getId: jest.fn().mockReturnValue('123'),
31+
getTitle: jest.fn().mockReturnValue('Test Item 1'),
32+
getType: jest.fn().mockReturnValue('movie'),
33+
getReleaseYear: jest.fn().mockReturnValue(2020),
34+
getCredits: jest.fn().mockReturnValue([]),
35+
addCredit: jest.fn(),
36+
} as unknown as MediaItem;
37+
38+
mockMediaItem2 = {
39+
getId: jest.fn().mockReturnValue('456'),
40+
getTitle: jest.fn().mockReturnValue('Test Item 2'),
41+
getType: jest.fn().mockReturnValue('book'),
42+
getReleaseYear: jest.fn().mockReturnValue(2021),
43+
getCredits: jest.fn().mockReturnValue([]),
44+
addCredit: jest.fn(),
45+
} as unknown as MediaItem;
46+
47+
// Mock the library service
48+
const mockLibraryService = {
49+
getLibrarians: jest.fn().mockReturnValue([mockLibrarian]),
50+
search: jest.fn(),
51+
hasMediaItemById: jest.fn(),
52+
addMediaItem: jest.fn(),
53+
removeMediaItemById: jest.fn(),
54+
};
55+
56+
// Create mock response transformer
57+
const mockMediaItemResponse1 = {
58+
id: '123',
59+
title: 'Test Item 1',
60+
type: 'movie',
61+
releaseYear: 2020,
62+
};
63+
64+
const mockMediaItemResponse2 = {
65+
id: '456',
66+
title: 'Test Item 2',
67+
type: 'book',
68+
releaseYear: 2021,
69+
};
70+
71+
(toMediaItemResponse as jest.Mock).mockImplementation((item: MediaItem) => {
72+
if (item.getId() === '123') return mockMediaItemResponse1;
73+
if (item.getId() === '456') return mockMediaItemResponse2;
74+
return null;
75+
});
76+
77+
(fromMediaItemRequest as jest.Mock).mockImplementation((requestItem) => {
78+
if (requestItem.id === '123') return mockMediaItem1;
79+
if (requestItem.id === '456') return mockMediaItem2;
80+
return mockMediaItem1; // Default to first mock item
81+
});
82+
83+
const module: TestingModule = await Test.createTestingModule({
84+
controllers: [MediaItemsController],
85+
providers: [
86+
{
87+
provide: LibraryService,
88+
useValue: mockLibraryService,
89+
},
90+
],
91+
}).compile();
92+
93+
controller = module.get<MediaItemsController>(MediaItemsController);
94+
libraryService = module.get<LibraryService>(LibraryService);
95+
});
96+
97+
afterEach(() => {
98+
jest.clearAllMocks();
99+
});
100+
101+
describe('getItems', () => {
102+
it('should return all media items', () => {
103+
// Arrange
104+
const itemsSet = new Set<MediaItem>([mockMediaItem1, mockMediaItem2]);
105+
(libraryService.search as jest.Mock).mockReturnValue(itemsSet);
106+
107+
// Act
108+
const result = controller.getItems();
109+
110+
// Assert
111+
expect(libraryService.search).toHaveBeenCalledWith({});
112+
expect(toMediaItemResponse).toHaveBeenCalledTimes(2);
113+
expect(result.items).toHaveLength(2);
114+
expect(result).toEqual({
115+
items: [
116+
{ id: '123', title: 'Test Item 1', type: 'movie', releaseYear: 2020 },
117+
{ id: '456', title: 'Test Item 2', type: 'book', releaseYear: 2021 },
118+
],
119+
});
120+
});
121+
122+
it('should return empty array when no items', () => {
123+
// Arrange
124+
const emptySet = new Set<MediaItem>([]);
125+
(libraryService.search as jest.Mock).mockReturnValue(emptySet);
126+
127+
// Act
128+
const result = controller.getItems();
129+
130+
// Assert
131+
expect(libraryService.search).toHaveBeenCalledWith({});
132+
expect(toMediaItemResponse).not.toHaveBeenCalled();
133+
expect(result.items).toHaveLength(0);
134+
expect(result).toEqual({ items: [] });
135+
});
136+
});
137+
138+
describe('getItem', () => {
139+
it('should return a specific media item when found', () => {
140+
// Arrange
141+
const itemsSet = new Set<MediaItem>([mockMediaItem1]);
142+
(libraryService.search as jest.Mock).mockReturnValue(itemsSet);
143+
const mockResponse = { status: jest.fn() };
144+
145+
// Act
146+
const result = controller.getItem('123', mockResponse as any);
147+
148+
// Assert
149+
expect(libraryService.search).toHaveBeenCalledWith({ id: '123' });
150+
expect(toMediaItemResponse).toHaveBeenCalledWith(mockMediaItem1);
151+
expect(mockResponse.status).not.toHaveBeenCalled();
152+
expect(result).toEqual({
153+
id: '123',
154+
title: 'Test Item 1',
155+
type: 'movie',
156+
releaseYear: 2020,
157+
});
158+
});
159+
160+
it('should return 404 when item not found', () => {
161+
// Arrange
162+
const emptySet = new Set<MediaItem>([]);
163+
(libraryService.search as jest.Mock).mockReturnValue(emptySet);
164+
const mockResponse = { status: jest.fn() };
165+
166+
// Act
167+
const result = controller.getItem('999', mockResponse as any);
168+
169+
// Assert
170+
expect(libraryService.search).toHaveBeenCalledWith({ id: '999' });
171+
expect(mockResponse.status).toHaveBeenCalledWith(HttpStatus.NOT_FOUND);
172+
expect(result).toBeUndefined();
173+
});
174+
});
175+
176+
describe('addItem', () => {
177+
it('should successfully add a media item', () => {
178+
// Arrange
179+
const mockRequest: CreateMediaItemRequest = {
180+
item: {
181+
id: '123',
182+
title: 'Test Item 1',
183+
type: 'movie',
184+
},
185+
};
186+
const mockResponse = { status: jest.fn() };
187+
188+
// Act
189+
const result = controller.addItem(mockRequest, mockResponse as any);
190+
191+
// Assert
192+
expect(fromMediaItemRequest).toHaveBeenCalledWith(mockRequest.item);
193+
expect(libraryService.addMediaItem).toHaveBeenCalledWith(
194+
mockMediaItem1,
195+
mockLibrarian,
196+
);
197+
expect(toMediaItemResponse).toHaveBeenCalledWith(mockMediaItem1);
198+
expect(result).toEqual({
199+
item: {
200+
id: '123',
201+
title: 'Test Item 1',
202+
type: 'movie',
203+
releaseYear: 2020,
204+
},
205+
});
206+
});
207+
208+
it('should return errors when item is missing', () => {
209+
// Arrange
210+
const mockRequest = {} as CreateMediaItemRequest;
211+
const mockResponse = { status: jest.fn() };
212+
213+
// Act
214+
const result = controller.addItem(mockRequest, mockResponse as any);
215+
216+
// Assert
217+
expect(mockResponse.status).toHaveBeenCalledWith(HttpStatus.BAD_REQUEST);
218+
expect(libraryService.addMediaItem).not.toHaveBeenCalled();
219+
expect(result).toEqual({ errors: ['Missing item'] });
220+
});
221+
});
222+
223+
describe('deleteItem', () => {
224+
it('should successfully delete an existing media item', () => {
225+
// Arrange
226+
(libraryService.hasMediaItemById as jest.Mock).mockReturnValue(true);
227+
const mockResponse = { status: jest.fn() };
228+
229+
// Act
230+
controller.deleteItem('123', mockResponse as any);
231+
232+
// Assert
233+
expect(libraryService.hasMediaItemById).toHaveBeenCalledWith('123');
234+
expect(libraryService.removeMediaItemById).toHaveBeenCalledWith(
235+
'123',
236+
mockLibrarian,
237+
);
238+
expect(mockResponse.status).not.toHaveBeenCalled();
239+
});
240+
241+
it('should return 404 when trying to delete non-existent item', () => {
242+
// Arrange
243+
(libraryService.hasMediaItemById as jest.Mock).mockReturnValue(false);
244+
const mockResponse = { status: jest.fn() };
245+
246+
// Act
247+
controller.deleteItem('999', mockResponse as any);
248+
249+
// Assert
250+
expect(libraryService.hasMediaItemById).toHaveBeenCalledWith('999');
251+
expect(mockResponse.status).toHaveBeenCalledWith(HttpStatus.NOT_FOUND);
252+
expect(libraryService.removeMediaItemById).toHaveBeenCalledWith(
253+
'999',
254+
mockLibrarian,
255+
);
256+
});
257+
});
258+
});

0 commit comments

Comments
 (0)