#nullable enable using System; using System.Collections.Generic; using Cysharp.Threading.Tasks; using NUnit.Framework; using ShrinkContext; using ShrinkNetwork; namespace ShrinkNetwork.Integration.App.Tests { /// /// 阶段 3 验收(EditMode 部分):Network 组件热插拔—— /// 停用时传输解绑、会话清理、服务键撤回、依赖者停用,全部为效应回滚结果。 /// public class ShrinkNetworkAppComponentTests { private ShrinkContextRuntime _runtime = null!; [SetUp] public void SetUp() { _runtime = new ShrinkContextRuntime(); } [TearDown] public void TearDown() { _runtime.ShutdownAsync().GetAwaiter().GetResult(); } private static void Run(UniTask task) => task.GetAwaiter().GetResult(); [Test] public void Apply_ProvidesServiceKey_AndBindsLoopbackWithSession() { var sessionsBefore = ShrinkNetworkRuntime.Default.Sessions.Count; var fiber = _runtime.Use(new ShrinkNetworkAppComponent(), new ShrinkNetworkAppModuleConfig { BindLoopbackTransport = true, LoopbackSessionId = 7 }); Assert.AreEqual(ShrinkFiberState.Active, fiber.State); Assert.IsTrue(_runtime.TryGetRaw(_runtime.RootContext, ShrinkNetworkAppComponent.ServiceKey, out var service)); Assert.AreSame(ShrinkNetworkRuntime.Default, service); Assert.AreEqual(sessionsBefore + 1, service.Sessions.Count, "loopback 绑定应新建一个会话"); } [Test] public void Retire_UnbindsTransport_ClearsSessions_AndStopsDependents() { var sessionsBefore = ShrinkNetworkRuntime.Default.Sessions.Count; var consumerFiber = _runtime.Use(new ServiceConsumerComponent()); Assert.AreEqual(ShrinkFiberState.Inactive, consumerFiber.State, "网络服务未提供前依赖者等待"); var networkFiber = _runtime.Use(new ShrinkNetworkAppComponent(), new ShrinkNetworkAppModuleConfig { BindLoopbackTransport = true }); Assert.AreEqual(ShrinkFiberState.Active, networkFiber.State); Assert.AreEqual(ShrinkFiberState.Active, consumerFiber.State, "服务键发布后依赖者激活"); Run(_runtime.RetireAsync(networkFiber)); Assert.AreEqual(sessionsBefore, ShrinkNetworkRuntime.Default.Sessions.Count, "停用必须解绑传输并清理自己的会话(效应回滚,非手写卸载;不影响他人遗留会话)"); Assert.IsFalse(_runtime.TryGetRaw(_runtime.RootContext, ShrinkNetworkAppComponent.ServiceKey, out _), "服务键随 set 的逆操作撤回"); Assert.AreEqual(ShrinkFiberState.Inactive, consumerFiber.State, "依赖者随提供者停用"); } [Test] public void Reactivate_RebindsLoopback() { var sessionsBefore = ShrinkNetworkRuntime.Default.Sessions.Count; var first = _runtime.Use(new ShrinkNetworkAppComponent(), new ShrinkNetworkAppModuleConfig { BindLoopbackTransport = true }); Run(_runtime.RetireAsync(first)); Assert.AreEqual(sessionsBefore, ShrinkNetworkRuntime.Default.Sessions.Count); var second = _runtime.Use(new ShrinkNetworkAppComponent(), new ShrinkNetworkAppModuleConfig { BindLoopbackTransport = true }); Assert.AreEqual(ShrinkFiberState.Active, second.State); Assert.AreEqual(sessionsBefore + 1, ShrinkNetworkRuntime.Default.Sessions.Count, "重新激活重新绑定传输"); } /// 注入网络服务键的消费者组件。 private sealed class ServiceConsumerComponent : IShrinkComponent { public string Name => "network-consumer"; public IReadOnlyList Inject { get; } = new[] { ShrinkNetworkAppComponent.ServiceKey }; public IReadOnlyList Provide => Array.Empty(); public ShrinkNetworkService? Seen { get; private set; } public UniTask ApplyAsync(ShrinkCtx ctx, object? config) { Seen = ctx.Get(ShrinkNetworkAppComponent.ServiceKey); return UniTask.CompletedTask; } } } }