using XFEEDToolBox.Core.Services;
using XFEExtension.NetCore.XUnit.Attributes;
namespace XFEEDToolBox.Test;
[TestFixture]
public sealed class ActivityLogTests
{
[Test]public void RepeatedPollFailuresKeepOneRowFirstLastCountAndBothDiagnostics()
{
var start=DateTimeOffset.Parse("2026-10-25T10:00:00Z");var now=start;var saved=new List<ActivityEntry>();
var log=new ActivityLog(()=>now,saved.Add);
for(var index=0;index<100;index++)
{
now=start.AddSeconds(index*3);
log.Report("system.telemetry","当前星系","等待飞行遥测",ActivitySeverity.Status,"stack "+index,"telemetry-not-ready");
}
var row=log.Snapshot().Single();Check.Equal(100,row.Occurrences);Check.Equal(start,row.Time);Check.Equal(now,row.LastSeen);
Check.Equal("stack 0",row.FirstDiagnostic);Check.Equal("stack 99",row.LastDiagnostic);Check.That(row.DiagnosticDisplay.Contains("stack 0")&&row.DiagnosticDisplay.Contains("stack 99"));
Check.Equal(5,saved.Count);now=start.AddSeconds(300);Check.That(log.Resolve("system.telemetry","当前星系","飞行遥测已恢复"));
var resolved=log.Snapshot().Single();Check.Equal(row.Id,resolved.Id);Check.That(resolved.Resolved);Check.Equal(row.LastSeen,resolved.LastSeen);Check.Equal(now,resolved.ClosedAt!.Value);
Check.Equal(100,saved.Last().Occurrences);Check.That(!log.Resolve("system.telemetry","当前星系","重复恢复"));
}
[Test]public void ChangedReasonEscalatesAndOrdinaryEventsDoNotLoseTheirSeparateTimeline()
{
var now=DateTimeOffset.UtcNow;var log=new ActivityLog(()=>now);
log.Append("执行","已到站");log.Append("执行","已到站");
log.Report("read","读取","等待加载",ActivitySeverity.Status,"first","same-type");var first=log.Snapshot()[0];
now=now.AddSeconds(3);log.Report("read","读取","读取持续失败",ActivitySeverity.Warning,"last","same-type");
var last=log.Snapshot()[0];Check.Equal(first.Id,last.Id);Check.Equal(ActivitySeverity.Warning,last.Severity);Check.Equal("等待加载",last.FirstMessage);Check.Equal("读取持续失败",last.Message);
log.Report("read","读取","权限不足",ActivitySeverity.Error,"permission","different-type");
var rows=log.Snapshot();Check.Equal(4,rows.Count);Check.That(rows[1].Superseded&&!rows[1].Resolved);Check.Equal("已更新",rows[1].SeverityDisplay);
Check.That(rows[2].Id!=rows[3].Id);Check.Equal(1,rows[2].Occurrences);
}
[Test]public void BoundedRetentionEvictsOldActiveKeysWithoutMergingUnrelatedFailures()
{
var log=new ActivityLog();
for(var index=0;index<400;index++)log.Report("failure:"+index,"读取","失败",ActivitySeverity.Warning);
Check.Equal(300,log.Snapshot(300).Count);Check.Equal(200,log.Snapshot().Count);
log.Report("failure:0","读取","失败",ActivitySeverity.Warning);
Check.Equal(1,log.Snapshot()[0].Occurrences);Check.Equal(300,log.Snapshot(300).Count);
}
[Test]public async Task RuntimeDiagnosticApiRetainsStacksWithoutPuttingThemInTheVisibleSummary()
{
using var temporary=new TemporaryDirectory();await using var runtime=new ToolboxRuntime(temporary.Path);
runtime.ReportDiagnostic("system.telemetry","当前星系",new InvalidOperationException("Native player telemetry unavailable or stale"),ActivitySeverity.Status,"等待游戏飞行遥测");
runtime.ReportDiagnostic("system.telemetry","当前星系",new InvalidOperationException("Native player telemetry unavailable or stale"),ActivitySeverity.Status,"等待游戏飞行遥测");
var entry=runtime.Activity.Single();Check.Equal(2,entry.Occurrences);Check.Equal("等待游戏飞行遥测",entry.Message);Check.That(entry.FirstDiagnostic.Contains("InvalidOperationException"));
runtime.ResolveDiagnostic("system.telemetry","当前星系","遥测已恢复");Check.That(runtime.Activity.Single().Resolved);
Check.That(File.ReadAllText(System.IO.Path.Combine(temporary.Path,"logs","activity.jsonl")).Contains("Occurrences"));
}
}
using XFEEDToolBox.Core.Services;
using XFEExtension.NetCore.XUnit.Attributes;
namespace XFEEDToolBox.Test;
[TestFixture]
public sealed class ActivityLogTests
{
[Test]public void RepeatedPollFailuresKeepOneRowFirstLastCountAndBothDiagnostics()
{
var start=DateTimeOffset.Parse("2026-10-25T10:00:00Z");var now=start;var saved=new List<ActivityEntry>();
var log=new ActivityLog(()=>now,saved.Add);
for(var index=0;index<100;index++)
{
now=start.AddSeconds(index*3);
log.Report("system.telemetry","当前星系","等待飞行遥测",ActivitySeverity.Status,"stack "+index,"telemetry-not-ready");
}
var row=log.Snapshot().Single();Check.Equal(100,row.Occurrences);Check.Equal(start,row.Time);Check.Equal(now,row.LastSeen);
Check.Equal("stack 0",row.FirstDiagnostic);Check.Equal("stack 99",row.LastDiagnostic);Check.That(row.DiagnosticDisplay.Contains("stack 0")&&row.DiagnosticDisplay.Contains("stack 99"));
Check.Equal(5,saved.Count);now=start.AddSeconds(300);Check.That(log.Resolve("system.telemetry","当前星系","飞行遥测已恢复"));
var resolved=log.Snapshot().Single();Check.Equal(row.Id,resolved.Id);Check.That(resolved.Resolved);Check.Equal(row.LastSeen,resolved.LastSeen);Check.Equal(now,resolved.ClosedAt!.Value);
Check.Equal(100,saved.Last().Occurrences);Check.That(!log.Resolve("system.telemetry","当前星系","重复恢复"));
}
[Test]public void ChangedReasonEscalatesAndOrdinaryEventsDoNotLoseTheirSeparateTimeline()
{
var now=DateTimeOffset.UtcNow;var log=new ActivityLog(()=>now);
log.Append("执行","已到站");log.Append("执行","已到站");
log.Report("read","读取","等待加载",ActivitySeverity.Status,"first","same-type");var first=log.Snapshot()[0];
now=now.AddSeconds(3);log.Report("read","读取","读取持续失败",ActivitySeverity.Warning,"last","same-type");
var last=log.Snapshot()[0];Check.Equal(first.Id,last.Id);Check.Equal(ActivitySeverity.Warning,last.Severity);Check.Equal("等待加载",last.FirstMessage);Check.Equal("读取持续失败",last.Message);
log.Report("read","读取","权限不足",ActivitySeverity.Error,"permission","different-type");
var rows=log.Snapshot();Check.Equal(4,rows.Count);Check.That(rows[1].Superseded&&!rows[1].Resolved);Check.Equal("已更新",rows[1].SeverityDisplay);
Check.That(rows[2].Id!=rows[3].Id);Check.Equal(1,rows[2].Occurrences);
}
[Test]public void BoundedRetentionEvictsOldActiveKeysWithoutMergingUnrelatedFailures()
{
var log=new ActivityLog();
for(var index=0;index<400;index++)log.Report("failure:"+index,"读取","失败",ActivitySeverity.Warning);
Check.Equal(300,log.Snapshot(300).Count);Check.Equal(200,log.Snapshot().Count);
log.Report("failure:0","读取","失败",ActivitySeverity.Warning);
Check.Equal(1,log.Snapshot()[0].Occurrences);Check.Equal(300,log.Snapshot(300).Count);
}
[Test]public async Task RuntimeDiagnosticApiRetainsStacksWithoutPuttingThemInTheVisibleSummary()
{
using var temporary=new TemporaryDirectory();await using var runtime=new ToolboxRuntime(temporary.Path);
runtime.ReportDiagnostic("system.telemetry","当前星系",new InvalidOperationException("Native player telemetry unavailable or stale"),ActivitySeverity.Status,"等待游戏飞行遥测");
runtime.ReportDiagnostic("system.telemetry","当前星系",new InvalidOperationException("Native player telemetry unavailable or stale"),ActivitySeverity.Status,"等待游戏飞行遥测");
var entry=runtime.Activity.Single();Check.Equal(2,entry.Occurrences);Check.Equal("等待游戏飞行遥测",entry.Message);Check.That(entry.FirstDiagnostic.Contains("InvalidOperationException"));
runtime.ResolveDiagnostic("system.telemetry","当前星系","遥测已恢复");Check.That(runtime.Activity.Single().Resolved);
Check.That(File.ReadAllText(System.IO.Path.Combine(temporary.Path,"logs","activity.jsonl")).Contains("Occurrences"));
}
}