using System.Text.Json; using MeterVault.Infrastructure.Ingestion; namespace MeterVault.Integration.Tests.Ingestion; /// Pure ingestion helpers — no broker or database needed. public sealed class PayloadExtractionTests { private const string TasmotaSensor = """{"Time":"2024-01-15T12:30:00","ENERGY":{"Total":1234.56,"Today":1.2,"Power":50}}"""; [Fact] public void Extracts_tasmota_energy_total() { Assert.True(PayloadExtractor.TryExtractValue(TasmotaSensor, "ENERGY.Total", out var value)); Assert.Equal(1234.56, value, 3); } [Fact] public void Extracts_tasmota_time() { Assert.True(PayloadExtractor.TryExtractTime(TasmotaSensor, "Time", out var time)); Assert.Equal(new DateTimeOffset(2024, 1, 15, 12, 30, 0, TimeSpan.Zero), time); } [Fact] public void Extracts_bare_scalar_when_no_path() { Assert.True(PayloadExtractor.TryExtractValue("42.7", null, out var value)); Assert.Equal(42.7, value, 3); } [Fact] public void Missing_path_yields_false() { Assert.False(PayloadExtractor.TryExtractValue(TasmotaSensor, "ENERGY.Nope", out _)); } [Theory] [InlineData("tele/+/SENSOR", "tele/plug1/SENSOR", true)] [InlineData("tele/plug1/SENSOR", "tele/plug1/SENSOR", true)] [InlineData("tele/#", "tele/plug1/SENSOR", true)] [InlineData("tele/+/SENSOR", "tele/plug1/STATE", false)] [InlineData("tele/+/SENSOR", "tele/a/b/SENSOR", false)] [InlineData("home/#", "office/x", false)] public void Topic_matcher_follows_mqtt_wildcards(string filter, string topic, bool expected) { Assert.Equal(expected, MqttTopicMatcher.Matches(filter, topic)); } [Fact] public void Ha_state_client_parses_numeric_state() { using var doc = JsonDocument.Parse( """{"entity_id":"sensor.p","state":"987.6","attributes":{"unit":"kWh"},"last_updated":"2024-02-01T08:00:00+00:00"}"""); var state = HaStateClient.ParseState(doc, attribute: null); Assert.NotNull(state); Assert.Equal(987.6, state!.Value.Value, 3); Assert.Equal(new DateTimeOffset(2024, 2, 1, 8, 0, 0, TimeSpan.Zero), state.Value.Time); } [Fact] public void Ha_state_client_parses_attribute_and_rejects_unavailable() { using var doc = JsonDocument.Parse( """{"entity_id":"sensor.p","state":"unavailable","attributes":{"power":123.4}}"""); Assert.Null(HaStateClient.ParseState(doc, attribute: null)); // non-numeric state Assert.Equal(123.4, HaStateClient.ParseState(doc, "power")!.Value.Value, 3); } }