Files
MeterVault/tests/Core.Tests/VirtualMeterTests.cs
T
schmidt.florian d972f67bad M1: pure normalization engine + default seed
Infrastructure-free normalization engine in Core dispatching on MeterMode:
- Cumulative/Generation counters: register deltas, first-reading baseline (Haus 411,
  Auto 3755), meter swaps (water …861→2 reconciles to 12 via boundary registers OR an
  explicit amount override), counter resets, anomaly-guarded decreases.
- RuntimeCounter: Δhours × rate (fixed/empirical).
- ConsumableBalance: tank level-Δ + deliveries → consumption, cm→litre calibration;
  delivery-only rows before the first dipstick emit nothing.
- DirectDelta, and Virtual meters via a small safe arithmetic evaluator (Netz Einsparung
  = Haus − Netz, Eigenverbrauch = Erzeugung − Einsparung) — data-driven, not hardcoded.
- DatabaseSeeder: default energy types, cost categories, base settings (idempotent).

24 Core unit tests + 4 integration tests green.

Claude-Session: https://claude.ai/code/session_01WujdMtMJPbxDpDnMeK22rr
2026-07-13 11:12:03 +02:00

76 lines
2.5 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using MeterVault.Core.Domain;
using MeterVault.Core.Normalization;
using static MeterVault.Core.Tests.TestData;
namespace MeterVault.Core.Tests;
/// <summary>
/// The electricity derived columns are data-driven virtual expressions, not hardcoded formulas
/// (SDD §2.2, §7.4): Netz Einsparung = Haus Netz, Anlage Eigenverbrauch = Erzeugung Einsparung.
/// </summary>
public sealed class VirtualMeterTests
{
private readonly INormalizationEngine _engine = NormalizationEngine.CreateDefault();
private static Consumption Cons(int meterId, DateTimeOffset time, double amount) => new()
{
MeterId = meterId,
Time = time,
Amount = amount,
Kind = ConsumptionKind.Consumption,
Quality = ReadingQuality.Imported,
};
[Fact]
public void Netz_einsparung_is_haus_minus_netz()
{
var oct = Month(2022, 10);
var nov = Month(2022, 11);
var ctx = new NormalizationContext
{
Meter = new MeterConfig
{
MeterId = 100,
Mode = MeterMode.Virtual,
Unit = "kWh",
Virtual = new VirtualSpec { Expression = "m1 - m2", ReferencedMeterIds = [1, 2] },
},
ReferencedSeries = new Dictionary<int, IReadOnlyList<Consumption>>
{
[1] = [Cons(1, oct, 411), Cons(1, nov, 742)], // Haus Verbrauch
[2] = [Cons(2, oct, 416), Cons(2, nov, 832)], // Netz Verbrauch
},
};
var result = _engine.Normalize(ctx);
Assert.Equal([-5d, -90d], result.Select(c => c.Amount));
}
[Fact]
public void Eigenverbrauch_is_erzeugung_minus_einsparung()
{
var oct = Month(2022, 10);
var ctx = new NormalizationContext
{
Meter = new MeterConfig
{
MeterId = 101,
Mode = MeterMode.Virtual,
Unit = "kWh",
Virtual = new VirtualSpec { Expression = "m50 - m100", ReferencedMeterIds = [50, 100] },
},
ReferencedSeries = new Dictionary<int, IReadOnlyList<Consumption>>
{
[50] = [Cons(50, oct, 76)], // Solar Erzeugung
[100] = [Cons(100, oct, -5)], // Netz Einsparung (from the previous test)
},
};
var result = _engine.Normalize(ctx);
// 76 (5) = 81 (Anlage Eigenverbrauch Okt 2022).
Assert.Equal([81d], result.Select(c => c.Amount));
}
}