Skip to content

Commit c3e8bd9

Browse files
committed
networkDesign backend: fix network design unit tests
Add a mock class for the `EvolutionaryTransitNetworkDesignJob`, to specify the data to prepare (lines, services, etc), that can be used in individual unit tests. Update the tests so they work with the new code.
1 parent 906071f commit c3e8bd9

File tree

4 files changed

+401
-266
lines changed

4 files changed

+401
-266
lines changed

packages/transition-backend/src/services/evolutionaryAlgorithm/candidate/__tests__/LineAndNumberOfVehiclesNetworkCandidate.test.ts

Lines changed: 89 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -15,22 +15,29 @@ import Line from 'transition-common/lib/services/line/Line';
1515
import Service from 'transition-common/lib/services/service/Service';
1616
import LineCollection from 'transition-common/lib/services/line/LineCollection';
1717
import ServiceCollection from 'transition-common/lib/services/service/ServiceCollection';
18-
18+
import AgencyCollection from 'transition-common/lib/services/agency/AgencyCollection';
1919
import Scenario from 'transition-common/lib/services/scenario/Scenario';
20-
import { TransitNetworkDesignJobWrapper } from '../../../networkDesign/transitNetworkDesign/TransitNetworkDesignJobWrapper';
2120
import { EvolutionaryTransitNetworkDesignJobParameters, EvolutionaryTransitNetworkDesignJobType } from '../../../networkDesign/transitNetworkDesign/evolutionary/types';
2221
import { ExecutableJob } from '../../../executableJob/ExecutableJob';
2322
import jobsDbQueries from '../../../../models/db/jobs.db.queries';
23+
import { createMockJobExecutor } from '../../../networkDesign/transitNetworkDesign/__tests__/MockTransitNetworkDesignJobWrapper';
24+
import OdTripSimulation from '../../../simulation/methods/OdTripSimulation';
2425

2526
const socketMock = new EventEmitter();
26-
const mockSimulateScenario = jest.fn();
27+
// Mock the od trip simulation method
28+
const mockSimulateScenario = jest.fn() as jest.MockedFunction<OdTripSimulation['simulate']>;
29+
OdTripSimulation.prototype.simulate = mockSimulateScenario;
2730

2831
// Mock random, cloning with seed does not seem to work for those tests
2932
jest.mock('random', () => ({
3033
float: jest.fn()
3134
}));
3235
const mockedRandomFloat = random.float as jest.MockedFunction<typeof random.float>;
3336

37+
// Mock the job loader
38+
jest.mock('../../../../models/db/jobs.db.queries');
39+
const mockJobsDbQueries = jobsDbQueries as jest.Mocked<typeof jobsDbQueries>;
40+
3441
const line1 = new Line({
3542
id: uuidV4(),
3643
internal_id: 'InternalId test 1',
@@ -121,9 +128,6 @@ const defaultJobParameters: EvolutionaryTransitNetworkDesignJobParameters = {
121128
}
122129
};
123130

124-
// Mock the job loader
125-
jest.mock('../../../models/db/jobs.db.queries');
126-
const mockJobsDbQueries = jobsDbQueries as jest.Mocked<typeof jobsDbQueries>;
127131
const jobId = 1;
128132
const mockJobAttributes = {
129133
id: jobId,
@@ -132,25 +136,27 @@ const mockJobAttributes = {
132136
status: 'pending' as const,
133137
internal_data: {},
134138
data: {
135-
parameters: {
136-
137-
} as Partial<EvolutionaryTransitNetworkDesignJobParameters>
139+
parameters: defaultJobParameters
138140
},
139141
resources: {
140142
files: {
141-
input: 'something.csv'
143+
transitDemand: 'demand.csv',
144+
nodeWeight: 'weights.csv'
142145
}
143146
}
144147
};
145-
let job: ExecutableJob<EvolutionaryTransitNetworkDesignJobType>;
146148

147-
const options: AlgoTypes.RuntimeAlgorithmData = {
148-
agencies: [],
149-
randomGenerator: random,
150-
lineCollection: new LineCollection([line1, line2, line3], {}),
151-
linesToKeep: [],
152-
services: new ServiceCollection([service], {}),
153-
lineServices: {
149+
const createMockJobExecutorForTest = async (parameters: Partial<EvolutionaryTransitNetworkDesignJobParameters> = {}) => {
150+
const testJobParameters = _cloneDeep(mockJobAttributes);
151+
testJobParameters.data.parameters = { ...defaultJobParameters, ...parameters };
152+
mockJobsDbQueries.read.mockResolvedValueOnce(testJobParameters);
153+
const job = await ExecutableJob.loadTask(1);
154+
155+
const lineCollection = new LineCollection([line1, line2, line3], {});
156+
const agencyCollection = new AgencyCollection([], {});
157+
const serviceCollection = new ServiceCollection([service], {});
158+
159+
const lineServices: AlgoTypes.LineServices = {
154160
[line1.getId()]: [
155161
{
156162
numberOfVehicles: 4,
@@ -181,33 +187,16 @@ const options: AlgoTypes.RuntimeAlgorithmData = {
181187
service: new Service({ id: uuidV4() }, false)
182188
}
183189
]
184-
},
185-
nonSimulatedServices: [],
186-
populationSize: 0,
187-
options: {
188-
populationSizeMin: 2,
189-
populationSizeMax: 2,
190-
numberOfElites: 0,
191-
numberOfRandoms: 0,
192-
crossoverNumberOfCuts: 1,
193-
crossoverProbability: 1,
194-
mutationProbability: 0,
195-
tournamentSize: 2,
196-
tournamentProbability: 1,
197-
numberOfGenerations: 3,
198-
shuffleGenes: false,
199-
keepCandidates: 1,
200-
keepGenerations: 1
201-
}
202-
}
190+
};
203191

204-
const getJobExecutor = async (parameters: Partial<EvolutionaryTransitNetworkDesignJobParameters>) => {
205-
const testJobParameters = _cloneDeep(mockJobAttributes);
206-
testJobParameters.data.parameters = parameters;
207-
mockJobsDbQueries.read.mockResolvedValueOnce(testJobParameters);
208-
job = await ExecutableJob.loadTask(1);
209-
return new TransitNetworkDesignJobWrapper(job as ExecutableJob<EvolutionaryTransitNetworkDesignJobType>, { progressEmitter: new EventEmitter(), isCancelled: () => false });
210-
}
192+
return createMockJobExecutor(job as ExecutableJob<EvolutionaryTransitNetworkDesignJobType>, {
193+
lineCollection,
194+
agencyCollection,
195+
serviceCollection,
196+
simulatedLineCollection: lineCollection,
197+
lineServices
198+
});
199+
};
211200

212201
describe('Test candidate preparation', () => {
213202

@@ -216,22 +205,25 @@ describe('Test candidate preparation', () => {
216205
// 7 vehicles, lvl 1 for line 1, lvl 0 for line 3
217206
const testParameters = _cloneDeep(defaultJobParameters);
218207
testParameters.transitNetworkDesignParameters.nbOfVehicles = 7;
219-
const jobExecutor = await getJobExecutor(testParameters);
208+
const jobExecutor = await createMockJobExecutorForTest(testParameters);
220209

221210
const networkCandidate = new NetworkCandidate({ lines: [true, false, true], name: 'test' }, jobExecutor);
222211
await networkCandidate.prepareScenario(socketMock);
223212
const scenario = networkCandidate.getScenario();
224213
expect(scenario).toBeDefined();
225214
expect((scenario as Scenario).attributes.services).toEqual([
226-
(options.lineServices[line1.getId()] as any)[1].service.getId(),
227-
(options.lineServices[line3.getId()] as any)[0].service.getId()
215+
jobExecutor.lineServices[line1.getId()][1].service.getId(),
216+
jobExecutor.lineServices[line3.getId()][0].service.getId()
228217
]);
229218
});
230219

231220
test('Test with too few vehicles', async () => {
232221
// 5 vehicles: startup service should have at least 6
233-
simulationRun.attributes.data.transitNetworkDesignParameters.nbOfVehicles = 5;
234-
const networkCandidate = new NetworkCandidate({ lines: [true, false, true], name: 'test' }, options);
222+
const testParameters = _cloneDeep(defaultJobParameters);
223+
testParameters.transitNetworkDesignParameters.nbOfVehicles = 5;
224+
const jobExecutor = await createMockJobExecutorForTest(testParameters);
225+
226+
const networkCandidate = new NetworkCandidate({ lines: [true, false, true], name: 'test' }, jobExecutor);
235227
await expect(networkCandidate.prepareScenario(socketMock))
236228
.rejects
237229
.toThrow('Impossible to assign minimal level of service for this combination');
@@ -246,14 +238,18 @@ describe('Test candidate preparation', () => {
246238
mockedRandomFloat.mockReturnValueOnce(0.1);
247239
mockedRandomFloat.mockReturnValueOnce(0.1);
248240
mockedRandomFloat.mockReturnValueOnce(0.6);
249-
simulationRun.attributes.data.transitNetworkDesignParameters.nbOfVehicles = 12;
250-
const networkCandidate = new NetworkCandidate({ lines: [true, false, true], name: 'test' }, options);
241+
242+
const testParameters = _cloneDeep(defaultJobParameters);
243+
testParameters.transitNetworkDesignParameters.nbOfVehicles = 12;
244+
const jobExecutor = await createMockJobExecutorForTest(testParameters);
245+
246+
const networkCandidate = new NetworkCandidate({ lines: [true, false, true], name: 'test' }, jobExecutor);
251247
await networkCandidate.prepareScenario(socketMock);
252248
const scenario = networkCandidate.getScenario();
253249
expect(scenario).toBeDefined();
254250
expect((scenario as Scenario).attributes.services).toEqual([
255-
(options.lineServices[line1.getId()] as any)[1].service.getId(),
256-
(options.lineServices[line3.getId()] as any)[1].service.getId()
251+
jobExecutor.lineServices[line1.getId()][1].service.getId(),
252+
jobExecutor.lineServices[line3.getId()][1].service.getId()
257253
]);
258254
});
259255

@@ -262,34 +258,49 @@ describe('Test candidate preparation', () => {
262258
describe('Simulate scenario and serialize result', () => {
263259

264260
let networkCandidate: NetworkCandidate;
265-
// Set the lines' schedules by services, which is supposed to exist when simulating candidates
266-
options.lineCollection.getFeatures().forEach(line => {
267-
options.lineServices[line.getId()]
268-
.forEach(lineService => line.attributes.scheduleByServiceId[lineService.service.getId()] = {
269-
service_id: lineService.service.getId()
270-
} as any);
271-
})
261+
let jobExecutor: any;
272262

273263
beforeEach(async () => {
274264
mockSimulateScenario.mockClear();
275-
// 7 vehicles, lvl 1 for line 1, lvl 0 for line 2
276-
mockedRandomFloat.mockReturnValue(0.1);
277-
simulationRun.attributes.data.transitNetworkDesignParameters.nbOfVehicles = 7;
278-
networkCandidate = new NetworkCandidate({ lines: [true, false, true], name: 'test' }, options);
279-
await networkCandidate.prepareScenario(socketMock);
265+
280266
})
281267

282268
test('Test successful simulation and serialization', async () => {
269+
// 7 vehicles, lvl 1 for line 1, lvl 0 for line 3
270+
mockedRandomFloat.mockReturnValue(0.1);
271+
272+
// Prepare job and candidates
273+
const testParameters = _cloneDeep(defaultJobParameters);
274+
testParameters.transitNetworkDesignParameters.nbOfVehicles = 7;
275+
jobExecutor = await createMockJobExecutorForTest(testParameters);
276+
277+
// Set the lines' schedules by services, which is supposed to exist when simulating candidates
278+
jobExecutor.simulatedLineCollection.getFeatures().forEach((line: Line) => {
279+
jobExecutor.lineServices[line.getId()]
280+
.forEach((lineService: AlgoTypes.LineLevelOfService) => {
281+
line.attributes.scheduleByServiceId[lineService.service.getId()] = {
282+
service_id: lineService.service.getId()
283+
} as any;
284+
});
285+
});
286+
networkCandidate = new NetworkCandidate({ lines: [true, false, true], name: 'test' }, jobExecutor);
287+
await networkCandidate.prepareScenario(socketMock);
288+
283289
// Mock result
284-
const result = {
285-
totalFitness: Number.NaN,
286-
results: {}
290+
const odSimulationResult = {
291+
fitness: 123.45,
292+
results: {} as any // FIXME Put some data if needed
287293
};
288-
mockSimulateScenario.mockResolvedValueOnce(result);
294+
mockSimulateScenario.mockResolvedValueOnce(odSimulationResult);
289295
await networkCandidate.simulate();
290296

291297
expect(mockSimulateScenario).toHaveBeenCalledTimes(1);
292-
expect(networkCandidate.getResult()).toEqual(result);
298+
expect(networkCandidate.getResult()).toEqual({
299+
totalFitness: NaN,
300+
results: {
301+
OdTripSimulation: odSimulationResult
302+
}
303+
});
293304

294305
expect(networkCandidate.serialize()).toEqual({
295306
maxNumberOfVehicles: 7,
@@ -298,14 +309,19 @@ describe('Simulate scenario and serialize result', () => {
298309
lines: {
299310
[line1.getId()]: {
300311
shortname: line1.attributes.shortname,
301-
nbVehicles: (options.lineServices[line1.getId()])[1].numberOfVehicles
312+
nbVehicles: jobExecutor.lineServices[line1.getId()][1].numberOfVehicles
302313
},
303314
[line3.getId()]: {
304315
shortname: line3.attributes.shortname,
305-
nbVehicles: (options.lineServices[line3.getId()])[0].numberOfVehicles
316+
nbVehicles: jobExecutor.lineServices[line3.getId()][0].numberOfVehicles
306317
}
307318
},
308-
result
319+
result: {
320+
totalFitness: NaN,
321+
results: {
322+
OdTripSimulation: odSimulationResult
323+
}
324+
}
309325
})
310326
});
311327

0 commit comments

Comments
 (0)