fix: isCoaster typo in top-level rides loop; simplify test structure
All checks were successful
Build and Deploy / Build & Push (push) Successful in 3m0s

- isCoaster → isCoasterMatch on line 109 (missed rename causing runtime crash
  which returned null from fetchLiveRides, breaking the entire ride panel)
- Rewrite test as two flat arrays: SHOULD_MATCH and SHOULD_NOT_MATCH pairs,
  each with the QT name, RCDB name, and park for context

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-04 15:49:47 -04:00
parent 8324f31972
commit 766fc296a1
2 changed files with 34 additions and 73 deletions

View File

@@ -106,7 +106,7 @@ export async function fetchLiveRides(
isOpen: r.is_open, isOpen: r.is_open,
waitMinutes: r.wait_time ?? 0, waitMinutes: r.wait_time ?? 0,
lastUpdated: r.last_updated, lastUpdated: r.last_updated,
isCoaster: coasterNames ? isCoaster(r.name, coasterNames) : false, isCoaster: coasterNames ? isCoasterMatch(r.name, coasterNames) : false,
}); });
} }

View File

@@ -1,8 +1,8 @@
/** /**
* Coaster name matching tests. * Coaster name matching tests.
* *
* Each case documents a real mismatch found between Queue-Times ride names * Each entry is a real case found between Queue-Times and RCDB names.
* and RCDB coaster names, along with the park where it was observed. * Add new cases here when fixing a mismatch or false positive.
* *
* Run with: npm test * Run with: npm test
*/ */
@@ -11,80 +11,41 @@ import { test } from "node:test";
import assert from "node:assert/strict"; import assert from "node:assert/strict";
import { isCoasterMatch, normalizeForMatch } from "../lib/coaster-match"; import { isCoasterMatch, normalizeForMatch } from "../lib/coaster-match";
// ── Helper ────────────────────────────────────────────────────────────────── function set(...rcdbNames: string[]): Set<string> {
function makeSet(...rcdbNames: string[]): Set<string> {
return new Set(rcdbNames.map(normalizeForMatch)); return new Set(rcdbNames.map(normalizeForMatch));
} }
// ── Should MATCH (Queue-Times name → RCDB name) ────────────────────────────── // ── Should match ─────────────────────────────────────────────────────────────
test("exact match after lowercasing", () => { const SHOULD_MATCH: [qtName: string, rcdbName: string, park: string][] = [
assert.ok(isCoasterMatch("Goliath", makeSet("Goliath"))); ["BATMAN™ The Ride", "Batman The Ride", "Over Georgia / Magic Mountain"],
}); ["THE RIDDLER Mindbender", "Riddler Mindbender", "Over Georgia"],
["THE RIDDLER™'s Revenge", "Riddler's Revenge", "Magic Mountain"],
["CATWOMAN™ Whip", "Catwoman's Whip", "New England"],
["SUPERMAN™: Ultimate Flight", "Superman - Ultimate Flight", "Over Georgia"],
["THE JOKER™ Funhouse Coaster", "Joker Funhouse Coaster", "Over Georgia"],
["The Great American Scream Machine", "Great American Scream Machine", "Over Georgia"],
["Apocalypse", "Apocalypse the Ride", "Magic Mountain"],
["The New Revolution - Classic", "New Revolution", "Magic Mountain"],
["SCREAM", "Scream!", "Magic Mountain"],
["BAT GIRL™: Coaster Chase", "Batgirl Coaster Chase", "Fiesta Texas"],
["THE JOKER™ 4D Free Fly Coaster", "Joker", "New England"],
];
test("trademark symbol stripped — BATMAN™ The Ride (Over Georgia, Magic Mountain)", () => { for (const [qt, rcdb, park] of SHOULD_MATCH) {
assert.ok(isCoasterMatch("BATMAN™ The Ride", makeSet("Batman The Ride"))); test(`match: "${qt}" = "${rcdb}" (${park})`, () => {
}); assert.ok(isCoasterMatch(qt, set(rcdb)), `Expected match`);
});
}
test("leading THE stripped — THE RIDDLER Mindbender (Over Georgia)", () => { // ── Should NOT match (false positives) ───────────────────────────────────────
assert.ok(isCoasterMatch("THE RIDDLER Mindbender", makeSet("Riddler Mindbender")));
});
test("trademark + leading THE — THE RIDDLER™'s Revenge (Magic Mountain)", () => { const SHOULD_NOT_MATCH: [qtName: string, rcdbName: string, park: string][] = [
assert.ok(isCoasterMatch("THE RIDDLER™'s Revenge", makeSet("Riddler's Revenge"))); ["Joker y Harley Quinn", "Joker", "Six Flags Mexico"],
}); ];
test("curly apostrophe possessive stripped — CATWOMAN™ Whip (New England)", () => { for (const [qt, rcdb, park] of SHOULD_NOT_MATCH) {
assert.ok(isCoasterMatch("CATWOMAN™ Whip", makeSet("Catwoman's Whip"))); test(`no match: "${qt}" ≠ "${rcdb}" (${park})`, () => {
}); assert.ok(!isCoasterMatch(qt, set(rcdb)), `Expected no match`);
});
test("straight apostrophe possessive stripped", () => { }
assert.ok(isCoasterMatch("Riddler's Revenge", makeSet("Riddler's Revenge")));
});
test("trademark + colon punctuation — SUPERMAN™: Ultimate Flight (Over Georgia)", () => {
assert.ok(isCoasterMatch("SUPERMAN™: Ultimate Flight", makeSet("Superman - Ultimate Flight")));
});
test("QT drops subtitle — Apocalypse (Magic Mountain)", () => {
assert.ok(isCoasterMatch("Apocalypse", makeSet("Apocalypse the Ride")));
});
test("QT adds subtitle — The New Revolution - Classic (Magic Mountain)", () => {
assert.ok(isCoasterMatch("The New Revolution - Classic", makeSet("New Revolution")));
});
test("QT exclamation stripped — SCREAM (Magic Mountain)", () => {
assert.ok(isCoasterMatch("SCREAM", makeSet("Scream!")));
});
test("space-split word — BAT GIRL™: Coaster Chase (Fiesta Texas)", () => {
assert.ok(isCoasterMatch("BAT GIRL™: Coaster Chase", makeSet("Batgirl Coaster Chase")));
});
test("trademark + 4D subtitle — THE JOKER™ 4D Free Fly Coaster (New England)", () => {
assert.ok(isCoasterMatch("THE JOKER™ 4D Free Fly Coaster", makeSet("Joker")));
});
test("Great American Scream Machine — top-level QT rides array (Over Georgia)", () => {
assert.ok(isCoasterMatch("The Great American Scream Machine", makeSet("Great American Scream Machine")));
});
test("THE JOKER™ Funhouse Coaster — top-level QT rides array (Over Georgia)", () => {
assert.ok(isCoasterMatch("THE JOKER™ Funhouse Coaster", makeSet("Joker Funhouse Coaster")));
});
// ── Should NOT MATCH (false positives) ──────────────────────────────────────
test("false positive: Joker y Harley Quinn ≠ Joker (Six Flags Mexico)", () => {
assert.ok(!isCoasterMatch("Joker y Harley Quinn", makeSet("Joker")));
});
test("false positive: unrelated ride does not match", () => {
assert.ok(!isCoasterMatch("SkyScreamer", makeSet("Goliath")));
});
test("false positive: short prefix with conjunction — de connector", () => {
assert.ok(!isCoasterMatch("Batman de Gotham", makeSet("Batman")));
});