using MeterVault.Core.Domain; using MeterVault.Core.Normalization; using MeterVault.Infrastructure.Import; using static MeterVault.Integration.Tests.Reconciliation.ReconciliationSupport; namespace MeterVault.Integration.Tests.Reconciliation; /// /// Reconciles the five electricity meters and a derived virtual meter against the Strom sheet's /// own Verbrauch / Netz Einsparung columns (SDD ยง2.2). Runs without a database. /// public sealed class ElectricityReconciliationTests { private static MeterConfig Cumulative(int id) => new() { MeterId = id, Mode = MeterMode.CumulativeCounter, Unit = "kWh" }; private static MeterConfig Generation(int id) => new() { MeterId = id, Mode = MeterMode.GenerationCounter, Unit = "kWh" }; [Theory] [InlineData(ReferenceProfiles.Haus, 7)] // Haus Verbrauch [InlineData(ReferenceProfiles.Netz, 8)] // Netz Verbrauch [InlineData(ReferenceProfiles.Auto, 9)] // Auto Verbrauch [InlineData(ReferenceProfiles.Solar1, 10)] // Solar Erzeugung 1 [InlineData(ReferenceProfiles.Solar2, 11)] // Solar Erzeugung 2 public void Meter_consumption_matches_the_sheet(int meterId, int oracleColumn) { var staged = Stage(ReferenceProfiles.Electricity(), Electricity); var config = meterId is ReferenceProfiles.Solar1 or ReferenceProfiles.Solar2 ? Generation(meterId) : Cumulative(meterId); var computed = ByMonth(Normalize(staged, config)); var oracle = OracleByMonth(ReadRows(Electricity), dateColumn: 0, valueColumn: oracleColumn, firstDataRow: 1); AssertReconciles(computed, oracle, tolerance: 1.0, $"meter {meterId}", minMatches: 20); } [Fact] public void Netz_einsparung_virtual_matches_the_sheet() { var staged = Stage(ReferenceProfiles.Electricity(), Electricity); var haus = Normalize(staged, Cumulative(ReferenceProfiles.Haus)); var netz = Normalize(staged, Cumulative(ReferenceProfiles.Netz)); var engine = NormalizationEngine.CreateDefault(); var virtualContext = new NormalizationContext { Meter = new MeterConfig { MeterId = 100, Mode = MeterMode.Virtual, Unit = "kWh", Virtual = new VirtualSpec { Expression = $"m{ReferenceProfiles.Haus} - m{ReferenceProfiles.Netz}", ReferencedMeterIds = [ReferenceProfiles.Haus, ReferenceProfiles.Netz], }, }, ReferencedSeries = new Dictionary> { [ReferenceProfiles.Haus] = haus, [ReferenceProfiles.Netz] = netz, }, }; var computed = ByMonth(engine.Normalize(virtualContext)); var oracle = OracleByMonth(ReadRows(Electricity), dateColumn: 0, valueColumn: 13, firstDataRow: 1); // Netz Einsparung AssertReconciles(computed, oracle, tolerance: 1.0, "Netz Einsparung", minMatches: 20); } }