using MeterVault.App; namespace MeterVault.Integration.Tests; /// /// The manual-reading keypad buffer: the pure part of entering a reading by hand at the meter. /// No database — these are the rules that decide what number gets stored, so they are pinned here /// rather than left to a render test. /// public sealed class ReadingEntryTests { [Theory] // The keypad's own output, and what a German keyboard produces. [InlineData("12345,6", 12345.6)] [InlineData("12345,", 12345)] // An English keyboard: a lone dot is decimal, because nobody groups thousands on a register. [InlineData("12345.6", 12345.6)] [InlineData("1.234", 1.234)] // Both separators present: the last one is decimal, whichever dialect it came from. [InlineData("1.234,5", 1234.5)] [InlineData("1,234.5", 1234.5)] [InlineData("1.234.567,89", 1234567.89)] [InlineData("0", 0)] [InlineData("-12,5", -12.5)] public void Parses_either_dialect_taking_the_last_separator_as_decimal(string text, double expected) { Assert.True(ReadingEntry.TryParse(text, out var value)); Assert.Equal(expected, value, 9); } [Theory] [InlineData("")] [InlineData(" ")] [InlineData(",")] [InlineData("-")] [InlineData("12 345")] [InlineData("abc")] public void Rejects_input_that_is_not_a_number(string text) => Assert.False(ReadingEntry.TryParse(text, out _)); [Fact] public void Prefill_shows_the_last_reading_in_the_dialect_the_keypad_types() { var entry = new ReadingEntry(); entry.Prefill(12345.6); Assert.Equal("12345,6", entry.Text); Assert.True(entry.IsPristine); Assert.Equal(12345.6, entry.Value!.Value, 9); } [Fact] public void Prefill_does_not_invent_precision_the_meter_never_reported() { var entry = new ReadingEntry(); entry.Prefill(2287); Assert.Equal("2287", entry.Text); } [Fact] public void First_digit_after_a_prefill_starts_a_fresh_value() { // Reading a completely different register: typing must not append to the prefill. var entry = new ReadingEntry(); entry.Prefill(12345.6); entry.AppendDigit('9'); Assert.Equal("9", entry.Text); Assert.False(entry.IsPristine); } [Fact] public void Backspace_after_a_prefill_edits_it_in_place() { // The walk-up case: only the last digits of a register have moved, so backspace keeps the // leading digits instead of clearing them the way a digit key does. var entry = new ReadingEntry(); entry.Prefill(12345.6); entry.Backspace(); // "12345," entry.Backspace(); // "12345" entry.AppendDigit('8'); entry.AppendSeparator(); entry.AppendDigit('9'); Assert.Equal("123458,9", entry.Text); Assert.Equal(123458.9, entry.Value!.Value, 9); } [Fact] public void Accepts_at_most_one_separator() { var entry = new ReadingEntry(); entry.AppendDigit('1'); entry.AppendSeparator(); entry.AppendSeparator(); entry.AppendDigit('5'); Assert.Equal("1,5", entry.Text); } [Fact] public void A_leading_separator_becomes_a_leading_zero() { var entry = new ReadingEntry(); entry.AppendSeparator(); entry.AppendDigit('5'); Assert.Equal("0,5", entry.Text); Assert.Equal(0.5, entry.Value!.Value, 9); } [Fact] public void A_leading_zero_is_replaced_rather_than_kept() { var entry = new ReadingEntry(); entry.AppendDigit('0'); entry.AppendDigit('7'); Assert.Equal("7", entry.Text); } [Fact] public void Clear_empties_the_buffer_and_leaves_no_value() { var entry = new ReadingEntry(); entry.Prefill(500); entry.Clear(); Assert.Equal(string.Empty, entry.Text); Assert.False(entry.IsPristine); Assert.Null(entry.Value); } [Fact] public void Backspace_on_an_empty_buffer_is_a_no_op() { var entry = new ReadingEntry(); entry.Backspace(); Assert.Equal(string.Empty, entry.Text); Assert.Null(entry.Value); } [Fact] public void Typed_text_keeps_only_what_can_form_a_number() { var entry = new ReadingEntry(); entry.Prefill(1); entry.SetText("1 234,5 kWh"); Assert.Equal("1234,5", entry.Text); Assert.False(entry.IsPristine); Assert.Equal(1234.5, entry.Value!.Value, 9); } [Fact] public void A_stuck_key_cannot_grow_the_buffer_without_bound() { var entry = new ReadingEntry(); for (var i = 0; i < ReadingEntry.MaxLength + 10; i++) { entry.AppendDigit('9'); } Assert.Equal(ReadingEntry.MaxLength, entry.Text.Length); } }