feat(sync): trust Hiboutik answers for deletions

A successful (2xx) answer from Hiboutik is now the truth, even an empty
list: records missing from it are deleted. The "empty list means API
issue" safety nets are removed.

Deletion stays safe because a failed call deletes nothing: HTTP errors
throw, and the new HiboutikClient.getList (used by getAllPages and the
non-paginated lists) also throws on a 2xx body that is not a list, so a
malformed answer is never mistaken for an empty list. A 2xx without body
is an empty list.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
MastersGame
2026-11-08 10:11:34 +01:00
parent 8b341c4933
commit 30c56f9330
12 changed files with 115 additions and 36 deletions

View File

@@ -89,12 +89,21 @@ describe('ProductHiboutikService', () => {
expect(calls[0]).toBe('deleteProducts');
});
it('does not delete anything when Hiboutik returns no products', async () => {
it('deletes every product when Hiboutik answers with an empty list', async () => {
productHiboutikRepository.getProducts.mockResolvedValue([]);
productDatabaseRepository.getAll.mockResolvedValue([{ id: 'uuid-1', hiboutikId: 1 }]);
await service.syncProducts();
expect(productDatabaseRepository.deleteAll).toHaveBeenCalledWith(['uuid-1']);
});
it('deletes nothing when the Hiboutik call fails', async () => {
productHiboutikRepository.getProducts.mockRejectedValue(new Error('Request failed with status code 500'));
productDatabaseRepository.getAll.mockResolvedValue([{ id: 'uuid-1', hiboutikId: 1 }]);
await expect(service.syncProducts()).rejects.toThrow('500');
expect(productDatabaseRepository.deleteAll).not.toHaveBeenCalled();
});