Files
Store.Management/Tests/MAX.Notification.SmokeTests/Program.cs
T
2026-10-27 14:35:03 +08:00

87 lines
4.0 KiB
C#

using MAX.Notification.NotificationPublishers;
using MAX.Notification;
var calls = new List<int>();
var services = new TestServices();
services.Handlers = [new First((_, _) => { calls.Add(1); return Task.CompletedTask; }),
new First((_, _) => { calls.Add(99); return Task.CompletedTask; }),
new Second((_, _) => { calls.Add(2); return Task.CompletedTask; })];
var publisher = new NotificationPublisher(services);
INotification message = new Changed();
await publisher.Publish(message);
Check(calls.SequenceEqual([1, 2]), "运行时类型分发、顺序和类型去重");
calls.Clear();
await publisher.Publish((object)message);
Check(calls.SequenceEqual([1, 2]) && services.Resolutions == 2, "对象分发和每次重新解析");
services.Handlers = [];
await publisher.Publish(message);
services.Handlers = null;
await publisher.Publish(message);
await Throws<ArgumentNullException>(() => publisher.Publish((object)null!));
await Throws<ArgumentException>(() => publisher.Publish(new object()));
await Throws<ArgumentNullException>(() => Task.FromResult(new NotificationPublisher(services, null!)));
using var cancelled = new CancellationTokenSource();
cancelled.Cancel();
// 原版只传递取消令牌;没有处理器时即使已取消也正常完成。
await publisher.Publish(message, cancelled.Token);
services.Handlers = [new First((_, _) => throw new InvalidOperationException("expected")),
new Second((_, _) => { calls.Add(3); return Task.CompletedTask; })];
calls.Clear();
await Throws<InvalidOperationException>(() => publisher.Publish(message));
Check(calls.Count == 0, "顺序模式失败后停止");
await Throws<InvalidOperationException>(() => new NotificationPublisher(services,
new TaskWhenAllPublisher()).Publish(message));
Check(calls.Count == 0, "原版并发模式同步异常阻止后续枚举");
var gate = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
var started = 0;
Task Handle(Changed _, CancellationToken token)
{
Check(token == cancelled.Token || !token.IsCancellationRequested, "取消令牌传递");
if (++started == 2) gate.SetResult();
return gate.Task;
}
services.Handlers = [new First(Handle), new Second(Handle)];
await new NotificationPublisher(services, new TaskWhenAllPublisher())
.Publish(message).WaitAsync(TimeSpan.FromSeconds(5));
Check(started == 2, "并发启动和等待全部完成");
using var during = new CancellationTokenSource();
services.Handlers = [new First((_, token) => { Check(token == during.Token, "原始令牌"); during.Cancel(); return Task.CompletedTask; }),
new Second((_, token) => { token.ThrowIfCancellationRequested(); return Task.CompletedTask; })];
await Throws<OperationCanceledException>(() => publisher.Publish(message, during.Token));
Console.WriteLine("All notification smoke tests passed.");
static void Check(bool condition, string name)
{
if (!condition) throw new Exception($"检查失败:{name}");
}
static async Task Throws<T>(Func<Task> action) where T : Exception
{
try { await action(); }
catch (T) { return; }
throw new Exception($"预期异常:{typeof(T).Name}");
}
sealed record Changed : INotification;
sealed class First(Func<Changed, CancellationToken, Task> action) : INotificationHandler<Changed>
{
public Task Handle(Changed notification, CancellationToken cancellationToken) => action(notification, cancellationToken);
}
sealed class Second(Func<Changed, CancellationToken, Task> action) : INotificationHandler<Changed>
{
public Task Handle(Changed notification, CancellationToken cancellationToken) => action(notification, cancellationToken);
}
sealed class TestServices : IServiceProvider
{
public INotificationHandler<Changed>[]? Handlers { get; set; }
public int Resolutions { get; private set; }
public object? GetService(Type serviceType)
{
if (serviceType != typeof(IEnumerable<INotificationHandler<Changed>>))
throw new Exception($"错误的解析类型:{serviceType}");
Resolutions++;
return Handlers;
}
}