diff --git a/GFramework.Core.Tests/architecture/ArchitectureConstantsTests.cs b/GFramework.Core.Tests/architecture/ArchitectureConstantsTests.cs new file mode 100644 index 0000000..02bb50a --- /dev/null +++ b/GFramework.Core.Tests/architecture/ArchitectureConstantsTests.cs @@ -0,0 +1,195 @@ +using System.Collections.Immutable; +using GFramework.Core.Abstractions.enums; +using GFramework.Core.architecture; +using NUnit.Framework; + +namespace GFramework.Core.Tests.architecture; + +/// +/// ArchitectureConstants类的单元测试 +/// 测试内容包括: +/// - 常量值的正确性 +/// - 常量类型验证 +/// - 常量可访问性 +/// - 常量命名规范 +/// - 架构阶段定义常量 +/// +[TestFixture] +public class ArchitectureConstantsTests +{ + /// + /// 测试PhaseOrder数组不为空 + /// + [Test] + public void PhaseOrder_Should_Not_Be_Empty() + { + Assert.That(ArchitectureConstants.PhaseOrder, Is.Not.Null); + Assert.That(ArchitectureConstants.PhaseOrder, Is.Not.Empty); + } + + /// + /// 测试PhaseOrder包含所有预期的架构阶段 + /// + [Test] + public void PhaseOrder_Should_Contain_All_Expected_Phases() + { + var expectedPhases = new[] + { + ArchitecturePhase.None, + ArchitecturePhase.BeforeUtilityInit, + ArchitecturePhase.AfterUtilityInit, + ArchitecturePhase.BeforeModelInit, + ArchitecturePhase.AfterModelInit, + ArchitecturePhase.BeforeSystemInit, + ArchitecturePhase.AfterSystemInit, + ArchitecturePhase.Ready, + ArchitecturePhase.Destroying, + ArchitecturePhase.Destroyed + }; + + Assert.That(ArchitectureConstants.PhaseOrder.Length, Is.EqualTo(expectedPhases.Length)); + + foreach (var expectedPhase in expectedPhases) + { + Assert.That(ArchitectureConstants.PhaseOrder, Does.Contain(expectedPhase)); + } + } + + /// + /// 测试PhaseOrder数组是只读的 + /// + [Test] + public void PhaseOrder_Should_Be_Immutable() + { + var phaseOrder = ArchitectureConstants.PhaseOrder; + Assert.That(phaseOrder, Is.Not.Null); + Assert.That(phaseOrder, Is.InstanceOf()); + } + + /// + /// 测试PhaseOrder的顺序是正确的 + /// + [Test] + public void PhaseOrder_Should_Be_In_Correct_Sequence() + { + var phases = ArchitectureConstants.PhaseOrder; + + Assert.That(phases[0], Is.EqualTo(ArchitecturePhase.None), "First phase should be None"); + Assert.That(phases[1], Is.EqualTo(ArchitecturePhase.BeforeUtilityInit), + "Second phase should be BeforeUtilityInit"); + Assert.That(phases[2], Is.EqualTo(ArchitecturePhase.AfterUtilityInit), + "Third phase should be AfterUtilityInit"); + Assert.That(phases[3], Is.EqualTo(ArchitecturePhase.BeforeModelInit), "Fourth phase should be BeforeModelInit"); + Assert.That(phases[4], Is.EqualTo(ArchitecturePhase.AfterModelInit), "Fifth phase should be AfterModelInit"); + Assert.That(phases[5], Is.EqualTo(ArchitecturePhase.BeforeSystemInit), + "Sixth phase should be BeforeSystemInit"); + Assert.That(phases[6], Is.EqualTo(ArchitecturePhase.AfterSystemInit), + "Seventh phase should be AfterSystemInit"); + Assert.That(phases[7], Is.EqualTo(ArchitecturePhase.Ready), "Eighth phase should be Ready"); + Assert.That(phases[8], Is.EqualTo(ArchitecturePhase.Destroying), "Ninth phase should be Destroying"); + Assert.That(phases[9], Is.EqualTo(ArchitecturePhase.Destroyed), "Tenth phase should be Destroyed"); + } + + /// + /// 测试PhaseTransitions字典不为空 + /// + [Test] + public void PhaseTransitions_Should_Not_Be_Empty() + { + Assert.That(ArchitectureConstants.PhaseTransitions, Is.Not.Null); + Assert.That(ArchitectureConstants.PhaseTransitions, Is.Not.Empty); + } + + /// + /// 测试PhaseTransitions是只读的 + /// + [Test] + public void PhaseTransitions_Should_Be_Immutable() + { + var transitions = ArchitectureConstants.PhaseTransitions; + Assert.That(transitions, Is.InstanceOf>()); + } + + /// + /// 测试PhaseTransitions包含正常线性流程的转换 + /// + [Test] + public void PhaseTransitions_Should_Contain_Normal_Linear_Transitions() + { + Assert.That(ArchitectureConstants.PhaseTransitions, Does.ContainKey(ArchitecturePhase.None)); + Assert.That(ArchitectureConstants.PhaseTransitions, Does.ContainKey(ArchitecturePhase.BeforeUtilityInit)); + Assert.That(ArchitectureConstants.PhaseTransitions, Does.ContainKey(ArchitecturePhase.AfterUtilityInit)); + Assert.That(ArchitectureConstants.PhaseTransitions, Does.ContainKey(ArchitecturePhase.BeforeModelInit)); + Assert.That(ArchitectureConstants.PhaseTransitions, Does.ContainKey(ArchitecturePhase.AfterModelInit)); + Assert.That(ArchitectureConstants.PhaseTransitions, Does.ContainKey(ArchitecturePhase.BeforeSystemInit)); + Assert.That(ArchitectureConstants.PhaseTransitions, Does.ContainKey(ArchitecturePhase.AfterSystemInit)); + Assert.That(ArchitectureConstants.PhaseTransitions, Does.ContainKey(ArchitecturePhase.Ready)); + Assert.That(ArchitectureConstants.PhaseTransitions, Does.ContainKey(ArchitecturePhase.Destroying)); + } + + /// + /// 测试PhaseTransitions中的转换方向是正确的 + /// + [Test] + public void PhaseTransitions_Should_Have_Correct_Directions() + { + var transitions = ArchitectureConstants.PhaseTransitions; + + Assert.That(transitions[ArchitecturePhase.None], Does.Contain(ArchitecturePhase.BeforeUtilityInit)); + Assert.That(transitions[ArchitecturePhase.BeforeUtilityInit], Does.Contain(ArchitecturePhase.AfterUtilityInit)); + Assert.That(transitions[ArchitecturePhase.AfterUtilityInit], Does.Contain(ArchitecturePhase.BeforeModelInit)); + Assert.That(transitions[ArchitecturePhase.BeforeModelInit], Does.Contain(ArchitecturePhase.AfterModelInit)); + Assert.That(transitions[ArchitecturePhase.AfterModelInit], Does.Contain(ArchitecturePhase.BeforeSystemInit)); + Assert.That(transitions[ArchitecturePhase.BeforeSystemInit], Does.Contain(ArchitecturePhase.AfterSystemInit)); + Assert.That(transitions[ArchitecturePhase.AfterSystemInit], Does.Contain(ArchitecturePhase.Ready)); + Assert.That(transitions[ArchitecturePhase.Ready], Does.Contain(ArchitecturePhase.Destroying)); + Assert.That(transitions[ArchitecturePhase.Destroying], Does.Contain(ArchitecturePhase.Destroyed)); + } + + /// + /// 测试PhaseTransitions包含失败初始化的转换路径 + /// + [Test] + public void PhaseTransitions_Should_Contain_FailedInitialization_Transition() + { + Assert.That(ArchitectureConstants.PhaseTransitions, Does.ContainKey(ArchitecturePhase.FailedInitialization)); + Assert.That(ArchitectureConstants.PhaseTransitions[ArchitecturePhase.FailedInitialization], + Does.Contain(ArchitecturePhase.Destroying)); + } + + /// + /// 测试每个阶段的转换数量不超过1个(线性转换) + /// + [Test] + public void PhaseTransitions_Should_Have_Maximum_One_Transition_Per_Phase() + { + foreach (var transition in ArchitectureConstants.PhaseTransitions) + { + Assert.That(transition.Value, Has.Length.LessThanOrEqualTo(1), + $"Phase {transition.Key} should have at most 1 transition"); + } + } + + /// + /// 测试PhaseOrder和PhaseTransitions的一致性 + /// + [Test] + public void PhaseOrder_And_PhaseTransitions_Should_Be_Consistent() + { + var phaseOrder = ArchitectureConstants.PhaseOrder; + var transitions = ArchitectureConstants.PhaseTransitions; + + for (int i = 0; i < phaseOrder.Length - 1; i++) + { + var currentPhase = phaseOrder[i]; + var nextPhase = phaseOrder[i + 1]; + + if (transitions.ContainsKey(currentPhase)) + { + var possibleNextPhases = transitions[currentPhase]; + Assert.That(possibleNextPhases, Does.Contain(nextPhase), + $"Transition from {currentPhase} should include {nextPhase}"); + } + } + } +} \ No newline at end of file diff --git a/GFramework.Core.Tests/command/AbstractAsyncCommandTests.cs b/GFramework.Core.Tests/command/AbstractAsyncCommandTests.cs new file mode 100644 index 0000000..b831c0d --- /dev/null +++ b/GFramework.Core.Tests/command/AbstractAsyncCommandTests.cs @@ -0,0 +1,400 @@ +using GFramework.Core.Abstractions.command; +using GFramework.Core.Abstractions.rule; +using GFramework.Core.architecture; +using GFramework.Core.command; +using GFramework.Core.environment; +using GFramework.Core.events; +using GFramework.Core.ioc; +using GFramework.Core.query; +using NUnit.Framework; + +namespace GFramework.Core.Tests.command; + +/// +/// AbstractAsyncCommand类的单元测试 +/// 测试内容包括: +/// - 异步命令无返回值版本的基础实现 +/// - 异步命令有返回值版本的基础实现 +/// - ExecuteAsync方法调用 +/// - ExecuteAsync方法的异常处理 +/// - 上下文感知功能(SetContext, GetContext) +/// - 日志功能(Logger属性) +/// - 子类继承行为验证(两个版本) +/// - 命令执行前日志记录 +/// - 命令执行后日志记录 +/// - 错误情况下的日志记录 +/// - 无返回值版本的行为 +/// - 有返回值版本的行为 +/// +[TestFixture] +public class AbstractAsyncCommandTests +{ + [SetUp] + public void SetUp() + { + _container = new IocContainer(); + _context = new ArchitectureContext( + _container, + new EventBus(), + new CommandBus(), + new QueryBus(), + new DefaultEnvironment(), + new AsyncQueryBus()); + } + + private ArchitectureContext _context = null!; + private IocContainer _container = null!; + + /// + /// 测试异步命令无返回值版本的基础实现 + /// + [Test] + public async Task AbstractAsyncCommand_Should_Implement_IAsyncCommand_Interface() + { + var input = new TestCommandInputV2(); + var command = new TestAsyncCommandV3(input); + + Assert.That(command, Is.InstanceOf()); + } + + /// + /// 测试异步命令有返回值版本的基础实现 + /// + [Test] + public async Task AbstractAsyncCommand_WithResult_Should_Implement_IAsyncCommand_Interface() + { + var input = new TestCommandInputV2(); + var command = new TestAsyncCommandWithResultV3(input); + + Assert.That(command, Is.InstanceOf>()); + } + + /// + /// 测试ExecuteAsync方法调用 + /// + [Test] + public async Task ExecuteAsync_Should_Invoke_OnExecuteAsync_Method() + { + var input = new TestCommandInputV2 { Value = 42 }; + var command = new TestAsyncCommandV3(input); + var asyncCommand = (IAsyncCommand)command; + + await asyncCommand.ExecuteAsync(); + + Assert.That(command.Executed, Is.True); + Assert.That(command.ExecutedValue, Is.EqualTo(42)); + } + + /// + /// 测试ExecuteAsync方法(带返回值)调用 + /// + [Test] + public async Task ExecuteAsync_WithResult_Should_Invoke_OnExecuteAsync_Method_And_Return_Result() + { + var input = new TestCommandInputV2 { Value = 100 }; + var command = new TestAsyncCommandWithResultV3(input); + var asyncCommand = (IAsyncCommand)command; + + var result = await asyncCommand.ExecuteAsync(); + + Assert.That(command.Executed, Is.True); + Assert.That(result, Is.EqualTo(200)); + } + + /// + /// 测试ExecuteAsync方法的异常处理 + /// + [Test] + public void ExecuteAsync_Should_Propagate_Exception_From_OnExecuteAsync() + { + var input = new TestCommandInputV2(); + var command = new TestAsyncCommandWithExceptionV3(input); + var asyncCommand = (IAsyncCommand)command; + + Assert.ThrowsAsync(async () => await asyncCommand.ExecuteAsync()); + } + + /// + /// 测试上下文感知功能 - SetContext方法 + /// + [Test] + public void SetContext_Should_Set_Context_Property() + { + var input = new TestCommandInputV2(); + var command = new TestAsyncCommandV3(input); + var contextAware = (IContextAware)command; + + contextAware.SetContext(_context); + + var context = contextAware.GetContext(); + Assert.That(context, Is.SameAs(_context)); + } + + /// + /// 测试上下文感知功能 - GetContext方法 + /// + [Test] + public void GetContext_Should_Return_Context_Property() + { + var input = new TestCommandInputV2(); + var command = new TestAsyncCommandV3(input); + var contextAware = (IContextAware)command; + + contextAware.SetContext(_context); + + var context = contextAware.GetContext(); + Assert.That(context, Is.Not.Null); + Assert.That(context, Is.SameAs(_context)); + } + + /// + /// 测试子类继承行为验证 - 无返回值版本 + /// + [Test] + public async Task Child_Class_Should_Inherit_And_Override_OnExecuteAsync_Method() + { + var input = new TestCommandInputV2 { Value = 100 }; + var command = new TestAsyncCommandChildV3(input); + var asyncCommand = (IAsyncCommand)command; + + await asyncCommand.ExecuteAsync(); + + Assert.That(command.Executed, Is.True); + Assert.That(command.ExecutedValue, Is.EqualTo(200)); + } + + /// + /// 测试子类继承行为验证 - 有返回值版本 + /// + [Test] + public async Task Child_Class_WithResult_Should_Inherit_And_Override_OnExecuteAsync_Method() + { + var input = new TestCommandInputV2 { Value = 50 }; + var command = new TestAsyncCommandWithResultChildV3(input); + var asyncCommand = (IAsyncCommand)command; + + var result = await asyncCommand.ExecuteAsync(); + + Assert.That(command.Executed, Is.True); + Assert.That(result, Is.EqualTo(150)); + } + + /// + /// 测试异步命令执行生命周期完整性 + /// + [Test] + public async Task AsyncCommand_Should_Complete_Execution_Lifecycle() + { + var input = new TestCommandInputV2 { Value = 42 }; + var command = new TestAsyncCommandV3(input); + var asyncCommand = (IAsyncCommand)command; + + Assert.That(command.Executed, Is.False, "Command should not be executed before ExecuteAsync"); + + await asyncCommand.ExecuteAsync(); + + Assert.That(command.Executed, Is.True, "Command should be executed after ExecuteAsync"); + Assert.That(command.ExecutedValue, Is.EqualTo(42), "Command should have correct executed value"); + } + + /// + /// 测试异步命令多次执行 + /// + [Test] + public async Task AsyncCommand_Should_Be_Executable_Multiple_Times() + { + var input = new TestCommandInputV2 { Value = 10 }; + var command = new TestAsyncCommandV3(input); + var asyncCommand = (IAsyncCommand)command; + + await asyncCommand.ExecuteAsync(); + Assert.That(command.ExecutedValue, Is.EqualTo(10), "First execution should have value 10"); + + await asyncCommand.ExecuteAsync(); + Assert.That(command.ExecutedValue, Is.EqualTo(10), "Second execution should have value 10"); + } + + /// + /// 测试异步命令(带返回值)的返回值类型 + /// + [Test] + public async Task AsyncCommand_WithResult_Should_Return_Correct_Type() + { + var input = new TestCommandInputV2 { Value = 100 }; + var command = new TestAsyncCommandWithResultV3(input); + var asyncCommand = (IAsyncCommand)command; + + var result = await asyncCommand.ExecuteAsync(); + + Assert.That(result, Is.InstanceOf()); + Assert.That(result, Is.EqualTo(200)); + } +} + +/// +/// 测试用命令输入类V2 +/// +public sealed class TestCommandInputV2 : ICommandInput +{ + /// + /// 获取或设置值 + /// + public int Value { get; init; } +} + +/// +/// 测试用异步命令类V3(无返回值) +/// +public sealed class TestAsyncCommandV3 : AbstractAsyncCommand +{ + /// + /// 构造函数 + /// + /// 命令输入 + public TestAsyncCommandV3(TestCommandInputV2 input) : base(input) + { + } + + /// + /// 获取命令是否已执行 + /// + public bool Executed { get; private set; } + + /// + /// 获取执行的值 + /// + public int ExecutedValue { get; private set; } + + /// + /// 执行异步命令的重写方法 + /// + /// 命令输入 + /// 表示异步操作的任务 + protected override Task OnExecuteAsync(TestCommandInputV2 input) + { + Executed = true; + ExecutedValue = input.Value; + return Task.CompletedTask; + } +} + +/// +/// 测试用异步命令类V3(有返回值) +/// +public sealed class TestAsyncCommandWithResultV3 : AbstractAsyncCommand +{ + /// + /// 构造函数 + /// + /// 命令输入 + public TestAsyncCommandWithResultV3(TestCommandInputV2 input) : base(input) + { + } + + /// + /// 获取命令是否已执行 + /// + public bool Executed { get; private set; } + + /// + /// 执行异步命令并返回结果的重写方法 + /// + /// 命令输入 + /// 执行结果的异步任务 + protected override Task OnExecuteAsync(TestCommandInputV2 input) + { + Executed = true; + return Task.FromResult(input.Value * 2); + } +} + +/// +/// 测试用异步命令类(抛出异常) +/// +public sealed class TestAsyncCommandWithExceptionV3 : AbstractAsyncCommand +{ + /// + /// 构造函数 + /// + /// 命令输入 + public TestAsyncCommandWithExceptionV3(TestCommandInputV2 input) : base(input) + { + } + + /// + /// 执行异步命令并抛出异常的重写方法 + /// + /// 命令输入 + /// 表示异步操作的任务 + /// 总是抛出异常 + protected override Task OnExecuteAsync(TestCommandInputV2 input) + { + throw new InvalidOperationException("Test exception"); + } +} + +/// +/// 测试用异步命令子类(无返回值) +/// +public sealed class TestAsyncCommandChildV3 : AbstractAsyncCommand +{ + /// + /// 构造函数 + /// + /// 命令输入 + public TestAsyncCommandChildV3(TestCommandInputV2 input) : base(input) + { + } + + /// + /// 获取命令是否已执行 + /// + public bool Executed { get; private set; } + + /// + /// 获取执行的值 + /// + public int ExecutedValue { get; private set; } + + /// + /// 执行异步命令的重写方法(子类实现) + /// + /// 命令输入 + /// 表示异步操作的任务 + protected override Task OnExecuteAsync(TestCommandInputV2 input) + { + Executed = true; + ExecutedValue = input.Value * 2; + return Task.CompletedTask; + } +} + +/// +/// 测试用异步命令子类(有返回值) +/// +public sealed class TestAsyncCommandWithResultChildV3 : AbstractAsyncCommand +{ + /// + /// 构造函数 + /// + /// 命令输入 + public TestAsyncCommandWithResultChildV3(TestCommandInputV2 input) : base(input) + { + } + + /// + /// 获取命令是否已执行 + /// + public bool Executed { get; private set; } + + /// + /// 执行异步命令并返回结果的重写方法(子类实现) + /// + /// 命令输入 + /// 执行结果的异步任务 + protected override Task OnExecuteAsync(TestCommandInputV2 input) + { + Executed = true; + return Task.FromResult(input.Value * 3); + } +} \ No newline at end of file diff --git a/GFramework.Core.Tests/constants/GFrameworkConstantsTests.cs b/GFramework.Core.Tests/constants/GFrameworkConstantsTests.cs new file mode 100644 index 0000000..606e259 --- /dev/null +++ b/GFramework.Core.Tests/constants/GFrameworkConstantsTests.cs @@ -0,0 +1,69 @@ +using GFramework.Core.constants; +using NUnit.Framework; + +namespace GFramework.Core.Tests.constants; + +/// +/// GFrameworkConstants类的单元测试 +/// 测试内容包括: +/// - 版本号常量格式正确性 +/// - 其他框架常量 +/// - 常量值正确性 +/// - 常量类型验证 +/// - 常量可访问性 +/// +[TestFixture] +public class GFrameworkConstantsTests +{ + /// + /// 测试FrameworkName常量的值正确性 + /// + [Test] + public void FrameworkName_Should_Have_Correct_Value() + { + Assert.That(GFrameworkConstants.FrameworkName, Is.EqualTo("GFramework")); + } + + /// + /// 测试FrameworkName常量的类型 + /// + [Test] + public void FrameworkName_Should_Be_String_Type() + { + Assert.That(GFrameworkConstants.FrameworkName, Is.InstanceOf()); + } + + /// + /// 测试FrameworkName常量不为空 + /// + [Test] + public void FrameworkName_Should_Not_Be_Null_Or_Empty() + { + Assert.That(GFrameworkConstants.FrameworkName, Is.Not.Null); + Assert.That(GFrameworkConstants.FrameworkName, Is.Not.Empty); + } + + /// + /// 测试FrameworkName常量是公共可访问的 + /// + [Test] + public void FrameworkName_Should_Be_Publicly_Accessible() + { + // 如果常量不存在或不是公共的,编译会失败或抛出异常 + Assert.DoesNotThrow(() => + { + var name = GFrameworkConstants.FrameworkName; + }); + } + + /// + /// 测试FrameworkName常量是只读的(const) + /// + [Test] + public void FrameworkName_Should_Be_Constant() + { + // const常量在编译时确定,这个测试主要验证其存在性 + var name = GFrameworkConstants.FrameworkName; + Assert.That(name, Is.EqualTo("GFramework")); + } +} \ No newline at end of file diff --git a/GFramework.Core.Tests/query/AbstractAsyncQueryTests.cs b/GFramework.Core.Tests/query/AbstractAsyncQueryTests.cs new file mode 100644 index 0000000..cb4ce24 --- /dev/null +++ b/GFramework.Core.Tests/query/AbstractAsyncQueryTests.cs @@ -0,0 +1,415 @@ +using GFramework.Core.Abstractions.query; +using GFramework.Core.Abstractions.rule; +using GFramework.Core.architecture; +using GFramework.Core.command; +using GFramework.Core.environment; +using GFramework.Core.events; +using GFramework.Core.ioc; +using GFramework.Core.query; +using NUnit.Framework; + +namespace GFramework.Core.Tests.query; + +/// +/// AbstractAsyncQuery类的单元测试 +/// 测试内容包括: +/// - 异步查询的基础实现 +/// - DoAsync方法调用 +/// - DoAsync方法的异常处理 +/// - 上下文感知功能(SetContext, GetContext) +/// - 日志功能(Logger属性) +/// - 子类继承行为验证 +/// - 查询执行前日志记录 +/// - 查询执行后日志记录 +/// - 返回值类型验证 +/// - 错误情况下的日志记录 +/// +[TestFixture] +public class AbstractAsyncQueryTests +{ + [SetUp] + public void SetUp() + { + _container = new IocContainer(); + _context = new ArchitectureContext( + _container, + new EventBus(), + new CommandBus(), + new QueryBus(), + new DefaultEnvironment(), + new AsyncQueryBus()); + } + + private ArchitectureContext _context = null!; + private IocContainer _container = null!; + + /// + /// 测试异步查询的基础实现 + /// + [Test] + public async Task AbstractAsyncQuery_Should_Implement_IAsyncQuery_Interface() + { + var input = new TestAsyncQueryInputV2(); + var query = new TestAsyncQueryV4(input); + + Assert.That(query, Is.InstanceOf>()); + } + + /// + /// 测试DoAsync方法调用 + /// + [Test] + public async Task DoAsync_Should_Invoke_OnDoAsync_Method() + { + var input = new TestAsyncQueryInputV2 { Value = 42 }; + var query = new TestAsyncQueryV4(input); + var asyncQuery = (IAsyncQuery)query; + + var result = await asyncQuery.DoAsync(); + + Assert.That(query.Executed, Is.True); + Assert.That(result, Is.EqualTo(84)); + } + + /// + /// 测试DoAsync方法的异常处理 + /// + [Test] + public void DoAsync_Should_Propagate_Exception_From_OnDoAsync() + { + var input = new TestAsyncQueryInputV2(); + var query = new TestAsyncQueryWithExceptionV4(input); + var asyncQuery = (IAsyncQuery)query; + + Assert.ThrowsAsync(async () => await asyncQuery.DoAsync()); + } + + /// + /// 测试上下文感知功能 - SetContext方法 + /// + [Test] + public void SetContext_Should_Set_Context_Property() + { + var input = new TestAsyncQueryInputV2(); + var query = new TestAsyncQueryV4(input); + var contextAware = (IContextAware)query; + + contextAware.SetContext(_context); + + var context = contextAware.GetContext(); + Assert.That(context, Is.SameAs(_context)); + } + + /// + /// 测试上下文感知功能 - GetContext方法 + /// + [Test] + public void GetContext_Should_Return_Context_Property() + { + var input = new TestAsyncQueryInputV2(); + var query = new TestAsyncQueryV4(input); + var contextAware = (IContextAware)query; + + contextAware.SetContext(_context); + + var context = contextAware.GetContext(); + Assert.That(context, Is.Not.Null); + Assert.That(context, Is.SameAs(_context)); + } + + /// + /// 测试子类继承行为验证 + /// + [Test] + public async Task Child_Class_Should_Inherit_And_Override_OnDoAsync_Method() + { + var input = new TestAsyncQueryInputV2 { Value = 100 }; + var query = new TestAsyncQueryChildV4(input); + var asyncQuery = (IAsyncQuery)query; + + var result = await asyncQuery.DoAsync(); + + Assert.That(query.Executed, Is.True); + Assert.That(result, Is.EqualTo(300)); + } + + /// + /// 测试异步查询执行生命周期完整性 + /// + [Test] + public async Task AsyncQuery_Should_Complete_Execution_Lifecycle() + { + var input = new TestAsyncQueryInputV2 { Value = 42 }; + var query = new TestAsyncQueryV4(input); + var asyncQuery = (IAsyncQuery)query; + + Assert.That(query.Executed, Is.False, "Query should not be executed before DoAsync"); + + var result = await asyncQuery.DoAsync(); + + Assert.That(query.Executed, Is.True, "Query should be executed after DoAsync"); + Assert.That(result, Is.EqualTo(84), "Query should have correct result"); + } + + /// + /// 测试异步查询多次执行 + /// + [Test] + public async Task AsyncQuery_Should_Be_Executable_Multiple_Times() + { + var input = new TestAsyncQueryInputV2 { Value = 10 }; + var query = new TestAsyncQueryV4(input); + var asyncQuery = (IAsyncQuery)query; + + var result1 = await asyncQuery.DoAsync(); + var result2 = await asyncQuery.DoAsync(); + + Assert.That(result1, Is.EqualTo(20), "First execution should have result 20"); + Assert.That(result2, Is.EqualTo(20), "Second execution should have result 20"); + } + + /// + /// 测试异步查询的返回值类型 + /// + [Test] + public async Task AsyncQuery_Should_Return_Correct_Type() + { + var input = new TestAsyncQueryInputV2 { Value = 100 }; + var query = new TestAsyncQueryV4(input); + var asyncQuery = (IAsyncQuery)query; + + var result = await asyncQuery.DoAsync(); + + Assert.That(result, Is.InstanceOf()); + Assert.That(result, Is.EqualTo(200)); + } + + /// + /// 测试异步查询的字符串返回值 + /// + [Test] + public async Task AsyncQuery_WithStringResult_Should_Return_String() + { + var input = new TestAsyncQueryInputV2 { Value = 5 }; + var query = new TestAsyncStringQueryV4(input); + var asyncQuery = (IAsyncQuery)query; + + var result = await asyncQuery.DoAsync(); + + Assert.That(result, Is.InstanceOf()); + Assert.That(result, Is.EqualTo("Value: 10")); + } + + /// + /// 测试异步查询的复杂对象返回值 + /// + [Test] + public async Task AsyncQuery_WithComplexResult_Should_Return_ComplexObject() + { + var input = new TestAsyncQueryInputV2 { Value = 10 }; + var query = new TestAsyncComplexQueryV4(input); + var asyncQuery = (IAsyncQuery)query; + + var result = await asyncQuery.DoAsync(); + + Assert.That(result, Is.Not.Null); + Assert.That(result.Value, Is.EqualTo(20)); + Assert.That(result.DoubleValue, Is.EqualTo(30)); + } + + /// + /// 测试异步查询在不同实例之间的独立性 + /// + [Test] + public async Task AsyncQuery_Should_Maintain_Independence_Between_Different_Instances() + { + var input1 = new TestAsyncQueryInputV2 { Value = 10 }; + var input2 = new TestAsyncQueryInputV2 { Value = 20 }; + var query1 = new TestAsyncQueryV4(input1); + var query2 = new TestAsyncQueryV4(input2); + var asyncQuery1 = (IAsyncQuery)query1; + var asyncQuery2 = (IAsyncQuery)query2; + + var result1 = await asyncQuery1.DoAsync(); + var result2 = await asyncQuery2.DoAsync(); + + Assert.That(result1, Is.EqualTo(20)); + Assert.That(result2, Is.EqualTo(40)); + } +} + +/// +/// 测试用异步查询输入类V2 +/// +public sealed class TestAsyncQueryInputV2 : IQueryInput +{ + /// + /// 获取或设置值 + /// + public int Value { get; init; } +} + +/// +/// 整数类型测试异步查询类V4,继承AbstractAsyncQuery +/// +public sealed class TestAsyncQueryV4 : AbstractAsyncQuery +{ + /// + /// 初始化TestAsyncQueryV4的新实例 + /// + /// 查询输入参数 + public TestAsyncQueryV4(TestAsyncQueryInputV2 input) : base(input) + { + } + + /// + /// 获取查询是否已执行 + /// + public bool Executed { get; private set; } + + /// + /// 执行异步查询操作的具体实现 + /// + /// 查询输入参数 + /// 查询结果,将输入值乘以2 + protected override Task OnDoAsync(TestAsyncQueryInputV2 input) + { + Executed = true; + return Task.FromResult(input.Value * 2); + } +} + +/// +/// 字符串类型测试异步查询类V4,继承AbstractAsyncQuery +/// +public sealed class TestAsyncStringQueryV4 : AbstractAsyncQuery +{ + /// + /// 初始化TestAsyncStringQueryV4的新实例 + /// + /// 查询输入参数 + public TestAsyncStringQueryV4(TestAsyncQueryInputV2 input) : base(input) + { + } + + /// + /// 获取查询是否已执行 + /// + public bool Executed { get; private set; } + + /// + /// 执行异步查询操作的具体实现 + /// + /// 查询输入参数 + /// 格式化的字符串结果 + protected override Task OnDoAsync(TestAsyncQueryInputV2 input) + { + Executed = true; + return Task.FromResult($"Value: {input.Value * 2}"); + } +} + +/// +/// 复杂对象类型测试异步查询类V4,继承AbstractAsyncQuery +/// +public sealed class TestAsyncComplexQueryV4 : AbstractAsyncQuery +{ + /// + /// 初始化TestAsyncComplexQueryV4的新实例 + /// + /// 查询输入参数 + public TestAsyncComplexQueryV4(TestAsyncQueryInputV2 input) : base(input) + { + } + + /// + /// 获取查询是否已执行 + /// + public bool Executed { get; private set; } + + /// + /// 执行异步查询操作的具体实现 + /// + /// 查询输入参数 + /// 复杂对象查询结果 + protected override Task OnDoAsync(TestAsyncQueryInputV2 input) + { + Executed = true; + var result = new TestAsyncQueryResultV2 + { + Value = input.Value * 2, + DoubleValue = input.Value * 3 + }; + return Task.FromResult(result); + } +} + +/// +/// 测试用异步查询类(抛出异常) +/// +public sealed class TestAsyncQueryWithExceptionV4 : AbstractAsyncQuery +{ + /// + /// 初始化TestAsyncQueryWithExceptionV4的新实例 + /// + /// 查询输入参数 + public TestAsyncQueryWithExceptionV4(TestAsyncQueryInputV2 input) : base(input) + { + } + + /// + /// 执行异步查询操作并抛出异常 + /// + /// 查询输入参数 + /// 总是抛出异常 + protected override Task OnDoAsync(TestAsyncQueryInputV2 input) + { + throw new InvalidOperationException("Test exception"); + } +} + +/// +/// 测试用异步查询子类V4,继承AbstractAsyncQuery +/// +public sealed class TestAsyncQueryChildV4 : AbstractAsyncQuery +{ + /// + /// 初始化TestAsyncQueryChildV4的新实例 + /// + /// 查询输入参数 + public TestAsyncQueryChildV4(TestAsyncQueryInputV2 input) : base(input) + { + } + + /// + /// 获取查询是否已执行 + /// + public bool Executed { get; private set; } + + /// + /// 执行异步查询操作的具体实现(子类实现,乘以3) + /// + /// 查询输入参数 + /// 查询结果,将输入值乘以3 + protected override Task OnDoAsync(TestAsyncQueryInputV2 input) + { + Executed = true; + return Task.FromResult(input.Value * 3); + } +} + +/// +/// 测试用复杂查询结果类V2 +/// +public sealed class TestAsyncQueryResultV2 +{ + /// + /// 获取或设置值 + /// + public int Value { get; init; } + + /// + /// 获取或设置双倍值 + /// + public int DoubleValue { get; init; } +} \ No newline at end of file diff --git a/GFramework.Core.Tests/query/AsyncQueryBusTests.cs b/GFramework.Core.Tests/query/AsyncQueryBusTests.cs new file mode 100644 index 0000000..8c748a4 --- /dev/null +++ b/GFramework.Core.Tests/query/AsyncQueryBusTests.cs @@ -0,0 +1,296 @@ +using GFramework.Core.Abstractions.query; +using GFramework.Core.query; +using NUnit.Framework; + +namespace GFramework.Core.Tests.query; + +/// +/// AsyncQueryBus类的单元测试 +/// 测试内容包括: +/// - SendAsync方法 - 正常查询发送 +/// - SendAsync方法 - 空查询异常 +/// - 异步查询结果正确性 +/// - 不同返回类型的异步查询支持 +/// - 异步查询的异常处理 +/// - 异步查询的上下文传递 +/// +[TestFixture] +public class AsyncQueryBusTests +{ + [SetUp] + public void SetUp() + { + _asyncQueryBus = new AsyncQueryBus(); + } + + private AsyncQueryBus _asyncQueryBus = null!; + + /// + /// 测试SendAsync方法正确返回查询结果 + /// + [Test] + public async Task SendAsync_Should_Return_Query_Result() + { + var input = new TestAsyncQueryInput { Value = 10 }; + var query = new TestAsyncQuery(input); + + var result = await _asyncQueryBus.SendAsync(query); + + Assert.That(result, Is.EqualTo(20)); + } + + /// + /// 测试SendAsync方法在传入空查询对象时是否会抛出ArgumentNullException异常 + /// + [Test] + public void SendAsync_WithNullQuery_Should_ThrowArgumentNullException() + { + Assert.ThrowsAsync(async () => await _asyncQueryBus.SendAsync(null!)); + } + + /// + /// 测试SendAsync方法是否能正确返回字符串类型的查询结果 + /// + [Test] + public async Task SendAsync_WithStringResult_Should_Return_String() + { + var input = new TestAsyncQueryInput { Value = 5 }; + var query = new TestAsyncStringQuery(input); + + var result = await _asyncQueryBus.SendAsync(query); + + Assert.That(result, Is.EqualTo("Result: 10")); + } + + /// + /// 测试SendAsync方法是否能正确返回布尔类型的查询结果 + /// + [Test] + public async Task SendAsync_WithBooleanResult_Should_Return_Boolean() + { + var input = new TestAsyncQueryInput { Value = 42 }; + var query = new TestAsyncBooleanQuery(input); + + var result = await _asyncQueryBus.SendAsync(query); + + Assert.That(result, Is.True); + } + + /// + /// 测试SendAsync方法是否能正确处理复杂对象的查询结果 + /// + [Test] + public async Task SendAsync_WithComplexObjectResult_Should_Return_ComplexObject() + { + var input = new TestAsyncQueryInput { Value = 100 }; + var query = new TestAsyncComplexQuery(input); + + var result = await _asyncQueryBus.SendAsync(query); + + Assert.That(result, Is.Not.Null); + Assert.That(result.Value, Is.EqualTo(200)); + Assert.That(result.DoubleValue, Is.EqualTo(300)); + } + + /// + /// 测试SendAsync方法是否能正确处理抛出异常的查询 + /// + [Test] + public void SendAsync_Should_Propagate_Exception_From_Query() + { + var input = new TestAsyncQueryInput { Value = 0 }; + var query = new TestAsyncQueryWithException(input); + + Assert.ThrowsAsync(async () => await _asyncQueryBus.SendAsync(query)); + } + + /// + /// 测试SendAsync方法多次调用 + /// + [Test] + public async Task SendAsync_Should_Be_Callable_Multiple_Times() + { + var input = new TestAsyncQueryInput { Value = 10 }; + var query = new TestAsyncQuery(input); + + var result1 = await _asyncQueryBus.SendAsync(query); + var result2 = await _asyncQueryBus.SendAsync(query); + + Assert.That(result1, Is.EqualTo(20)); + Assert.That(result2, Is.EqualTo(20)); + } + + /// + /// 测试SendAsync方法在不同查询之间保持独立性 + /// + [Test] + public async Task SendAsync_Should_Maintain_Independence_Between_Different_Queries() + { + var input1 = new TestAsyncQueryInput { Value = 10 }; + var input2 = new TestAsyncQueryInput { Value = 20 }; + var query1 = new TestAsyncQuery(input1); + var query2 = new TestAsyncQuery(input2); + + var result1 = await _asyncQueryBus.SendAsync(query1); + var result2 = await _asyncQueryBus.SendAsync(query2); + + Assert.That(result1, Is.EqualTo(20)); + Assert.That(result2, Is.EqualTo(40)); + } +} + +/// +/// 测试用异步查询输入类,实现IQueryInput接口 +/// +public sealed class TestAsyncQueryInput : IQueryInput +{ + /// + /// 获取或设置查询值 + /// + public int Value { get; init; } +} + +/// +/// 整数类型测试异步查询类,继承AbstractAsyncQuery +/// 实现具体的异步查询逻辑并返回整数结果 +/// +public sealed class TestAsyncQuery : AbstractAsyncQuery +{ + /// + /// 初始化TestAsyncQuery的新实例 + /// + /// 查询输入参数 + public TestAsyncQuery(TestAsyncQueryInput input) : base(input) + { + } + + /// + /// 执行异步查询操作的具体实现 + /// + /// 查询输入参数 + /// 查询结果,将输入值乘以2 + protected override Task OnDoAsync(TestAsyncQueryInput input) + { + return Task.FromResult(input.Value * 2); + } +} + +/// +/// 字符串类型测试异步查询类,继承AbstractAsyncQuery +/// 实现具体的异步查询逻辑并返回字符串结果 +/// +public sealed class TestAsyncStringQuery : AbstractAsyncQuery +{ + /// + /// 初始化TestAsyncStringQuery的新实例 + /// + /// 查询输入参数 + public TestAsyncStringQuery(TestAsyncQueryInput input) : base(input) + { + } + + /// + /// 执行异步查询操作的具体实现 + /// + /// 查询输入参数 + /// 格式化的字符串结果 + protected override Task OnDoAsync(TestAsyncQueryInput input) + { + return Task.FromResult($"Result: {input.Value * 2}"); + } +} + +/// +/// 布尔类型测试异步查询类,继承AbstractAsyncQuery +/// 实现具体的异步查询逻辑并返回布尔结果 +/// +public sealed class TestAsyncBooleanQuery : AbstractAsyncQuery +{ + /// + /// 初始化TestAsyncBooleanQuery的新实例 + /// + /// 查询输入参数 + public TestAsyncBooleanQuery(TestAsyncQueryInput input) : base(input) + { + } + + /// + /// 执行异步查询操作的具体实现 + /// + /// 查询输入参数 + /// 如果值大于0返回true,否则返回false + protected override Task OnDoAsync(TestAsyncQueryInput input) + { + return Task.FromResult(input.Value > 0); + } +} + +/// +/// 复杂对象类型测试异步查询类,继承AbstractAsyncQuery +/// 实现具体的异步查询逻辑并返回复杂对象结果 +/// +public sealed class TestAsyncComplexQuery : AbstractAsyncQuery +{ + /// + /// 初始化TestAsyncComplexQuery的新实例 + /// + /// 查询输入参数 + public TestAsyncComplexQuery(TestAsyncQueryInput input) : base(input) + { + } + + /// + /// 执行异步查询操作的具体实现 + /// + /// 查询输入参数 + /// 复杂对象查询结果 + protected override Task OnDoAsync(TestAsyncQueryInput input) + { + var result = new TestAsyncQueryResult + { + Value = input.Value * 2, + DoubleValue = input.Value * 3 + }; + return Task.FromResult(result); + } +} + +/// +/// 测试用异步查询类(抛出异常) +/// +public sealed class TestAsyncQueryWithException : AbstractAsyncQuery +{ + /// + /// 初始化TestAsyncQueryWithException的新实例 + /// + /// 查询输入参数 + public TestAsyncQueryWithException(TestAsyncQueryInput input) : base(input) + { + } + + /// + /// 执行异步查询操作并抛出异常 + /// + /// 查询输入参数 + /// 总是抛出异常 + protected override Task OnDoAsync(TestAsyncQueryInput input) + { + throw new InvalidOperationException("Test exception"); + } +} + +/// +/// 测试用复杂查询结果类 +/// +public sealed class TestAsyncQueryResult +{ + /// + /// 获取或设置值 + /// + public int Value { get; init; } + + /// + /// 获取或设置双倍值 + /// + public int DoubleValue { get; init; } +} \ No newline at end of file diff --git a/GFramework.Core.Tests/utility/AbstractContextUtilityTests.cs b/GFramework.Core.Tests/utility/AbstractContextUtilityTests.cs new file mode 100644 index 0000000..0ce990a --- /dev/null +++ b/GFramework.Core.Tests/utility/AbstractContextUtilityTests.cs @@ -0,0 +1,296 @@ +using GFramework.Core.Abstractions.logging; +using GFramework.Core.Abstractions.rule; +using GFramework.Core.Abstractions.utility; +using GFramework.Core.architecture; +using GFramework.Core.command; +using GFramework.Core.environment; +using GFramework.Core.events; +using GFramework.Core.ioc; +using GFramework.Core.query; +using GFramework.Core.utility; +using NUnit.Framework; + +namespace GFramework.Core.Tests.utility; + +/// +/// AbstractContextUtility类的单元测试 +/// 测试内容包括: +/// - 抽象工具类实现 +/// - IContextUtility接口实现 +/// - Init方法调用 +/// - 日志初始化 +/// - 上下文感知功能(SetContext, GetContext) +/// - 子类继承行为 +/// - 工具初始化日志记录 +/// - 工具生命周期完整性 +/// +[TestFixture] +public class AbstractContextUtilityTests +{ + [SetUp] + public void SetUp() + { + _container = new IocContainer(); + _context = new ArchitectureContext( + _container, + new EventBus(), + new CommandBus(), + new QueryBus(), + new DefaultEnvironment(), + new AsyncQueryBus()); + } + + private ArchitectureContext _context = null!; + private IocContainer _container = null!; + + /// + /// 测试AbstractContextUtility实现IContextUtility接口 + /// + [Test] + public void AbstractContextUtility_Should_Implement_IContextUtility_Interface() + { + var utility = new TestContextUtilityV1(); + + Assert.That(utility, Is.InstanceOf()); + } + + /// + /// 测试Init方法调用 + /// + [Test] + public void Init_Should_Call_OnInit_Method() + { + var utility = new TestContextUtilityV1(); + + Assert.That(utility.Initialized, Is.False, "Utility should not be initialized before Init"); + + utility.Init(); + + Assert.That(utility.Initialized, Is.True, "Utility should be initialized after Init"); + } + + /// + /// 测试Init方法设置Logger属性 + /// + [Test] + public void Init_Should_Set_Logger_Property() + { + var utility = new TestContextUtilityV1(); + + Assert.That(utility.GetLogger(), Is.Null, "Logger should be null before Init"); + + utility.Init(); + + Assert.That(utility.GetLogger(), Is.Not.Null, "Logger should be set after Init"); + } + + /// + /// 测试Init方法记录初始化日志 + /// + [Test] + public void Init_Should_Log_Initialization() + { + var utility = new TestContextUtilityV1(); + + Assert.That(utility.InitCalled, Is.False, "InitCalled should be false before Init"); + + utility.Init(); + + Assert.That(utility.InitCalled, Is.True, "InitCalled should be true after Init"); + } + + /// + /// 测试Destroy方法调用 + /// + [Test] + public void Destroy_Should_Call_OnDestroy_Method() + { + var utility = new TestContextUtilityV1(); + + utility.Init(); + Assert.That(utility.Destroyed, Is.False, "Utility should not be destroyed before Destroy"); + + utility.Destroy(); + + Assert.That(utility.Destroyed, Is.True, "Utility should be destroyed after Destroy"); + } + + /// + /// 测试上下文感知功能 - SetContext方法 + /// + [Test] + public void SetContext_Should_Set_Context_Property() + { + var utility = new TestContextUtilityV1(); + var contextAware = (IContextAware)utility; + + contextAware.SetContext(_context); + + var context = contextAware.GetContext(); + Assert.That(context, Is.SameAs(_context)); + } + + /// + /// 测试上下文感知功能 - GetContext方法 + /// + [Test] + public void GetContext_Should_Return_Context_Property() + { + var utility = new TestContextUtilityV1(); + var contextAware = (IContextAware)utility; + + contextAware.SetContext(_context); + + var context = contextAware.GetContext(); + Assert.That(context, Is.Not.Null); + Assert.That(context, Is.SameAs(_context)); + } + + /// + /// 测试子类继承行为 + /// + [Test] + public void Child_Class_Should_Override_OnInit_Method() + { + var utility = new TestContextUtilityV2(); + + Assert.That(utility.Initialized, Is.False); + Assert.That(utility.CustomInitializationDone, Is.False); + + utility.Init(); + + Assert.That(utility.Initialized, Is.True); + Assert.That(utility.CustomInitializationDone, Is.True); + } + + /// + /// 测试工具生命周期完整性 + /// + [Test] + public void ContextUtility_Should_Complete_Full_Lifecycle() + { + var utility = new TestContextUtilityV1(); + + // 初始状态 + Assert.That(utility.Initialized, Is.False); + Assert.That(utility.Destroyed, Is.False); + + // 初始化 + utility.Init(); + Assert.That(utility.Initialized, Is.True); + Assert.That(utility.Destroyed, Is.False); + + // 销毁 + utility.Destroy(); + Assert.That(utility.Initialized, Is.True); + Assert.That(utility.Destroyed, Is.True); + } + + /// + /// 测试工具类可以多次初始化和销毁 + /// + [Test] + public void ContextUtility_Should_Be_Initializable_And_Destroyable_Multiple_Times() + { + var utility = new TestContextUtilityV1(); + + // 第一次初始化和销毁 + utility.Init(); + Assert.That(utility.Initialized, Is.True); + utility.Destroy(); + Assert.That(utility.Destroyed, Is.True); + + // 重置状态 + utility.Destroyed = false; + + // 第二次初始化和销毁 + utility.Init(); + Assert.That(utility.Initialized, Is.True); + utility.Destroy(); + Assert.That(utility.Destroyed, Is.True); + } +} + +/// +/// 测试用上下文工具类V1 +/// +public sealed class TestContextUtilityV1 : AbstractContextUtility +{ + /// + /// 获取工具是否已初始化 + /// + public bool Initialized { get; private set; } + + /// + /// 获取工具是否已销毁 + /// + public bool Destroyed { get; set; } + + /// + /// 获取Init方法是否被调用 + /// + public bool InitCalled { get; private set; } + + /// + /// 获取Logger对象(用于测试) + /// + public ILogger? GetLogger() + { + return Logger; + } + + /// + /// 初始化方法 + /// + protected override void OnInit() + { + Initialized = true; + InitCalled = true; + } + + /// + /// 销毁方法 + /// + protected override void OnDestroy() + { + Destroyed = true; + } +} + +/// +/// 测试用上下文工具类V2,自定义初始化逻辑 +/// +public sealed class TestContextUtilityV2 : AbstractContextUtility +{ + /// + /// 获取工具是否已初始化 + /// + public bool Initialized { get; private set; } + + /// + /// 获取工具是否已销毁 + /// + public bool Destroyed { get; set; } + + /// + /// 获取自定义初始化是否完成 + /// + public bool CustomInitializationDone { get; private set; } + + /// + /// 初始化方法(自定义逻辑) + /// + protected override void OnInit() + { + Initialized = true; + CustomInitializationDone = true; + } + + /// + /// 销毁方法 + /// + protected override void OnDestroy() + { + Destroyed = true; + } +} \ No newline at end of file