feat: add 5s timeout and warning log for station name search (refs #141)
Add 5000ms timeout to Places Text Search API call in searchStationByName. Timeout errors log a warning instead of error and return null gracefully. Add timeout test case to station-matching unit tests. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -128,6 +128,7 @@ export class GoogleMapsClient {
|
|||||||
type: 'gas_station',
|
type: 'gas_station',
|
||||||
key: this.apiKey,
|
key: this.apiKey,
|
||||||
},
|
},
|
||||||
|
timeout: 5000,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -145,8 +146,12 @@ export class GoogleMapsClient {
|
|||||||
|
|
||||||
await cacheService.set(cacheKey, station, this.cacheTTL);
|
await cacheService.set(cacheKey, station, this.cacheTTL);
|
||||||
return station;
|
return station;
|
||||||
} catch (error) {
|
} catch (error: any) {
|
||||||
|
if (error.code === 'ECONNABORTED' || error.message?.includes('timeout')) {
|
||||||
|
logger.warn('Station name search timed out', { merchantName, timeoutMs: 5000 });
|
||||||
|
} else {
|
||||||
logger.error('Station name search failed', { error, merchantName });
|
logger.error('Station name search failed', { error, merchantName });
|
||||||
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ import { GoogleMapsClient } from '../../external/google-maps/google-maps.client'
|
|||||||
import { StationsService } from '../../domain/stations.service';
|
import { StationsService } from '../../domain/stations.service';
|
||||||
import { StationsRepository } from '../../data/stations.repository';
|
import { StationsRepository } from '../../data/stations.repository';
|
||||||
import { googleMapsClient } from '../../external/google-maps/google-maps.client';
|
import { googleMapsClient } from '../../external/google-maps/google-maps.client';
|
||||||
|
import { logger } from '../../../../core/logging/logger';
|
||||||
import { mockStations } from '../fixtures/mock-stations';
|
import { mockStations } from '../fixtures/mock-stations';
|
||||||
|
|
||||||
describe('Station Matching from Receipt', () => {
|
describe('Station Matching from Receipt', () => {
|
||||||
@@ -162,6 +163,23 @@ describe('Station Matching from Receipt', () => {
|
|||||||
expect(result).toBeNull();
|
expect(result).toBeNull();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should return null with logged warning on Places API timeout', async () => {
|
||||||
|
const timeoutError = new Error('timeout of 5000ms exceeded') as any;
|
||||||
|
timeoutError.code = 'ECONNABORTED';
|
||||||
|
mockAxios.get.mockRejectedValue(timeoutError);
|
||||||
|
|
||||||
|
const mockLogger = logger as jest.Mocked<typeof logger>;
|
||||||
|
|
||||||
|
const result = await client.searchStationByName('Shell');
|
||||||
|
|
||||||
|
expect(result).toBeNull();
|
||||||
|
expect(mockLogger.warn).toHaveBeenCalledWith(
|
||||||
|
'Station name search timed out',
|
||||||
|
expect.objectContaining({ merchantName: 'Shell', timeoutMs: 5000 })
|
||||||
|
);
|
||||||
|
expect(mockLogger.error).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
it('should include rating and photo reference when available', async () => {
|
it('should include rating and photo reference when available', async () => {
|
||||||
mockAxios.get.mockResolvedValue({
|
mockAxios.get.mockResolvedValue({
|
||||||
data: {
|
data: {
|
||||||
|
|||||||
Reference in New Issue
Block a user