UNPKG

@bedrock/resource-restriction

Version:
1,012 lines (954 loc) 33 kB
/*! * Copyright (c) 2020-2024 Digital Bazaar, Inc. All rights reserved. */ import * as database from '@bedrock/mongodb'; import { ACQUIRER_ID, assertCheckResult, cleanDB, generateId, insertRecord, RESOURCES, ZONES } from './helpers.js'; import {resources, restrictions} from '@bedrock/resource-restriction'; import {mockAcquisition} from './mock.data.js'; import {setTimeout} from 'node:timers/promises'; import uuid from 'uuid-random'; describe('Resources', function() { it('should authorize a request with no restrictions', async function() { const now = Date.now(); const acquirerId = ACQUIRER_ID; const request = [ {resource: RESOURCES.APPLE, count: 1, requested: now} ]; const acquisitionTtl = 30000; const zones = [ZONES.ONE, ZONES.TWO]; const result = await resources.check( {acquirerId, request, acquisitionTtl, zones}); const expectedResult = { authorized: true, excessResources: [], untrackedResources: [RESOURCES.APPLE] }; assertCheckResult(result, expectedResult); }); it('check should throw error if missing "acquirerId"', async function() { const now = Date.now(); const request = [ {resource: RESOURCES.APPLE, count: 1, requested: now} ]; const acquisitionTtl = 30000; const zones = [ZONES.ONE, ZONES.TWO]; let result; let err; try { result = await resources.check( {request, acquisitionTtl, zones}); } catch(e) { err = e; } should.not.exist(result); should.exist(err); err.message.should.equal('acquirerId (string) is required'); }); it('should allow acquire on an unrestricted resource', async function() { const now = Date.now(); const acquirerId = ACQUIRER_ID; const request = [ {resource: RESOURCES.APPLE, count: 1, requested: now} ]; const acquisitionTtl = 30000; const zones = [ZONES.ONE, ZONES.TWO]; const result = await resources.acquire( {acquirerId, request, acquisitionTtl, zones}); const expectedResult = { authorized: true, excessResources: [], untrackedResources: [RESOURCES.APPLE] }; assertCheckResult(result, expectedResult); }); it('should authorize a request with a restriction', async function() { const id = await generateId(); await restrictions.insert({ restriction: { id, zone: ZONES.ONE, resource: RESOURCES.ORANGE, method: 'limitOverDuration', methodOptions: { limit: 1, duration: 'P30D' } } }); const now = Date.now(); const acquirerId = ACQUIRER_ID; const request = [ {resource: RESOURCES.ORANGE, count: 1, requested: now} ]; const acquisitionTtl = 30000; const zones = [ZONES.ONE, ZONES.TWO]; const result = await resources.check( {acquirerId, request, acquisitionTtl, zones}); const expectedResult = { authorized: true, excessResources: [], untrackedResources: [] }; assertCheckResult(result, expectedResult); }); it('should deny a request with a restriction', async function() { const now = Date.now(); const acquirerId = ACQUIRER_ID; const request = [ {resource: RESOURCES.ORANGE, count: 2, requested: now} ]; const acquisitionTtl = 30000; const zones = [ZONES.ONE, ZONES.TWO]; const result = await resources.check( {acquirerId, request, acquisitionTtl, zones}); const expectedResult = { authorized: false, excessResources: [{ resource: RESOURCES.ORANGE, count: 1 }], untrackedResources: [] }; assertCheckResult(result, expectedResult); }); it('should acquire with a restriction', async function() { const now = Date.now(); const acquirerId = ACQUIRER_ID; const request = [ {resource: RESOURCES.ORANGE, count: 1, requested: now} ]; const acquisitionTtl = 30000; const zones = [ZONES.ONE, ZONES.TWO]; const result = await resources.acquire( {acquirerId, request, acquisitionTtl, zones}); const expectedResult = { authorized: true, excessResources: [], untrackedResources: [] }; assertCheckResult(result, expectedResult); }); it('acquire should throw error if missing "acquirerId"', async function() { const now = Date.now(); const request = [ {resource: RESOURCES.ORANGE, count: 1, requested: now} ]; const acquisitionTtl = 30000; const zones = [ZONES.ONE, ZONES.TWO]; let result; let err; try { result = await resources.acquire( {request, acquisitionTtl, zones}); } catch(e) { err = e; } should.not.exist(result); should.exist(err); err.message.should.equal('acquirerId (string) is required'); }); it('should deny an acquire request with a restriction', async function() { // TODO: make independent, currently depends on previous `acquire` const now = Date.now(); const acquirerId = ACQUIRER_ID; const request = [ {resource: RESOURCES.ORANGE, count: 1, requested: now} ]; const acquisitionTtl = 30000; const zones = [ZONES.ONE, ZONES.TWO]; const result = await resources.acquire( {acquirerId, request, acquisitionTtl, zones}); const expectedResult = { authorized: false, excessResources: [{ resource: RESOURCES.ORANGE, count: 1 }], untrackedResources: [] }; assertCheckResult(result, expectedResult); }); it('should deny an acquire request with multiple time restrictions', async function() { // add monthly restriction const monthId = await generateId(); await restrictions.insert({ restriction: { id: monthId, zone: ZONES.ONE, resource: RESOURCES.LIME, method: 'limitOverDuration', methodOptions: { limit: 8, duration: 'P30D' } } }); // add weekly restriction const weekId = await generateId(); await restrictions.insert({ restriction: { id: weekId, zone: ZONES.ONE, resource: RESOURCES.LIME, method: 'limitOverDuration', methodOptions: { limit: 2, duration: 'P7D' } } }); // acquire resource const now = Date.now(); const acquirerId = ACQUIRER_ID; const request = [ {resource: RESOURCES.LIME, count: 3, requested: now} ]; const acquisitionTtl = 30000; const zones = [ZONES.ONE]; const result = await resources.acquire( {acquirerId, request, acquisitionTtl, zones}); const expectedResult = { authorized: false, excessResources: [{ resource: RESOURCES.LIME, count: 1 }], untrackedResources: [] }; assertCheckResult(result, expectedResult); }); it('should allow acquisitions with multiple time restrictions over time', async function() { // add monthly restriction await restrictions.insert({ restriction: { id: await generateId(), zone: ZONES.ONE, resource: RESOURCES.TANGERINE, method: 'limitOverDuration', methodOptions: { limit: 2, duration: 'P30D' } } }); // add minute restriction await restrictions.insert({ restriction: { id: await generateId(), zone: ZONES.TWO, resource: RESOURCES.TANGERINE, method: 'limitOverDuration', methodOptions: { limit: 1, duration: 'PT1M' } } }); const now = Date.now(); const acquirerId = ACQUIRER_ID; // `acquisitionTtl` is only a default (it should not be used, because the // restriction types indicate the TTL) const acquisitionTtl = 30000; // acquire resource { const request = [ {resource: RESOURCES.TANGERINE, count: 1, requested: now} ]; const zones = [ZONES.ONE, ZONES.TWO]; const result = await resources.acquire( {acquirerId, request, acquisitionTtl, zones, now}); const expectedResult = { authorized: true, excessResources: [], untrackedResources: [] }; assertCheckResult(result, expectedResult); } // fail to acquire another resource at the same time { const request = [ {resource: RESOURCES.TANGERINE, count: 1, requested: now} ]; const zones = [ZONES.ONE, ZONES.TWO]; const result = await resources.acquire( {acquirerId, request, acquisitionTtl, zones, now}); const expectedResult = { authorized: false, excessResources: [{ resource: RESOURCES.TANGERINE, count: 1 }], untrackedResources: [] }; assertCheckResult(result, expectedResult); } // fail to acquire another resource later but before zone two expiration { const lessThan1MLater = now + 1000 * 59; const request = [{ resource: RESOURCES.TANGERINE, count: 1, requested: lessThan1MLater }]; const zones = [ZONES.ONE, ZONES.TWO]; const result = await resources.acquire( {acquirerId, request, acquisitionTtl, zones, now: lessThan1MLater}); const expectedResult = { authorized: false, excessResources: [{ resource: RESOURCES.TANGERINE, count: 1 }], untrackedResources: [] }; assertCheckResult(result, expectedResult); } // acquire resource after zone two restriction expires { const oneMinuteLater = now + 1000 * 61; const request = [{ resource: RESOURCES.TANGERINE, count: 1, requested: oneMinuteLater }]; const zones = [ZONES.ONE, ZONES.TWO]; const result = await resources.acquire( {acquirerId, request, acquisitionTtl, zones, now: oneMinuteLater}); const expectedResult = { authorized: true, excessResources: [], untrackedResources: [] }; assertCheckResult(result, expectedResult); } // fail to acquire another resource later due to zone one restriction { const fiveMinutesLater = now + 1000 * 60 * 5; const request = [{ resource: RESOURCES.TANGERINE, count: 1, requested: fiveMinutesLater }]; const zones = [ZONES.ONE]; const result = await resources.acquire( {acquirerId, request, acquisitionTtl, zones, now: fiveMinutesLater}); const expectedResult = { authorized: false, excessResources: [{ resource: RESOURCES.TANGERINE, count: 1 }], untrackedResources: [] }; assertCheckResult(result, expectedResult); } // release resources (to avoid invalidating assertions in // inter-related tests) const request = [{resource: RESOURCES.TANGERINE, count: 2}]; await resources.release({acquirerId, request, acquisitionTtl}); }); it('should acquire with updated restriction', async function() { const id = await generateId(); await restrictions.insert({ restriction: { id, zone: ZONES.ONE, resource: RESOURCES.ASPARAGUS, method: 'limitOverDuration', methodOptions: { limit: 1, duration: 'P30D' } } }); // change restriction to allow additional acquisition await restrictions.update({ restriction: { id, zone: ZONES.ONE, resource: RESOURCES.ASPARAGUS, method: 'limitOverDuration', methodOptions: { limit: 2, duration: 'P30D' } } }); const now = Date.now(); const acquirerId = ACQUIRER_ID; const request = [ {resource: RESOURCES.ASPARAGUS, count: 2, requested: now} ]; const acquisitionTtl = 30000; const zones = [ZONES.ONE, ZONES.TWO]; const result = await resources.acquire( {acquirerId, request, acquisitionTtl, zones}); const expectedResult = { authorized: true, excessResources: [], untrackedResources: [] }; assertCheckResult(result, expectedResult); }); it('should force-record an unauthorized acquisition', async function() { // TODO: make independent, currently depends on previous `acquire` const now = Date.now(); const acquirerId = ACQUIRER_ID; const request = [ {resource: RESOURCES.ORANGE, count: 1, requested: now} ]; const acquisitionTtl = 30000; const zones = [ZONES.ONE, ZONES.TWO]; const result = await resources.acquire( {acquirerId, request, acquisitionTtl, zones, forceAcquisition: true}); const expectedResult = { authorized: false, excessResources: [{ resource: RESOURCES.ORANGE, count: 1 }], untrackedResources: [] }; assertCheckResult(result, expectedResult); // TODO: get acquisition record and add assertions }); it('should acquire and then release more than acquired with excess', async function() { // add restriction const id = await generateId(); await restrictions.insert({ restriction: { id, zone: ZONES.ONE, resource: RESOURCES.CHERRY, method: 'limitOverDuration', methodOptions: { limit: 5, duration: 'P30D' } } }); // acquire resource const now = Date.now(); const acquirerId = ACQUIRER_ID; let request = [ {resource: RESOURCES.CHERRY, count: 5, requested: now} ]; const acquisitionTtl = 30000; const zones = [ZONES.ONE, ZONES.TWO]; const acquireResult = await resources.acquire( {acquirerId, request, acquisitionTtl, zones}); const expectedAcquireResult = { authorized: true, excessResources: [], untrackedResources: [] }; assertCheckResult(acquireResult, expectedAcquireResult); // release resource request = [ {resource: RESOURCES.CHERRY, count: 6} ]; const releaseResult = await resources.release( {acquirerId, request, acquisitionTtl}); const expectedExcess = [{resource: RESOURCES.CHERRY, count: 1}]; should.exist(releaseResult); releaseResult.should.be.an('object'); releaseResult.should.have.property('excessResources'); releaseResult.excessResources.should.be.an('array'); releaseResult.excessResources.should.deep.equal(expectedExcess); }); it('should acquire and then release fewer than acquired', async function() { // add restriction const id = await generateId(); await restrictions.insert({ restriction: { id, zone: ZONES.ONE, resource: RESOURCES.CUCUMBER, method: 'limitOverDuration', methodOptions: { limit: 5, duration: 'P30D' } } }); // acquire resource const now = Date.now(); const acquirerId = ACQUIRER_ID; let request = [ {resource: RESOURCES.CUCUMBER, count: 5, requested: now} ]; const acquisitionTtl = 30000; const zones = [ZONES.ONE, ZONES.TWO]; const acquireResult = await resources.acquire( {acquirerId, request, acquisitionTtl, zones}); const expectedAcquireResult = { authorized: true, excessResources: [], untrackedResources: [] }; assertCheckResult(acquireResult, expectedAcquireResult); // release resource request = [ {resource: RESOURCES.CUCUMBER, count: 1} ]; let releaseResult = await resources.release( {acquirerId, request, acquisitionTtl}); let expectedExcess = []; should.exist(releaseResult); releaseResult.should.be.an('object'); releaseResult.should.have.property('excessResources'); releaseResult.excessResources.should.be.an('array'); releaseResult.excessResources.should.deep.equal(expectedExcess); // release remaining resources and check for excess request = [ {resource: RESOURCES.CUCUMBER, count: 5} ]; releaseResult = await resources.release( {acquirerId, request, acquisitionTtl}); expectedExcess = [{resource: RESOURCES.CUCUMBER, count: 1}]; should.exist(releaseResult); releaseResult.should.be.an('object'); releaseResult.should.have.property('excessResources'); releaseResult.excessResources.should.be.an('array'); releaseResult.excessResources.should.deep.equal(expectedExcess); }); it('should fail to release a non-acquired resource', async function() { const acquirerId = ACQUIRER_ID; const request = [ {resource: RESOURCES.APPLE, count: 1} ]; const acquisitionTtl = 30000; const result = await resources.release( {acquirerId, request, acquisitionTtl}); const expectedExcess = [{resource: RESOURCES.APPLE, count: 1}]; should.exist(result); result.should.be.an('object'); result.should.have.property('excessResources'); result.excessResources.should.be.an('array'); result.excessResources.should.deep.equal(expectedExcess); }); it('release should throw error if missing "acquirerId"', async function() { const request = [ {resource: RESOURCES.APPLE, count: 1} ]; const acquisitionTtl = 30000; let result; let err; try { result = await resources.release( {request, acquisitionTtl}); } catch(e) { err = e; } should.not.exist(result); should.exist(err); err.message.should.equal('acquirerId (string) is required'); }); it('should release acquired resources', async function() { const acquirerId = ACQUIRER_ID; const request = [ {resource: RESOURCES.ORANGE, count: 2} ]; const acquisitionTtl = 30000; const result = await resources.release( {acquirerId, request, acquisitionTtl}); should.exist(result); result.should.be.an('object'); result.should.have.property('excessResources'); result.excessResources.should.be.an('array'); result.excessResources.should.deep.equal([]); }); it('should acquire successfully again after release', async function() { const now = Date.now(); const acquirerId = ACQUIRER_ID; const request = [ {resource: RESOURCES.ORANGE, count: 1, requested: now} ]; const acquisitionTtl = 30000; const zones = [ZONES.ONE, ZONES.TWO]; const result = await resources.acquire( {acquirerId, request, acquisitionTtl, zones}); const expectedResult = { authorized: true, excessResources: [], untrackedResources: [] }; assertCheckResult(result, expectedResult); }); it('should release an early acquisition', async function() { // add restriction const id = await generateId(); await restrictions.insert({ restriction: { id, zone: ZONES.ONE, resource: RESOURCES.CARROT, method: 'limitOverDuration', methodOptions: { limit: 10, duration: 'P30D' } } }); // acquire resource at early time const acquirerId = ACQUIRER_ID; const zones = [ZONES.ONE]; const now = Date.now(); const acquisitionTtl = 30000; let request = [ {resource: RESOURCES.CARROT, count: 1, requested: now - 2} ]; let result = await resources.acquire( {acquirerId, request, acquisitionTtl, zones}); const expectedResult = { authorized: true, excessResources: [], untrackedResources: [] }; assertCheckResult(result, expectedResult); // acquire resource at middle time request = [ {resource: RESOURCES.CARROT, count: 1, requested: now - 1} ]; result = await resources.acquire( {acquirerId, request, acquisitionTtl, zones}); assertCheckResult(result, expectedResult); // acquire resource at latest time request = [ {resource: RESOURCES.CARROT, count: 1, requested: now} ]; result = await resources.acquire( {acquirerId, request, acquisitionTtl, zones}); assertCheckResult(result, expectedResult); // release earliest resource request = [ {resource: RESOURCES.CARROT, count: 1} ]; result = await resources.release( {acquirerId, request, acquisitionTtl}); should.exist(result); result.should.be.an('object'); result.should.have.property('expires'); result.expires.should.be.a('number'); // expires result should reflect the expiration of the "latest" acquisition const {expires: latestExpires} = result; // release latest resource request = [ {resource: RESOURCES.CARROT, count: 1, latest: true} ]; result = await resources.release( {acquirerId, request, acquisitionTtl}); should.exist(result); result.should.be.an('object'); result.should.have.property('expires'); result.expires.should.be.a('number'); // expires should should now reflect the expiration of the "middle" // acquisition since the latest has been removed const {expires: middleExpires} = result; // the difference between when the middle acquisition and the "latest" // should be 1 per the above parameters used when acquiring them /* NOTE: This test requires that no other test have acquired resources for longer, causing a longer `expires` value in the response. */ const diff = latestExpires - middleExpires; const expectedDiff = 1; diff.should.equal(expectedDiff); }); it('should use "getAcquisitionMap" in a restriction', async function() { // create restriction; use `limitOneGeographicalRegion`; // see test.js for this restriction implementation with uses // `getAcquisitionMap` internally const restriction = { id: await generateId(), zone: ZONES.ONE, resource: RESOURCES.GEOGRAPHICAL_ANY, method: 'limitOneGeographicalRegion', methodOptions: {} }; await restrictions.insert({restriction}); // should authorize restriction for `GEOGRAPHICAL_EAST` { const now = Date.now(); const acquirerId = ACQUIRER_ID; const request = [{ resource: RESOURCES.GEOGRAPHICAL_ANY, count: 1, requested: now }, { resource: RESOURCES.GEOGRAPHICAL_EAST, count: 1, requested: now }]; const acquisitionTtl = 30000; const zones = [ZONES.ONE]; const result = await resources.acquire( {acquirerId, request, acquisitionTtl, zones}); const expectedResult = { authorized: true, excessResources: [], untrackedResources: [RESOURCES.GEOGRAPHICAL_ANY] }; assertCheckResult(result, expectedResult); } // should deny request because it tries to acquire `GEOGRAPHICAL_WEST` // and a resource in `GEOGRAPHICAL_EAST` is already acquired and this // restriction only allows resources in only one geographical region to be // acquired at a time { const now = Date.now(); const acquirerId = ACQUIRER_ID; const request = [{ resource: RESOURCES.GEOGRAPHICAL_ANY, count: 1, requested: now }, { resource: RESOURCES.GEOGRAPHICAL_WEST, count: 1, requested: now }]; const acquisitionTtl = 30000; const zones = [ZONES.ONE]; const result = await resources.check( {acquirerId, request, acquisitionTtl, zones}); const expectedResult = { authorized: false, excessResources: [{ resource: RESOURCES.GEOGRAPHICAL_WEST, count: 1 }], untrackedResources: [RESOURCES.GEOGRAPHICAL_ANY] }; assertCheckResult(result, expectedResult); } // should authorize restriction for GEO EAST again { const now = Date.now(); const acquirerId = ACQUIRER_ID; const request = [{ resource: RESOURCES.GEOGRAPHICAL_ANY, count: 1, requested: now }, { resource: RESOURCES.GEOGRAPHICAL_EAST, count: 1, requested: now }]; const acquisitionTtl = 30000; const zones = [ZONES.ONE]; const result = await resources.acquire( {acquirerId, request, acquisitionTtl, zones}); const expectedResult = { authorized: true, excessResources: [], untrackedResources: [RESOURCES.GEOGRAPHICAL_ANY] }; assertCheckResult(result, expectedResult); } }); // only run this test during CI as it is a long-running test if(process.env.CI) { it('should acquire successfully after ttl', async function() { // use local `acquirerId` so uninfluenced by previous acquisitions const acquirerId = uuid(); const seconds = 2; const id = await generateId(); await restrictions.insert({ restriction: { id, zone: ZONES.ONE, resource: RESOURCES.PLUM, method: 'limitOverDuration', methodOptions: { limit: 1, duration: `PT${seconds}S` } } }); // acquire first resource { const now = Date.now(); const request = [ {resource: RESOURCES.PLUM, count: 1, requested: now} ]; const acquisitionTtl = 30000; const zones = [ZONES.ONE, ZONES.TWO]; const result = await resources.acquire( {acquirerId, request, acquisitionTtl, zones}); const expectedResult = { authorized: true, excessResources: [], untrackedResources: [] }; assertCheckResult(result, expectedResult); } // fail to acquire second resource { const now = Date.now(); const request = [ {resource: RESOURCES.PLUM, count: 1, requested: now} ]; const acquisitionTtl = 30000; const zones = [ZONES.ONE, ZONES.TWO]; const result = await resources.acquire( {acquirerId, request, acquisitionTtl, zones}); const expectedResult = { authorized: false, excessResources: [{ resource: RESOURCES.PLUM, count: 1 }], untrackedResources: [] }; assertCheckResult(result, expectedResult); } // wait for ttl-based expiration period await setTimeout(seconds * 1000); // acquire second resource { const now = Date.now(); const request = [ {resource: RESOURCES.PLUM, count: 1, requested: now} ]; const acquisitionTtl = 30000; const zones = [ZONES.ONE, ZONES.TWO]; const result = await resources.acquire( {acquirerId, request, acquisitionTtl, zones}); const expectedResult = { authorized: true, excessResources: [], untrackedResources: [] }; assertCheckResult(result, expectedResult); } }); } // only run this test during CI as it is a long-running test if(process.env.CI) { it('should acquire successfully after expiration', async function() { // this test needs to run for up to 3 minutes to allow mongodb's record // clean up worker to execute this.timeout(3 * 60 * 1000); // use local `acquirerId` so uninfluenced by previous acquisitions const acquirerId = uuid(); const seconds = 2; const id = await generateId(); await restrictions.insert({ restriction: { id, zone: ZONES.ONE, resource: RESOURCES.PLUM, method: 'limitOverDuration', methodOptions: { limit: 1, duration: `PT${seconds}S` } } }); // acquire first resource { const now = Date.now(); const request = [ {resource: RESOURCES.PLUM, count: 1, requested: now} ]; const acquisitionTtl = 30000; const zones = [ZONES.ONE, ZONES.TWO]; const result = await resources.acquire( {acquirerId, request, acquisitionTtl, zones}); const expectedResult = { authorized: true, excessResources: [], untrackedResources: [] }; assertCheckResult(result, expectedResult); } // fail to acquire second resource { const now = Date.now(); const request = [ {resource: RESOURCES.PLUM, count: 1, requested: now} ]; const acquisitionTtl = 30000; const zones = [ZONES.ONE, ZONES.TWO]; const result = await resources.acquire( {acquirerId, request, acquisitionTtl, zones}); const expectedResult = { authorized: false, excessResources: [{ resource: RESOURCES.PLUM, count: 1 }], untrackedResources: [] }; assertCheckResult(result, expectedResult); } // wait for mongodb-worker-driven expiration period (mongodb's worker runs // every 60 seconds but we don't know when, so we need to allow for two // full 60 second cycles + time for the record removal to occur to ensure // the record is cleaned up; hence we wait for 2 minutes + 10 seconds) await setTimeout((60 * 2 + 10) * 1000); // check to see that the record has been removed const query = {'acquisition.acquirerId': acquirerId}; const projection = {_id: 0}; const collection = database.collections['resource-restriction-acquisition']; const record = await collection.findOne(query, projection); should.not.exist(record); }); } }); describe('Resources Database Tests', function() { describe('Indexes', function() { beforeEach(async () => { await cleanDB(); const collectionName = 'resource-restriction-acquisition'; const mockAcquisition2 = JSON.parse(JSON.stringify(mockAcquisition)); mockAcquisition2.acquisition .acquirerId = '22287e9c-5c32-4e59-b103-138f99fe872d'; await insertRecord({record: mockAcquisition, collectionName}); // second record is inserted here in order to do proper assertions for // 'nReturned', 'totalKeysExamined' and 'totalDocsExamined'. await insertRecord({record: mockAcquisition2, collectionName}); }); it(`is properly indexed for 'acquisition.acquirerId' in ` + '_getAcquisitionRecord()', async function() { const {acquirerId} = mockAcquisition.acquisition; const {executionStats} = await resources._getAcquisitionRecord({ acquirerId, explain: true }); executionStats.nReturned.should.equal(1); executionStats.totalKeysExamined.should.equal(1); executionStats.totalDocsExamined.should.equal(1); executionStats.executionStages.inputStage.inputStage.inputStage.stage .should.equal('IXSCAN'); executionStats.executionStages.inputStage.inputStage.inputStage.keyPattern .should.eql({'acquisition.acquirerId': 1}); }); it(`is properly indexed for 'acquisition.acquirerId' and ` + `'acquisition.tokenized' in ` + '_updateAcquisitionRecord()', async function() { const {acquirerId, expires, ttl, tokenized} = mockAcquisition.acquisition; const newTokenized = [...JSON.parse(JSON.stringify(tokenized))]; newTokenized[0].tokenizerId = '371593a1-a5aa-4346-8663-dba5ebc854b9'; const {executionStats} = await resources._updateAcquisitionRecord({ acquirerId, acquisitionRecord: mockAcquisition, newTokenized, expires: expires.getTime(), ttl, explain: true }); executionStats.nReturned.should.equal(1); executionStats.totalKeysExamined.should.equal(1); executionStats.totalDocsExamined.should.equal(1); executionStats.executionStages.inputStage.inputStage.stage .should.equal('IXSCAN'); executionStats.executionStages.inputStage.inputStage.keyPattern .should.eql({'acquisition.acquirerId': 1}); }); it(`is properly indexed for 'acquisition.acquirerId' and ` + `'acquisition.tokenized' in ` + '_removeAcquisitionRecord()', async function() { const {acquirerId} = mockAcquisition.acquisition; const {executionStats} = await resources._removeAcquisitionRecord({ acquirerId, acquisitionRecord: mockAcquisition, explain: true }); executionStats.nReturned.should.equal(1); executionStats.totalKeysExamined.should.equal(1); executionStats.totalDocsExamined.should.equal(1); executionStats.executionStages.inputStage.inputStage.stage .should.equal('IXSCAN'); executionStats.executionStages.inputStage.inputStage.keyPattern .should.eql({'acquisition.acquirerId': 1}); }); }); });