using System.Threading.Channels;
using Xunit;
using YZWater.Core.Services;
namespace YZWater.Core.Tests.Services;
///
/// 报警/审计写入队列"满了丢最旧"的可观测性回归。
/// 背景:两个服务都用 BoundedChannelFullMode.DropOldest,并且忽略 TryWrite 的返回值 ——
/// 队列塞满时记录被静默丢掉,界面和日志都不显示,事后看台账会以为"当时只发生过这些"。
/// 现在丢弃条数被计数、周期性写日志,并在报警页/审计页底部提示。
///
public class QueueDropAccountingTests
{
///
/// 这条测试盯的是整个改动的立足点:DropOldest 下 TryWrite 依然返回 true,
/// 所以"写成功"不能当成"没丢数据",唯一的丢弃入口是 itemDropped 回调。
///
[Fact]
public void DropOldest_KeepsTryWriteTrue_And_ReportsDroppedItem()
{
var dropped = new List();
var channel = Channel.CreateBounded(
new BoundedChannelOptions(2) { FullMode = BoundedChannelFullMode.DropOldest },
item => dropped.Add(item));
Assert.True(channel.Writer.TryWrite(1));
Assert.True(channel.Writer.TryWrite(2));
// 队列已满:这一次写仍然"成功",但把最旧的 1 挤了出去
Assert.True(channel.Writer.TryWrite(3));
Assert.Equal(new[] { 1 }, dropped);
Assert.Equal(new[] { 2, 3 }, ReadAll(channel));
}
///
/// 反向验证:只有 Wait 模式会在队满时让 TryWrite 返回 false,且不产生任何丢弃
/// (实测 .NET 8:DropWrite/DropNewest/DropOldest 三种丢弃模式下 TryWrite 全部返回 true,
/// 所以"写入成功"根本不能当成"没丢数据")
///
[Fact]
public void WaitMode_ReportsFailureOnTryWrite_And_DropsNothing()
{
var dropped = new List();
var channel = Channel.CreateBounded(
new BoundedChannelOptions(2) { FullMode = BoundedChannelFullMode.Wait },
item => dropped.Add(item));
Assert.True(channel.Writer.TryWrite(1));
Assert.True(channel.Writer.TryWrite(2));
Assert.False(channel.Writer.TryWrite(3));
Assert.Empty(dropped);
Assert.Equal(new[] { 1, 2 }, ReadAll(channel));
}
/// 正常写入不该被算成丢弃:计数只应在队列真的满过时才涨
[Fact]
public void NewAlarmService_StartsWithNoDroppedRecords()
{
var service = new AlarmService(PlcPollingService.Instance);
try
{
service.RecordJournalAlarm("测试用流水报警", 1);
Assert.Equal(0, service.DroppedRecordCount);
}
finally
{
service.Dispose();
}
}
private static int[] ReadAll(Channel channel)
{
var items = new List();
while (channel.Reader.TryRead(out var item)) items.Add(item);
return items.ToArray();
}
}