mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-23 23:46:35 +00:00
c83eb42730
RUN-01. Run joins the tour types in the planner, with Running as its first profile, chosen on purpose like a bike tour's. A run is routed on foot for runnable, mostly paved ways: tracks and ferries only where there is no other way (use_tracks 0, use_ferry 0, measured against the public Valhalla), no hiking limit and no via ferrata. The runner types a pace in min/km; the running time is the distance at that pace and nothing else, shown under the field as badges (8 km x 6:00 /km = 48 min). TREK suggests no pace: without one the time stays unknown and Save waits, text that is no pace or a pace outside 2:00 to 20:00 is marked. The info dot, the overview and the head say climbs, descents and difficulty are not in the time, and the time reads as planned everywhere, so it never passes for a recorded one; the heights are shown as for any tour on land. Type, profile, pace and route come back on opening, survive a draft reload, an undo of the type, a copy and a take-over; switching to another type drops profile and pace. Day assignment, the 3D terrain view (with topo and satellite, no ferratas) and a routing failure keeping the last route work as for every tour. A GPX file can be imported as a run with its profile and no pace, and planned over as a run. Runs export to GPX as running and get their own places along the way (water, toilets, parks, viewpoints). The server keeps tours.run_profile (CHECK list, already holding trail for RUN-02, since SQLite cannot widen a CHECK in place) and tours.run_pace_seconds_per_km (120 to 1200), switches the run type on, refuses a run without a profile and run fields on any other type, and list_tours names both with what the planned time does not include.
27 lines
994 B
TypeScript
27 lines
994 B
TypeScript
import {
|
|
MAX_RUN_PACE_SECONDS_PER_KM,
|
|
MIN_RUN_PACE_SECONDS_PER_KM,
|
|
runMinutesAtPace,
|
|
runPaceSecondsPerKmSchema,
|
|
} from './run-pace';
|
|
|
|
import { describe, expect, it } from 'vitest';
|
|
|
|
describe('run pace (RUN-01)', () => {
|
|
it('plans the distance at the pace: 8 km at 6:00 min/km is 48 minutes', () => {
|
|
expect(runMinutesAtPace(8, 360)).toBe(48);
|
|
expect(runMinutesAtPace(10, 330)).toBe(55);
|
|
// Rounded to the minute, never cut short: 5.03 km at 5:00 is 25 min 9 s.
|
|
expect(runMinutesAtPace(5.03, 300)).toBe(25);
|
|
expect(runMinutesAtPace(0, 360)).toBe(0);
|
|
});
|
|
|
|
it('takes whole seconds between 2:00 and 20:00 min/km only', () => {
|
|
expect([MIN_RUN_PACE_SECONDS_PER_KM, MAX_RUN_PACE_SECONDS_PER_KM]).toEqual([120, 1200]);
|
|
expect(runPaceSecondsPerKmSchema.safeParse(360).success).toBe(true);
|
|
for (const pace of [0, -1, 119, 1201, 300.5, Number.POSITIVE_INFINITY]) {
|
|
expect(runPaceSecondsPerKmSchema.safeParse(pace).success).toBe(false);
|
|
}
|
|
});
|
|
});
|