Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 66 additions & 0 deletions web/src/config.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { describe, it, expect } from "vitest";
import { VEHICLE_PROFILES, CLASS_LABELS } from "./config";

// Canonical pipeline tokens, hardcoded from pipeline/normalize.py:50-72
// (CLASS_DATEFIELD keys + EBIKE_FIELDS keys) as the source of truth.
const CANONICAL_TOKENS = new Set([
"passenger",
"high_clearance",
"truck",
"bus",
"motorhome",
"4wd_gt50",
"2wd_gt50",
"tracked_ohv_gt50",
"other_ohv_gt50",
"atv",
"motorcycle",
"other_wheeled_ohv",
"tracked_ohv_lt50",
"other_ohv_lt50",
"e_bike1",
"e_bike2",
"e_bike3",
]);

describe("VEHICLE_PROFILES", () => {
it("suv4x4 includes the 4WD/2WD >50″ classes so gt50-only trails show open", () => {
const suv4x4 = VEHICLE_PROFILES.find((p) => p.key === "suv4x4");
expect(suv4x4).toBeDefined();
expect(suv4x4!.tokens).toContain("4wd_gt50");
expect(suv4x4!.tokens).toContain("2wd_gt50");
});

it("every token used by every profile has a label in CLASS_LABELS", () => {
for (const profile of VEHICLE_PROFILES) {
for (const token of profile.tokens) {
expect(
typeof CLASS_LABELS[token] === "string" && CLASS_LABELS[token].length > 0,
`expected CLASS_LABELS["${token}"] (used by profile "${profile.key}") to be a non-empty string`,
).toBe(true);
}
}
});

it("every token used by every profile is a canonical pipeline token", () => {
for (const profile of VEHICLE_PROFILES) {
for (const token of profile.tokens) {
expect(
CANONICAL_TOKENS.has(token),
`token "${token}" (used by profile "${profile.key}") is not in the canonical pipeline token list`,
).toBe(true);
}
}
});

// Deliberate: see plans/003 "decided mapping" — whether a green-sticker
// UTV >50″ counts as a "4WD vehicle >50″" for a given forest's
// designation is a genuine domain ambiguity, so utv_wide is left
// unchanged. Changing this requires the domain check noted in plans/003's
// maintenance notes (checking against an official MVUM PDF).
it("utv_wide tokens are unchanged (deliberate, see plans/003)", () => {
const utvWide = VEHICLE_PROFILES.find((p) => p.key === "utv_wide");
expect(utvWide).toBeDefined();
expect(utvWide!.tokens).toEqual(["other_ohv_gt50", "tracked_ohv_gt50"]);
});
});
7 changes: 6 additions & 1 deletion web/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@
// So each selector option is a *profile* that maps to a set of underlying MVUM
// class tokens (emitted in `classes` by pipeline/normalize.py); a route is open
// to the profile if it permits ANY of those tokens.
//
// suv4x4 includes the 4WD/2WD >50″ classes: MVUM designates by vehicle
// type/width, and a street-legal 4×4 IS a 4WD >50″ — without these tokens,
// gt50-only jeep trails (e.g. Bronco Peak, Cleveland NF) would never show
// open for anyone.

export interface VehicleProfile {
key: string;
Expand All @@ -19,7 +24,7 @@ export interface VehicleProfile {
export const VEHICLE_PROFILES: VehicleProfile[] = [
// Street-legal: highway-legal roads are the big set; plated bikes add moto trails.
{ key: "moto_plated", label: "Dual-sport motorcycle (plated)", group: "Street-legal (plated)", tokens: ["passenger", "high_clearance", "motorcycle"] },
{ key: "suv4x4", label: "SUV / 4×4 / truck (street-legal)", group: "Street-legal (plated)", tokens: ["passenger", "high_clearance"] },
{ key: "suv4x4", label: "SUV / 4×4 / truck (street-legal)", group: "Street-legal (plated)", tokens: ["passenger", "high_clearance", "4wd_gt50", "2wd_gt50"] },
{ key: "car", label: "Car / passenger (street-legal)", group: "Street-legal (plated)", tokens: ["passenger"] },
// Off-road only: OHV-designated routes for that class.
{ key: "dirtbike", label: "Dirt bike (OHV / green sticker)", group: "Off-road only (green / red sticker)", tokens: ["motorcycle"] },
Expand Down
Loading