I wrote about the Electricity Planning Engine a little while back, including a timezone bug that made a correct price look "not found" after a database round trip. A few days later, Alex Shev left this comment:
Timezone bugs are brutal in planning engines because the result can look mathematically correct while being operationally wrong. Energy workflows especially need tests around boundaries, not just averages.
That is a genuinely sharp way to put it, and it is not just a comment about the bug I already wrote about. It is a comment about how I test the project in general, and I did not like how well it applied once I went and checked.
The part that stung a little
"Looks mathematically correct while being operationally wrong" is exactly what the original timezone bug was. PriceSeries::priceAt() threw a clean "price not found" error, which is arguably the good version of that failure mode: loud, easy to catch, hard to ship. A quieter version of the same class of mistake, off by one hour instead of missing entirely, would not throw anything. It would just return a plan that looks completely reasonable and is wrong the entire time it runs.
Alex's second point, boundaries over averages, is the one I actually had to go check rather than just agree with in the abstract. So I opened tests/Unit/Domain/Contract/PricingStrategyTest.php and looked at every hour used in every peak/off-peak assertion:
new DateTimeImmutable('2026-07-18 14:00:00') // peak
new DateTimeImmutable('2026-07-18 23:00:00') // off-peak
new DateTimeImmutable('2026-07-18 05:00:00') // off-peak
Enter fullscreen mode Exit fullscreen mode
14:00, 23:00, 05:00. Every single one comfortably inside its window. None of them anywhere near the actual transition. The off-peak slot in the config is 22:00 to 06:00, and the comparison behind that lives in TimeSlot::contains():
// wraparound slot, e.g. 22:00 -> 06:00
return $minuteOfDay >= $this->startMinuteOfDay || $minuteOfDay < $this->endMinuteOfDay;
Enter fullscreen mode Exit fullscreen mode
That >= versus < is exactly the kind of one-character decision that determines whether 22:00:00 itself is off-peak or not, and whether 06:00:00 itself is off-peak or already peak again. Nothing in the suite exercised either instant. A boundary mistake here would not crash, would not warn, would just silently bill one hour a day at the wrong rate, forever, until someone happened to notice their bill looked slightly off. That is Alex's point, precisely, and it was sitting in my own repo.
The fix
public function test_peak_off_peak_strategy_resolves_the_exact_boundary_minute_correctly(): void
{
$strategy = PricingStrategyFactory::fromConfig(ContractType::PeakOffPeak, [
'off_peak_slots' => [['start' => '22:00', 'end' => '06:00']],
'seasons' => [[
'label' => 'year_round',
'months' => range(1, 12),
'rates' => [
['slot' => 'peak', 'price_per_kwh' => 0.27],
['slot' => 'off_peak', 'price_per_kwh' => 0.20],
],
]],
]);
// Just before the off-peak window opens: still peak.
self::assertTrue($strategy->priceForHour(new DateTimeImmutable('2026-07-18 21:59:00'))->equals(Money::of(0.27)));
// The window opens exactly at 22:00:00: start is inclusive.
self::assertTrue($strategy->priceForHour(new DateTimeImmutable('2026-07-18 22:00:00'))->equals(Money::of(0.20)));
// Just before the off-peak window closes: still off-peak.
self::assertTrue($strategy->priceForHour(new DateTimeImmutable('2026-07-19 05:59:00'))->equals(Money::of(0.20)));
// The window closes exactly at 06:00:00: end is exclusive, already peak.
self::assertTrue($strategy->priceForHour(new DateTimeImmutable('2026-07-19 06:00:00'))->equals(Money::of(0.27)));
}
Enter fullscreen mode Exit fullscreen mode
Four instants instead of three comfortable ones: one minute before the window opens, the exact opening second, one minute before it closes, the exact closing second. That is the whole idea of testing boundaries instead of averages, written down as assertions instead of just agreed with in a comment thread.
Proving the test actually tests something
A boundary test that would pass against a broken implementation is worse than no test, it is a false sense of safety. So before trusting this one, I broke the code on purpose: flipped the wraparound comparison from >= / < to > / <=, one character each, and reran just this test.
It failed immediately, on the 22:00:00 assertion, exactly where it should:
Failed asserting that false is true.
at tests/Unit/Domain/Contract/PricingStrategyTest.php:73
Enter fullscreen mode Exit fullscreen mode
Then I reverted the one-character change and ran the full suite: 104 tests, 752 assertions, green. That failure-then-pass cycle is the only way I trust a new test is doing its job rather than just decorating the file with more green checkmarks.
Thanks, Alex
None of this was a bug in production, it was a gap in coverage that a bug could have hidden in later. Comments like Alex's are exactly how that gap gets found before the bug does instead of after. If you have opinions on where else this project's tests are testing averages instead of edges, the repo is open, and so are the issues.
0 Comments
Log in to join the conversation.No comments yet. Be the first to share your thoughts.