diff --git a/public/v1/components/schemas.yaml b/public/v1/components/schemas.yaml index 6f4f79d2..d51fccd6 100644 --- a/public/v1/components/schemas.yaml +++ b/public/v1/components/schemas.yaml @@ -571,6 +571,7 @@ components: - continent - region - country + - state - city - asn - network diff --git a/src/measurement/store.ts b/src/measurement/store.ts index 1a0dd543..370b3ac8 100644 --- a/src/measurement/store.ts +++ b/src/measurement/store.ts @@ -52,7 +52,7 @@ export class MeasurementStore { return ips || []; } - async createMeasurement (request: MeasurementRequest, probes: Map, allProbes: (Probe | OfflineProbe)[]): Promise { + async createMeasurement (request: MeasurementRequest, onlineProbesMap: Map, allProbes: (Probe | OfflineProbe)[]): Promise { const id = cryptoRandomString({ length: 16, type: 'alphanumeric' }); const key = getMeasurementKey(id); @@ -76,11 +76,11 @@ export class MeasurementStore { await Promise.all([ this.redis.hSet('gp:in-progress', id, startTime.getTime()), - this.redis.set(getMeasurementKey(id, 'probes_awaiting'), probes.size, { EX: probesAwaitingTtl }), - this.redis.json.set(getMeasurementKey(id, 'ips'), '$', allProbes.map(probe => probe.ipAddress)), + this.redis.set(getMeasurementKey(id, 'probes_awaiting'), onlineProbesMap.size, { EX: probesAwaitingTtl }), this.redis.json.set(key, '$', measurementWithoutDefaults), - this.redis.expire(getMeasurementKey(id, 'ips'), config.get('measurement.resultTTL')), this.redis.expire(key, config.get('measurement.resultTTL')), + this.redis.json.set(getMeasurementKey(id, 'ips'), '$', allProbes.map(probe => probe.ipAddress)), + this.redis.expire(getMeasurementKey(id, 'ips'), config.get('measurement.resultTTL')), ]); return id; diff --git a/src/probe/probes-location-filter.ts b/src/probe/probes-location-filter.ts index 2b32c425..ba7e0b28 100644 --- a/src/probe/probes-location-filter.ts +++ b/src/probe/probes-location-filter.ts @@ -68,7 +68,7 @@ export class ProbesLocationFilter { Object.keys(location).forEach((key) => { if (key === 'tags') { // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - probes = probes.filter(probe => location.tags!.every(tag => ProbesLocationFilter.hasTag(probe, tag))); + filteredProbes = probes.filter(probe => location.tags!.every(tag => ProbesLocationFilter.hasTag(probe, tag))); } else if (key === 'magic') { // eslint-disable-next-line @typescript-eslint/no-non-null-assertion filteredProbes = ProbesLocationFilter.magicFilter(filteredProbes, location.magic!); diff --git a/src/probe/router.ts b/src/probe/router.ts index c86de152..fbe2f063 100644 --- a/src/probe/router.ts +++ b/src/probe/router.ts @@ -4,15 +4,16 @@ import type { LocationWithLimit, MeasurementRecord, MeasurementResult } from '.. import type { Location } from '../lib/location/types.js'; import type { OfflineProbe, Probe } from './types.js'; import { ProbesLocationFilter } from './probes-location-filter.js'; -import { getMeasurementStore } from '../measurement/store.js'; +import { getMeasurementStore, MeasurementStore } from '../measurement/store.js'; import { normalizeFromPublicName, normalizeNetworkName } from '../lib/geoip/utils.js'; export class ProbeRouter { private readonly probesFilter = new ProbesLocationFilter(); - private readonly store = getMeasurementStore(); - - constructor (private readonly fetchWsSockets: typeof fetchSockets) {} + constructor ( + private readonly fetchWsSockets: typeof fetchSockets, + private readonly store: MeasurementStore, + ) {} public async findMatchingProbes ( locations: LocationWithLimit[] | string = [], @@ -165,7 +166,7 @@ let router: ProbeRouter; export const getProbeRouter = () => { if (!router) { - router = new ProbeRouter(fetchSockets); + router = new ProbeRouter(fetchSockets, getMeasurementStore()); } return router; diff --git a/test/tests/integration/probes/get-probes.test.ts b/test/tests/integration/probes/get-probes.test.ts index 785c1121..d25089e8 100644 --- a/test/tests/integration/probes/get-probes.test.ts +++ b/test/tests/integration/probes/get-probes.test.ts @@ -82,6 +82,7 @@ describe('Get Probes', () => { continent: 'SA', region: 'South America', country: 'AR', + state: null, city: 'Buenos Aires', asn: 61004, latitude: -34.6131, @@ -163,6 +164,7 @@ describe('Get Probes', () => { continent: 'SA', region: 'South America', country: 'AR', + state: null, city: 'Buenos Aires', asn: 61004, latitude: -34.6131, @@ -197,6 +199,7 @@ describe('Get Probes', () => { continent: 'SA', region: 'South America', country: 'AR', + state: null, city: 'Buenos Aires', asn: 61004, latitude: -34.6131, @@ -276,6 +279,7 @@ describe('Get Probes', () => { continent: 'SA', region: 'South America', country: 'AR', + state: null, city: 'Cordoba', latitude: -31.4135, longitude: -64.18105, diff --git a/test/tests/unit/adopted-probes.test.ts b/test/tests/unit/adopted-probes.test.ts index c731b3fb..569ade43 100644 --- a/test/tests/unit/adopted-probes.test.ts +++ b/test/tests/unit/adopted-probes.test.ts @@ -15,6 +15,7 @@ const defaultAdoptedProbe = { status: 'ready', version: '0.26.0', country: 'IE', + state: null, countryOfCustomCity: '', city: 'Dublin', latitude: 53.3331, @@ -33,6 +34,7 @@ const defaultConnectedProbe: Probe = { continent: 'EU', region: 'Northern Europe', country: 'IE', + state: null, city: 'Dublin', normalizedCity: 'dublin', asn: 16509, @@ -225,13 +227,14 @@ describe('AdoptedProbes', () => { continent: 'EU', region: 'Northern Europe', country: 'GB', + state: null, city: 'London', asn: 20473, latitude: 51.50853, longitude: -0.12574, network: 'The Constant Company, LLC', }, - }, + } as Probe, }, }]); @@ -269,6 +272,7 @@ describe('AdoptedProbes', () => { continent: 'EU', region: 'Northern Europe', country: 'GB', + state: null, city: 'London', asn: 20473, latitude: 51.50853, @@ -320,6 +324,7 @@ describe('AdoptedProbes', () => { continent: 'EU', region: 'Northern Europe', country: 'GB', + state: null, city: 'London', asn: 20473, latitude: 51.50853, @@ -393,6 +398,7 @@ describe('AdoptedProbes', () => { region: 'Northern Europe', country: 'IE', city: 'Dundalk', + state: null, normalizedCity: 'dundalk', asn: 16509, latitude: 54, diff --git a/test/tests/unit/measurement/runner.test.ts b/test/tests/unit/measurement/runner.test.ts index 9011e3a9..b00f3678 100644 --- a/test/tests/unit/measurement/runner.test.ts +++ b/test/tests/unit/measurement/runner.test.ts @@ -1,8 +1,8 @@ +import type { Context } from 'koa'; import * as sinon from 'sinon'; import { Server } from 'socket.io'; import { expect } from 'chai'; import * as td from 'testdouble'; -import type { RedisClient } from '../../../../src/lib/redis/client.js'; import { MeasurementStore } from '../../../../src/measurement/store.js'; import { ProbeRouter } from '../../../../src/probe/router.js'; import { MetricsAgent } from '../../../../src/lib/metrics.js'; @@ -12,13 +12,17 @@ import type { MeasurementRecord, MeasurementResultMessage } from '../../../../sr const getProbe = (id: number) => ({ client: id } as unknown as Probe); +const req = { + headers: { + 'x-client-ip': '1.1.1.1', + }, +}; + describe('MeasurementRunner', () => { + const set = sinon.stub(); const emit = sinon.stub(); const to = sinon.stub(); const io = sinon.createStubInstance(Server); - const redis = { - recordResult: sinon.stub(), - } as sinon.SinonStubbedInstance; const store = sinon.createStubInstance(MeasurementStore); const router = sinon.createStubInstance(ProbeRouter); const metrics = sinon.createStubInstance(MetricsAgent); @@ -28,7 +32,7 @@ describe('MeasurementRunner', () => { before(async () => { td.replaceEsm('crypto-random-string', null, () => testId++); const { MeasurementRunner } = await import('../../../../src/measurement/runner.js'); - runner = new MeasurementRunner(io, redis, store, router, metrics); + runner = new MeasurementRunner(io, store, router, metrics); }); beforeEach(() => { @@ -40,30 +44,49 @@ describe('MeasurementRunner', () => { store.createMeasurement.reset(); store.createMeasurement.resolves('measurementid'); metrics.recordMeasurement.reset(); - redis.recordResult.reset(); testId = 0; }); it('should run measurement for the required amount of probes', async () => { - router.findMatchingProbes.resolves([ getProbe(0), getProbe(1), getProbe(2), getProbe(3) ]); + router.findMatchingProbes.resolves({ + onlineProbesMap: new Map([ getProbe(0), getProbe(1), getProbe(2), getProbe(3) ].entries()), + allProbes: [ getProbe(0), getProbe(1), getProbe(2), getProbe(3) ], + }); await runner.run({ - type: 'ping', - target: 'jsdelivr.com', - measurementOptions: { - packets: 3, + set, + req, + request: { + body: { + type: 'ping', + target: 'jsdelivr.com', + measurementOptions: { + packets: 3, + }, + locations: [], + limit: 10, + inProgressUpdates: false, + }, }, - locations: [], - limit: 10, - inProgressUpdates: false, - }); + } as unknown as Context); expect(router.findMatchingProbes.callCount).to.equal(1); expect(router.findMatchingProbes.args[0]).to.deep.equal([ [], 10 ]); expect(store.createMeasurement.callCount).to.equal(1); - expect(store.createMeasurement.args[0]![1]).to.deep.equal([{ client: 0 }, { client: 1 }, { client: 2 }, { client: 3 }]); + expect(store.createMeasurement.args[0]).to.deep.equal([ + { + type: 'ping', + target: 'jsdelivr.com', + measurementOptions: { packets: 3 }, + locations: [], + limit: 10, + inProgressUpdates: false, + }, + new Map([ getProbe(0), getProbe(1), getProbe(2), getProbe(3) ].entries()), + [ getProbe(0), getProbe(1), getProbe(2), getProbe(3) ], + ]); expect(to.callCount).to.equal(4); expect(emit.callCount).to.equal(4); @@ -124,25 +147,45 @@ describe('MeasurementRunner', () => { }); it('should send `inProgressUpdates: true` to the first N probes if requested', async () => { - router.findMatchingProbes.resolves([ getProbe(0), getProbe(1), getProbe(2), getProbe(3) ]); + router.findMatchingProbes.resolves({ + onlineProbesMap: new Map([ getProbe(0), getProbe(1), getProbe(2), getProbe(3) ].entries()), + allProbes: [ getProbe(0), getProbe(1), getProbe(2), getProbe(3) ], + }); await runner.run({ - type: 'ping', - target: 'jsdelivr.com', - measurementOptions: { - packets: 3, + set, + req, + request: { + body: { + type: 'ping', + target: 'jsdelivr.com', + measurementOptions: { + packets: 3, + }, + locations: [], + limit: 10, + inProgressUpdates: true, + }, }, - locations: [], - limit: 10, - inProgressUpdates: true, - }); + } as unknown as Context); expect(router.findMatchingProbes.callCount).to.equal(1); expect(router.findMatchingProbes.args[0]).to.deep.equal([ [], 10 ]); expect(store.createMeasurement.callCount).to.equal(1); - expect(store.createMeasurement.args[0]![1]).to.deep.equal([{ client: 0 }, { client: 1 }, { client: 2 }, { client: 3 }]); + expect(store.createMeasurement.args[0]).to.deep.equal([ + { + type: 'ping', + target: 'jsdelivr.com', + measurementOptions: { packets: 3 }, + locations: [], + limit: 10, + inProgressUpdates: true, + }, + new Map([ getProbe(0), getProbe(1), getProbe(2), getProbe(3) ].entries()), + [ getProbe(0), getProbe(1), getProbe(2), getProbe(3) ], + ]); expect(to.callCount).to.equal(4); expect(emit.callCount).to.equal(4); @@ -197,7 +240,7 @@ describe('MeasurementRunner', () => { it('should properly handle result events from probes', async () => { const sandbox = sinon.createSandbox({ useFakeTimers: { now: new Date('2023-05-24T09:56:55.000Z').getTime() } }); - redis.recordResult + store.storeMeasurementResult .onFirstCall().resolves(null) .onSecondCall().resolves({ type: 'ping', createdAt: '2023-05-24T09:56:30.000Z' } as MeasurementRecord) .onThirdCall().resolves(null); @@ -206,10 +249,10 @@ describe('MeasurementRunner', () => { await runner.recordResult({ measurementId: 'measurementid', testId: 'testid2', result: {} as MeasurementResultMessage['result'] }); await runner.recordResult({ measurementId: 'measurementid', testId: 'testid3', result: {} as MeasurementResultMessage['result'] }); - expect(redis.recordResult.callCount).to.equal(3); - expect(redis.recordResult.args[0]).to.deep.equal([ 'measurementid', 'testid1', {}]); - expect(redis.recordResult.args[1]).to.deep.equal([ 'measurementid', 'testid2', {}]); - expect(redis.recordResult.args[2]).to.deep.equal([ 'measurementid', 'testid3', {}]); + expect(store.storeMeasurementResult.callCount).to.equal(3); + expect(store.storeMeasurementResult.args[0]).to.deep.equal([{ measurementId: 'measurementid', testId: 'testid1', result: {} }]); + expect(store.storeMeasurementResult.args[1]).to.deep.equal([{ measurementId: 'measurementid', testId: 'testid2', result: {} }]); + expect(store.storeMeasurementResult.args[2]).to.deep.equal([{ measurementId: 'measurementid', testId: 'testid3', result: {} }]); expect(metrics.recordMeasurementTime.callCount).to.equal(1); expect(metrics.recordMeasurementTime.args[0]).to.deep.equal([ 'ping', 25000 ]); sandbox.restore(); diff --git a/test/tests/unit/measurement/store.test.ts b/test/tests/unit/measurement/store.test.ts index 9ba7bec6..f713d751 100644 --- a/test/tests/unit/measurement/store.test.ts +++ b/test/tests/unit/measurement/store.test.ts @@ -127,6 +127,7 @@ describe('measurement store', () => { limit: 1, inProgressUpdates: false, }, + new Map([ [ 0, getProbe('id') ] ]), [ getProbe('id') ], ); @@ -182,6 +183,7 @@ describe('measurement store', () => { limit: 1, inProgressUpdates: false, }, + new Map([ [ 0, getProbe('id') ] ]), [ getProbe('id') ], ); @@ -234,14 +236,15 @@ describe('measurement store', () => { limit: 4, inProgressUpdates: false, }, + new Map([ getProbe('z'), getProbe('10'), getProbe('x'), getProbe('0') ].entries()), [ getProbe('z'), getProbe('10'), getProbe('x'), getProbe('0') ], ); expect(redisMock.hSet.callCount).to.equal(1); expect(redisMock.hSet.args[0]).to.deep.equal([ 'gp:in-progress', 'measurementid', 1678000000000 ]); expect(redisMock.set.callCount).to.equal(1); - expect(redisMock.set.args[0]).to.deep.equal([ 'gp:measurement:measurementid:probes_awaiting', 4, { EX: 35 }]); - expect(redisMock.json.set.callCount).to.equal(1); + expect(redisMock.set.args[0]).to.deep.equal([ 'gp:measurement:probes_awaiting:measurementid', 4, { EX: 35 }]); + expect(redisMock.json.set.callCount).to.equal(2); expect(redisMock.json.set.args[0]).to.deep.equal([ 'gp:measurement:measurementid', '$', { id: 'measurementid', @@ -318,7 +321,7 @@ describe('measurement store', () => { }], }]); - expect(redisMock.expire.callCount).to.equal(1); + expect(redisMock.expire.callCount).to.equal(2); expect(redisMock.expire.args[0]).to.deep.equal([ 'gp:measurement:measurementid', 604800 ]); }); @@ -349,6 +352,7 @@ describe('measurement store', () => { limit: 2, inProgressUpdates: false, }, + new Map([ [ 0, getProbe('id') ] ]), [ getProbe('id') ], ); @@ -393,7 +397,7 @@ describe('measurement store', () => { }], }]); - expect(redisMock.expire.callCount).to.equal(1); + expect(redisMock.expire.callCount).to.equal(2); expect(redisMock.expire.args[0]).to.deep.equal([ 'gp:measurement:measurementid', 604800 ]); }); @@ -416,6 +420,7 @@ describe('measurement store', () => { locations: [], inProgressUpdates: false, }, + new Map([ [ 0, getProbe('id') ] ]), [ getProbe('id') ], ); @@ -450,7 +455,7 @@ describe('measurement store', () => { }], }]); - expect(redisMock.expire.callCount).to.equal(1); + expect(redisMock.expire.callCount).to.equal(2); expect(redisMock.expire.args[0]).to.deep.equal([ 'gp:measurement:measurementid', 604800 ]); }); diff --git a/test/tests/unit/probe/router.test.ts b/test/tests/unit/probe/router.test.ts index 7bffde5f..65414210 100644 --- a/test/tests/unit/probe/router.test.ts +++ b/test/tests/unit/probe/router.test.ts @@ -4,11 +4,12 @@ import { expect } from 'chai'; import * as td from 'testdouble'; import { ProbeRouter } from '../../../../src/probe/router.js'; +import { getRegionByCountry } from '../../../../src/lib/location/location.js'; import type { RemoteProbeSocket } from '../../../../src/lib/ws/server.js'; import type { DeepPartial } from '../../../types.js'; import type { Probe, ProbeLocation } from '../../../../src/probe/types.js'; import type { Location } from '../../../../src/lib/location/types.js'; -import { getRegionByCountry } from '../../../../src/lib/location/location.js'; +import type { MeasurementStore } from '../../../../src/measurement/store.js'; const defaultLocation = { continent: '', @@ -25,12 +26,16 @@ const defaultLocation = { }; describe('probe router', () => { - const sandbox = sinon.createSandbox(); const fetchSocketsMock = sinon.stub(); const geoLookupMock = sinon.stub(); const getRegionMock = sinon.stub(); - const router = new ProbeRouter(fetchSocketsMock); + const store = { + getIpsByMeasurementId: sinon.stub(), + getMeasurementJson: sinon.stub(), + } as unknown as MeasurementStore; + const router = new ProbeRouter(fetchSocketsMock, store); let buildProbe: (socket: RemoteProbeSocket) => Promise; + let sandbox: sinon.SinonSandbox; const buildSocket = async ( id: string, @@ -57,6 +62,7 @@ describe('probe router', () => { }; before(async () => { + sandbox = sinon.createSandbox(); await td.replaceEsm('../../../../src/lib/geoip/client.ts', { createGeoipClient: () => ({ lookup: geoLookupMock }) }); await td.replaceEsm('../../../../src/lib/ip-ranges.ts', { getRegion: getRegionMock }); buildProbe = (await import('../../../../src/probe/builder.js')).buildProbe as unknown as (socket: RemoteProbeSocket) => Promise; @@ -73,6 +79,7 @@ describe('probe router', () => { }); after(() => { + sandbox.restore(); td.reset(); }); @@ -87,16 +94,16 @@ describe('probe router', () => { ]; fetchSocketsMock.resolves(sockets as never); - const probes = await router.findMatchingProbes([ + const { allProbes } = await router.findMatchingProbes([ { country: 'UA', limit: 2 }, { country: 'PL', limit: 2 }, ]); expect(fetchSocketsMock.calledOnce).to.be.true; expect(fetchSocketsMock.firstCall.args).to.deep.equal([]); - expect(probes.length).to.equal(4); - expect(probes.filter(p => p.location.country === 'UA').length).to.equal(2); - expect(probes.filter(p => p.location.country === 'PL').length).to.equal(2); + expect(allProbes.length).to.equal(4); + expect(allProbes.filter(p => p.location.country === 'UA').length).to.equal(2); + expect(allProbes.filter(p => p.location.country === 'PL').length).to.equal(2); }); it('should return 1 probe if location limit is not set', async () => { @@ -110,7 +117,7 @@ describe('probe router', () => { fetchSocketsMock.resolves(sockets as never); - const probes = await router.findMatchingProbes([ + const { allProbes } = await router.findMatchingProbes([ { country: 'UA', limit: 2 }, { country: 'PL' }, ]); @@ -118,9 +125,9 @@ describe('probe router', () => { expect(fetchSocketsMock.calledOnce).to.be.true; expect(fetchSocketsMock.firstCall.args).to.deep.equal([]); - expect(probes.length).to.equal(3); - expect(probes.filter(p => p.location.country === 'UA').length).to.equal(2); - expect(probes.filter(p => p.location.country === 'PL').length).to.equal(1); + expect(allProbes.length).to.equal(3); + expect(allProbes.filter(p => p.location.country === 'UA').length).to.equal(2); + expect(allProbes.filter(p => p.location.country === 'PL').length).to.equal(1); }); it('should shuffle result probes', async () => { @@ -138,10 +145,10 @@ describe('probe router', () => { ]; fetchSocketsMock.resolves(sockets as never); - const probes1 = await router.findMatchingProbes([ + const { allProbes: probes1 } = await router.findMatchingProbes([ { continent: 'EU', limit: 10 }, ]); - const probes2 = await router.findMatchingProbes([ + const { allProbes: probes2 } = await router.findMatchingProbes([ { continent: 'EU', limit: 10 }, ]); @@ -165,7 +172,7 @@ describe('probe router', () => { fetchSocketsMock.resolves(sockets as never); - const probes = await router.findMatchingProbes([ + const { allProbes } = await router.findMatchingProbes([ { country: 'GB', limit: 2 }, { country: 'PL', limit: 2 }, ]); @@ -173,9 +180,9 @@ describe('probe router', () => { expect(fetchSocketsMock.calledOnce).to.be.true; expect(fetchSocketsMock.firstCall.args).to.deep.equal([]); - expect(probes.length).to.equal(2); - expect(probes.filter(p => p.location.country === 'GB').length).to.equal(1); - expect(probes.filter(p => p.location.country === 'PL').length).to.equal(1); + expect(allProbes.length).to.equal(2); + expect(allProbes.filter(p => p.location.country === 'GB').length).to.equal(1); + expect(allProbes.filter(p => p.location.country === 'PL').length).to.equal(1); }); }); @@ -186,10 +193,10 @@ describe('probe router', () => { fetchSocketsMock.resolves(sockets as never); - const probes = await router.findMatchingProbes([], 100); - const grouped = _.groupBy(probes, 'location.continent'); + const { allProbes } = await router.findMatchingProbes([], 100); + const grouped = _.groupBy(allProbes, 'location.continent'); - expect(probes.length).to.equal(100); + expect(allProbes.length).to.equal(100); expect(grouped['AF']?.length).to.equal(5); expect(grouped['AS']?.length).to.equal(15); expect(grouped['EU']?.length).to.equal(30); @@ -210,10 +217,10 @@ describe('probe router', () => { fetchSocketsMock.resolves(sockets as never); - const probes = await router.findMatchingProbes([], 100); - const grouped = _.groupBy(probes, 'location.continent'); + const { allProbes } = await router.findMatchingProbes([], 100); + const grouped = _.groupBy(allProbes, 'location.continent'); - expect(probes.length).to.equal(100); + expect(allProbes.length).to.equal(100); expect(grouped['AF']?.length).to.equal(13); expect(grouped['AS']?.length).to.equal(15); expect(grouped['EU']?.length).to.equal(20); @@ -231,8 +238,8 @@ describe('probe router', () => { fetchSocketsMock.resolves(sockets as never); - const probes = await router.findMatchingProbes([{ continent: 'AF' }, { continent: 'NA' }, { continent: 'SA' }], 7); - expect(probes.length).to.equal(7); + const { allProbes } = await router.findMatchingProbes([{ continent: 'AF' }, { continent: 'NA' }, { continent: 'SA' }], 7); + expect(allProbes.length).to.equal(7); }); it('should find when probes not enough', async () => { @@ -245,10 +252,10 @@ describe('probe router', () => { fetchSocketsMock.resolves(sockets as never); - const probes = await router.findMatchingProbes([], 100); - const grouped = _.groupBy(probes, 'location.continent'); + const { allProbes } = await router.findMatchingProbes([], 100); + const grouped = _.groupBy(allProbes, 'location.continent'); - expect(probes.length).to.equal(65); + expect(allProbes.length).to.equal(65); expect(grouped['AF']?.length).to.equal(15); expect(grouped['EU']?.length).to.equal(20); expect(grouped['OC']?.length).to.equal(10); @@ -270,8 +277,8 @@ describe('probe router', () => { ]; fetchSocketsMock.resolves(sockets as never); - const probes1 = await router.findMatchingProbes([], 100); - const probes2 = await router.findMatchingProbes([], 100); + const { allProbes: probes1 } = await router.findMatchingProbes([], 100); + const { allProbes: probes2 } = await router.findMatchingProbes([], 100); expect(fetchSocketsMock.calledTwice).to.be.true; expect(probes1.length).to.equal(10); @@ -289,7 +296,16 @@ describe('probe router', () => { const cached = cache[location.country]; if (cached) { - return { ...cached }; + return { + ...cached, + data: { + ...cached.data, + probe: { + ...cached.data.probe, + }, + }, + id, + }; } const socket = await buildSocket(id, location); @@ -314,10 +330,10 @@ describe('probe router', () => { fetchSocketsMock.resolves(sockets as never); - const probes = await router.findMatchingProbes(locations, 100); - const grouped = _.groupBy(probes, 'location.country'); + const { allProbes } = await router.findMatchingProbes(locations, 100); + const grouped = _.groupBy(allProbes, 'location.country'); - expect(probes.length).to.equal(100); + expect(allProbes.length).to.equal(100); expect(grouped['PL']?.length).to.equal(99); expect(grouped['UA']?.length).to.equal(1); }); @@ -336,10 +352,10 @@ describe('probe router', () => { fetchSocketsMock.resolves(sockets as never); - const probes = await router.findMatchingProbes(locations, 100); - const grouped = _.groupBy(probes, 'location.country'); + const { allProbes } = await router.findMatchingProbes(locations, 100); + const grouped = _.groupBy(allProbes, 'location.country'); - expect(probes.length).to.equal(100); + expect(allProbes.length).to.equal(100); expect(grouped['PL']?.length).to.equal(34); expect(grouped['UA']?.length).to.equal(33); expect(grouped['NL']?.length).to.equal(33); @@ -360,10 +376,10 @@ describe('probe router', () => { ]; fetchSocketsMock.resolves(sockets as never); - const probes1 = await router.findMatchingProbes([ + const { allProbes: probes1 } = await router.findMatchingProbes([ { continent: 'EU' }, ], 100); - const probes2 = await router.findMatchingProbes([ + const { allProbes: probes2 } = await router.findMatchingProbes([ { continent: 'EU' }, ], 100); @@ -399,10 +415,10 @@ describe('probe router', () => { fetchSocketsMock.resolves(sockets as never); - const probes = await router.findMatchingProbes(locations, 100); + const { allProbes } = await router.findMatchingProbes(locations, 100); - expect(probes.length).to.equal(1); - expect(probes[0]!.location.country).to.equal('US'); + expect(allProbes.length).to.equal(1); + expect(allProbes[0]!.location.country).to.equal('US'); }); it('should not find probe by continent alias if it is used not in magic field', async () => { @@ -411,9 +427,9 @@ describe('probe router', () => { ]; fetchSocketsMock.resolves(sockets as never); - const probes = await router.findMatchingProbes([{ continent: 'NA' }], 100); - expect(probes.length).to.equal(1); - const probes2 = await router.findMatchingProbes([{ continent: 'North America' }], 100); + const { allProbes } = await router.findMatchingProbes([{ continent: 'NA' }], 100); + expect(allProbes.length).to.equal(1); + const { allProbes: probes2 } = await router.findMatchingProbes([{ continent: 'North America' }], 100); expect(probes2.length).to.equal(0); }); @@ -424,9 +440,9 @@ describe('probe router', () => { fetchSocketsMock.resolves(sockets as never); - const probes = await router.findMatchingProbes([{ region: 'Northern Africa' }], 100); - expect(probes.length).to.equal(1); - const probes2 = await router.findMatchingProbes([{ region: 'North Africa' }], 100); + const { allProbes } = await router.findMatchingProbes([{ region: 'Northern Africa' }], 100); + expect(allProbes.length).to.equal(1); + const { allProbes: probes2 } = await router.findMatchingProbes([{ region: 'North Africa' }], 100); expect(probes2.length).to.equal(0); }); }); @@ -450,10 +466,10 @@ describe('probe router', () => { fetchSocketsMock.resolves(sockets as never); - const probes = await router.findMatchingProbes([{ magic: 'europe' }], 100); + const { allProbes } = await router.findMatchingProbes([{ magic: 'europe' }], 100); - expect(probes.length).to.equal(1); - expect(probes[0]!.location.country).to.equal('GB'); + expect(allProbes.length).to.equal(1); + expect(allProbes[0]!.location.country).to.equal('GB'); }); it('should return match (region alias)', async () => { @@ -463,10 +479,10 @@ describe('probe router', () => { fetchSocketsMock.resolves(sockets as never); - const probes = await router.findMatchingProbes([{ magic: 'north africa' }], 100); + const { allProbes } = await router.findMatchingProbes([{ magic: 'north africa' }], 100); - expect(probes.length).to.equal(1); - expect(probes[0]!.location.region).to.equal('Northern Africa'); + expect(allProbes.length).to.equal(1); + expect(allProbes[0]!.location.region).to.equal('Northern Africa'); }); it('should not return match (non-existing region alias)', async () => { @@ -476,9 +492,9 @@ describe('probe router', () => { fetchSocketsMock.resolves(sockets as never); - const probes = await router.findMatchingProbes([{ magic: 'south africa' }], 100); + const { allProbes } = await router.findMatchingProbes([{ magic: 'south africa' }], 100); - expect(probes.length).to.equal(0); + expect(allProbes.length).to.equal(0); }); it('should return match (country alias)', async () => { @@ -492,10 +508,10 @@ describe('probe router', () => { fetchSocketsMock.resolves(sockets as never); - const probes = await router.findMatchingProbes(locations, 100); + const { allProbes } = await router.findMatchingProbes(locations, 100); - expect(probes.length).to.equal(1); - expect(probes[0]!.location.country).to.equal('GB'); + expect(allProbes.length).to.equal(1); + expect(allProbes[0]!.location.country).to.equal('GB'); }); it('should return match (magic nested)', async () => { @@ -509,10 +525,10 @@ describe('probe router', () => { fetchSocketsMock.resolves(sockets as never); - const probes = await router.findMatchingProbes(locations, 100); + const { allProbes } = await router.findMatchingProbes(locations, 100); - expect(probes.length).to.equal(1); - expect(probes[0]!.location.country).to.equal('GB'); + expect(allProbes.length).to.equal(1); + expect(allProbes[0]!.location.country).to.equal('GB'); }); it('should return result sorted by priority of magic fields in case of partial match', async () => { @@ -523,15 +539,15 @@ describe('probe router', () => { ]; fetchSocketsMock.resolves(sockets as never); - const probes = await router.findMatchingProbes([ + const { allProbes } = await router.findMatchingProbes([ { magic: 'd' }, ], 100); expect(fetchSocketsMock.calledOnce).to.be.true; - expect(probes.length).to.equal(3); - expect(probes[0]!.location.country).to.equal('DE'); - expect(probes[1]!.location.country).to.equal('RS'); - expect(probes[2]!.location.country).to.equal('CZ'); + expect(allProbes.length).to.equal(3); + expect(allProbes[0]!.location.country).to.equal('DE'); + expect(allProbes[1]!.location.country).to.equal('RS'); + expect(allProbes[2]!.location.country).to.equal('CZ'); }); it('should ignore low-priority partial matches if there is an exact match', async () => { @@ -542,13 +558,13 @@ describe('probe router', () => { ]; fetchSocketsMock.resolves(sockets as never); - const probes = await router.findMatchingProbes([ + const { allProbes } = await router.findMatchingProbes([ { magic: 'vn' }, ], 100); expect(fetchSocketsMock.calledOnce).to.be.true; - expect(probes.length).to.equal(1); - expect(probes[0]!.location.country).to.equal('VN'); + expect(allProbes.length).to.equal(1); + expect(allProbes[0]!.location.country).to.equal('VN'); }); it('should ignore high-priority partial matches if there is an exact match', async () => { @@ -559,13 +575,13 @@ describe('probe router', () => { ]; fetchSocketsMock.resolves(sockets as never); - const probes = await router.findMatchingProbes([ + const { allProbes } = await router.findMatchingProbes([ { magic: 'wars' }, ], 100); expect(fetchSocketsMock.calledOnce).to.be.true; - expect(probes.length).to.equal(1); - expect(probes[0]!.location.normalizedCity).to.equal('poznan'); + expect(allProbes.length).to.equal(1); + expect(allProbes[0]!.location.normalizedCity).to.equal('poznan'); }); it('should ignore same-level partial matches if there is an exact match', async () => { @@ -575,13 +591,13 @@ describe('probe router', () => { ]; fetchSocketsMock.resolves(sockets as never); - const probes = await router.findMatchingProbes([ + const { allProbes } = await router.findMatchingProbes([ { magic: 'york' }, ], 100); expect(fetchSocketsMock.calledOnce).to.be.true; - expect(probes.length).to.equal(1); - expect(probes[0]!.location.country).to.equal('GB'); + expect(allProbes.length).to.equal(1); + expect(allProbes[0]!.location.country).to.equal('GB'); }); it('should shuffle result considering priority of magic fields', async () => { @@ -619,10 +635,10 @@ describe('probe router', () => { ]; fetchSocketsMock.resolves(sockets as never); - const probes1 = await router.findMatchingProbes([ + const { allProbes: probes1 } = await router.findMatchingProbes([ { magic: 'd' }, ], 100); - const probes2 = await router.findMatchingProbes([ + const { allProbes: probes2 } = await router.findMatchingProbes([ { magic: 'd' }, ], 100); @@ -661,10 +677,10 @@ describe('probe router', () => { ]; fetchSocketsMock.resolves(sockets as never); - const probes1 = await router.findMatchingProbes([ + const { allProbes: probes1 } = await router.findMatchingProbes([ { magic: 'de' }, ], 100); - const probes2 = await router.findMatchingProbes([ + const { allProbes: probes2 } = await router.findMatchingProbes([ { magic: 'de' }, ], 100); @@ -689,10 +705,10 @@ describe('probe router', () => { fetchSocketsMock.resolves(sockets as never); - const probes = await router.findMatchingProbes(locations, 100); + const { allProbes } = await router.findMatchingProbes(locations, 100); - expect(probes.length).to.equal(1); - expect(probes[0]!.location.country).to.equal('GB'); + expect(allProbes.length).to.equal(1); + expect(allProbes[0]!.location.country).to.equal('GB'); }); } }); @@ -710,10 +726,10 @@ describe('probe router', () => { fetchSocketsMock.resolves(sockets as never); - const probes = await router.findMatchingProbes(locations, 100); + const { allProbes } = await router.findMatchingProbes(locations, 100); - expect(probes.length).to.equal(1); - expect(probes[0]!.location.country).to.equal('GB'); + expect(allProbes.length).to.equal(1); + expect(allProbes[0]!.location.country).to.equal('GB'); }); } }); @@ -741,10 +757,10 @@ describe('probe router', () => { fetchSocketsMock.resolves(sockets as never); - const probes = await router.findMatchingProbes(locations, 100); + const { allProbes } = await router.findMatchingProbes(locations, 100); - expect(probes.length).to.equal(1); - expect(probes[0]!.location.country).to.equal('US'); + expect(allProbes.length).to.equal(1); + expect(allProbes[0]!.location.country).to.equal('US'); }); } }); @@ -763,10 +779,10 @@ describe('probe router', () => { fetchSocketsMock.resolves(sockets as never); - const probes = await router.findMatchingProbes(locations, 100); + const { allProbes } = await router.findMatchingProbes(locations, 100); - expect(probes.length).to.equal(1); - expect(probes[0]!.location.country).to.equal('GB'); + expect(allProbes.length).to.equal(1); + expect(allProbes[0]!.location.country).to.equal('GB'); }); } }); @@ -795,10 +811,10 @@ describe('probe router', () => { fetchSocketsMock.resolves(sockets as never); - const probes = await router.findMatchingProbes(locations, 100); + const { allProbes } = await router.findMatchingProbes(locations, 100); - expect(probes.length).to.equal(1); - expect(probes[0]!.location.country).to.equal('GB'); + expect(allProbes.length).to.equal(1); + expect(allProbes[0]!.location.country).to.equal('GB'); }); it('should return 0 matches for partial tag value', async () => { @@ -813,9 +829,8 @@ describe('probe router', () => { fetchSocketsMock.resolves(sockets as never); - const probes = await router.findMatchingProbes(locations, 100); - - expect(probes.length).to.equal(0); + const { allProbes } = await router.findMatchingProbes(locations, 100); + expect(allProbes.length).to.equal(0); }); it('should return match for user tag', async () => { @@ -833,10 +848,9 @@ describe('probe router', () => { fetchSocketsMock.resolves(sockets as never); - const probes = await router.findMatchingProbes(locations, 100); - - expect(probes.length).to.equal(1); - expect(probes[0]!.location.country).to.equal('GB'); + const { allProbes } = await router.findMatchingProbes(locations, 100); + expect(allProbes.length).to.equal(1); + expect(allProbes[0]!.location.country).to.equal('GB'); }); }); });