Skip to content

Commit ee50d0b

Browse files
chore: stack service unit tests (immich-app#13441)
1 parent b852468 commit ee50d0b

File tree

1 file changed

+193
-0
lines changed

1 file changed

+193
-0
lines changed
+193
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
import { BadRequestException } from '@nestjs/common';
2+
import { IEventRepository } from 'src/interfaces/event.interface';
3+
import { IStackRepository } from 'src/interfaces/stack.interface';
4+
import { StackService } from 'src/services/stack.service';
5+
import { assetStub, stackStub } from 'test/fixtures/asset.stub';
6+
import { authStub } from 'test/fixtures/auth.stub';
7+
import { IAccessRepositoryMock } from 'test/repositories/access.repository.mock';
8+
import { newTestService } from 'test/utils';
9+
import { Mocked } from 'vitest';
10+
11+
describe(StackService.name, () => {
12+
let sut: StackService;
13+
14+
let accessMock: IAccessRepositoryMock;
15+
let eventMock: Mocked<IEventRepository>;
16+
let stackMock: Mocked<IStackRepository>;
17+
18+
beforeEach(() => {
19+
({ sut, accessMock, eventMock, stackMock } = newTestService(StackService));
20+
});
21+
22+
it('should be defined', () => {
23+
expect(sut).toBeDefined();
24+
});
25+
26+
describe('search', () => {
27+
it('should search stacks', async () => {
28+
stackMock.search.mockResolvedValue([stackStub('stack-id', [assetStub.image])]);
29+
30+
await sut.search(authStub.admin, { primaryAssetId: assetStub.image.id });
31+
expect(stackMock.search).toHaveBeenCalledWith({
32+
ownerId: authStub.admin.user.id,
33+
primaryAssetId: assetStub.image.id,
34+
});
35+
});
36+
});
37+
38+
describe('create', () => {
39+
it('should require asset.update permissions', async () => {
40+
await expect(
41+
sut.create(authStub.admin, { assetIds: [assetStub.image.id, assetStub.image1.id] }),
42+
).rejects.toBeInstanceOf(BadRequestException);
43+
44+
expect(accessMock.asset.checkOwnerAccess).toHaveBeenCalled();
45+
expect(stackMock.create).not.toHaveBeenCalled();
46+
});
47+
48+
it('should create a stack', async () => {
49+
accessMock.asset.checkOwnerAccess.mockResolvedValue(new Set([assetStub.image.id, assetStub.image1.id]));
50+
stackMock.create.mockResolvedValue(stackStub('stack-id', [assetStub.image, assetStub.image1]));
51+
await expect(
52+
sut.create(authStub.admin, { assetIds: [assetStub.image.id, assetStub.image1.id] }),
53+
).resolves.toEqual({
54+
id: 'stack-id',
55+
primaryAssetId: assetStub.image.id,
56+
assets: [
57+
expect.objectContaining({ id: assetStub.image.id }),
58+
expect.objectContaining({ id: assetStub.image1.id }),
59+
],
60+
});
61+
62+
expect(eventMock.emit).toHaveBeenCalledWith('stack.create', {
63+
stackId: 'stack-id',
64+
userId: authStub.admin.user.id,
65+
});
66+
expect(accessMock.asset.checkOwnerAccess).toHaveBeenCalled();
67+
});
68+
});
69+
70+
describe('get', () => {
71+
it('should require stack.read permissions', async () => {
72+
await expect(sut.get(authStub.admin, 'stack-id')).rejects.toBeInstanceOf(BadRequestException);
73+
74+
expect(accessMock.stack.checkOwnerAccess).toHaveBeenCalled();
75+
expect(stackMock.getById).not.toHaveBeenCalled();
76+
});
77+
78+
it('should fail if stack could not be found', async () => {
79+
accessMock.stack.checkOwnerAccess.mockResolvedValue(new Set(['stack-id']));
80+
81+
await expect(sut.get(authStub.admin, 'stack-id')).rejects.toBeInstanceOf(Error);
82+
83+
expect(accessMock.stack.checkOwnerAccess).toHaveBeenCalled();
84+
expect(stackMock.getById).toHaveBeenCalledWith('stack-id');
85+
});
86+
87+
it('should get stack', async () => {
88+
accessMock.stack.checkOwnerAccess.mockResolvedValue(new Set(['stack-id']));
89+
stackMock.getById.mockResolvedValue(stackStub('stack-id', [assetStub.image, assetStub.image1]));
90+
91+
await expect(sut.get(authStub.admin, 'stack-id')).resolves.toEqual({
92+
id: 'stack-id',
93+
primaryAssetId: assetStub.image.id,
94+
assets: [
95+
expect.objectContaining({ id: assetStub.image.id }),
96+
expect.objectContaining({ id: assetStub.image1.id }),
97+
],
98+
});
99+
expect(accessMock.stack.checkOwnerAccess).toHaveBeenCalled();
100+
expect(stackMock.getById).toHaveBeenCalledWith('stack-id');
101+
});
102+
});
103+
104+
describe('update', () => {
105+
it('should require stack.update permissions', async () => {
106+
await expect(sut.update(authStub.admin, 'stack-id', {})).rejects.toBeInstanceOf(BadRequestException);
107+
108+
expect(stackMock.getById).not.toHaveBeenCalled();
109+
expect(stackMock.update).not.toHaveBeenCalled();
110+
expect(eventMock.emit).not.toHaveBeenCalled();
111+
});
112+
113+
it('should fail if stack could not be found', async () => {
114+
accessMock.stack.checkOwnerAccess.mockResolvedValue(new Set(['stack-id']));
115+
116+
await expect(sut.update(authStub.admin, 'stack-id', {})).rejects.toBeInstanceOf(Error);
117+
118+
expect(stackMock.getById).toHaveBeenCalledWith('stack-id');
119+
expect(stackMock.update).not.toHaveBeenCalled();
120+
expect(eventMock.emit).not.toHaveBeenCalled();
121+
});
122+
123+
it('should fail if the provided primary asset id is not in the stack', async () => {
124+
accessMock.stack.checkOwnerAccess.mockResolvedValue(new Set(['stack-id']));
125+
stackMock.getById.mockResolvedValue(stackStub('stack-id', [assetStub.image, assetStub.image1]));
126+
127+
await expect(sut.update(authStub.admin, 'stack-id', { primaryAssetId: 'unknown-asset' })).rejects.toBeInstanceOf(
128+
BadRequestException,
129+
);
130+
131+
expect(stackMock.getById).toHaveBeenCalledWith('stack-id');
132+
expect(stackMock.update).not.toHaveBeenCalled();
133+
expect(eventMock.emit).not.toHaveBeenCalled();
134+
});
135+
136+
it('should update stack', async () => {
137+
accessMock.stack.checkOwnerAccess.mockResolvedValue(new Set(['stack-id']));
138+
stackMock.getById.mockResolvedValue(stackStub('stack-id', [assetStub.image, assetStub.image1]));
139+
stackMock.update.mockResolvedValue(stackStub('stack-id', [assetStub.image, assetStub.image1]));
140+
141+
await sut.update(authStub.admin, 'stack-id', { primaryAssetId: assetStub.image1.id });
142+
143+
expect(stackMock.getById).toHaveBeenCalledWith('stack-id');
144+
expect(stackMock.update).toHaveBeenCalledWith({ id: 'stack-id', primaryAssetId: assetStub.image1.id });
145+
expect(eventMock.emit).toHaveBeenCalledWith('stack.update', {
146+
stackId: 'stack-id',
147+
userId: authStub.admin.user.id,
148+
});
149+
});
150+
});
151+
152+
describe('delete', () => {
153+
it('should require stack.delete permissions', async () => {
154+
await expect(sut.delete(authStub.admin, 'stack-id')).rejects.toBeInstanceOf(BadRequestException);
155+
156+
expect(stackMock.delete).not.toHaveBeenCalled();
157+
expect(eventMock.emit).not.toHaveBeenCalled();
158+
});
159+
160+
it('should delete stack', async () => {
161+
accessMock.stack.checkOwnerAccess.mockResolvedValue(new Set(['stack-id']));
162+
163+
await sut.delete(authStub.admin, 'stack-id');
164+
165+
expect(stackMock.delete).toHaveBeenCalledWith('stack-id');
166+
expect(eventMock.emit).toHaveBeenCalledWith('stack.delete', {
167+
stackId: 'stack-id',
168+
userId: authStub.admin.user.id,
169+
});
170+
});
171+
});
172+
173+
describe('deleteAll', () => {
174+
it('should require stack.delete permissions', async () => {
175+
await expect(sut.deleteAll(authStub.admin, { ids: ['stack-id'] })).rejects.toBeInstanceOf(BadRequestException);
176+
177+
expect(stackMock.deleteAll).not.toHaveBeenCalled();
178+
expect(eventMock.emit).not.toHaveBeenCalled();
179+
});
180+
181+
it('should delete all stacks', async () => {
182+
accessMock.stack.checkOwnerAccess.mockResolvedValue(new Set(['stack-id']));
183+
184+
await sut.deleteAll(authStub.admin, { ids: ['stack-id'] });
185+
186+
expect(stackMock.deleteAll).toHaveBeenCalledWith(['stack-id']);
187+
expect(eventMock.emit).toHaveBeenCalledWith('stacks.delete', {
188+
stackIds: ['stack-id'],
189+
userId: authStub.admin.user.id,
190+
});
191+
});
192+
});
193+
});

0 commit comments

Comments
 (0)