From 2a60ee41537bfe66054318780edc5236eb2de296 Mon Sep 17 00:00:00 2001 From: Tim van Oostrom Date: Tue, 17 Dec 2024 13:09:23 +0100 Subject: [PATCH] Rename file, add tests --- .../adoptable-trash-containers.test.ts | 4 +- .../services/adoptable-trash-containers.ts | 2 +- src/server/services/afval/afval.ts | 2 +- src/server/services/controller.ts | 2 +- src/server/services/my-locations.test.ts | 293 ++++++++++++++++++ .../services/{home.ts => my-locations.ts} | 78 ++--- src/server/services/wior.ts | 2 +- 7 files changed, 324 insertions(+), 59 deletions(-) create mode 100644 src/server/services/my-locations.test.ts rename src/server/services/{home.ts => my-locations.ts} (61%) diff --git a/src/server/services/adoptable-trash-containers.test.ts b/src/server/services/adoptable-trash-containers.test.ts index 1865e1b169..241772baf0 100644 --- a/src/server/services/adoptable-trash-containers.test.ts +++ b/src/server/services/adoptable-trash-containers.test.ts @@ -4,7 +4,7 @@ import { forTesting } from './adoptable-trash-containers'; import { fetchAdoptableTrashContainers } from './adoptable-trash-containers'; import { fetchBRP } from './brp'; import { fetchDataset } from './buurt/buurt'; -import { fetchMyLocation } from './home'; +import { fetchMyLocation } from './my-locations'; import { generateRandomPoints, getAuthProfileAndToken, @@ -19,7 +19,7 @@ vi.mock('./brp', () => ({ fetchBRP: vi.fn(), })); -vi.mock('./home', () => ({ +vi.mock('./my-locations', () => ({ fetchMyLocation: vi.fn(), })); diff --git a/src/server/services/adoptable-trash-containers.ts b/src/server/services/adoptable-trash-containers.ts index 273daed82b..62c29af8eb 100644 --- a/src/server/services/adoptable-trash-containers.ts +++ b/src/server/services/adoptable-trash-containers.ts @@ -13,7 +13,7 @@ import { filterFeaturesinRadius, getBboxFromFeatures, } from './buurt/helpers'; -import { fetchMyLocation } from './home'; +import { fetchMyLocation } from './my-locations'; import { AppRoutes } from '../../universal/config/routes'; import { Themas } from '../../universal/config/thema'; import { diff --git a/src/server/services/afval/afval.ts b/src/server/services/afval/afval.ts index e0d2ef9172..3c23e52d15 100644 --- a/src/server/services/afval/afval.ts +++ b/src/server/services/afval/afval.ts @@ -3,7 +3,7 @@ import { apiSuccessResult, } from '../../../universal/helpers/api'; import { AuthProfileAndToken } from '../../auth/auth-types'; -import { fetchMyLocation } from '../home'; +import { fetchMyLocation } from '../my-locations'; import { fetchAfvalpuntenByLatLng } from './afvalpunten'; import { fetchAfvalwijzer } from './afvalwijzer'; diff --git a/src/server/services/controller.ts b/src/server/services/controller.ts index a5c0c1c9f0..8bc047d20d 100644 --- a/src/server/services/controller.ts +++ b/src/server/services/controller.ts @@ -22,7 +22,7 @@ import { fetchBRP } from './brp'; import { fetchCMSCONTENT } from './cms-content'; import { fetchMaintenanceNotificationsActual } from './cms-maintenance-notifications'; import { fetchHLI } from './hli/hli'; -import { fetchMyLocation } from './home'; +import { fetchMyLocation } from './my-locations'; import { fetchHorecaVergunningen } from './horeca'; import { fetchAllKlachten } from './klachten/klachten'; import { fetchKrefia } from './krefia'; diff --git a/src/server/services/my-locations.test.ts b/src/server/services/my-locations.test.ts new file mode 100644 index 0000000000..5a8aee4125 --- /dev/null +++ b/src/server/services/my-locations.test.ts @@ -0,0 +1,293 @@ +import { describe, it, expect, vi, Mock } from 'vitest'; + +import { fetchBAG } from './bag'; +import { fetchBRP } from './brp'; +import { fetchKVK, getKvkAddresses } from './kvk'; +import { fetchMyLocation, forTesting } from './my-locations'; +import { getAuthProfileAndToken } from '../../testing/utils'; +import { apiSuccessResult, apiErrorResult } from '../../universal/helpers/api'; +import { Adres } from '../../universal/types'; + +vi.mock('./brp', () => ({ + fetchBRP: vi.fn(), +})); + +vi.mock('./bag', () => ({ + fetchBAG: vi.fn(), +})); + +vi.mock('./kvk', async (importOriginal) => ({ + ...(await importOriginal()), + getKvkAddresses: vi.fn(), + fetchKVK: vi.fn(), +})); + +const adres = { straatnaam: 'address1' } as Adres; +const adres2 = { straatnaam: 'address1' } as Adres; + +describe('fetchPrivate', () => { + const requestID = 'test-request-id'; + const authProfileAndToken = getAuthProfileAndToken(); + + it('should return private addresses if fetching BRP data is successful', async () => { + (fetchBRP as Mock).mockResolvedValueOnce( + apiSuccessResult({ + mokum: true, + adres, + }) + ); + + (fetchBAG as Mock).mockResolvedValueOnce( + apiSuccessResult({ latlng: { lat: 1, lng: 1 }, address: 'Een adres' }) + ); + + const result = await forTesting.fetchPrivate( + requestID, + authProfileAndToken + ); + + expect(fetchBAG).toHaveBeenCalledWith(requestID, adres); + + expect(result.status).toBe('OK'); + expect(result.content).toHaveLength(1); + expect(result.content).toEqual([ + { + address: 'Een adres', + latlng: { + lat: 1, + lng: 1, + }, + profileType: 'private', + }, + ]); + }); + + it('should return default location if no BAG location is found', async () => { + (fetchBRP as Mock).mockResolvedValueOnce( + apiSuccessResult({ + mokum: true, + adres, + }) + ); + + (fetchBAG as Mock).mockResolvedValueOnce( + apiSuccessResult({ latlng: null, address: null }) + ); + + const result = await forTesting.fetchPrivate( + requestID, + authProfileAndToken + ); + + expect(result).toStrictEqual({ + content: [ + { + address: null, + latlng: { + lat: 52.3676842478192, + lng: 4.90022569871861, + }, + profileType: 'private', + }, + ], + status: 'OK', + }); + }); + + it('should return a bare response if BRP data is not a Mokum address', async () => { + (fetchBRP as Mock).mockResolvedValueOnce( + apiSuccessResult({ mokum: false, adres: null }) + ); + + const result = await forTesting.fetchPrivate( + requestID, + authProfileAndToken + ); + + expect(result.status).toBe('OK'); + expect(result.content).toHaveLength(1); + expect(result.content).toEqual([ + { + address: null, + latlng: null, + profileType: 'private', + }, + ]); + }); + + it('should return an error if fetching BRP data fails', async () => { + (fetchBRP as Mock).mockResolvedValueOnce( + apiErrorResult('Error fetching BRP data', null) + ); + + const result = await forTesting.fetchPrivate( + requestID, + authProfileAndToken + ); + expect(result.status === 'DEPENDENCY_ERROR' && result.message).toBe( + '[BRP] Error fetching BRP data' + ); + }); +}); + +describe('fetchCommercial', () => { + const requestID = 'test-request-id'; + const authProfileAndToken = getAuthProfileAndToken('commercial'); + + it('should return commercial addresses if fetching KVK data is successful', async () => { + (fetchKVK as Mock).mockResolvedValueOnce( + apiSuccessResult({ vestigingen: [adres, adres2] }) + ); + + (getKvkAddresses as Mock).mockReturnValueOnce([adres, adres2]); + + (fetchBAG as Mock).mockResolvedValueOnce( + apiSuccessResult({ latlng: { lat: 1, lng: 1 }, address: 'Een adres' }) + ); + (fetchBAG as Mock).mockResolvedValueOnce( + apiSuccessResult({ latlng: { lat: 1, lng: 1 }, address: 'Een 2e adres' }) + ); + + const result = await forTesting.fetchCommercial( + requestID, + authProfileAndToken + ); + + expect(fetchBAG).toHaveBeenCalledWith(requestID, adres); + expect(fetchBAG).toHaveBeenCalledWith(requestID, adres2); + + expect(result).toMatchInlineSnapshot(` + { + "content": [ + { + "address": "Een adres", + "latlng": { + "lat": 1, + "lng": 1, + }, + "profileType": "commercial", + }, + { + "address": "Een 2e adres", + "latlng": { + "lat": 1, + "lng": 1, + }, + "profileType": "commercial", + }, + ], + "status": "OK", + } + `); + }); + + it('should return an error if fetching KVK data fails', async () => { + (fetchKVK as Mock).mockResolvedValueOnce( + apiErrorResult('Error fetching KVK data', null) + ); + + const result = await forTesting.fetchCommercial( + requestID, + authProfileAndToken + ); + expect(result.status).toBe('DEPENDENCY_ERROR'); + }); +}); + +describe('fetchMyLocation', () => { + const requestID = 'test-request-id'; + const authProfileAndTokenPrivate = getAuthProfileAndToken(); + const authProfileAndTokenCommercial = getAuthProfileAndToken('commercial'); + + it('should return locations if fetching commercial and private data is successful', async () => { + (fetchBRP as Mock).mockResolvedValueOnce( + apiSuccessResult({ + mokum: true, + adres, + }) + ); + + (fetchBAG as Mock).mockResolvedValueOnce( + apiSuccessResult({ latlng: { lat: 1, lng: 1 }, address: 'Een adres' }) + ); + + (fetchBAG as Mock).mockResolvedValueOnce( + apiSuccessResult({ latlng: { lat: 2, lng: 2 }, address: 'Een 2e adres' }) + ); + + (fetchKVK as Mock).mockResolvedValueOnce( + apiSuccessResult({ vestigingen: [adres2] }) + ); + + (getKvkAddresses as Mock).mockReturnValueOnce([adres2]); + + const result = await fetchMyLocation(requestID, authProfileAndTokenPrivate); + expect(result).toEqual({ + content: [ + { + address: 'Een 2e adres', + latlng: { + lat: 2, + lng: 2, + }, + profileType: 'private', + }, + { + address: 'Een adres', + latlng: { + lat: 1, + lng: 1, + }, + profileType: 'commercial', + }, + ], + status: 'OK', + }); + }); + + it('should return commercial locations if profile type is commercial', async () => { + (fetchBAG as Mock).mockResolvedValueOnce( + apiSuccessResult({ latlng: { lat: 2, lng: 2 }, address: 'Een 2e adres' }) + ); + + (fetchKVK as Mock).mockResolvedValueOnce( + apiSuccessResult({ vestigingen: [adres2] }) + ); + + (getKvkAddresses as Mock).mockReturnValueOnce([adres2]); + + const result = await fetchMyLocation( + requestID, + authProfileAndTokenCommercial + ); + expect(result.status).toBe('OK'); + expect(result.content).toHaveLength(1); + }); + + it('should return an error if fetching commercial data fails', async () => { + (fetchKVK as Mock).mockResolvedValueOnce( + apiErrorResult('Server down', null) + ); + + const result = await fetchMyLocation( + requestID, + authProfileAndTokenCommercial + ); + expect(result.status).toBe('DEPENDENCY_ERROR'); + }); + + it('should return an error if no locations found', async () => { + (fetchKVK as Mock).mockResolvedValueOnce( + apiSuccessResult({ vestigingen: [] }) + ); + (getKvkAddresses as Mock).mockReturnValueOnce([]); + (fetchBRP as Mock).mockResolvedValueOnce(apiErrorResult('mwa', null)); + + const result = await fetchMyLocation(requestID, authProfileAndTokenPrivate); + expect(result).toEqual({ + content: null, + message: 'Could not fetch locations.', + status: 'ERROR', + }); + }); +}); diff --git a/src/server/services/home.ts b/src/server/services/my-locations.ts similarity index 61% rename from src/server/services/home.ts rename to src/server/services/my-locations.ts index 373770c331..5072d60582 100644 --- a/src/server/services/home.ts +++ b/src/server/services/my-locations.ts @@ -61,10 +61,10 @@ async function fetchPrivate( async function fetchCommercial( requestID: RequestID, authProfileAndToken: AuthProfileAndToken -) { +): Promise> { const KVK = await fetchKVK(requestID, authProfileAndToken); - let MY_LOCATION: ApiResponse<(BAGData | null)[] | null>; + let MY_LOCATION: ApiResponse; if (KVK.status === 'OK') { const addresses: Adres[] = getKvkAddresses(KVK.content); @@ -73,13 +73,15 @@ async function fetchCommercial( const locations = await Promise.all( addresses.map((address) => fetchBAG(requestID, address)) ).then((results) => { - return results.map((result) => - result.content !== null - ? Object.assign(result.content, { - profileType: 'commercial', - }) - : null - ); + return results + .map((result) => + result.content !== null + ? Object.assign(result.content, { + profileType: 'commercial', + }) + : null + ) + .filter((location) => location !== null); }); MY_LOCATION = apiSuccessResult(locations); @@ -100,63 +102,33 @@ export async function fetchMyLocation( requestID: RequestID, authProfileAndToken: AuthProfileAndToken ): Promise> { - - const {content: commercialAddresses} = await fetchCommercial( + const commercialResponse = await fetchCommercial( requestID, authProfileAndToken ); - + if (authProfileAndToken.profile.profileType === 'commercial') { - return apiSuccessResult((commercialAddresses || []).filter((location) => location !== null)) + return commercialResponse; } - - const {content: privateAddresses} = await fetchPrivate( + + const { content: privateAddresses } = await fetchPrivate( requestID, authProfileAndToken ); - + const locations: BAGData[] = [ ...(privateAddresses || []), - ...(commercialAddresses || []), - ].filter((location) => location !== null) - + ...(commercialResponse.content || []), + ].filter((location) => location !== null); + if (locations.length === 0) { return apiErrorResult('Could not fetch locations.', null); } - return apiSuccessResult(locations) + return apiSuccessResult(locations); } - requestID: RequestID, - authProfileAndToken: AuthProfileAndToken -): Promise> { - switch (authProfileAndToken.profile.profileType) { - case 'commercial': - return fetchCommercial(requestID, authProfileAndToken); - - case 'private': - default: { - const privateAddresses = await fetchPrivate( - requestID, - authProfileAndToken - ); - const commercialAddresses = await fetchCommercial( - requestID, - authProfileAndToken - ); - switch (true) { - case privateAddresses.content !== null && - commercialAddresses.content !== null: { - const locations: BAGData[] = [ - ...privateAddresses.content!.filter((a) => a !== null), - ...commercialAddresses.content!.filter((a) => a !== null), - ]; - return apiSuccessResult(locations); - } - case privateAddresses.content !== null: - return privateAddresses; - } - return apiErrorResult('Could not fetch locations.', null); - } - } -} +export const forTesting = { + fetchPrivate, + fetchCommercial, +}; diff --git a/src/server/services/wior.ts b/src/server/services/wior.ts index 142855be67..18ea6ebe98 100644 --- a/src/server/services/wior.ts +++ b/src/server/services/wior.ts @@ -14,7 +14,7 @@ import { filterFeaturesinRadius, getBboxFromFeatures, } from './buurt/helpers'; -import { fetchMyLocation } from './home'; +import { fetchMyLocation } from './my-locations'; import { AppRoutes } from '../../universal/config/routes'; const WITHIN_RADIUS_KM = 1;