Files
dinggadingga-backend/src/models/converter/customerAddress.converter.spec.ts
MastersGame 35a7c27cb7 feat(sync): validate Hiboutik payloads, read French dates and guard deletions
- Hiboutik payloads are zod schemas, validated by HiboutikClient (getOne
  added for single resources: null on 404 or empty answer).
- Hiboutik dates are read as Europe/Paris whatever the server time zone.
- Paginated lists driving deletions are walked twice when they span several
  pages (brands, categories, products); categories are sorted by id.
- A customer missing from the list is deleted only once Hiboutik confirms
  it; completed orders that lost their customer are linked back.
- Each product and its versions are written in one transaction.
- An email removed in Hiboutik is erased in database.
- A 429 is retried with backoff, then stops the synchronization.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-08 12:20:58 +01:00

80 lines
2.7 KiB
TypeScript

import { CustomerAddressConverter } from './customerAddress.converter';
import { CustomerAddressHiboutikDto } from '../hiboutikDTO/customer.hiboutik.dto';
const hiboutikAddress = (overrides: Partial<CustomerAddressHiboutikDto> = {}): CustomerAddressHiboutikDto => ({
address_id: 7,
customers_id: 42,
first_name: 'Jane',
last_name: 'Doe',
company: 'ACME',
phone: '0600000000',
address: ' 1 rue de la Paix ',
zip_code: '75002',
city: 'Paris',
state: '',
country: 'FRA',
other: 'Bât. B',
default: 1,
...overrides,
});
describe('CustomerAddressConverter', () => {
const converter = new CustomerAddressConverter();
it('maps a Hiboutik address', () => {
expect(converter.fromHiboutik(hiboutikAddress())).toEqual({
address: '1 rue de la Paix',
city: 'Paris',
company: 'ACME',
country: 'FRA',
firstName: 'Jane',
hiboutikId: 7,
isDefault: true,
lastName: 'Doe',
other: 'Bât. B',
phone: '0600000000',
state: null,
zipCode: '75002',
customer: { connect: { hiboutikId: 42 } },
});
});
it('erases empty optional fields (null) and defaults empty required ones to ""', () => {
const address = converter.fromHiboutik(hiboutikAddress({ company: ' ', phone: '', other: '', first_name: '' }));
expect(address.company).toBeNull();
expect(address.phone).toBeNull();
expect(address.other).toBeNull();
expect(address.firstName).toBe('');
});
describe('company details', () => {
const details = { tax_number: ' FR123 ', company_number: '12345678900012', legal_status: 'SAS', updated_at: '2026-11-01 10:00:00' };
it('maps them when fetched for an address with a company', () => {
const address = converter.fromHiboutik({ ...hiboutikAddress(), ...details });
expect(address.taxNumber).toBe('FR123');
expect(address.companyNumber).toBe('12345678900012');
expect(address.legalStatus).toBe('SAS');
});
it('erases empty fetched values (null)', () => {
const address = converter.fromHiboutik({ ...hiboutikAddress(), ...details, tax_number: '' });
expect(address.taxNumber).toBeNull();
});
it('keeps the stored ones when not fetched (undefined)', () => {
const address = converter.fromHiboutik(hiboutikAddress());
expect(address.taxNumber).toBeUndefined();
expect(address.companyNumber).toBeUndefined();
expect(address.legalStatus).toBeUndefined();
});
it('erases them when the address has no company (null)', () => {
const address = converter.fromHiboutik({ ...hiboutikAddress({ company: '' }), ...details });
expect(address.taxNumber).toBeNull();
expect(address.companyNumber).toBeNull();
expect(address.legalStatus).toBeNull();
});
});
});