diff --git a/le-taxi-api-node.js/src/features/latestTaxiPositions/latestTaxiPosition.mapper.ts b/le-taxi-api-node.js/src/features/latestTaxiPositions/latestTaxiPosition.mapper.ts index 8bbdeec..9527c02 100644 --- a/le-taxi-api-node.js/src/features/latestTaxiPositions/latestTaxiPosition.mapper.ts +++ b/le-taxi-api-node.js/src/features/latestTaxiPositions/latestTaxiPosition.mapper.ts @@ -75,6 +75,7 @@ class LatestTaxiPositionMapper { } if ( taxiSummary.isMpv && + !taxiSummary.isSpecialNeedVehicle && userModel.minivan_booking_inquiries_starts_at && userModel.minivan_booking_inquiries_starts_at < now ) { diff --git a/le-taxi-api-node.js/src/features/shared/caching/modelMapCache.test.ts b/le-taxi-api-node.js/src/features/shared/caching/modelMapCache.test.ts index c719415..e0378a0 100644 --- a/le-taxi-api-node.js/src/features/shared/caching/modelMapCache.test.ts +++ b/le-taxi-api-node.js/src/features/shared/caching/modelMapCache.test.ts @@ -47,6 +47,36 @@ describe('ModelMapCache cache', () => { assert.strictEqual(mapInCache['three'], 3); }); + it('Can update a cached value', async () => { + const mockRepo = { number: { one: 1 } }; + const mockRepoWithCaching = ModelMapCache.createFromSingle( + async key => { + return new Promise(async (resolve, reject) => { + resolve(mockRepo[key]); + }); + }, + { + maxCapacity: cacheMaximumCapacity, + maxAge: cacheExpirationInSeconds * 1000 + } + ); + + const number = await mockRepoWithCaching.getByKey('number'); + assert.deepEqual(number, { one: 1 }); + + mockRepo['number'] = { one: 2 }; + await aFewSeconds(delayToWaitForCacheToExpireInSeconds); + + const numberUpdated = await mockRepoWithCaching.getByKey('number'); + assert.deepEqual(numberUpdated, { one: 2 }); + + mockRepo['number'] = { one: null }; + await aFewSeconds(delayToWaitForCacheToExpireInSeconds); + + const numberNullified = await mockRepoWithCaching.getByKey('number'); + assert.deepEqual(numberNullified, { one: null }); + }); + it('Can use getByKey() on cache fed by single accessor', async () => { const mockRepo = { one: 1, two: 2, three: 3 }; const mockRepoWithCaching = buildMockRepoWithSingleCaching(mockRepo); @@ -136,6 +166,38 @@ describe('ModelMapCache cache', () => { assert.strictEqual(mapExpired['three'], undefined); }); + it('Should clear entities after expiration (when hit)', async () => { + const expirationForTestInSec = 3; + const mockRepo = { one: 1 }; + const mockRepoWithCaching = ModelMapCache.createFromSingle( + async key => { + return new Promise(async (resolve, reject) => { + resolve(mockRepo[key]); + }); + }, + { + maxCapacity: cacheMaximumCapacity, + maxAge: expirationForTestInSec * 1000 + } + ); + + const one = await mockRepoWithCaching.getByKey('one'); + assert.strictEqual(one, 1); + delete mockRepo['one']; + + await aFewSeconds(expirationForTestInSec / 3); + const oneFirstHit = await mockRepoWithCaching.getByKey('one'); + assert.strictEqual(oneFirstHit, 1); + + await aFewSeconds(expirationForTestInSec / 3); + const oneSecondHit = await mockRepoWithCaching.getByKey('one'); + assert.strictEqual(oneSecondHit, 1); + + await aFewSeconds(expirationForTestInSec / 3); + const oneThirdHit = await mockRepoWithCaching.getByKey('one'); + assert.strictEqual(oneThirdHit, undefined); + }); + it('Should roll out extra entites on max capacity', async () => { const mockRepo = { one: 1, two: 2, three: 3 }; const mockRepoWithCaching = buildMockRepoWithManyCaching(mockRepo); @@ -171,8 +233,10 @@ function buildMockRepoWithSingleCaching(mockRepo: { [id: string]: number }) { resolve(mockRepo[key]); }); }, - - { maxCapacity: cacheMaximumCapacity, maxAge: cacheExpirationInSeconds * 1000 } + { + maxCapacity: cacheMaximumCapacity, + maxAge: cacheExpirationInSeconds * 1000 + } ); } @@ -185,7 +249,10 @@ function buildMockRepoWithManyCaching(mockRepo: { [id: string]: number }) { resolve(map); }); }, - { maxCapacity: cacheMaximumCapacity, maxAge: cacheExpirationInSeconds * 1000 } + { + maxCapacity: cacheMaximumCapacity, + maxAge: cacheExpirationInSeconds * 1000 + } ); } @@ -196,6 +263,9 @@ function buildMockRepoWithErrorCaching(mockRepo: { [id: string]: number }) { reject(new Error('Bang!')); }); }, - { maxCapacity: cacheMaximumCapacity, maxAge: cacheExpirationInSeconds * 1000 } + { + maxCapacity: cacheMaximumCapacity, + maxAge: cacheExpirationInSeconds * 1000 + } ); } diff --git a/le-taxi-api-tests/src/gtfsInquiry/crudGtfsInquiry.apiTest.ts b/le-taxi-api-tests/src/gtfsInquiry/crudGtfsInquiry.apiTest.ts index b180a7b..c3432af 100644 --- a/le-taxi-api-tests/src/gtfsInquiry/crudGtfsInquiry.apiTest.ts +++ b/le-taxi-api-tests/src/gtfsInquiry/crudGtfsInquiry.apiTest.ts @@ -50,10 +50,54 @@ export async function crudGtfsInquiryTests(): Promise { const inquiryResponse = await postGtfsInquiry(inquiryRequest); assert.strictEqual(inquiryResponse.status, StatusCodes.OK); + assert.isDefined(inquiryResponse.body?.options); + assert.strictEqual(inquiryResponse.body.options.length, 1); + }); + + it(`Should receive a standard vehicle if promoted`, async () => { + const operators = await createTaxisWithPromotions([{ ...generateApiTestCoordinates(), type: 'sedan' }], { + standard: true, + minivan: false, + special_need: false + }); + const inquiryRequest = buildInquiryRequest( + generateApiTestCoordinates(), + generateApiTestCoordinates(), + [AssetTypes.Normal], + operators + ); + const inquiryResponse = await postGtfsInquiry(inquiryRequest); + + assert.strictEqual(inquiryResponse.status, StatusCodes.OK); + assert.isDefined(inquiryResponse.body?.options); + assert.strictEqual(inquiryResponse.body.options.length, 1); + }); + + it(`Should not receive a standard vehicle if not promoted`, async () => { + const operators = await createTaxisWithPromotions([{ ...generateApiTestCoordinates(), type: 'sedan' }], { + standard: false, + minivan: false, + special_need: false + }); + const inquiryRequest = buildInquiryRequest( + generateApiTestCoordinates(), + generateApiTestCoordinates(), + [AssetTypes.Normal], + operators + ); + const inquiryResponse = await postGtfsInquiry(inquiryRequest); + + assert.strictEqual(inquiryResponse.status, StatusCodes.OK); + assert.isDefined(inquiryResponse.body?.options); + assert.strictEqual(inquiryResponse.body.options.length, 0); }); - it(`Should be able to request a minivan`, async () => { - const operators = await createTaxisWithPromotions([{ ...generateApiTestCoordinates(), type: 'mpv' }]); + it(`Should receive a minivan if promoted`, async () => { + const operators = await createTaxisWithPromotions([{ ...generateApiTestCoordinates(), type: 'mpv' }], { + standard: true, + minivan: true, + special_need: false + }); const inquiryRequest = buildInquiryRequest( generateApiTestCoordinates(), generateApiTestCoordinates(), @@ -63,11 +107,16 @@ export async function crudGtfsInquiryTests(): Promise { const inquiryResponse = await postGtfsInquiry(inquiryRequest); assert.strictEqual(inquiryResponse.status, StatusCodes.OK); + assert.isDefined(inquiryResponse.body?.options); + assert.strictEqual(inquiryResponse.body.options.length, 1); }); - it(`Should be able to receive a minivan when requesting minivan and minivan is promoted`, async () => { - const promotions = { standard: true, minivan: true, special_need: false }; - const operators = await createTaxisWithPromotions([{ ...generateApiTestCoordinates(), type: 'mpv' }], promotions); + it(`Should not receive a minivan if not promoted`, async () => { + const operators = await createTaxisWithPromotions([{ ...generateApiTestCoordinates(), type: 'mpv' }], { + standard: true, + minivan: false, + special_need: true + }); const inquiryRequest = buildInquiryRequest( generateApiTestCoordinates(), generateApiTestCoordinates(), @@ -77,11 +126,16 @@ export async function crudGtfsInquiryTests(): Promise { const inquiryResponse = await postGtfsInquiry(inquiryRequest); assert.strictEqual(inquiryResponse.status, StatusCodes.OK); + assert.isDefined(inquiryResponse.body?.options); + assert.strictEqual(inquiryResponse.body.options.length, 0); }); - it(`Should be able to receive a minivan when requesting standard vehicle and minivan is promoted`, async () => { - const promotions = { standard: true, minivan: true, special_need: false }; - const operators = await createTaxisWithPromotions([{ ...generateApiTestCoordinates(), type: 'mpv' }], promotions); + it(`Should receive a minivan as a standard vehicle if promoted`, async () => { + const operators = await createTaxisWithPromotions([{ ...generateApiTestCoordinates(), type: 'mpv' }], { + standard: true, + minivan: true, + special_need: false + }); const inquiryRequest = buildInquiryRequest( generateApiTestCoordinates(), generateApiTestCoordinates(), @@ -91,12 +145,88 @@ export async function crudGtfsInquiryTests(): Promise { const inquiryResponse = await postGtfsInquiry(inquiryRequest); assert.strictEqual(inquiryResponse.status, StatusCodes.OK); + assert.isDefined(inquiryResponse.body?.options); + assert.strictEqual(inquiryResponse.body.options.length, 1); }); - it(`Should be able to request a special need vehicle`, async () => { - const operators = await createTaxisWithPromotions([ - { ...generateApiTestCoordinates(), type: 'sedan', specialNeedVehicle: true } - ]); + it(`Should not receive a minivan as a standard vehicle if not promoted`, async () => { + const operators = await createTaxisWithPromotions([{ ...generateApiTestCoordinates(), type: 'mpv' }], { + standard: true, + minivan: false, + special_need: true + }); + const inquiryRequest = buildInquiryRequest( + generateApiTestCoordinates(), + generateApiTestCoordinates(), + [AssetTypes.Normal], + operators + ); + const inquiryResponse = await postGtfsInquiry(inquiryRequest); + + assert.strictEqual(inquiryResponse.status, StatusCodes.OK); + assert.isDefined(inquiryResponse.body?.options); + assert.strictEqual(inquiryResponse.body.options.length, 0); + }); + + it(`Should receive a special need vehicle if promoted (sedan)`, async () => { + const operators = await createTaxisWithPromotions( + [ + { + ...generateApiTestCoordinates(), + type: 'sedan', + specialNeedVehicle: true + } + ], + { standard: false, minivan: false, special_need: true } + ); + const inquiryRequest = buildInquiryRequest( + generateApiTestCoordinates(), + generateApiTestCoordinates(), + [AssetTypes.SpecialNeed], + operators + ); + const inquiryResponse = await postGtfsInquiry(inquiryRequest); + + assert.strictEqual(inquiryResponse.status, StatusCodes.OK); + assert.isDefined(inquiryResponse.body?.options); + assert.strictEqual(inquiryResponse.body.options.length, 1); + }); + + it(`Should receive a special need vehicle if promoted (mpv)`, async () => { + const operators = await createTaxisWithPromotions( + [ + { + ...generateApiTestCoordinates(), + type: 'mpv', + specialNeedVehicle: true + } + ], + { standard: false, minivan: false, special_need: true } + ); + const inquiryRequest = buildInquiryRequest( + generateApiTestCoordinates(), + generateApiTestCoordinates(), + [AssetTypes.SpecialNeed], + operators + ); + const inquiryResponse = await postGtfsInquiry(inquiryRequest); + + assert.strictEqual(inquiryResponse.status, StatusCodes.OK); + assert.isDefined(inquiryResponse.body?.options); + assert.strictEqual(inquiryResponse.body.options.length, 1); + }); + + it(`Should not receive a minivan as a special need vehicle`, async () => { + const operators = await createTaxisWithPromotions( + [ + { + ...generateApiTestCoordinates(), + type: 'mpv', + specialNeedVehicle: true + } + ], + { standard: true, minivan: true, special_need: false } + ); const inquiryRequest = buildInquiryRequest( generateApiTestCoordinates(), generateApiTestCoordinates(), @@ -106,6 +236,8 @@ export async function crudGtfsInquiryTests(): Promise { const inquiryResponse = await postGtfsInquiry(inquiryRequest); assert.strictEqual(inquiryResponse.status, StatusCodes.OK); + assert.isDefined(inquiryResponse.body?.options); + assert.strictEqual(inquiryResponse.body.options.length, 0); }); it(`Should return expected DTO format`, async () => {