d972f67bad
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
37 lines
1.0 KiB
C#
37 lines
1.0 KiB
C#
using MeterVault.Core.Normalization.Expressions;
|
|
|
|
namespace MeterVault.Core.Tests;
|
|
|
|
public sealed class ExpressionEvaluatorTests
|
|
{
|
|
private static readonly Dictionary<string, double> Vars = new()
|
|
{
|
|
["m1"] = 411,
|
|
["m2"] = 416,
|
|
};
|
|
|
|
[Theory]
|
|
[InlineData("1 + 2 * 3", 7)]
|
|
[InlineData("(1 + 2) * 3", 9)]
|
|
[InlineData("-5", -5)]
|
|
[InlineData("10 / 4", 2.5)]
|
|
[InlineData("2 - 3 - 4", -5)] // left-associative
|
|
[InlineData("m1 - m2", -5)]
|
|
[InlineData("m1 + m2", 827)]
|
|
[InlineData("unknown + 1", 1)] // unknown identifiers resolve to 0
|
|
public void Evaluates_arithmetic(string expression, double expected)
|
|
{
|
|
var result = ExpressionEvaluator.Compile(expression).Evaluate(Vars);
|
|
|
|
Assert.Equal(expected, result, 6);
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData("1 +")]
|
|
[InlineData("(1 + 2")]
|
|
[InlineData("1 2")]
|
|
[InlineData("")]
|
|
public void Rejects_malformed_expressions(string expression) =>
|
|
Assert.ThrowsAny<Exception>(() => ExpressionEvaluator.Compile(expression));
|
|
}
|