using System.Net; using MeterVault.App; using MeterVault.Core.Domain; using Microsoft.AspNetCore.Localization; using Microsoft.EntityFrameworkCore; namespace MeterVault.Integration.Tests.Editor; /// /// The tariff and settings pages as the server prerenders them (D-37, D-52, D-57): a scoped tariff link lists what can /// price that meter and nothing else, Bonus/Discount/Tax say they are not applied, and settings label raw retention as /// not enforced, with the reason, beside the analysis data state. /// [Collection("Timescale")] public sealed class AdminPagesRenderTests(TimescaleFixture fx) : IAsyncLifetime { private short _type; private short _otherType; private int _meter; private readonly List _tariffs = []; public async Task InitializeAsync() { await using var db = fx.CreateContext(); var type = new EnergyType { Key = $"tariffs-{Guid.NewGuid():N}", DisplayName = "Tariff water", BaseUnit = "m3", DefaultMode = MeterMode.CumulativeCounter }; var other = new EnergyType { Key = $"tariffs-{Guid.NewGuid():N}", DisplayName = "Tariff heat", BaseUnit = "kWh", DefaultMode = MeterMode.CumulativeCounter }; db.EnergyTypes.AddRange(type, other); await db.SaveChangesAsync(); _type = type.Id; _otherType = other.Id; var meter = new Meter { Name = "Tap meter", EnergyTypeId = _type, Mode = MeterMode.CumulativeCounter, Unit = "m3", Meta = "{}" }; db.Meters.Add(meter); await db.SaveChangesAsync(); _meter = meter.Id; Tariff[] tariffs = [ Tariff(TariffScope.Meter, _meter, TariffComponent.UnitPrice, 1.2345, "EUR/m3"), Tariff(TariffScope.EnergyType, _type, TariffComponent.Bonus, 2.3456, "EUR"), Tariff(TariffScope.EnergyType, _otherType, TariffComponent.UnitPrice, 9.8765, "EUR/kWh"), ]; db.Tariffs.AddRange(tariffs); await db.SaveChangesAsync(); _tariffs.AddRange(tariffs.Select(t => t.Id)); } public async Task DisposeAsync() { await using var db = fx.CreateContext(); await db.Tariffs.Where(t => _tariffs.Contains(t.Id)).ExecuteDeleteAsync(); await db.Meters.Where(m => m.Id == _meter).ExecuteDeleteAsync(); await db.EnergyTypes.Where(t => t.Id == _type || t.Id == _otherType).ExecuteDeleteAsync(); } [Fact] public async Task A_scoped_tariff_link_lists_what_can_price_the_meter() { using var factory = new MeterVaultAppFactory(fx.ConnectionString); using var client = factory.CreateClient(); var scoped = WebUtility.HtmlDecode(await client.GetStringAsync(new Uri($"{TariffLinks.Path}?scope=meter&id={_meter}", UriKind.Relative))); Assert.Contains("Tariffs that can price Tap meter: its own, its energy type's and global ones.", scoped, StringComparison.Ordinal); Assert.Contains("1.2345", scoped, StringComparison.Ordinal); // its own price Assert.Contains("2.3456", scoped, StringComparison.Ordinal); // its type's bonus … Assert.Contains("Not applied yet", scoped, StringComparison.Ordinal); // … which is not applied yet Assert.DoesNotContain("9.8765", scoped, StringComparison.Ordinal); // another type's price is not listed Assert.Contains("Bonus, discount and tax tariffs are stored but not applied to costs yet.", scoped, StringComparison.Ordinal); var all = WebUtility.HtmlDecode(await client.GetStringAsync(new Uri(TariffLinks.Path, UriKind.Relative))); Assert.Contains("9.8765", all, StringComparison.Ordinal); Assert.DoesNotContain("Tariffs that can price", all, StringComparison.Ordinal); // The deep link of a missing price renders; its dialog opens only once the page is interactive. var link = TariffLinks.New(TariffScope.Meter, _meter, TariffComponent.UnitPrice, new DateOnly(2027, 1, 1)); (await client.GetAsync(new Uri(link, UriKind.Relative))).EnsureSuccessStatusCode(); } [Fact] public async Task Settings_label_raw_retention_as_not_enforced_in_both_languages() { using var factory = new MeterVaultAppFactory(fx.ConnectionString); using var client = factory.CreateClient(); var english = WebUtility.HtmlDecode(await client.GetStringAsync(new Uri("/admin/settings", UriKind.Relative))); Assert.Contains("Not enforced", english, StringComparison.Ordinal); Assert.Contains("Raw readings are kept indefinitely (configured: 1095 days).", english, StringComparison.Ordinal); Assert.Contains("Analysis data", english, StringComparison.Ordinal); Assert.Contains("Meters with current analysis data", english, StringComparison.Ordinal); Assert.DoesNotContain("could not be read", english, StringComparison.Ordinal); using var german = factory.CreateClient(); german.DefaultRequestHeaders.Add( "Cookie", CookieRequestCultureProvider.DefaultCookieName + "=" + Uri.EscapeDataString(CookieRequestCultureProvider.MakeCookieValue(new RequestCulture("de")))); var deutsch = WebUtility.HtmlDecode(await german.GetStringAsync(new Uri("/admin/settings", UriKind.Relative))); Assert.Contains("Nicht aktiv", deutsch, StringComparison.Ordinal); Assert.Contains("Auswertungsdaten", deutsch, StringComparison.Ordinal); } private static Tariff Tariff(TariffScope scope, int id, TariffComponent component, double value, string unit) => new() { ScopeType = scope, ScopeId = id, Component = component, Value = value, Unit = unit, ValidFrom = new DateOnly(2020, 1, 1) }; }