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
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ Pumperly combines route planning with real-time fuel prices and EV charging stat

- **Route planning** — Geocoding via [Photon](https://github.com/komoot/photon), routing via [Valhalla](https://github.com/valhalla/valhalla), with alternative routes
- **Real-time fuel prices** — From government open data APIs and community sources
- **EV charging stations** — Via [Open Charge Map](https://openchargemap.org) across all supported countries, plus the official [Mapa REVE](https://www.mapareve.es) registry in Spain and the [BNetzA Ladesäulenregister](https://www.bundesnetzagentur.de/DE/Fachthemen/ElektrizitaetundGas/E-Mobilitaet/Ladesaeulenkarte/start.html) in Germany
- **EV charging stations** — Official registries where they exist ([Mapa REVE](https://www.mapareve.es) in Spain, the [BNetzA Ladesäulenregister](https://www.bundesnetzagentur.de/DE/Fachthemen/ElektrizitaetundGas/E-Mobilitaet/Ladesaeulenkarte/start.html) in Germany), [Open Charge Map](https://openchargemap.org) everywhere else
- **Detour calculation** — Each station shows estimated detour time from your route
- **"Cheapest within N min"** — Slider filters stations by maximum detour, highlights the best deal
- **Corridor station list** — Sorted by position along route, with price deltas vs average
Expand Down
16 changes: 16 additions & 0 deletions src/scrapers/bnetza.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -526,6 +526,22 @@ describe("BNetzAScraper cleanup", () => {
expect(stub.queries).toHaveLength(1); // fell back to the 10,000 default
});

it("treats a floor below 1 as nonsensical too", async () => {
// 0.5 floors to 0, and `refreshed < 0` is never true: the guard would be
// gone without anyone noticing. Same fallback as a non-numeric value.
for (const raw of ["0.5", "0", "-3"]) {
vi.stubEnv("PUMPERLY_BNETZA_MIN_STATIONS", raw);
const stub = await setup({ results: [[{ count: BigInt(42) }]] });
const { BNetzAScraper } = await import("./bnetza");

await new BNetzAScraper().run();

expect(stub.queries, `floor "${raw}"`).toHaveLength(1);
vi.resetModules();
vi.restoreAllMocks();
}
});

it("does not clean up after a run that reported errors", async () => {
const stub = await setup({ errors: ["Station batch 0-500: boom"] });
const { BNetzAScraper } = await import("./bnetza");
Expand Down
4 changes: 3 additions & 1 deletion src/scrapers/bnetza.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,10 @@ const COORD_PRECISION = 5;
// clears the empty-fetch guard must not be able to retire OpenChargeMap or bulk
// delete yesterday's stations. The register holds ~74k; 10k is a wide margin.
const rawMinStations = Number(process.env.PUMPERLY_BNETZA_MIN_STATIONS ?? "10000");
// Anything that floors below 1 (0, 0.5, negatives) would make `refreshed < MIN_STATIONS`
// impossible to satisfy and silently disable the guard, so it falls back too.
const MIN_STATIONS =
Number.isFinite(rawMinStations) && rawMinStations > 0 ? Math.floor(rawMinStations) : 10_000;
Number.isFinite(rawMinStations) && rawMinStations >= 1 ? Math.floor(rawMinStations) : 10_000;

// Slack added to the staleness cutoff so the sweep can never delete a row
// written in the opening moments of the run it belongs to.
Expand Down
Loading