The Sandpoint lat/lng from .creds.json had been baked into the API-doc example and the integration test. Doc now uses a rounded ~downtown point; the test reads the coordinate from the gitignored .creds.json (rounded, non-personal fallback). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
72 lines
3.7 KiB
JavaScript
72 lines
3.7 KiB
JavaScript
// Live integration tests against a ParkSmarter instance, using the Sandpoint
|
|
// zones. Targets the `test` instance by default (PS_TEST_ENV to override).
|
|
//
|
|
// NOTE: the non-prod instances (dev/stage/test) are IP-restricted to IPS's
|
|
// network and return 403 "Web App - Unavailable" from the public internet, so
|
|
// these SKIP with a clear reason unless the instance is reachable. To run them
|
|
// live against prod (read-only) with the Sandpoint zones:
|
|
// PS_TEST_ENV=prodv2 node --test test/integration.test.mjs
|
|
// (needs parksmarter-client/.creds.json for the authenticated calls).
|
|
import { test } from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import { readFileSync, existsSync } from 'node:fs';
|
|
import { ParkSmarterClient, isFreeEstimate } from '../dist/index.js';
|
|
|
|
const ENV = process.env.PS_TEST_ENV || 'test';
|
|
const credsUrl = new URL('../.creds.json', import.meta.url);
|
|
const creds = existsSync(credsUrl) ? JSON.parse(readFileSync(credsUrl, 'utf8')) : {};
|
|
// Search point: your own coordinate from the gitignored .creds.json when present,
|
|
// else a rounded, non-personal downtown-Sandpoint point (~1 km precision — not a home).
|
|
const SANDPOINT = { latitude: Number(creds.lat) || 48.28, longitude: Number(creds.lng) || -116.55 };
|
|
|
|
// Probe reachability once; if the instance blocks us, skip the whole suite.
|
|
let reason = false;
|
|
try {
|
|
const av = await new ParkSmarterClient({ environment: ENV, timeoutMs: 12000 }).getApplicationValidity();
|
|
if (!av || (!av.SessionId && !av.Config)) throw new Error('no bootstrap payload');
|
|
} catch (e) {
|
|
reason = `'${ENV}' instance unreachable (${e.name} ${e.status ?? ''}); non-prod is IP-restricted. `
|
|
+ `Run PS_TEST_ENV=prodv2 with .creds.json to exercise these live.`;
|
|
}
|
|
|
|
async function client() {
|
|
const ps = new ParkSmarterClient({ environment: ENV, timeoutMs: 15000 });
|
|
if (creds.phoneNumber) await ps.loginWithPhone({ phoneNumber: creds.phoneNumber, password: creds.password });
|
|
return ps;
|
|
}
|
|
|
|
test(`[${ENV}] ApplicationValidity bootstraps a session`, { skip: reason }, async () => {
|
|
const av = await new ParkSmarterClient({ environment: ENV }).getApplicationValidity();
|
|
assert.ok(av.SessionId || av.Config, 'expected a bootstrap payload');
|
|
});
|
|
|
|
test(`[${ENV}] Sandpoint (DSB) zones are returned near downtown`, { skip: reason }, async () => {
|
|
const ps = await client();
|
|
const m = await ps.getMetersByLocation(SANDPOINT);
|
|
assert.ok(Array.isArray(m.Zones) && m.Zones.length > 0, 'expected zones near Sandpoint');
|
|
const names = m.Zones.map((z) => z.ZoneName || '');
|
|
assert.ok(names.some((n) => /^DSB/i.test(n)), `expected DSB* zones, got: ${names.slice(0, 8).join(',')}`);
|
|
});
|
|
|
|
test(`[${ENV}] single estimate is well-formed and free/paid is coherent`, { skip: reason }, async () => {
|
|
const ps = await client();
|
|
const me = creds.phoneNumber ? await ps.getUserDetail() : null;
|
|
const vehicleId = me?.VehicleDetails?.[0]?.VehicleID ?? 0;
|
|
const m = await ps.getMetersByLocation(SANDPOINT);
|
|
const z = m.Zones[0];
|
|
const sp = z.Spaces?.[0];
|
|
const base = { zoneId: z.ZoneId, spaceId: sp.SpaceId, customerId: z.CustomerId, vehicleId };
|
|
|
|
const single = await ps.getParkingEstimateSingle({ ...base, durationInMinutes: 30, creditCardId: 0 });
|
|
assert.equal(single.Response?.Status, 'Success', 'single estimate should succeed');
|
|
|
|
// Multi may be empty or error for some zones/windows — assert only the shape.
|
|
const multi = await ps.getParkingEstimateMulti(base);
|
|
assert.ok(Object.prototype.hasOwnProperty.call(multi, 'ParkingDetail'), 'multi has ParkingDetail');
|
|
|
|
if (isFreeEstimate(single)) {
|
|
assert.equal(Number(single.MaxTime) || 0, 0, 'free window => MaxTime 0');
|
|
} else {
|
|
assert.ok(Number(single.ParkingDetail?.ParkingCost) >= 0, 'paid window => a numeric cost');
|
|
}
|
|
});
|