All checks were successful
build-apk / build (push) Successful in 49m41s
App: - When the single-estimate fallback sees MaxTime 0 (or an all-$0.00 ladder), show "Parking is currently free" instead of a bogus $0.00 purchase ladder. Free-detection extracted to tested client helpers (isFreeEstimate/ladderAllFree). Client + tests (node:test, zero deps): - src/estimates.ts: pure isFreeEstimate/ladderAllFree helpers (exported). - test/estimates + test/http (mocked fetch): headers, query serialization, error->ParkSmarterApiError, rolling Auth_Token refresh, 401 clears token. - test/integration: live checks against the Sandpoint (DSB) zones; targets the `test` instance by default and SKIPS when it's unreachable (non-prod is IP-restricted -> 403). Verified 3/3 green against prodv2 read-only. - CI runs `npm test` as a build gate. Note: dev/stage/test instances return 403 "Web App - Unavailable" from the public internet (IP-restricted). API schema is otherwise unchanged vs Jul 6. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
24 lines
1,013 B
TypeScript
24 lines
1,013 B
TypeScript
/**
|
|
* Helpers for interpreting ParkSmarter parking estimates.
|
|
*
|
|
* Pulled out as pure functions so the "is this a free window?" decision is
|
|
* testable without the app, the network, or React Native. The BigBrainParking
|
|
* app's single-estimate fallback uses these to avoid showing a bogus "$0.00"
|
|
* purchase ladder during free windows.
|
|
*/
|
|
|
|
/**
|
|
* A free window: the single estimate reports no purchasable time. Live captures
|
|
* show free zones return `MaxTime: 0` (and `$0.00`), while paid zones return a
|
|
* positive MaxTime. Anything <= 0 (or non-numeric) counts as free.
|
|
*/
|
|
export function isFreeEstimate(probe: { MaxTime?: number | string | null } | null | undefined): boolean {
|
|
return (Number(probe?.MaxTime) || 0) <= 0;
|
|
}
|
|
|
|
/** Every rung of a built ladder is $0.00 — effectively a free window too. */
|
|
export function ladderAllFree(
|
|
rungs: ReadonlyArray<{ ParkingCost?: number | string | null }>,
|
|
): boolean {
|
|
return rungs.length > 0 && rungs.every((r) => (Number(r?.ParkingCost) || 0) === 0);
|
|
}
|