Skip to content

Commit e14efe6

Browse files
authored
feat: adds & updates service user profile related tests for error catching (#478)
* adds generic test for external api call error suppression under the service users profiles spec * adds dedicated mailchimp api call fails test for partner access service - requires some extra mocking and minor reorganisation * adds dedicated crisp and mailchimp api call fail tests for create and update user * make sure to await the calls on service user profile methods (probably was not an issue in production but under test the runs could complete before mocked methods were called) * remove the awaits
1 parent bb63d7c commit e14efe6

File tree

4 files changed

+123
-4
lines changed

4 files changed

+123
-4
lines changed

src/partner-access/partner-access.service.spec.ts

+24-3
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { Test, TestingModule } from '@nestjs/testing';
33
import { getRepositoryToken } from '@nestjs/typeorm';
44
import { sub } from 'date-fns';
55
import * as crispApi from 'src/api/crisp/crisp-api';
6+
import * as mailchimpApi from 'src/api/mailchimp/mailchimp-api';
67
import { PartnerEntity } from 'src/entities/partner.entity';
78
import { GetUserDto } from 'src/user/dtos/get-user.dto';
89
import * as profileData from 'src/utils/serviceUserProfiles';
@@ -46,11 +47,12 @@ jest.mock('src/api/crisp/crisp-api', () => ({
4647
getCrispProfileData: jest.fn(),
4748
updateCrispProfileBase: jest.fn(),
4849
updateCrispProfile: jest.fn(),
49-
updateServiceUserProfilesPartnerAccess: jest.fn(),
5050
}));
5151

52-
jest.mock('src/utils/serviceUserProfiles', () => ({
53-
updateServiceUserProfilesPartnerAccess: jest.fn(),
52+
jest.mock('src/api/mailchimp/mailchimp-api', () => ({
53+
createMailchimpMergeField: jest.fn(),
54+
createMailchimpProfile: jest.fn(),
55+
updateMailchimpProfile: jest.fn(),
5456
}));
5557

5658
describe('PartnerAccessService', () => {
@@ -143,6 +145,8 @@ describe('PartnerAccessService', () => {
143145
});
144146
// Mocks that the accesscode already exists
145147
jest.spyOn(repo, 'findOne').mockResolvedValueOnce(mockPartnerAccessEntity);
148+
// Observer on the service user profiles method
149+
jest.spyOn(profileData, 'updateServiceUserProfilesPartnerAccess');
146150

147151
const partnerAccess = await service.assignPartnerAccess(mockUserEntity, '123456');
148152

@@ -175,6 +179,23 @@ describe('PartnerAccessService', () => {
175179
});
176180
});
177181

182+
it('should assign partner access even if mailchimp profile api fails', async () => {
183+
// Mocks that the accesscode already exists
184+
jest.spyOn(repo, 'findOne').mockResolvedValueOnce(mockPartnerAccessEntity);
185+
186+
jest.spyOn(mailchimpApi, 'updateMailchimpProfile').mockImplementationOnce(async () => {
187+
throw new Error('Test throw');
188+
});
189+
190+
const partnerAccess = await service.assignPartnerAccess(mockUserEntity, '123456');
191+
192+
expect(partnerAccess).toEqual({
193+
...mockPartnerAccessEntity,
194+
userId: mockUserEntity.id,
195+
activatedAt: partnerAccess.activatedAt,
196+
});
197+
});
198+
178199
it('should return an error when partner access code has already been used by another user account', async () => {
179200
jest.spyOn(repo, 'findOne').mockResolvedValueOnce({
180201
...mockPartnerAccessEntity,

src/user/user.service.spec.ts

+51
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { HttpException, HttpStatus } from '@nestjs/common';
44
import { Test, TestingModule } from '@nestjs/testing';
55
import { getRepositoryToken } from '@nestjs/typeorm';
66
import { createCrispProfile, updateCrispProfile } from 'src/api/crisp/crisp-api';
7+
import { createMailchimpProfile, updateMailchimpProfile } from 'src/api/mailchimp/mailchimp-api';
78
import { PartnerAccessEntity } from 'src/entities/partner-access.entity';
89
import { PartnerEntity } from 'src/entities/partner.entity';
910
import { SubscriptionUserService } from 'src/subscription-user/subscription-user.service';
@@ -226,6 +227,30 @@ describe('UserService', () => {
226227
{ ...partnerAccessData, therapySessions: [mockTherapySessionDto] },
227228
]);
228229
});
230+
231+
it('should not fail on crisp api call errors', async () => {
232+
const mocked = jest.mocked(createCrispProfile);
233+
mocked.mockRejectedValue(new Error('Crisp API call failed'));
234+
235+
const user = await service.createUser(createUserDto);
236+
237+
expect(mocked).toHaveBeenCalled();
238+
expect(user.user.email).toBe('[email protected]');
239+
240+
mocked.mockReset();
241+
});
242+
243+
it('should not fail on mailchimp api call errors', async () => {
244+
const mocked = jest.mocked(createMailchimpProfile);
245+
mocked.mockRejectedValue(new Error('Mailchimp API call failed'));
246+
247+
const user = await service.createUser(createUserDto);
248+
249+
expect(mocked).toHaveBeenCalled();
250+
expect(user.user.email).toBe('[email protected]');
251+
252+
mocked.mockReset();
253+
});
229254
});
230255

231256
describe('getUser', () => {
@@ -264,6 +289,32 @@ describe('UserService', () => {
264289
expect(repoSaveSpy).toHaveBeenCalledWith({ ...mockUserEntity, ...updateUserDto });
265290
expect(repoSaveSpy).toHaveBeenCalled();
266291
});
292+
293+
it('should not fail on crisp api call errors', async () => {
294+
const mocked = jest.mocked(updateCrispProfile);
295+
mocked.mockRejectedValue(new Error('Crisp API call failed'));
296+
297+
const user = await service.updateUser(updateUserDto, { user: mockUserEntity });
298+
299+
expect(mocked).toHaveBeenCalled();
300+
expect(user.name).toBe('new name');
301+
expect(user.email).toBe('[email protected]');
302+
303+
mocked.mockReset();
304+
});
305+
306+
it('should not fail on mailchimp api call errors', async () => {
307+
const mocked = jest.mocked(updateMailchimpProfile);
308+
mocked.mockRejectedValue(new Error('Mailchimp API call failed'));
309+
310+
const user = await service.updateUser(updateUserDto, { user: mockUserEntity });
311+
312+
expect(mocked).toHaveBeenCalled();
313+
expect(user.name).toBe('new name');
314+
expect(user.email).toBe('[email protected]');
315+
316+
mocked.mockReset();
317+
});
267318
});
268319

269320
describe('deleteUserById', () => {

src/user/user.service.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ export class UserService {
9191
this.logger.log(`Create user: created public user in db. User: ${email}`);
9292
}
9393

94-
createServiceUserProfiles(user, partner, partnerAccess);
94+
await createServiceUserProfiles(user, partner, partnerAccess);
9595

9696
const userDto = formatUserObject({
9797
...user,

src/utils/serviceUserProfiles.spec.ts

+47
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,15 @@ describe('Service user profiles', () => {
129129
},
130130
});
131131
});
132+
133+
it('should not propagate external api call errors', async () => {
134+
const mocked = jest.mocked(createCrispProfile);
135+
mocked.mockRejectedValue(new Error('Crisp API call failed'));
136+
await expect(createServiceUserProfiles(mockUserEntity)).resolves.not.toThrow();
137+
mocked.mockReset();
138+
});
132139
});
140+
133141
describe('updateServiceUserProfilesUser', () => {
134142
it('should update crisp and mailchimp profile user data', async () => {
135143
await updateServiceUserProfilesUser(mockUserEntity, false, mockUserEntity.email);
@@ -209,6 +217,15 @@ describe('Service user profiles', () => {
209217
mockUserEntity.email,
210218
);
211219
});
220+
221+
it('should not propagate external api call errors', async () => {
222+
const mocked = jest.mocked(updateMailchimpProfile);
223+
mocked.mockRejectedValue(new Error('Mailchimp API call failed'));
224+
await expect(
225+
updateServiceUserProfilesUser(mockUserEntity, false, mockUserEntity.email),
226+
).resolves.not.toThrow();
227+
mocked.mockReset();
228+
});
212229
});
213230

214231
describe('updateServiceUserProfilesPartnerAccess', () => {
@@ -286,6 +303,15 @@ describe('Service user profiles', () => {
286303
mockUserEntity.email,
287304
);
288305
});
306+
307+
it('should not propagate external api call errors', async () => {
308+
const mocked = jest.mocked(updateCrispProfile);
309+
mocked.mockRejectedValue(new Error('Crisp API call failed'));
310+
await expect(
311+
updateServiceUserProfilesPartnerAccess([mockPartnerAccessEntity], mockUserEntity.email),
312+
).resolves.not.toThrow();
313+
mocked.mockReset();
314+
});
289315
});
290316

291317
describe('updateServiceUserProfilesTherapy', () => {
@@ -441,6 +467,18 @@ describe('Service user profiles', () => {
441467
mockUserEntity.email,
442468
);
443469
});
470+
471+
it('should not propagate external api call errors', async () => {
472+
const mocked = jest.mocked(updateMailchimpProfile);
473+
mocked.mockRejectedValue(new Error('Mailchimp API call failed'));
474+
await expect(
475+
updateServiceUserProfilesTherapy(
476+
[mockPartnerAccessEntity, mockAltPartnerAccessEntity],
477+
mockUserEntity.email,
478+
),
479+
).resolves.not.toThrow();
480+
mocked.mockReset();
481+
});
444482
});
445483

446484
describe('updateServiceUserProfilesCourse', () => {
@@ -465,6 +503,15 @@ describe('Service user profiles', () => {
465503
mockUserEntity.email,
466504
);
467505
});
506+
507+
it('should not propagate external api call errors', async () => {
508+
const mocked = jest.mocked(updateCrispProfile);
509+
mocked.mockRejectedValue(new Error('Crisp API call failed'));
510+
await expect(
511+
updateServiceUserProfilesCourse(mockCourseUserEntity, mockUserEntity.email),
512+
).resolves.not.toThrow();
513+
mocked.mockReset();
514+
});
468515
});
469516

470517
describe('createMailchimpCourseMergeField', () => {

0 commit comments

Comments
 (0)