import 'package:etm_powersync_app/services/energy_ratios.dart'; import 'package:flutter_test/flutter_test.dart'; /// Couvre l'interim ratios (§2.1) : dérivation par Δ cumuls, reseed (jour / /// non-monotone), garde-fous (n/a, pas de NaN, clamp), formule autoconso band. void main() { final d1 = DateTime(2026, 6, 28, 12); final d1bis = DateTime(2026, 6, 28, 13); final d2 = DateTime(2026, 6, 29, 9); test('1er échantillon : Δ=0 → n/a (null), jamais NaN', () { final s = EnergyRatiosInterim(); final r = s.compute( totalProduction: 10, totalReturn: 2, totalConsumption: 8, totalAcquisition: 3, now: d1); expect(r.autoconsommation, isNull); expect(r.autonomie, isNull); }); test('Δ cumuls même jour → ratios déterministes, bornés [0,100]', () { final s = EnergyRatiosInterim(); s.compute( totalProduction: 10, totalReturn: 2, totalConsumption: 8, totalAcquisition: 3, now: d1); // baseline final r = s.compute( totalProduction: 20, // Δprod=10 totalReturn: 4, // Δret=2 → autoconso=(10-2)/10=80 totalConsumption: 18, // Δcons=10 totalAcquisition: 8, // Δacq=5 → autonomie=(10-5)/10=50 now: d1bis); expect(r.autoconsommation, closeTo(80, 0.001)); expect(r.autonomie, closeTo(50, 0.001)); }); test('nouveau jour local → reseed baseline (Δ repart de 0)', () { final s = EnergyRatiosInterim(); s.compute( totalProduction: 10, totalReturn: 2, totalConsumption: 8, totalAcquisition: 3, now: d1); final r = s.compute( totalProduction: 99, totalReturn: 9, totalConsumption: 99, totalAcquisition: 9, now: d2); // jour différent → reseed → Δ=0 expect(r.autoconsommation, isNull); expect(r.autonomie, isNull); }); test('compteur non monotone (Δ<0) → reseed, jamais de ratio négatif', () { final s = EnergyRatiosInterim(); s.compute( totalProduction: 100, totalReturn: 10, totalConsumption: 80, totalAcquisition: 5, now: d1); // baseline haute // restart nymead : cumuls repartent bas → non monotone → reseed final r = s.compute( totalProduction: 5, totalReturn: 1, totalConsumption: 4, totalAcquisition: 1, now: d1bis); expect(r.autoconsommation, anyOf(isNull, greaterThanOrEqualTo(0))); expect(r.autonomie, anyOf(isNull, greaterThanOrEqualTo(0))); }); test('dénominateur nul (nuit sans prod) → n/a, pas de NaN', () { final s = EnergyRatiosInterim(); s.compute( totalProduction: 10, totalReturn: 2, totalConsumption: 8, totalAcquisition: 3, now: d1); final r = s.compute( totalProduction: 10, // Δprod=0 → autoconso n/a totalReturn: 2, totalConsumption: 8, // Δcons=0 → autonomie n/a totalAcquisition: 3, now: d1bis); expect(r.autoconsommation, isNull); expect(r.autonomie, isNull); }); test('selfConsumptionPower (§2.2) : net-signé, sans abs', () { // export (acq<0) retranché de la production expect(EnergyRatiosInterim.selfConsumptionPower(4869, -231), closeTo(4638, 0.001)); // import (acq>0) → tout est autoconsommé expect(EnergyRatiosInterim.selfConsumptionPower(2000, 500), 2000); // export > production → borné à 0 (pas de négatif) expect(EnergyRatiosInterim.selfConsumptionPower(100, -300), 0); }); }