almost full coverage for graphhopper georouter

This commit is contained in:
sbriat
2023-08-23 14:32:17 +02:00
parent 158b12b150
commit 66d4d58dd1
7 changed files with 718 additions and 50 deletions

View File

@@ -0,0 +1,14 @@
import { Geodesic } from '@modules/geography/infrastructure/geodesic';
describe('Matcher geodesic', () => {
it('should be defined', () => {
const geodesic: Geodesic = new Geodesic();
expect(geodesic).toBeDefined();
});
it('should get inverse values', () => {
const geodesic: Geodesic = new Geodesic();
const inv = geodesic.inverse(0, 0, 1, 1);
expect(Math.round(inv.azimuth)).toBe(45);
expect(Math.round(inv.distance)).toBe(156900);
});
});

View File

@@ -1,33 +1,259 @@
import { DefaultParamsProviderPort } from '@modules/geography/core/application/ports/default-params-provider.port';
import { GeodesicPort } from '@modules/geography/core/application/ports/geodesic.port';
import {
Path,
PathType,
Route,
} from '@modules/geography/core/domain/route.types';
import { PARAMS_PROVIDER } from '@modules/geography/geography.di-tokens';
GeorouterUnavailableException,
RouteNotFoundException,
} from '@modules/geography/core/domain/route.errors';
import { PathType, Route } from '@modules/geography/core/domain/route.types';
import {
GEODESIC,
PARAMS_PROVIDER,
} from '@modules/geography/geography.di-tokens';
import { GraphhopperGeorouter } from '@modules/geography/infrastructure/graphhopper-georouter';
import { HttpService } from '@nestjs/axios';
import { Test, TestingModule } from '@nestjs/testing';
import { AxiosError } from 'axios';
import { of, throwError } from 'rxjs';
const driverPath: Path = {
type: PathType.DRIVER,
points: [
{
lon: 6,
lat: 47,
},
{
lon: 6.1,
lat: 47.1,
},
{
lon: 6.2,
lat: 47.2,
},
],
const mockHttpService = {
get: jest
.fn()
.mockImplementationOnce(() => {
return throwError(
() => new AxiosError('Axios error', AxiosError.ERR_BAD_REQUEST),
);
})
.mockImplementationOnce(() => {
return throwError(() => 'Router unavailable');
})
.mockImplementationOnce(() => {
return of({
status: 200,
data: {
paths: [
{
distance: 50000,
time: 1800000,
snapped_waypoints: {
coordinates: [
[0, 0],
[10, 10],
],
},
},
],
},
});
})
.mockImplementationOnce(() => {
return of({
status: 200,
data: {
paths: [
{
distance: 50000,
time: 1800000,
points: {
coordinates: [
[0, 0],
[1, 1],
[2, 2],
[3, 3],
[4, 4],
[5, 5],
[6, 6],
[7, 7],
[8, 8],
[9, 9],
[10, 10],
],
},
snapped_waypoints: {
coordinates: [
[0, 0],
[10, 10],
],
},
},
],
},
});
})
.mockImplementationOnce(() => {
return of({
status: 200,
data: {
paths: [
{
distance: 50000,
time: 1800000,
points: {
coordinates: [
[0, 0],
[1, 1],
[2, 2],
[3, 3],
[4, 4],
[5, 5],
[6, 6],
[7, 7],
[8, 8],
[9, 9],
[10, 10],
],
},
details: {
time: [
[0, 1, 180000],
[1, 2, 180000],
[2, 3, 180000],
[3, 4, 180000],
[4, 5, 180000],
[5, 6, 180000],
[6, 7, 180000],
[7, 9, 360000],
[9, 10, 180000],
],
},
snapped_waypoints: {
coordinates: [
[0, 0],
[10, 10],
],
},
},
],
},
});
})
.mockImplementationOnce(() => {
return of({
status: 200,
data: {
paths: [
{
distance: 50000,
time: 1800000,
points: {
coordinates: [
[0, 0],
[1, 1],
[2, 2],
[3, 3],
[4, 4],
[7, 7],
[8, 8],
[9, 9],
[10, 10],
],
},
snapped_waypoints: {
coordinates: [
[0, 0],
[5, 5],
[10, 10],
],
},
details: {
time: [
[0, 1, 180000],
[1, 2, 180000],
[2, 3, 180000],
[3, 4, 180000],
[4, 7, 540000],
[7, 9, 360000],
[9, 10, 180000],
],
},
},
],
},
});
})
.mockImplementationOnce(() => {
return of({
status: 200,
data: {
paths: [
{
distance: 50000,
time: 1800000,
points: {
coordinates: [
[0, 0],
[1, 1],
[2, 2],
[3, 3],
[4, 4],
[5, 5],
[6, 6],
[7, 7],
[8, 8],
[9, 9],
[10, 10],
],
},
snapped_waypoints: {
coordinates: [
[0, 0],
[5, 5],
[10, 10],
],
},
details: {
time: [
[0, 1, 180000],
[1, 2, 180000],
[2, 3, 180000],
[3, 4, 180000],
[4, 7, 540000],
[7, 9, 360000],
[9, 10, 180000],
],
},
instructions: [
{
distance: 25000,
sign: 0,
interval: [0, 5],
text: 'Some instructions',
time: 900000,
},
{
distance: 0,
sign: 5,
interval: [5, 5],
text: 'Waypoint 1',
time: 0,
},
{
distance: 25000,
sign: 2,
interval: [5, 10],
text: 'Some instructions',
time: 900000,
},
{
distance: 0.0,
sign: 4,
interval: [10, 10],
text: 'Arrive at destination',
time: 0,
},
],
},
],
},
});
}),
};
const mockHttpService = {};
const mockGeodesic: GeodesicPort = {
inverse: jest.fn().mockImplementation(() => ({
azimuth: 45,
distance: 50000,
})),
};
const mockDefaultParamsProvider: DefaultParamsProviderPort = {
getParams: jest.fn().mockImplementation(() => ({
@@ -51,6 +277,10 @@ describe('Graphhopper Georouter', () => {
provide: PARAMS_PROVIDER,
useValue: mockDefaultParamsProvider,
},
{
provide: GEODESIC,
useValue: mockGeodesic,
},
],
}).compile();
@@ -62,13 +292,209 @@ describe('Graphhopper Georouter', () => {
expect(graphhopperGeorouter).toBeDefined();
});
it('should return basic driver routes', async () => {
const paths: Path[] = [driverPath];
const driverRoutes: Route[] = await graphhopperGeorouter.routes(paths, {
detailedDistance: false,
detailedDuration: false,
points: true,
});
expect(driverRoutes.length).toBe(1);
it('should fail if route is not found', async () => {
await expect(
graphhopperGeorouter.routes(
[
{
type: PathType.DRIVER,
points: [
{
lon: 0,
lat: 0,
},
{
lon: 1,
lat: 1,
},
],
},
],
{
detailedDistance: false,
detailedDuration: false,
points: false,
},
),
).rejects.toBeInstanceOf(RouteNotFoundException);
});
it('should fail if georouter is unavailable', async () => {
await expect(
graphhopperGeorouter.routes(
[
{
type: PathType.DRIVER,
points: [
{
lon: 0,
lat: 0,
},
{
lon: 1,
lat: 1,
},
],
},
],
{
detailedDistance: false,
detailedDuration: false,
points: false,
},
),
).rejects.toBeInstanceOf(GeorouterUnavailableException);
});
it('should create a basic route', async () => {
const routes: Route[] = await graphhopperGeorouter.routes(
[
{
type: PathType.DRIVER,
points: [
{
lon: 0,
lat: 0,
},
{
lon: 10,
lat: 10,
},
],
},
],
{
detailedDistance: false,
detailedDuration: false,
points: false,
},
);
expect(routes).toHaveLength(1);
expect(routes[0].distance).toBe(50000);
});
it('should create one route with points', async () => {
const routes = await graphhopperGeorouter.routes(
[
{
type: PathType.DRIVER,
points: [
{
lat: 0,
lon: 0,
},
{
lat: 10,
lon: 10,
},
],
},
],
{
detailedDistance: false,
detailedDuration: false,
points: true,
},
);
expect(routes).toHaveLength(1);
expect(routes[0].distance).toBe(50000);
expect(routes[0].duration).toBe(1800);
expect(routes[0].fwdAzimuth).toBe(45);
expect(routes[0].backAzimuth).toBe(225);
expect(routes[0].points).toHaveLength(11);
});
it('should create one route with points and time', async () => {
const routes = await graphhopperGeorouter.routes(
[
{
type: PathType.DRIVER,
points: [
{
lat: 0,
lon: 0,
},
{
lat: 10,
lon: 10,
},
],
},
],
{
detailedDistance: false,
detailedDuration: true,
points: true,
},
);
expect(routes).toHaveLength(1);
expect(routes[0].spacetimeWaypoints).toHaveLength(2);
expect(routes[0].spacetimeWaypoints[1].duration).toBe(1800);
expect(routes[0].spacetimeWaypoints[1].distance).toBeUndefined();
});
it('should create one route with points and missed waypoints extrapolations', async () => {
const routes = await graphhopperGeorouter.routes(
[
{
type: PathType.DRIVER,
points: [
{
lat: 0,
lon: 0,
},
{
lat: 5,
lon: 5,
},
{
lat: 10,
lon: 10,
},
],
},
],
{
detailedDistance: false,
detailedDuration: true,
points: true,
},
);
expect(routes).toHaveLength(1);
expect(routes[0].spacetimeWaypoints).toHaveLength(3);
expect(routes[0].distance).toBe(50000);
expect(routes[0].duration).toBe(1800);
expect(routes[0].fwdAzimuth).toBe(45);
expect(routes[0].backAzimuth).toBe(225);
expect(routes[0].points.length).toBe(9);
});
it('should create one route with points, time and distance', async () => {
const routes = await graphhopperGeorouter.routes(
[
{
type: PathType.DRIVER,
points: [
{
lat: 0,
lon: 0,
},
{
lat: 10,
lon: 10,
},
],
},
],
{
detailedDistance: true,
detailedDuration: true,
points: true,
},
);
expect(routes).toHaveLength(1);
expect(routes[0].spacetimeWaypoints.length).toBe(3);
expect(routes[0].spacetimeWaypoints[1].duration).toBe(990);
expect(routes[0].spacetimeWaypoints[1].distance).toBe(25000);
});
});