#Jest test promise observable with retry delay (RxJS)
6 messages · Page 1 of 1 (latest)
code i try to test:
{ afbeeldingen, voertuignr_hexon }: HexonVehicleData,
queueId: number
): Promise<WooCommerceImage[] | undefined> {
let tempImages = afbeeldingen.afbeelding;
if (!tempImages) return;
if (!Array.isArray(tempImages)) tempImages = [tempImages];
const images = tempImages.map((afbeelding) => afbeelding.url);
let failedImages = 0;
let downloadsFailed = 0;
const imagesObservable = from(images).pipe(
concatMap((image, index) => {
return this.httpService
.get(image, { responseType: 'arraybuffer' })
.pipe(
retry({ count: 5, delay: 3000 }),
map((response) => ({
buffer: Buffer.from(response.data),
mimetype: response.headers['content-type'],
originalname: image.split('/').pop(),
})),
tap(() => {}),
catchError((error) => {
failedImages++;
return of(null);
})
);
}),
filter((file) => file !== null),
toArray(),
map((files) =>
files.sort((a, b) => {
const numberA = Number(a.originalname.split('-').pop().split('.')[0]);
const numberB = Number(b.originalname.split('-').pop().split('.')[0]);
return numberA - numberB;
})
),
tap(() => {
downloadsFailed = failedImages;
}),
concatMap((sortedFiles) => from(sortedFiles)),
concatMap((file, index) => {
return this.wordpressService.uploadMedia(file).pipe(
tap(() => {}),
catchError((error) => {
failedImages++;
return of(null);
})
);
}),
filter((result) => result !== null),
toArray(),
map((results) => results.map((result) => ({ id: result.id }))),
tap(() => {})
);
return firstValueFrom(imagesObservable as Observable<WooCommerceImage[]>);
}
The test i write
it('should skip failed downloads and continue uploading others', async () => {
const vehicleData: HexonVehicleData = {
afbeeldingen: {
afbeelding: [
{ url: 'http://example.com/image-1.jpg' },
{ url: 'http://example.com/image-2.jpg' },
],
},
voertuignr_hexon: 'FAIL123',
} as HexonVehicleData;
const queue: Queue = {
id: 3,
client: 'Client',
vendor: 'Vendor',
} as Queue;
queueRepositoryMock.findOneByOrFail.mockResolvedValue(queue);
jest.useFakeTimers();
httpServiceMock.get.mockImplementation((url) => {
console.log(url, 'url');
// TODO fix timeout
if (url.includes('image-1')) {
return throwError(() => new Error('Download failed'));
}
return of({
data: Buffer.from('image2'),
headers: { 'content-type': 'image/jpeg' },
});
});
wordpressServiceMock.uploadMedia.mockReturnValue(
of({ id: 'uploaded-image-2.jpg' })
);
const setQueueStatusSpy = jest
.spyOn(service, <any>'setQueueStatus')
.mockImplementation();
const result = service['createImages2'](vehicleData, 3);
jest.runAllTicks();
jest.runAllTimers();
await expect(result).resolves.toEqual([{ id: 'uploaded-image-2.jpg' }]);
// expect(result).toEqual([{ id: 'uploaded-image-2.jpg' }]);
expect(setQueueStatusSpy).toHaveBeenCalled();
expect(httpServiceMock.get).toHaveBeenCalledTimes(2);
expect(wordpressServiceMock.uploadMedia).toHaveBeenCalledTimes(1);
});
That's alot for just an example. I think this would be a good opportunity to introduce a Rx Marble test, where you can describe a RxJS Observable using Marble syntax to define expected behavior like nexts, errors and completions.
Is that not only for observables?
Well yes, but you’re turning it into a Promise? You may question yourself why you’re doing that, but also do the same in your test here?