Merge pull request 'Эпик 2 ревью: race-guard в геокодинге ПВЗ + фикс flaky теста' (#2) from chore/review-E2-pvz-fixes into marketplace2
Reviewed-on: #2
This commit was merged in pull request #2.
This commit is contained in:
+9
-1
@@ -101,17 +101,24 @@ export class KuDetailsService {
|
||||
try {
|
||||
const result = await this.geocoder.geocode(addressFull);
|
||||
if (result.status === 'OK') {
|
||||
await this.repo.updateGeocode(coopname, coreBraname, {
|
||||
const saved = await this.repo.updateGeocode(coopname, coreBraname, {
|
||||
status: 'OK',
|
||||
lat: result.lat,
|
||||
lng: result.lng,
|
||||
geocodedAt: new Date(),
|
||||
expectedAddressFull: addressFull,
|
||||
});
|
||||
if (!saved) {
|
||||
this.logger.warn(
|
||||
`Геокодинг (${coopname}, ${coreBraname}) завершён, но адрес был изменён до записи — координаты не сохранены`
|
||||
);
|
||||
}
|
||||
} else {
|
||||
await this.repo.updateGeocode(coopname, coreBraname, {
|
||||
status: 'FAILED',
|
||||
errorMessage: result.errorMessage,
|
||||
geocodedAt: new Date(),
|
||||
expectedAddressFull: addressFull,
|
||||
});
|
||||
}
|
||||
} catch (err: unknown) {
|
||||
@@ -121,6 +128,7 @@ export class KuDetailsService {
|
||||
status: 'FAILED',
|
||||
errorMessage: message,
|
||||
geocodedAt: new Date(),
|
||||
expectedAddressFull: addressFull,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
+7
@@ -12,6 +12,12 @@ export interface KuDetailsDomainRepository {
|
||||
|
||||
save(entity: KuDetailsDomainEntity): Promise<KuDetailsDomainEntity>;
|
||||
|
||||
/**
|
||||
* Записывает результат геокодинга. `expectedAddressFull` — guard от race
|
||||
* condition: если адрес КУ был изменён пока геокодинг работал, результат
|
||||
* не сохраняется (репозиторий возвращает null), и UI не получает
|
||||
* координаты несуществующего адреса.
|
||||
*/
|
||||
updateGeocode(
|
||||
coopname: string,
|
||||
coreBraname: string,
|
||||
@@ -21,6 +27,7 @@ export interface KuDetailsDomainRepository {
|
||||
lng?: number;
|
||||
errorMessage?: string;
|
||||
geocodedAt: Date;
|
||||
expectedAddressFull?: string;
|
||||
}
|
||||
): Promise<KuDetailsDomainEntity | null>;
|
||||
|
||||
|
||||
+5
@@ -62,11 +62,16 @@ export class KuDetailsRepositoryAdapter implements KuDetailsDomainRepository {
|
||||
lng?: number;
|
||||
errorMessage?: string;
|
||||
geocodedAt: Date;
|
||||
expectedAddressFull?: string;
|
||||
}
|
||||
): Promise<KuDetailsDomainEntity | null> {
|
||||
const existing = await this.repo.findOne({ where: { coopname, coreBraname } });
|
||||
if (!existing) return null;
|
||||
|
||||
if (payload.expectedAddressFull !== undefined && existing.addressFull !== payload.expectedAddressFull) {
|
||||
return null;
|
||||
}
|
||||
|
||||
existing.geocodeStatus = payload.status;
|
||||
existing.lat = payload.lat;
|
||||
existing.lng = payload.lng;
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
// Unit-тесты фабрики geocoderPortFactory: выбор реализации по
|
||||
// config.geocoder.provider. Noop-провайдер всегда отвечает FAILED.
|
||||
//
|
||||
// Тест использует jest.resetModules + динамический import для подмены
|
||||
// config.geocoder.provider между кейсами. Из-за этого статический
|
||||
// import класса adapter'а и динамический import factory отдают разные
|
||||
// инстансы конструктора — `instanceof` сравнение не работает. Поэтому
|
||||
// проверяем имя конструктора через `constructor.name`.
|
||||
|
||||
jest.mock('~/config/config', () => ({
|
||||
__esModule: true,
|
||||
@@ -14,9 +20,6 @@ jest.mock('~/config/config', () => ({
|
||||
},
|
||||
}));
|
||||
|
||||
import { NoopGeocoderAdapter } from '~/extensions/marketplace/infrastructure/adapters/noop-geocoder.adapter';
|
||||
import { YandexGeocoderAdapter } from '~/extensions/marketplace/infrastructure/adapters/yandex-geocoder.adapter';
|
||||
|
||||
describe('geocoderPortFactory', () => {
|
||||
afterEach(() => {
|
||||
jest.resetModules();
|
||||
@@ -27,7 +30,7 @@ describe('geocoderPortFactory', () => {
|
||||
'~/extensions/marketplace/infrastructure/adapters/geocoder.factory'
|
||||
);
|
||||
const port = geocoderPortFactory();
|
||||
expect(port).toBeInstanceOf(NoopGeocoderAdapter);
|
||||
expect(port.constructor.name).toBe('NoopGeocoderAdapter');
|
||||
const r = await port.geocode('addr');
|
||||
expect(r.status).toBe('FAILED');
|
||||
if (r.status === 'FAILED') expect(r.errorMessage).toMatch(/GEOCODER_PROVIDER=noop/);
|
||||
@@ -51,6 +54,6 @@ describe('geocoderPortFactory', () => {
|
||||
'~/extensions/marketplace/infrastructure/adapters/geocoder.factory'
|
||||
);
|
||||
const port = geocoderPortFactory();
|
||||
expect(port).toBeInstanceOf(YandexGeocoderAdapter);
|
||||
expect(port.constructor.name).toBe('YandexGeocoderAdapter');
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user