mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-22 23:16:46 +00:00
3659297af1
A route planned in Google My Maps or Google Earth can now become a tour without converting it to GPX first. The import button and a file dropped on the tours workspace take .kml and .kmz files beside .gpx, through the same preview, the same import routes and the same transaction as a GPX file. The preview shows every line of the file on its own, numbered, with its distance and heights; two lines of one placemark are numbered after its name and never joined. Heights count only where they are heights: kept for an absolute line, dropped for any other altitude mode and for the all-zero placeholder My Maps writes, so a missing height stays unknown. What the file holds besides its lines (pins, areas, overlays, links to other files) is counted in a dashed card, and nothing is fetched from elsewhere. The KML reading reuses the place import's codec. A KMZ only ever has its KML entry unpacked, counted byte by byte against the decompressed cap whatever its header claims. A file that cannot be read is refused with its reason (damaged, an archive without KML, too large unpacked), and the toast names the file and the reason. A KML or KMZ import keeps the gpx origin of a file import; its provenance names the real format from the stored file name.
78 lines
4.0 KiB
TypeScript
78 lines
4.0 KiB
TypeScript
/**
|
|
* IO-02: the file a Tours import reads. A GPX file goes through the GPX reader
|
|
* without its waypoints, as it always did; a KML or KMZ file, by its
|
|
* extension, through the KML reader for its lines and what it leaves out. A
|
|
* file that cannot be read is a 400 that names its reason, and a file without
|
|
* a line answers GPX's body as before and a format-named one with a code.
|
|
*/
|
|
import type { PlaceImportService } from '../../../src/nest/place-import/place-import.service';
|
|
import { KmlReadError } from '../../../src/nest/place-import/place-import.types';
|
|
import { noTrackFound, readTourFile } from '../../../src/nest/tours/tour-file-import.helpers';
|
|
import { buildPlaceImportService } from '../../helpers/place-import';
|
|
import { HttpException } from '@nestjs/common';
|
|
|
|
import { describe, expect, it, vi } from 'vitest';
|
|
|
|
const GPX = Buffer.from(`<gpx><wpt lat="1" lon="2"><name>Hut</name></wpt>
|
|
<trk><trkseg><trkpt lat="48" lon="11"/><trkpt lat="48.01" lon="11.01"/></trkseg></trk></gpx>`);
|
|
const KML = Buffer.from(`<kml><Placemark><name>Coast</name>
|
|
<LineString><coordinates>11,48 11.01,48.01</coordinates></LineString></Placemark>
|
|
<Placemark><Polygon/></Placemark></kml>`);
|
|
|
|
describe('readTourFile', () => {
|
|
it('TOUR-FILE-001: reads a GPX file for its tracks, never its waypoints, and counts nothing left out', async () => {
|
|
const read = await readTourFile(buildPlaceImportService(), GPX, 'ridge.gpx');
|
|
expect(read.rows.map((row) => row.name)).toEqual(['ridge']);
|
|
expect(read.leftOut).toBeUndefined();
|
|
});
|
|
|
|
it('TOUR-FILE-002: reads a file without a known extension, or without a name, as GPX as before', async () => {
|
|
expect((await readTourFile(buildPlaceImportService(), GPX, 'track.xml')).rows).toHaveLength(1);
|
|
expect((await readTourFile(buildPlaceImportService(), GPX, undefined)).rows).toHaveLength(1);
|
|
});
|
|
|
|
it('TOUR-FILE-003: reads a KML file for its lines and says what it held besides', async () => {
|
|
const read = await readTourFile(buildPlaceImportService(), KML, 'coast.KML');
|
|
expect(read.rows.map((row) => row.name)).toEqual(['Coast']);
|
|
expect(read.leftOut).toEqual({ points: 0, areas: 1, other: 0 });
|
|
});
|
|
|
|
it('TOUR-FILE-004: a file it cannot read is a 400 carrying the reason as its code', async () => {
|
|
let error: unknown;
|
|
try {
|
|
await readTourFile(buildPlaceImportService(), Buffer.from('not a zip'), 'trip.kmz');
|
|
} catch (err) {
|
|
error = err;
|
|
}
|
|
expect(error).toBeInstanceOf(HttpException);
|
|
expect((error as HttpException).getStatus()).toBe(400);
|
|
expect((error as HttpException).getResponse()).toEqual({ error: 'Invalid KMZ archive.', code: 'archive_invalid' });
|
|
});
|
|
|
|
it('TOUR-FILE-005: any other failure passes through as it is', async () => {
|
|
const placeImport = {
|
|
readMapFileTracks: vi.fn().mockRejectedValue(new Error('disk gone')),
|
|
} as unknown as PlaceImportService;
|
|
await expect(readTourFile(placeImport, KML, 'coast.kml')).rejects.toThrow('disk gone');
|
|
const refused = { readMapFileTracks: vi.fn().mockRejectedValue(new KmlReadError('archive_too_large', 'Too big')) };
|
|
await expect(readTourFile(refused as unknown as PlaceImportService, KML, 'x.kmz')).rejects.toMatchObject({
|
|
code: 'tour_file_unreadable',
|
|
message: 'Too big',
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('noTrackFound', () => {
|
|
it('TOUR-FILE-006: answers a GPX file with the body the route has always sent', () => {
|
|
const error = noTrackFound('ridge.gpx');
|
|
expect(error.getStatus()).toBe(400);
|
|
expect(error.getResponse()).toEqual({ error: 'No track or route found in GPX file' });
|
|
expect(noTrackFound(undefined).getResponse()).toEqual({ error: 'No track or route found in GPX file' });
|
|
});
|
|
|
|
it('TOUR-FILE-007: names the format of a KML or KMZ file and carries the code', () => {
|
|
expect(noTrackFound('pins.kml').getResponse()).toEqual({ error: 'No line found in KML file', code: 'no_track' });
|
|
expect(noTrackFound('pins.kmz').getResponse()).toEqual({ error: 'No line found in KMZ file', code: 'no_track' });
|
|
});
|
|
});
|