feat: add per-ride history charts with wait time and uptime tracking
Build and Deploy / Build & Push (push) Successful in 3m7s
Build and Deploy / Build & Push (push) Successful in 3m7s
Adds a cron-driven sampler that snapshots Queue-Times waits and Six Flags Fast Lane data every 5 minutes into a new ride_wait_samples table, and a clickable per-ride detail page at /park/[id]/ride/[slug] with Today / 7d / 30d Recharts views plus a 30d uptime pill. Rides are keyed by Queue-Times' stable qt_ride_id so renames don't fragment history. Samples store pre-bucketed local_date and local_time in the park's IANA timezone so aggregations are pure SQL and DST-safe. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,45 @@
|
||||
/**
|
||||
* Slug determinism tests. The slug is a URL-safe secondary key on the
|
||||
* `rides` table — same name must always produce the same slug.
|
||||
*
|
||||
* Run with: npm test
|
||||
*/
|
||||
|
||||
import { test } from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
import { slugifyRideName } from "../lib/ride-slug";
|
||||
|
||||
const CASES: [name: string, expected: string][] = [
|
||||
["Goliath", "goliath"],
|
||||
["X²", "x"],
|
||||
["Lex Luthor: Drop of Doom", "lex-luthor-drop-of-doom"],
|
||||
["Catwoman's Whip", "catwoman-s-whip"],
|
||||
["Façade", "facade"],
|
||||
["Le Monstre", "le-monstre"],
|
||||
["Batman™ The Ride", "batman-the-ride"],
|
||||
["THE RIDDLER's Revenge", "the-riddler-s-revenge"],
|
||||
["Joker y Harley Quinn", "joker-y-harley-quinn"],
|
||||
["Apocalypse the Ride", "apocalypse-the-ride"],
|
||||
[" Leading and trailing ", "leading-and-trailing"],
|
||||
["123 Numeric", "123-numeric"],
|
||||
["!!!", ""],
|
||||
];
|
||||
|
||||
for (const [name, expected] of CASES) {
|
||||
test(`slugify "${name}" → "${expected}"`, () => {
|
||||
assert.equal(slugifyRideName(name), expected);
|
||||
});
|
||||
}
|
||||
|
||||
test("slug is idempotent — slugifying the result yields the same value", () => {
|
||||
for (const [name] of CASES) {
|
||||
const once = slugifyRideName(name);
|
||||
if (once === "") continue;
|
||||
assert.equal(slugifyRideName(once), once, `Expected idempotent slug for "${name}"`);
|
||||
}
|
||||
});
|
||||
|
||||
test("same name always produces the same slug", () => {
|
||||
const name = "Twisted Cyclone";
|
||||
assert.equal(slugifyRideName(name), slugifyRideName(name));
|
||||
});
|
||||
Reference in New Issue
Block a user