predict(Carbon::parse('2024-06-03')); expect($prediction->magnitudePence)->toBe(0.0) ->and($prediction->direction)->toBe('flat'); }); it('has an empty FeatureSpec (no features by design)', function () { $model = new NaiveZeroChangeModel; $spec = $model->featureSpec(); expect($spec->modelLabel)->toBe('naive-zero') ->and($spec->features)->toBe([]) ->and($spec->modelVersion())->toStartWith('naive-zero-'); }); it('runs cleanly through the backtest harness on real-shape data', function () { // 8 weeks gently rising — naive predicts flat → expect 0% accuracy. $start = Carbon::parse('2024-01-01'); for ($i = 0; $i < 8; $i++) { DB::table('weekly_pump_prices')->insert([ 'date' => $start->copy()->addWeeks($i)->toDateString(), 'ulsp_pence' => 14000 + ($i * 100), 'ulsd_pence' => 15000 + ($i * 80), 'ulsp_duty_pence' => 5295, 'ulsd_duty_pence' => 5295, 'ulsp_vat_pct' => 20, 'ulsd_vat_pct' => 20, ]); } $result = (new BacktestRunner)->run( new NaiveZeroChangeModel, trainStart: Carbon::parse('2024-01-01'), trainEnd: Carbon::parse('2024-01-29'), evalStart: Carbon::parse('2024-02-05'), evalEnd: Carbon::parse('2024-02-19'), ); expect((float) $result->directional_accuracy)->toBe(0.0) ->and((float) $result->mae_pence)->toBe(1.0) ->and($result->leak_suspected)->toBeFalse(); });