food-market/tests/food-market.UnitTests/ScaleMonitorTests.cs
nns 195dfb1a2c feat(pos): весовой товар на кассе — вес с весов/вручную + автопоиск весов
Часть 3 + автопоиск (данные sysadmin: касса МойСклад спамит «Порт не выбран»):
- Весовой товар при сканировании/плитке → вес с весов (если на связи), иначе
  модалка ручного ввода веса. Сумма = цена/кг × вес; подпись «1,235 кг × 690 ₸/кг».
- ScaleMonitor (pos.core): опрос ТОЛЬКО по требованию (при весовом товаре), мягкая
  деградация на ручной ввод, запись о потере/восстановлении связи РОВНО ОДИН раз
  (не сотни «Таймаут»). Юнит-тесты (100 опросов → 1 запись).
- ScaleDiscovery (pos): автоскан всех COM-портов (вкл. USB-COM CH340/Prolific) ×
  протоколы Масса-К/CAS × скорости; кнопка «Найти весы» в верхней панели; найденный
  порт запоминается (kv) и восстанавливается при старте.
- UnitShort («шт»/«кг»/«л») протянут через sync→SQLite→PosProduct/CartLine; цена
  весового показывается «690 ₸/кг» вместо кода ОКЕИ «690 ₸/796».
- Разбор этикеток в кассе — по настройкам орг (по умолчанию ВЫКЛ, точный штрихкод).
- Юнит-тесты 321/321 (обновлены PosBarcodeTests под дефолт-off).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 11:17:35 +05:00

74 lines
2.6 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using FluentAssertions;
using foodmarket.Pos.Core.Hardware;
using foodmarket.Pos.Core.Models;
using Xunit;
namespace foodmarket.UnitTests;
public class ScaleMonitorTests
{
[Fact]
public async Task No_reader_returns_null_and_disconnected()
{
var m = new ScaleMonitor();
(await m.TryReadWeightAsync()).Should().BeNull();
m.IsConnected.Should().BeFalse();
m.HasReader.Should().BeFalse();
m.StatusText.Should().Be("Весы не подключены");
}
[Fact]
public async Task Reader_weight_is_returned_and_connected()
{
var m = new ScaleMonitor();
m.SetReader(_ => Task.FromResult(1.234m));
(await m.TryReadWeightAsync()).Should().Be(1.234m);
m.IsConnected.Should().BeTrue();
m.StatusText.Should().Be("Весы на связи");
}
[Fact]
public async Task Loss_of_connection_is_logged_exactly_once()
{
var log = new List<string>();
var m = new ScaleMonitor(log.Add);
m.SetReader(_ => throw new InvalidOperationException("Порт не выбран"));
// Сто опросов подряд при оборванной связи — одна запись о потере (не сотни).
for (int i = 0; i < 100; i++) (await m.TryReadWeightAsync()).Should().BeNull();
log.Count(l => l.Contains("нет связи")).Should().Be(1, "потеря связи логируется один раз");
m.IsConnected.Should().BeFalse();
}
[Fact]
public async Task Recovery_after_loss_is_logged_once()
{
var log = new List<string>();
var m = new ScaleMonitor(log.Add);
var fail = true;
m.SetReader(_ => fail ? throw new TimeoutException() : Task.FromResult(0.5m));
await m.TryReadWeightAsync(); // потеря
await m.TryReadWeightAsync(); // ещё раз — не логируем повторно
fail = false;
(await m.TryReadWeightAsync()).Should().Be(0.5m); // восстановление
log.Count(l => l.Contains("нет связи")).Should().Be(1);
log.Count(l => l.Contains("восстановлена")).Should().Be(1);
m.IsConnected.Should().BeTrue();
}
[Fact]
public void Weighed_line_text_shows_kg_times_price_per_kg()
{
var line = new CartLine
{
ProductId = Guid.NewGuid(), Name = "Яблоки", UnitCode = "166", UnitShort = "кг",
IsWeighed = true, UnitPrice = 690m, Quantity = 1.235m,
};
line.WeighedQtyText.Should().Be("1,235 кг × 690 ₸/кг");
line.LineTotal.Should().Be(852.15m);
}
}