@volontariapp/domain-user 2.3.5-snap-8a230de → 2.3.5-snap-7ff4433

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (104) hide show
  1. package/CHANGELOG.md +2 -0
  2. package/dist/entities/user.entity.d.ts +3 -1
  3. package/dist/entities/user.entity.d.ts.map +1 -1
  4. package/dist/entities/user.entity.js +41 -0
  5. package/dist/entities/user.entity.js.map +1 -1
  6. package/dist/models/mapper.d.ts +2 -0
  7. package/dist/models/mapper.d.ts.map +1 -0
  8. package/dist/models/mapper.js +36 -0
  9. package/dist/models/mapper.js.map +1 -0
  10. package/dist/models/user.model.js +3 -3
  11. package/dist/models/user.model.js.map +1 -1
  12. package/dist/repositories/index.d.ts +4 -0
  13. package/dist/repositories/index.d.ts.map +1 -0
  14. package/dist/repositories/index.js +4 -0
  15. package/dist/repositories/index.js.map +1 -0
  16. package/dist/repositories/interfaces/badge.repository.d.ts +11 -0
  17. package/dist/repositories/interfaces/badge.repository.d.ts.map +1 -0
  18. package/dist/repositories/interfaces/badge.repository.js +2 -0
  19. package/dist/repositories/interfaces/badge.repository.js.map +1 -0
  20. package/dist/repositories/interfaces/index.d.ts +3 -0
  21. package/dist/repositories/interfaces/index.d.ts.map +1 -0
  22. package/dist/repositories/interfaces/index.js +3 -0
  23. package/dist/repositories/interfaces/index.js.map +1 -0
  24. package/dist/repositories/interfaces/user.repository.d.ts +16 -0
  25. package/dist/repositories/interfaces/user.repository.d.ts.map +1 -0
  26. package/dist/repositories/interfaces/user.repository.js +2 -0
  27. package/dist/repositories/interfaces/user.repository.js.map +1 -0
  28. package/dist/repositories/postgres-badge.repository.d.ts +12 -0
  29. package/dist/repositories/postgres-badge.repository.d.ts.map +1 -0
  30. package/dist/repositories/postgres-badge.repository.js +38 -0
  31. package/dist/repositories/postgres-badge.repository.js.map +1 -0
  32. package/dist/repositories/postgres-user.repository.d.ts +23 -0
  33. package/dist/repositories/postgres-user.repository.d.ts.map +1 -0
  34. package/dist/repositories/postgres-user.repository.js +96 -0
  35. package/dist/repositories/postgres-user.repository.js.map +1 -0
  36. package/dist/services/auth.service.d.ts +29 -0
  37. package/dist/services/auth.service.d.ts.map +1 -0
  38. package/dist/services/auth.service.js +93 -0
  39. package/dist/services/auth.service.js.map +1 -0
  40. package/dist/services/badge.service.d.ts +15 -0
  41. package/dist/services/badge.service.d.ts.map +1 -0
  42. package/dist/services/badge.service.js +139 -0
  43. package/dist/services/badge.service.js.map +1 -0
  44. package/dist/services/user.service.d.ts +19 -0
  45. package/dist/services/user.service.d.ts.map +1 -0
  46. package/dist/services/user.service.js +188 -0
  47. package/dist/services/user.service.js.map +1 -0
  48. package/dist/test/__test-utils__/factories/badge.factory.d.ts +7 -0
  49. package/dist/test/__test-utils__/factories/badge.factory.d.ts.map +1 -0
  50. package/dist/test/__test-utils__/factories/badge.factory.js +29 -0
  51. package/dist/test/__test-utils__/factories/badge.factory.js.map +1 -0
  52. package/dist/test/__test-utils__/factories/commands.factory.d.ts +6 -0
  53. package/dist/test/__test-utils__/factories/commands.factory.d.ts.map +1 -0
  54. package/dist/test/__test-utils__/factories/commands.factory.js +24 -0
  55. package/dist/test/__test-utils__/factories/commands.factory.js.map +1 -0
  56. package/dist/test/__test-utils__/factories/user.factory.d.ts +8 -0
  57. package/dist/test/__test-utils__/factories/user.factory.d.ts.map +1 -0
  58. package/dist/test/__test-utils__/factories/user.factory.js +42 -0
  59. package/dist/test/__test-utils__/factories/user.factory.js.map +1 -0
  60. package/dist/test/__test-utils__/mocks/badge.repository.mock.d.ts +4 -0
  61. package/dist/test/__test-utils__/mocks/badge.repository.mock.d.ts.map +1 -0
  62. package/dist/test/__test-utils__/mocks/badge.repository.mock.js +11 -0
  63. package/dist/test/__test-utils__/mocks/badge.repository.mock.js.map +1 -0
  64. package/dist/test/__test-utils__/mocks/user.repository.mock.d.ts +4 -0
  65. package/dist/test/__test-utils__/mocks/user.repository.mock.d.ts.map +1 -0
  66. package/dist/test/__test-utils__/mocks/user.repository.mock.js +16 -0
  67. package/dist/test/__test-utils__/mocks/user.repository.mock.js.map +1 -0
  68. package/dist/test/data-source.d.ts +8 -0
  69. package/dist/test/data-source.d.ts.map +1 -0
  70. package/dist/test/data-source.js +36 -0
  71. package/dist/test/data-source.js.map +1 -0
  72. package/dist/test/integration/postgres-badge.repository.int.spec.d.ts +2 -0
  73. package/dist/test/integration/postgres-badge.repository.int.spec.d.ts.map +1 -0
  74. package/dist/test/integration/postgres-badge.repository.int.spec.js +129 -0
  75. package/dist/test/integration/postgres-badge.repository.int.spec.js.map +1 -0
  76. package/dist/test/integration/postgres-user.repository.int.spec.d.ts +2 -0
  77. package/dist/test/integration/postgres-user.repository.int.spec.d.ts.map +1 -0
  78. package/dist/test/integration/postgres-user.repository.int.spec.js +222 -0
  79. package/dist/test/integration/postgres-user.repository.int.spec.js.map +1 -0
  80. package/dist/test/migrations/1776334421317-InitialUserSchema.d.ts +7 -0
  81. package/dist/test/migrations/1776334421317-InitialUserSchema.d.ts.map +1 -0
  82. package/dist/test/migrations/1776334421317-InitialUserSchema.js +58 -0
  83. package/dist/test/migrations/1776334421317-InitialUserSchema.js.map +1 -0
  84. package/dist/test/setup.d.ts +2 -0
  85. package/dist/test/setup.d.ts.map +1 -0
  86. package/dist/test/setup.js +2 -0
  87. package/dist/test/setup.js.map +1 -0
  88. package/dist/test/unit/auth.service.unit.spec.d.ts +2 -0
  89. package/dist/test/unit/auth.service.unit.spec.d.ts.map +1 -0
  90. package/dist/test/unit/auth.service.unit.spec.js +236 -0
  91. package/dist/test/unit/auth.service.unit.spec.js.map +1 -0
  92. package/dist/test/unit/badge.service.unit.spec.d.ts +2 -0
  93. package/dist/test/unit/badge.service.unit.spec.d.ts.map +1 -0
  94. package/dist/test/unit/badge.service.unit.spec.js +120 -0
  95. package/dist/test/unit/badge.service.unit.spec.js.map +1 -0
  96. package/dist/test/unit/mapper.unit.spec.d.ts +2 -0
  97. package/dist/test/unit/mapper.unit.spec.d.ts.map +1 -0
  98. package/dist/test/unit/mapper.unit.spec.js +358 -0
  99. package/dist/test/unit/mapper.unit.spec.js.map +1 -0
  100. package/dist/test/unit/user.service.unit.spec.d.ts +2 -0
  101. package/dist/test/unit/user.service.unit.spec.d.ts.map +1 -0
  102. package/dist/test/unit/user.service.unit.spec.js +480 -0
  103. package/dist/test/unit/user.service.unit.spec.js.map +1 -0
  104. package/package.json +16 -4
@@ -0,0 +1,236 @@
1
+ import 'reflect-metadata';
2
+ import { jest, describe, it, expect, beforeEach, afterEach } from '@jest/globals';
3
+ import { AuthService } from '../../services/auth.service.js';
4
+ import { Logger } from '@volontariapp/logger';
5
+ import { createUserRepositoryMock } from '../__test-utils__/mocks/user.repository.mock.js';
6
+ import { UserFactory } from '../__test-utils__/factories/user.factory.js';
7
+ import { CommandsFactory } from '../__test-utils__/factories/commands.factory.js';
8
+ import { hashPassword } from '@volontariapp/crypto';
9
+ describe('AuthService (Unit)', () => {
10
+ let service;
11
+ let mockRepository;
12
+ let jwtServiceMock;
13
+ let loggerWarnSpy;
14
+ let loggerErrorSpy;
15
+ beforeEach(() => {
16
+ mockRepository = createUserRepositoryMock();
17
+ jwtServiceMock = {
18
+ signAccessToken: jest.fn().mockResolvedValue('access-token'),
19
+ signRefreshToken: jest
20
+ .fn()
21
+ .mockResolvedValue('refresh-token'),
22
+ };
23
+ service = new AuthService(mockRepository, jwtServiceMock);
24
+ loggerWarnSpy = jest
25
+ .spyOn(Logger.prototype, 'warn')
26
+ .mockImplementation(() => undefined);
27
+ loggerErrorSpy = jest
28
+ .spyOn(Logger.prototype, 'error')
29
+ .mockImplementation(() => undefined);
30
+ });
31
+ afterEach(() => {
32
+ jest.restoreAllMocks();
33
+ });
34
+ describe('signUp()', () => {
35
+ describe('HAPPY PATH: User registered successfully', () => {
36
+ it('should create user and return tokens when valid command provided', async () => {
37
+ const command = CommandsFactory.buildSignUpCommand({
38
+ email: 'neworg@example.com',
39
+ password: 'SecurePass123!',
40
+ pseudo: 'neworg',
41
+ });
42
+ const createdUser = UserFactory.build({
43
+ email: 'neworg@example.com',
44
+ pseudo: 'neworg',
45
+ });
46
+ mockRepository.findByEmail.mockResolvedValue(null);
47
+ mockRepository.createWithHashedPassword.mockResolvedValue(createdUser);
48
+ const result = await service.signUp(command);
49
+ expect(result).toEqual({ accessToken: 'access-token', refreshToken: 'refresh-token' });
50
+ expect(mockRepository.findByEmail).toHaveBeenCalledWith('neworg@example.com');
51
+ expect(mockRepository.createWithHashedPassword).toHaveBeenCalledTimes(1);
52
+ expect(jwtServiceMock.signAccessToken).toHaveBeenCalled();
53
+ });
54
+ it('should create organization user when RNA is provided', async () => {
55
+ const command = CommandsFactory.buildSignUpCommand({
56
+ organisationInfo: { rna: 'W123456789' },
57
+ });
58
+ const createdUser = UserFactory.build({ rna: 'W123456789' });
59
+ mockRepository.findByEmail.mockResolvedValue(null);
60
+ mockRepository.createWithHashedPassword.mockResolvedValue(createdUser);
61
+ const result = await service.signUp(command);
62
+ expect(result).toEqual({ accessToken: 'access-token', refreshToken: 'refresh-token' });
63
+ expect(createdUser.rna).toBe('W123456789');
64
+ expect(mockRepository.createWithHashedPassword).toHaveBeenCalled();
65
+ });
66
+ it('should create volunteer user when no RNA is provided', async () => {
67
+ const command = CommandsFactory.buildSignUpCommand({
68
+ organisationInfo: undefined,
69
+ });
70
+ const createdUser = UserFactory.build({ rna: undefined });
71
+ mockRepository.findByEmail.mockResolvedValue(null);
72
+ mockRepository.createWithHashedPassword.mockResolvedValue(createdUser);
73
+ const result = await service.signUp(command);
74
+ expect(result).toEqual({ accessToken: 'access-token', refreshToken: 'refresh-token' });
75
+ expect(createdUser.rna).toBeUndefined();
76
+ expect(jwtServiceMock.signAccessToken).toHaveBeenCalled();
77
+ });
78
+ });
79
+ describe('SAD PATH: User already exists', () => {
80
+ it('should throw CONFLICT error when email is already registered', async () => {
81
+ const command = CommandsFactory.buildSignUpCommand({ email: 'existing@example.com' });
82
+ const existingUser = UserFactory.build({ email: 'existing@example.com' });
83
+ mockRepository.findByEmail.mockResolvedValue(existingUser);
84
+ await expect(service.signUp(command)).rejects.toMatchObject({
85
+ code: 'CONFLICT',
86
+ });
87
+ expect(loggerWarnSpy).toHaveBeenCalled();
88
+ expect(mockRepository.createWithHashedPassword).not.toHaveBeenCalled();
89
+ });
90
+ });
91
+ describe('SAD PATH: Validation fails', () => {
92
+ it('should throw BAD_REQUEST when RNA format is invalid', async () => {
93
+ const command = CommandsFactory.buildSignUpCommand({
94
+ organisationInfo: { rna: 'INVALID-RNA' },
95
+ });
96
+ mockRepository.findByEmail.mockResolvedValue(null);
97
+ await expect(service.signUp(command)).rejects.toMatchObject({
98
+ code: 'BAD_REQUEST',
99
+ });
100
+ expect(mockRepository.createWithHashedPassword).not.toHaveBeenCalled();
101
+ });
102
+ it('should throw BAD_REQUEST when RNA lacks W prefix', async () => {
103
+ const command = CommandsFactory.buildSignUpCommand({
104
+ organisationInfo: { rna: '123456789' },
105
+ });
106
+ mockRepository.findByEmail.mockResolvedValue(null);
107
+ await expect(service.signUp(command)).rejects.toMatchObject({
108
+ code: 'BAD_REQUEST',
109
+ });
110
+ expect(mockRepository.createWithHashedPassword).not.toHaveBeenCalled();
111
+ });
112
+ });
113
+ describe('ERROR HANDLING: Duplicate email (DB constraint)', () => {
114
+ it('should throw CONFLICT error on unique constraint violation (code 23505)', async () => {
115
+ const command = CommandsFactory.buildSignUpCommand({ email: 'duplicate@example.com' });
116
+ mockRepository.findByEmail.mockResolvedValue(null);
117
+ const dbError = new Error('unique constraint violation');
118
+ Object.defineProperty(dbError, 'code', { value: '23505' });
119
+ mockRepository.createWithHashedPassword.mockRejectedValue(dbError);
120
+ await expect(service.signUp(command)).rejects.toMatchObject({
121
+ code: 'CONFLICT',
122
+ });
123
+ });
124
+ });
125
+ describe('ERROR HANDLING: Database failures', () => {
126
+ it('should throw DATABASE_ERROR on unexpected repository error', async () => {
127
+ const command = CommandsFactory.buildSignUpCommand();
128
+ mockRepository.findByEmail.mockResolvedValue(null);
129
+ mockRepository.createWithHashedPassword.mockRejectedValue(new Error('Connection timeout'));
130
+ await expect(service.signUp(command)).rejects.toMatchObject({
131
+ code: 'DATABASE_ERROR',
132
+ });
133
+ expect(loggerErrorSpy).toHaveBeenCalled();
134
+ });
135
+ it('should log error details when repository fails', async () => {
136
+ const command = CommandsFactory.buildSignUpCommand();
137
+ mockRepository.findByEmail.mockResolvedValue(null);
138
+ const dbError = new Error('Network unreachable');
139
+ mockRepository.createWithHashedPassword.mockRejectedValue(dbError);
140
+ await service.signUp(command).catch(() => { });
141
+ expect(loggerErrorSpy).toHaveBeenCalledWith(expect.stringContaining('Error while creating user'));
142
+ });
143
+ });
144
+ });
145
+ describe('logIn()', () => {
146
+ describe('HAPPY PATH: User authenticated successfully', () => {
147
+ it('should return tokens when credentials are valid', async () => {
148
+ const command = CommandsFactory.buildLoginCommand({
149
+ email: 'user@example.com',
150
+ password: 'CorrectPassword123!',
151
+ });
152
+ const user = UserFactory.build({ email: 'user@example.com' });
153
+ const passwordHash = hashPassword('CorrectPassword123!');
154
+ mockRepository.findByEmail.mockResolvedValue(user);
155
+ mockRepository.findPasswordHashByEmail.mockResolvedValue(passwordHash);
156
+ const result = await service.logIn(command);
157
+ expect(result).toEqual({ accessToken: 'access-token', refreshToken: 'refresh-token' });
158
+ expect(mockRepository.findByEmail).toHaveBeenCalledWith('user@example.com');
159
+ expect(mockRepository.findPasswordHashByEmail).toHaveBeenCalledWith('user@example.com');
160
+ expect(jwtServiceMock.signAccessToken).toHaveBeenCalled();
161
+ });
162
+ });
163
+ describe('SAD PATH: User not found', () => {
164
+ it('should throw NOT_FOUND when user does not exist', async () => {
165
+ const command = CommandsFactory.buildLoginCommand({ email: 'nonexistent@example.com' });
166
+ mockRepository.findByEmail.mockResolvedValue(null);
167
+ await expect(service.logIn(command)).rejects.toMatchObject({
168
+ code: 'NOT_FOUND',
169
+ });
170
+ expect(loggerWarnSpy).toHaveBeenCalled();
171
+ expect(mockRepository.findPasswordHashByEmail).not.toHaveBeenCalled();
172
+ });
173
+ });
174
+ describe('SAD PATH: Invalid credentials', () => {
175
+ it('should throw NOT_FOUND when password hash is missing', async () => {
176
+ const command = CommandsFactory.buildLoginCommand({ email: 'user@example.com' });
177
+ const user = UserFactory.build({ email: 'user@example.com' });
178
+ mockRepository.findByEmail.mockResolvedValue(user);
179
+ mockRepository.findPasswordHashByEmail.mockResolvedValue(null);
180
+ await expect(service.logIn(command)).rejects.toMatchObject({
181
+ code: 'NOT_FOUND',
182
+ });
183
+ expect(loggerWarnSpy).toHaveBeenCalledWith(expect.stringContaining('Invalid password attempt'));
184
+ });
185
+ it('should throw NOT_FOUND when password is incorrect', async () => {
186
+ const command = CommandsFactory.buildLoginCommand({
187
+ email: 'user@example.com',
188
+ password: 'WrongPassword',
189
+ });
190
+ const user = UserFactory.build({ email: 'user@example.com' });
191
+ const correctPasswordHash = hashPassword('CorrectPassword');
192
+ mockRepository.findByEmail.mockResolvedValue(user);
193
+ mockRepository.findPasswordHashByEmail.mockResolvedValue(correctPasswordHash);
194
+ await expect(service.logIn(command)).rejects.toMatchObject({
195
+ code: 'NOT_FOUND',
196
+ });
197
+ expect(loggerWarnSpy).toHaveBeenCalledWith(expect.stringContaining('Invalid password attempt'));
198
+ });
199
+ it('should return same error type for both missing user and wrong password', async () => {
200
+ const nonExistentCommand = CommandsFactory.buildLoginCommand({
201
+ email: 'nonexistent@example.com',
202
+ });
203
+ const wrongPasswordCommand = CommandsFactory.buildLoginCommand({
204
+ email: 'user@example.com',
205
+ password: 'WrongPassword',
206
+ });
207
+ const user = UserFactory.build({ email: 'user@example.com' });
208
+ const correctHash = hashPassword('CorrectPassword');
209
+ mockRepository.findByEmail.mockResolvedValueOnce(null);
210
+ await expect(service.logIn(nonExistentCommand)).rejects.toMatchObject({
211
+ code: 'NOT_FOUND',
212
+ });
213
+ mockRepository.findByEmail.mockResolvedValueOnce(user);
214
+ mockRepository.findPasswordHashByEmail.mockResolvedValue(correctHash);
215
+ await expect(service.logIn(wrongPasswordCommand)).rejects.toMatchObject({
216
+ code: 'NOT_FOUND',
217
+ });
218
+ });
219
+ });
220
+ describe('ERROR HANDLING: Database failures', () => {
221
+ it('should bubble up error when finding user fails', async () => {
222
+ const command = CommandsFactory.buildLoginCommand();
223
+ mockRepository.findByEmail.mockRejectedValue(new Error('Connection failed'));
224
+ await expect(service.logIn(command)).rejects.toThrow('Connection failed');
225
+ });
226
+ it('should bubble up error when finding password hash fails', async () => {
227
+ const command = CommandsFactory.buildLoginCommand();
228
+ const user = UserFactory.build();
229
+ mockRepository.findByEmail.mockResolvedValue(user);
230
+ mockRepository.findPasswordHashByEmail.mockRejectedValue(new Error('DB lost'));
231
+ await expect(service.logIn(command)).rejects.toThrow('DB lost');
232
+ });
233
+ });
234
+ });
235
+ });
236
+ //# sourceMappingURL=auth.service.unit.spec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.service.unit.spec.js","sourceRoot":"","sources":["../../../src/test/unit/auth.service.unit.spec.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=badge.service.unit.spec.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"badge.service.unit.spec.d.ts","sourceRoot":"","sources":["../../../src/test/unit/badge.service.unit.spec.ts"],"names":[],"mappings":""}
@@ -0,0 +1,120 @@
1
+ import { describe, it, expect, beforeEach } from '@jest/globals';
2
+ import { BadgeService } from '../../services/badge.service.js';
3
+ import { BadgeFactory } from '../__test-utils__/factories/badge.factory.js';
4
+ import { createBadgeRepositoryMock } from '../__test-utils__/mocks/badge.repository.mock.js';
5
+ describe('BadgeService (Unit)', () => {
6
+ let service;
7
+ let mockRepository;
8
+ beforeEach(() => {
9
+ mockRepository = createBadgeRepositoryMock();
10
+ service = new BadgeService(mockRepository);
11
+ });
12
+ describe('findById()', () => {
13
+ it('should return badge when found', async () => {
14
+ const badge = BadgeFactory.build({ id: 'b-1' });
15
+ mockRepository.findById.mockResolvedValue(badge);
16
+ const result = await service.findById('b-1');
17
+ expect(result).toEqual(badge);
18
+ expect(mockRepository.findById.mock.calls).toEqual([['b-1']]);
19
+ });
20
+ it('should throw BADGE_NOT_FOUND when not found', async () => {
21
+ mockRepository.findById.mockResolvedValue(null);
22
+ await expect(service.findById('missing')).rejects.toMatchObject({ code: 'NOT_FOUND' });
23
+ });
24
+ it('should throw DATABASE_ERROR when repository throws', async () => {
25
+ mockRepository.findById.mockRejectedValue(new Error('fail'));
26
+ await expect(service.findById('b-1')).rejects.toMatchObject({ code: 'DATABASE_ERROR' });
27
+ });
28
+ });
29
+ describe('findBySlug()', () => {
30
+ it('should return badge when found by slug', async () => {
31
+ const badge = BadgeFactory.build({ slug: 's-1' });
32
+ mockRepository.findBySlug.mockResolvedValue(badge);
33
+ const result = await service.findBySlug('s-1');
34
+ expect(result).toEqual(badge);
35
+ expect(mockRepository.findBySlug.mock.calls).toEqual([['s-1']]);
36
+ });
37
+ it('should throw BADGE_NOT_FOUND when missing', async () => {
38
+ mockRepository.findBySlug.mockResolvedValue(null);
39
+ await expect(service.findBySlug('missing')).rejects.toMatchObject({ code: 'NOT_FOUND' });
40
+ });
41
+ });
42
+ describe('findAll()', () => {
43
+ it('should return all badges', async () => {
44
+ const badges = BadgeFactory.buildMany(3);
45
+ mockRepository.findAll.mockResolvedValue(badges);
46
+ const result = await service.findAll();
47
+ expect(result).toEqual(badges);
48
+ });
49
+ it('should throw DATABASE_ERROR when repository fails', async () => {
50
+ mockRepository.findAll.mockRejectedValue(new Error('fail'));
51
+ await expect(service.findAll()).rejects.toMatchObject({ code: 'DATABASE_ERROR' });
52
+ });
53
+ });
54
+ describe('findManyByIds()', () => {
55
+ it('should return badges by ids', async () => {
56
+ const badges = BadgeFactory.buildMany(2);
57
+ mockRepository.findManyByIds.mockResolvedValue(badges);
58
+ const result = await service.findManyByIds(['a', 'b']);
59
+ expect(result).toEqual(badges);
60
+ expect(mockRepository.findManyByIds.mock.calls).toEqual([[['a', 'b']]]);
61
+ });
62
+ it('should throw error when repository fails', async () => {
63
+ mockRepository.findManyByIds.mockRejectedValue(new Error('fail'));
64
+ await expect(service.findManyByIds(['a'])).rejects.toThrow('fail');
65
+ });
66
+ });
67
+ describe('create()', () => {
68
+ it('should create and return badge', async () => {
69
+ const input = BadgeFactory.buildInput();
70
+ const created = BadgeFactory.build({ ...input });
71
+ mockRepository.create.mockResolvedValue(created);
72
+ const result = await service.create(input);
73
+ expect(result).toEqual(created);
74
+ expect(mockRepository.create.mock.calls).toEqual([[input]]);
75
+ });
76
+ it('should throw BADGE_ALREADY_EXISTS on duplicate slug', async () => {
77
+ const input = BadgeFactory.buildInput();
78
+ mockRepository.create.mockRejectedValue({ code: '23505' });
79
+ await expect(service.create(input)).rejects.toMatchObject({ code: 'CONFLICT' });
80
+ });
81
+ it('should throw DATABASE_ERROR when repository fails', async () => {
82
+ const input = BadgeFactory.buildInput();
83
+ mockRepository.create.mockRejectedValue(new Error('fail'));
84
+ await expect(service.create(input)).rejects.toMatchObject({ code: 'DATABASE_ERROR' });
85
+ });
86
+ });
87
+ describe('update()', () => {
88
+ it('should update and return badge', async () => {
89
+ const updated = BadgeFactory.build({ id: 'b-1', name: 'New' });
90
+ mockRepository.update.mockResolvedValue(updated);
91
+ const result = await service.update('b-1', { name: 'New' });
92
+ expect(result).toEqual(updated);
93
+ expect(mockRepository.update.mock.calls).toEqual([['b-1', { name: 'New' }]]);
94
+ });
95
+ it('should throw BADGE_NOT_FOUND when repository returns null', async () => {
96
+ mockRepository.update.mockResolvedValue(null);
97
+ await expect(service.update('missing', {})).rejects.toMatchObject({ code: 'NOT_FOUND' });
98
+ });
99
+ it('should throw DATABASE_ERROR when repository fails', async () => {
100
+ mockRepository.update.mockRejectedValue(new Error('fail'));
101
+ await expect(service.update('b-1', {})).rejects.toMatchObject({ code: 'DATABASE_ERROR' });
102
+ });
103
+ });
104
+ describe('delete()', () => {
105
+ it('should delete badge when repository returns true', async () => {
106
+ mockRepository.delete.mockResolvedValue(true);
107
+ await service.delete('b-1');
108
+ expect(mockRepository.delete.mock.calls).toEqual([['b-1']]);
109
+ });
110
+ it('should throw BADGE_NOT_FOUND when repository returns false', async () => {
111
+ mockRepository.delete.mockResolvedValue(false);
112
+ await expect(service.delete('missing')).rejects.toMatchObject({ code: 'NOT_FOUND' });
113
+ });
114
+ it('should throw DATABASE_ERROR when repository fails', async () => {
115
+ mockRepository.delete.mockRejectedValue(new Error('fail'));
116
+ await expect(service.delete('b-1')).rejects.toMatchObject({ code: 'DATABASE_ERROR' });
117
+ });
118
+ });
119
+ });
120
+ //# sourceMappingURL=badge.service.unit.spec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"badge.service.unit.spec.js","sourceRoot":"","sources":["../../../src/test/unit/badge.service.unit.spec.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AACjE,OAAO,EAAE,YAAY,EAAE,MAAM,iCAAiC,CAAC;AAE/D,OAAO,EAAE,YAAY,EAAE,MAAM,8CAA8C,CAAC;AAC5E,OAAO,EAAE,yBAAyB,EAAE,MAAM,kDAAkD,CAAC;AAE7F,QAAQ,CAAC,qBAAqB,EAAE,GAAG,EAAE;IACnC,IAAI,OAAqB,CAAC;IAC1B,IAAI,cAA6C,CAAC;IAElD,UAAU,CAAC,GAAG,EAAE;QACd,cAAc,GAAG,yBAAyB,EAAE,CAAC;QAC7C,OAAO,GAAG,IAAI,YAAY,CAAC,cAAc,CAAC,CAAC;IAC7C,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,YAAY,EAAE,GAAG,EAAE;QAC1B,EAAE,CAAC,gCAAgC,EAAE,KAAK,IAAI,EAAE;YAC9C,MAAM,KAAK,GAAG,YAAY,CAAC,KAAK,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC;YAChD,cAAc,CAAC,QAAQ,CAAC,iBAAiB,CAAC,KAAK,CAAC,CAAC;YAEjD,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;YAE7C,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;YAC9B,MAAM,CAAC,cAAc,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;QAChE,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,6CAA6C,EAAE,KAAK,IAAI,EAAE;YAC3D,cAAc,CAAC,QAAQ,CAAC,iBAAiB,CAAC,IAAI,CAAC,CAAC;YAChD,MAAM,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,aAAa,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,CAAC,CAAC;QACzF,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,oDAAoD,EAAE,KAAK,IAAI,EAAE;YAClE,cAAc,CAAC,QAAQ,CAAC,iBAAiB,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC;YAC7D,MAAM,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,aAAa,CAAC,EAAE,IAAI,EAAE,gBAAgB,EAAE,CAAC,CAAC;QAC1F,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,cAAc,EAAE,GAAG,EAAE;QAC5B,EAAE,CAAC,wCAAwC,EAAE,KAAK,IAAI,EAAE;YACtD,MAAM,KAAK,GAAG,YAAY,CAAC,KAAK,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;YAClD,cAAc,CAAC,UAAU,CAAC,iBAAiB,CAAC,KAAK,CAAC,CAAC;YACnD,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;YAC/C,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;YAC9B,MAAM,CAAC,cAAc,CAAC,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;QAClE,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,2CAA2C,EAAE,KAAK,IAAI,EAAE;YACzD,cAAc,CAAC,UAAU,CAAC,iBAAiB,CAAC,IAAI,CAAC,CAAC;YAClD,MAAM,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,aAAa,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,CAAC,CAAC;QAC3F,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,WAAW,EAAE,GAAG,EAAE;QACzB,EAAE,CAAC,0BAA0B,EAAE,KAAK,IAAI,EAAE;YACxC,MAAM,MAAM,GAAG,YAAY,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;YACzC,cAAc,CAAC,OAAO,CAAC,iBAAiB,CAAC,MAAM,CAAC,CAAC;YACjD,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,OAAO,EAAE,CAAC;YACvC,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;QACjC,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,mDAAmD,EAAE,KAAK,IAAI,EAAE;YACjE,cAAc,CAAC,OAAO,CAAC,iBAAiB,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC;YAC5D,MAAM,MAAM,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC,OAAO,CAAC,aAAa,CAAC,EAAE,IAAI,EAAE,gBAAgB,EAAE,CAAC,CAAC;QACpF,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,iBAAiB,EAAE,GAAG,EAAE;QAC/B,EAAE,CAAC,6BAA6B,EAAE,KAAK,IAAI,EAAE;YAC3C,MAAM,MAAM,GAAG,YAAY,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;YACzC,cAAc,CAAC,aAAa,CAAC,iBAAiB,CAAC,MAAM,CAAC,CAAC;YACvD,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,aAAa,CAAC,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC;YACvD,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;YAC/B,MAAM,CAAC,cAAc,CAAC,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;QAC1E,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,0CAA0C,EAAE,KAAK,IAAI,EAAE;YACxD,cAAc,CAAC,aAAa,CAAC,iBAAiB,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC;YAClE,MAAM,MAAM,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;QACrE,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,UAAU,EAAE,GAAG,EAAE;QACxB,EAAE,CAAC,gCAAgC,EAAE,KAAK,IAAI,EAAE;YAC9C,MAAM,KAAK,GAAG,YAAY,CAAC,UAAU,EAAE,CAAC;YACxC,MAAM,OAAO,GAAG,YAAY,CAAC,KAAK,CAAC,EAAE,GAAG,KAAK,EAAE,CAAC,CAAC;YACjD,cAAc,CAAC,MAAM,CAAC,iBAAiB,CAAC,OAAO,CAAC,CAAC;YAEjD,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YAC3C,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;YAChC,MAAM,CAAC,cAAc,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;QAC9D,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,qDAAqD,EAAE,KAAK,IAAI,EAAE;YACnE,MAAM,KAAK,GAAG,YAAY,CAAC,UAAU,EAAE,CAAC;YACxC,cAAc,CAAC,MAAM,CAAC,iBAAiB,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC;YAC3D,MAAM,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,aAAa,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC,CAAC;QAClF,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,mDAAmD,EAAE,KAAK,IAAI,EAAE;YACjE,MAAM,KAAK,GAAG,YAAY,CAAC,UAAU,EAAE,CAAC;YACxC,cAAc,CAAC,MAAM,CAAC,iBAAiB,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC;YAC3D,MAAM,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,aAAa,CAAC,EAAE,IAAI,EAAE,gBAAgB,EAAE,CAAC,CAAC;QACxF,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,UAAU,EAAE,GAAG,EAAE;QACxB,EAAE,CAAC,gCAAgC,EAAE,KAAK,IAAI,EAAE;YAC9C,MAAM,OAAO,GAAG,YAAY,CAAC,KAAK,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;YAC/D,cAAc,CAAC,MAAM,CAAC,iBAAiB,CAAC,OAAO,CAAC,CAAC;YACjD,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,MAAM,CAAC,KAAK,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;YAC5D,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;YAChC,MAAM,CAAC,cAAc,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC;QAC/E,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,2DAA2D,EAAE,KAAK,IAAI,EAAE;YACzE,cAAc,CAAC,MAAM,CAAC,iBAAiB,CAAC,IAAI,CAAC,CAAC;YAC9C,MAAM,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,aAAa,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,CAAC,CAAC;QAC3F,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,mDAAmD,EAAE,KAAK,IAAI,EAAE;YACjE,cAAc,CAAC,MAAM,CAAC,iBAAiB,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC;YAC3D,MAAM,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,aAAa,CAAC,EAAE,IAAI,EAAE,gBAAgB,EAAE,CAAC,CAAC;QAC5F,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,UAAU,EAAE,GAAG,EAAE;QACxB,EAAE,CAAC,kDAAkD,EAAE,KAAK,IAAI,EAAE;YAChE,cAAc,CAAC,MAAM,CAAC,iBAAiB,CAAC,IAAI,CAAC,CAAC;YAC9C,MAAM,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YAC5B,MAAM,CAAC,cAAc,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;QAC9D,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,4DAA4D,EAAE,KAAK,IAAI,EAAE;YAC1E,cAAc,CAAC,MAAM,CAAC,iBAAiB,CAAC,KAAK,CAAC,CAAC;YAC/C,MAAM,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,aAAa,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,CAAC,CAAC;QACvF,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,mDAAmD,EAAE,KAAK,IAAI,EAAE;YACjE,cAAc,CAAC,MAAM,CAAC,iBAAiB,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC;YAC3D,MAAM,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,aAAa,CAAC,EAAE,IAAI,EAAE,gBAAgB,EAAE,CAAC,CAAC;QACxF,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=mapper.unit.spec.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mapper.unit.spec.d.ts","sourceRoot":"","sources":["../../../src/test/unit/mapper.unit.spec.ts"],"names":[],"mappings":""}