mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-22 15:06:32 +00:00
51bfb5abd1
RUN-02. Trail running joins Running as a run profile, chosen on purpose in the planner and in the GPX import. It asks Valhalla's pedestrian costing for tracks wherever they serve (use_tracks 1) and admits mountain paths up to T2 (max_hiking_difficulty 2), never T3 and above, with ferries only where no way on land exists. Checked with trace_attributes against the public instance: above Freiburg Guenterstal up to the Schauinsland the route went from 82 % roads and 83 % paved (running) to 78 % forest tracks and 81 % compacted, gravel and dirt; Rathen to the Bastei took 28 % of T2 path that running leaves out; Heidelberg's old town stayed as it was. Leaning on footways (walkway_factor) was tried and left out, it moved runs onto paved footways. While a run is planned, a card says what it runs on: its surfaces, the hiking grades of its paths (sac_scale, T1 to T6, as mappers gave them) and its kinds of way, each as shares with lengths that point at their stretches on the map. A way without a grade or a surface is its own row, unknown and never easy, and an info dot says the grades say nothing about whether a way can be run. The head carries the unpaved share and the hardest graded path as badges. The line is matched on foot for this, so steps and mountain paths stay on it; a bike tour's is matched as before. Reading fails, goes offline or is retried as for a bike tour, and nothing of it changes the run. Pace and time are RUN-01's: the running time stays the distance at the runner's pace, with climbs, descents and difficulty left out, whatever the profile. A profile change plans the route again and keeps the pace; a failed trail route keeps the last route and holds Save back. The card's building blocks moved out of the bike card into RouteWaySections, so both cards share one set of rows, bars and status lines. The run_profile column already allowed trail (RUN-01), so no migration. GPX export names a trail run trail_running, list_tours describes the profile as a routing preference, never a statement that a way can be run.
69 lines
3.0 KiB
TypeScript
69 lines
3.0 KiB
TypeScript
/**
|
|
* RUN-01: `Migration20200101045510_a_tour_can_be_a_run` switches the run type
|
|
* on and gives a tour its run profile and pace. The profile column holds a
|
|
* CHECK list the entity carries as its enum, the pace a range the contract
|
|
* holds too: a value one side takes and the other refuses would be a run the
|
|
* planner can save and the database cannot store.
|
|
*/
|
|
import { ToursRunProfile } from '../../../src/db/entities/Tours.entity';
|
|
import { createMigrationOrm, migrateTo, pendingNames, rawExec, rawQuery } from '../../helpers/migration-step';
|
|
import type { MikroORM } from '@mikro-orm/sqlite';
|
|
import { MAX_RUN_PACE_SECONDS_PER_KM, MIN_RUN_PACE_SECONDS_PER_KM, RUN_PROFILES } from '@trek/shared';
|
|
|
|
import { describe, expect, it } from 'vitest';
|
|
|
|
const TARGET = 'Migration20200101045510_a_tour_can_be_a_run';
|
|
|
|
async function ormBeforeTarget(): Promise<MikroORM> {
|
|
const orm = await createMigrationOrm();
|
|
const names = await pendingNames(orm);
|
|
const idx = names.indexOf(TARGET);
|
|
expect(idx).toBeGreaterThan(0);
|
|
await migrateTo(orm, names[idx - 1]);
|
|
return orm;
|
|
}
|
|
|
|
describe('run columns match the contract', () => {
|
|
it('TOURRUNMIG-001: stores exactly the profiles the contract knows, trail running (RUN-02) included', () => {
|
|
expect([...Object.values(ToursRunProfile)].sort()).toEqual([...RUN_PROFILES].sort());
|
|
});
|
|
|
|
it('TOURRUNMIG-002: switches the run type on, leaves every tour as it was and refuses a pace out of bounds', async () => {
|
|
const orm = await ormBeforeTarget();
|
|
try {
|
|
await rawExec(
|
|
orm,
|
|
"INSERT INTO users (id, username, email, password_hash) VALUES (1, 'owner', 'owner@test', 'x')",
|
|
);
|
|
await rawExec(orm, "INSERT INTO trips (id, user_id, title) VALUES (10, 1, 'Alps')");
|
|
await rawExec(
|
|
orm,
|
|
"INSERT INTO places (id, trip_id, name) VALUES (100, 10, 'Ridge walk'), (101, 10, 'River loop')",
|
|
);
|
|
await rawExec(orm, "INSERT INTO tours (place_id, tour_type, distance) VALUES (100, 'hike', 12.5)");
|
|
|
|
await migrateTo(orm, TARGET);
|
|
|
|
expect(await rawQuery(orm, "SELECT enabled FROM tour_types WHERE key = 'run'")).toEqual([{ enabled: 1 }]);
|
|
expect(await rawQuery(orm, 'SELECT run_profile, run_pace_seconds_per_km FROM tours')).toEqual([
|
|
{ run_profile: null, run_pace_seconds_per_km: null },
|
|
]);
|
|
await rawExec(
|
|
orm,
|
|
"INSERT INTO tours (place_id, tour_type, run_profile, run_pace_seconds_per_km) VALUES (101, 'run', 'running', 360)",
|
|
);
|
|
const pace = (value: number) =>
|
|
rawExec(orm, 'UPDATE tours SET run_pace_seconds_per_km = ? WHERE place_id = 101', [value]);
|
|
await pace(MIN_RUN_PACE_SECONDS_PER_KM);
|
|
await pace(MAX_RUN_PACE_SECONDS_PER_KM);
|
|
await expect(pace(MIN_RUN_PACE_SECONDS_PER_KM - 1)).rejects.toThrow(/CHECK/);
|
|
await expect(pace(MAX_RUN_PACE_SECONDS_PER_KM + 1)).rejects.toThrow(/CHECK/);
|
|
await expect(rawExec(orm, "UPDATE tours SET run_profile = 'sprint' WHERE place_id = 101")).rejects.toThrow(
|
|
/CHECK/,
|
|
);
|
|
} finally {
|
|
await orm.close(true);
|
|
}
|
|
}, 30000);
|
|
});
|