- supprime selfRate/autoRate de _parsePowerBalance, le getter autoconsommationW, les .abs() interim - ratios dérivés par Δ-de-cumuls (§2.1/§8.1) dans lib/services/energy_ratios.dart - reseed jour-roulant & Δ non-monotone ; gardes : dén≤0→null, pas de NaN, clamp [0,100] - selfConsumptionPower net-signé sans .abs() (§2.2) ; signes nymea bruts préservés (§7.1) - seam swappable vers un state plugin en Phase 2 (une seule fonction) Interim (Phase 1). Gestion du signe d'affichage renvoyée à la Phase 4 (§1.1). Réf. UI_DATA_CONTRACT.md rev.5. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
91 lines
3.4 KiB
Dart
91 lines
3.4 KiB
Dart
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);
|
|
});
|
|
}
|