Files
MeterVault/tests/Core.Tests/MeterMetaTests.cs
T
schmidt.florian 09cd435c2b
ci / build-test (push) Successful in 1m19s
Admin write-CRUD, Home Assistant connector config, wiring audit
Three requested phases.

1) Admin section (SDD §8.7) — MudBlazor inline-dialog CRUD, consistent pattern,
   delete guards, snackbar feedback, shared Confirm helper:
   - Energy types: create/edit/delete (blocks delete when meters reference it).
   - Meters: create/edit/delete; recomputes consumption when mode/baseline
     changes (NormalizationService over a fresh factory context, in a tx);
     delete cascades data (consumption+readings are Restrict → removed first).
   - A meter's ingest sources: manage on the meter-detail Sources tab
     (add/edit/delete MQTT/Tasmota/HA sources with typed config).
   - Tariffs: full CRUD (scope/component/value/validity).
   - Cost categories: CRUD + member management (meter or energy-type members).
   - Connectors: ingestion_endpoint CRUD (MQTT broker + Home Assistant);
     secrets referenced by env-var name only, never stored.
   - Settings: read-only effective-config view (settings are env-driven and
     reproducible, so an editable form would change nothing — kept honest).
   PV role is now editable on meters (MeterMeta.SetRole can clear a role).

2) Read Home Assistant — extracted a shared public HaEndpointConfig (was a
   private record in the worker), added HaConnectionTester (powers the connector
   "Test connection": checks base URL + env-resolved token, optionally reads one
   entity). Configuring an HA connector + an HA source on a meter drives the
   existing REST-poll worker end to end. (WebSocket push stays a future
   optimization; REST poll already reads HA.)

3) Wiring/placeholder audit — swept every OnClick/Href: all handlers are real,
   all internal links resolve to real routes, no TODO/stub/placeholder code.
   Fixed one genuine gap: MainLayout had no drawer toggle, so the nav was
   unreachable on narrow screens — added a hamburger button.

Tests: +6 (MeterMeta.SetRole role-removal; HaConnectionTester fail-closed
guard branches with a throwing HttpClientFactory proving no network on bad
config); render test now covers all admin routes. 69 Core + 45 Integration =
114 green. Live-verified in Docker: all admin pages 200, drawer toggle present,
Settings shows real effective config.

Claude-Session: https://claude.ai/code/session_01Lz2RqAsnQhetqWNoCDfexK
2026-07-14 11:22:17 +02:00

62 lines
1.7 KiB
C#

using MeterVault.Core.Domain;
namespace MeterVault.Core.Tests;
public sealed class MeterMetaTests
{
[Fact]
public void Role_reads_configured_role()
{
Assert.Equal(MeterRoles.GridImport, MeterMeta.Role("{\"role\":\"grid_import\"}"));
}
[Theory]
[InlineData("")]
[InlineData(" ")]
[InlineData("{}")]
[InlineData("not json")]
[InlineData("{\"role\":123}")]
[InlineData("[1,2,3]")]
public void Role_is_null_when_absent_or_malformed(string meta)
{
Assert.Null(MeterMeta.Role(meta));
}
[Fact]
public void WithRole_sets_role_and_preserves_other_keys()
{
var updated = MeterMeta.WithRole("{\"expression\":\"a-b\"}", MeterRoles.TotalLoad);
Assert.Equal(MeterRoles.TotalLoad, MeterMeta.Role(updated));
Assert.Equal("a-b", MeterMeta.ReadString(updated, "expression"));
}
[Fact]
public void WithRole_overwrites_existing_role()
{
var updated = MeterMeta.WithRole("{\"role\":\"grid_import\"}", MeterRoles.GridExport);
Assert.Equal(MeterRoles.GridExport, MeterMeta.Role(updated));
}
[Fact]
public void WithRole_handles_empty_meta()
{
var updated = MeterMeta.WithRole("", MeterRoles.GridImport);
Assert.Equal(MeterRoles.GridImport, MeterMeta.Role(updated));
}
[Theory]
[InlineData(null)]
[InlineData("")]
[InlineData(" ")]
public void SetRole_removes_role_when_blank_and_keeps_other_keys(string? role)
{
var updated = MeterMeta.SetRole("{\"role\":\"grid_import\",\"expression\":\"a-b\"}", role);
Assert.Null(MeterMeta.Role(updated));
Assert.Equal("a-b", MeterMeta.ReadString(updated, "expression"));
}
}