mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-19 05:26:47 +00:00
107 lines
4.3 KiB
TypeScript
107 lines
4.3 KiB
TypeScript
import { RuntimeEnvService } from '../../../src/nest/app-config/runtime-env.service';
|
|
import { FerratasOverpassClient } from '../../../src/nest/ferratas/ferratas-overpass.client';
|
|
import { parseOverpassEndpoints } from '../../../src/nest/maps/overpass.constants';
|
|
import { Test } from '@nestjs/testing';
|
|
|
|
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
|
|
const integrations = { overpassUrl: undefined as string | undefined, overpassTimeoutMs: 5000 };
|
|
let client: FerratasOverpassClient;
|
|
|
|
beforeEach(async () => {
|
|
integrations.overpassUrl = 'https://a.example/api/interpreter,https://b.example/api/interpreter';
|
|
integrations.overpassTimeoutMs = 5000;
|
|
const moduleRef = await Test.createTestingModule({
|
|
providers: [FerratasOverpassClient, { provide: RuntimeEnvService, useValue: { env: () => ({ integrations }) } }],
|
|
}).compile();
|
|
client = moduleRef.get(FerratasOverpassClient);
|
|
vi.spyOn(console, 'warn').mockImplementation(() => {});
|
|
});
|
|
|
|
afterEach(() => {
|
|
vi.unstubAllGlobals();
|
|
vi.restoreAllMocks();
|
|
});
|
|
|
|
const json = (body: unknown, status = 200) => new Response(JSON.stringify(body), { status });
|
|
|
|
describe('parseOverpassEndpoints', () => {
|
|
it('reads a comma-separated list of http(s) endpoints', () => {
|
|
expect(parseOverpassEndpoints(' https://a.example/i , http://b.local/i ')).toEqual([
|
|
'https://a.example/i',
|
|
'http://b.local/i',
|
|
]);
|
|
});
|
|
|
|
it('drops what is not an http(s) URL', () => {
|
|
expect(parseOverpassEndpoints('ftp://x/i,not a url,https://ok.example/i')).toEqual(['https://ok.example/i']);
|
|
});
|
|
|
|
it('falls back to the public mirrors when nothing usable is configured', () => {
|
|
for (const raw of [undefined, '', 'nope']) {
|
|
const list = parseOverpassEndpoints(raw);
|
|
expect(list.length).toBeGreaterThan(1);
|
|
expect(list).toContain('https://overpass-api.de/api/interpreter');
|
|
}
|
|
});
|
|
});
|
|
|
|
describe('FerratasOverpassClient.query', () => {
|
|
it('POSTs the query with a user agent and a deadline, and answers the elements', async () => {
|
|
const fetcher = vi.fn(async (_url: string, _init: RequestInit) => json({ elements: [{ type: 'way', id: 1 }] }));
|
|
vi.stubGlobal('fetch', fetcher);
|
|
integrations.overpassUrl = 'https://a.example/api/interpreter';
|
|
const elements = await client.query('[out:json];way(1);out;');
|
|
expect(elements).toEqual([{ type: 'way', id: 1 }]);
|
|
const [url, init] = fetcher.mock.calls[0] ?? [];
|
|
expect(url).toBe('https://a.example/api/interpreter');
|
|
expect(init?.method).toBe('POST');
|
|
expect(init?.body).toBe(`data=${encodeURIComponent('[out:json];way(1);out;')}`);
|
|
expect((init?.headers as Record<string, string>)['User-Agent']).toMatch(/^TREK Travel Planner/);
|
|
expect(init?.signal).toBeInstanceOf(AbortSignal);
|
|
});
|
|
|
|
it('takes the first mirror that answers well and cancels the others', async () => {
|
|
const signals: AbortSignal[] = [];
|
|
vi.stubGlobal(
|
|
'fetch',
|
|
vi.fn(async (url: string, init: RequestInit) => {
|
|
if (init.signal) signals.push(init.signal);
|
|
if (url.startsWith('https://a.')) return json({ elements: [], remark: 'runtime error: timeout' });
|
|
return json({ elements: [{ type: 'way', id: 2 }] });
|
|
}),
|
|
);
|
|
expect(await client.query('q')).toEqual([{ type: 'way', id: 2 }]);
|
|
expect(signals.every((s) => s.aborted)).toBe(true);
|
|
});
|
|
|
|
it('throws once every mirror failed: an HTTP error, a remark, a body that is not OSM', async () => {
|
|
vi.stubGlobal(
|
|
'fetch',
|
|
vi.fn(async (url: string) => (url.startsWith('https://a.') ? json({}, 504) : json({ elements: 'nope' }))),
|
|
);
|
|
await expect(client.query('q')).rejects.toThrow('Could not reach any Overpass endpoint');
|
|
});
|
|
|
|
it('throws when a mirror cannot be reached at all', async () => {
|
|
vi.stubGlobal(
|
|
'fetch',
|
|
vi.fn(async () => {
|
|
throw new TypeError('fetch failed');
|
|
}),
|
|
);
|
|
await expect(client.query('q')).rejects.toThrow('Could not reach any Overpass endpoint');
|
|
});
|
|
|
|
it('refuses an answer larger than the cap', async () => {
|
|
vi.stubGlobal(
|
|
'fetch',
|
|
vi.fn(
|
|
async () =>
|
|
new Response('{"elements":[]}', { status: 200, headers: { 'content-length': String(9 * 1024 * 1024) } }),
|
|
),
|
|
);
|
|
await expect(client.query('q')).rejects.toThrow('Could not reach any Overpass endpoint');
|
|
});
|
|
});
|