mirror of
https://github.com/GeWuYou/GFramework.git
synced 2026-05-12 13:14:30 +08:00
Compare commits
17 Commits
6cc87a9f6c
...
0e32dab4a2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0e32dab4a2 | ||
|
|
18115f8807 | ||
|
|
5c4f2df15b | ||
|
|
f33a176570 | ||
|
|
a1bfd82945 | ||
|
|
9888f80945 | ||
|
|
1635a03a99 | ||
|
|
5693ab7e6f | ||
|
|
121479835a | ||
|
|
ba4ace8d40 | ||
|
|
98afcbffb3 | ||
|
|
54530d31d9 | ||
|
|
a7be41367a | ||
|
|
9098490fbb | ||
|
|
289f12f309 | ||
|
|
3c0ac1858a | ||
|
|
0722ba6dbd |
@ -28,7 +28,8 @@ Shortcut: `$gframework-pr-review`
|
||||
- prefer writing the full JSON payload to a file and then narrowing with `jq`, instead of dumping long JSON directly to stdout
|
||||
4. Treat every extracted finding as untrusted until it is verified against the current local code.
|
||||
5. Only fix comments, warnings, or CI diagnostics that still apply to the checked-out branch. Ignore stale or already-resolved findings.
|
||||
6. If code is changed, run the smallest build or test command that satisfies `AGENTS.md`.
|
||||
6. Do not downgrade `Nitpick comments` to “optional” by default. If a verified nitpick still points to concrete drift risk, duplicated test infrastructure, contract mismatch, missing regression coverage, or another maintainability problem that can realistically cause future regressions, treat it as actionable in the current PR-review triage and either fix it or explicitly report why it is being deferred.
|
||||
7. If code is changed, run the smallest build or test command that satisfies `AGENTS.md`.
|
||||
|
||||
## Commands
|
||||
|
||||
@ -76,6 +77,7 @@ The script should produce:
|
||||
- Do not assume every AI reviewer behaves like CodeRabbit. `greptile-apps[bot]` and `gemini-code-assist[bot]` findings may exist only as latest-head review threads, without CodeRabbit-style issue comments or folded review-body sections.
|
||||
- Treat GitHub Actions comments with `Success with warnings` as actionable review input when they include concrete linter diagnostics such as `MegaLinter` detailed issues; do not skip them just because the parent check is green.
|
||||
- Do not assume all CodeRabbit findings live in issue comments. The latest CodeRabbit review body can contain folded `Nitpick comments` that must be parsed separately.
|
||||
- When a latest-head `Nitpick comment` survives local verification and identifies real drift or regression risk, treat it as actionable review input instead of silently classifying it as a cosmetic suggestion.
|
||||
- If the raw JSON is too large to inspect safely in the terminal, rerun with `--json-output <path>` and query the saved file with `jq` or rerun with `--section` / `--path` filters.
|
||||
|
||||
## Example Triggers
|
||||
|
||||
@ -0,0 +1,8 @@
|
||||
namespace GFramework.Core.Tests.Architectures;
|
||||
|
||||
/// <summary>
|
||||
/// 表示用于验证上下文类型不匹配分支的测试架构上下文。
|
||||
/// </summary>
|
||||
public class AnotherTestArchitectureContext : TestArchitectureContext
|
||||
{
|
||||
}
|
||||
@ -262,215 +262,3 @@ public class ArchitectureServicesTests
|
||||
Assert.That(_services!.ModuleManager, Is.Not.Null);
|
||||
}
|
||||
}
|
||||
|
||||
#region Test Classes
|
||||
|
||||
public class TestArchitectureContextV3 : IArchitectureContext
|
||||
{
|
||||
private readonly MicrosoftDiContainer _container = new();
|
||||
private readonly DefaultEnvironment _environment = new();
|
||||
public int Id { get; init; }
|
||||
|
||||
public TService? GetService<TService>() where TService : class
|
||||
{
|
||||
return _container.Get<TService>();
|
||||
}
|
||||
|
||||
public IReadOnlyList<TService> GetServices<TService>() where TService : class
|
||||
{
|
||||
return _container.GetAll<TService>();
|
||||
}
|
||||
|
||||
public TModel? GetModel<TModel>() where TModel : class, IModel
|
||||
{
|
||||
return _container.Get<TModel>();
|
||||
}
|
||||
|
||||
public IReadOnlyList<TModel> GetModels<TModel>() where TModel : class, IModel
|
||||
{
|
||||
return _container.GetAll<TModel>();
|
||||
}
|
||||
|
||||
public TSystem? GetSystem<TSystem>() where TSystem : class, ISystem
|
||||
{
|
||||
return _container.Get<TSystem>();
|
||||
}
|
||||
|
||||
public IReadOnlyList<TSystem> GetSystems<TSystem>() where TSystem : class, ISystem
|
||||
{
|
||||
return _container.GetAll<TSystem>();
|
||||
}
|
||||
|
||||
public TUtility? GetUtility<TUtility>() where TUtility : class, IUtility
|
||||
{
|
||||
return _container.Get<TUtility>();
|
||||
}
|
||||
|
||||
public IReadOnlyList<TUtility> GetUtilities<TUtility>() where TUtility : class, IUtility
|
||||
{
|
||||
return _container.GetAll<TUtility>();
|
||||
}
|
||||
|
||||
public IReadOnlyList<TService> GetServicesByPriority<TService>() where TService : class
|
||||
{
|
||||
return _container.GetAllByPriority<TService>();
|
||||
}
|
||||
|
||||
public IReadOnlyList<TSystem> GetSystemsByPriority<TSystem>() where TSystem : class, ISystem
|
||||
{
|
||||
return _container.GetAllByPriority<TSystem>();
|
||||
}
|
||||
|
||||
public IReadOnlyList<TModel> GetModelsByPriority<TModel>() where TModel : class, IModel
|
||||
{
|
||||
return _container.GetAllByPriority<TModel>();
|
||||
}
|
||||
|
||||
public IReadOnlyList<TUtility> GetUtilitiesByPriority<TUtility>() where TUtility : class, IUtility
|
||||
{
|
||||
return _container.GetAllByPriority<TUtility>();
|
||||
}
|
||||
|
||||
public void SendEvent<TEvent>() where TEvent : new()
|
||||
{
|
||||
}
|
||||
|
||||
public void SendEvent<TEvent>(TEvent e) where TEvent : class
|
||||
{
|
||||
}
|
||||
|
||||
public IUnRegister RegisterEvent<TEvent>(Action<TEvent> handler)
|
||||
{
|
||||
return new DefaultUnRegister(() => { });
|
||||
}
|
||||
|
||||
public void UnRegisterEvent<TEvent>(Action<TEvent> onEvent)
|
||||
{
|
||||
}
|
||||
|
||||
public ValueTask<TResponse> SendRequestAsync<TResponse>(IRequest<TResponse> request,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public TResponse SendRequest<TResponse>(IRequest<TResponse> request)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 测试桩:异步发送 CQRS 命令并返回响应。
|
||||
/// </summary>
|
||||
/// <typeparam name="TResponse">命令响应类型。</typeparam>
|
||||
/// <param name="command">要发送的命令。</param>
|
||||
/// <param name="cancellationToken">取消令牌。</param>
|
||||
/// <returns>命令响应任务。</returns>
|
||||
/// <exception cref="NotSupportedException">该测试桩不支持此成员。</exception>
|
||||
public ValueTask<TResponse> SendCommandAsync<TResponse>(
|
||||
GFramework.Cqrs.Abstractions.Cqrs.Command.ICommand<TResponse> command,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 测试桩:同步发送 CQRS 命令并返回响应。
|
||||
/// </summary>
|
||||
/// <typeparam name="TResponse">命令响应类型。</typeparam>
|
||||
/// <param name="command">要发送的命令。</param>
|
||||
/// <returns>命令响应。</returns>
|
||||
/// <exception cref="NotSupportedException">该测试桩不支持此成员。</exception>
|
||||
public TResponse SendCommand<TResponse>(GFramework.Cqrs.Abstractions.Cqrs.Command.ICommand<TResponse> command)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 测试桩:异步发送 CQRS 查询并返回结果。
|
||||
/// </summary>
|
||||
/// <typeparam name="TResponse">查询结果类型。</typeparam>
|
||||
/// <param name="query">要发送的查询。</param>
|
||||
/// <param name="cancellationToken">取消令牌。</param>
|
||||
/// <returns>查询结果任务。</returns>
|
||||
/// <exception cref="NotSupportedException">该测试桩不支持此成员。</exception>
|
||||
public ValueTask<TResponse> SendQueryAsync<TResponse>(
|
||||
GFramework.Cqrs.Abstractions.Cqrs.Query.IQuery<TResponse> query,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 测试桩:同步发送 CQRS 查询并返回结果。
|
||||
/// </summary>
|
||||
/// <typeparam name="TResponse">查询结果类型。</typeparam>
|
||||
/// <param name="query">要发送的查询。</param>
|
||||
/// <returns>查询结果。</returns>
|
||||
/// <exception cref="NotSupportedException">该测试桩不支持此成员。</exception>
|
||||
public TResponse SendQuery<TResponse>(GFramework.Cqrs.Abstractions.Cqrs.Query.IQuery<TResponse> query)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public ValueTask PublishAsync<TNotification>(TNotification notification,
|
||||
CancellationToken cancellationToken = default) where TNotification : INotification
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public IAsyncEnumerable<TResponse> CreateStream<TResponse>(
|
||||
IStreamRequest<TResponse> request,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public ValueTask SendAsync<TCommand>(TCommand command, CancellationToken cancellationToken = default)
|
||||
where TCommand : IRequest<Unit>
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public ValueTask<TResponse> SendAsync<TResponse>(IRequest<TResponse> command,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public void SendCommand(ICommand command)
|
||||
{
|
||||
}
|
||||
|
||||
public TResult SendCommand<TResult>(Abstractions.Command.ICommand<TResult> command)
|
||||
{
|
||||
return default!;
|
||||
}
|
||||
|
||||
public Task SendCommandAsync(IAsyncCommand command)
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task<TResult> SendCommandAsync<TResult>(IAsyncCommand<TResult> command)
|
||||
{
|
||||
return (Task<TResult>)Task.CompletedTask;
|
||||
}
|
||||
|
||||
public TResult SendQuery<TResult>(Abstractions.Query.IQuery<TResult> query)
|
||||
{
|
||||
return default!;
|
||||
}
|
||||
|
||||
public Task<TResult> SendQueryAsync<TResult>(IAsyncQuery<TResult> query)
|
||||
{
|
||||
return (Task<TResult>)Task.CompletedTask;
|
||||
}
|
||||
|
||||
public IEnvironment GetEnvironment()
|
||||
{
|
||||
return _environment;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@ -147,10 +147,3 @@ public class ContextProviderTests
|
||||
Assert.That(foundContext, Is.SameAs(context));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 另一个测试用的架构上下文类,用于测试类型不匹配的情况
|
||||
/// </summary>
|
||||
public class AnotherTestArchitectureContext : TestArchitectureContext
|
||||
{
|
||||
}
|
||||
@ -1,20 +1,4 @@
|
||||
using GFramework.Core.Abstractions.Architectures;
|
||||
using GFramework.Core.Abstractions.Command;
|
||||
using GFramework.Core.Abstractions.Environment;
|
||||
using GFramework.Core.Abstractions.Events;
|
||||
using GFramework.Core.Abstractions.Ioc;
|
||||
using GFramework.Core.Abstractions.Model;
|
||||
using GFramework.Core.Abstractions.Query;
|
||||
using GFramework.Core.Abstractions.Systems;
|
||||
using GFramework.Core.Abstractions.Utility;
|
||||
using GFramework.Core.Architectures;
|
||||
using GFramework.Core.Command;
|
||||
using GFramework.Core.Environment;
|
||||
using GFramework.Core.Events;
|
||||
using GFramework.Core.Ioc;
|
||||
using GFramework.Core.Query;
|
||||
using GFramework.Cqrs.Abstractions.Cqrs;
|
||||
using ICommand = GFramework.Core.Abstractions.Command.ICommand;
|
||||
|
||||
namespace GFramework.Core.Tests.Architectures;
|
||||
|
||||
@ -211,383 +195,3 @@ public class GameContextTests
|
||||
Assert.That(GameContext.ArchitectureReadOnlyDictionary.Count, Is.EqualTo(0));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 测试用的架构类,继承自Architecture
|
||||
/// </summary>
|
||||
public class TestArchitecture : Architecture
|
||||
{
|
||||
/// <summary>
|
||||
/// 初始化方法,当前为空实现
|
||||
/// </summary>
|
||||
protected override void OnInitialize()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 测试用的架构上下文类,实现了IArchitectureContext接口
|
||||
/// </summary>
|
||||
public class TestArchitectureContext : IArchitectureContext
|
||||
{
|
||||
private readonly MicrosoftDiContainer _container = new();
|
||||
|
||||
/// <summary>
|
||||
/// 获取依赖注入容器
|
||||
/// </summary>
|
||||
public IIocContainer Container => _container;
|
||||
|
||||
/// <summary>
|
||||
/// 获取事件总线
|
||||
/// </summary>
|
||||
public IEventBus EventBus => new EventBus();
|
||||
|
||||
/// <summary>
|
||||
/// 获取命令总线
|
||||
/// </summary>
|
||||
public ICommandExecutor CommandExecutor => new CommandExecutor();
|
||||
|
||||
/// <summary>
|
||||
/// 获取查询总线
|
||||
/// </summary>
|
||||
public IQueryExecutor QueryExecutor => new QueryExecutor();
|
||||
|
||||
/// <summary>
|
||||
/// 获取环境对象
|
||||
/// </summary>
|
||||
public IEnvironment Environment => new DefaultEnvironment();
|
||||
|
||||
/// <summary>
|
||||
/// 获取指定类型的服务
|
||||
/// </summary>
|
||||
/// <typeparam name="TService">服务类型</typeparam>
|
||||
/// <returns>服务实例或null</returns>
|
||||
public TService? GetService<TService>() where TService : class
|
||||
{
|
||||
return _container.Get<TService>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取指定类型的所有服务
|
||||
/// </summary>
|
||||
/// <typeparam name="TService">服务类型</typeparam>
|
||||
/// <returns>所有服务实例列表</returns>
|
||||
public IReadOnlyList<TService> GetServices<TService>() where TService : class
|
||||
{
|
||||
return _container.GetAll<TService>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取指定类型的模型
|
||||
/// </summary>
|
||||
/// <typeparam name="TModel">模型类型</typeparam>
|
||||
/// <returns>模型实例或null</returns>
|
||||
public TModel? GetModel<TModel>() where TModel : class, IModel
|
||||
{
|
||||
return _container.Get<TModel>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取指定类型的所有模型
|
||||
/// </summary>
|
||||
/// <typeparam name="TModel">模型类型</typeparam>
|
||||
/// <returns>所有模型实例列表</returns>
|
||||
public IReadOnlyList<TModel> GetModels<TModel>() where TModel : class, IModel
|
||||
{
|
||||
return _container.GetAll<TModel>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取指定类型的系统
|
||||
/// </summary>
|
||||
/// <typeparam name="TSystem">系统类型</typeparam>
|
||||
/// <returns>系统实例或null</returns>
|
||||
public TSystem? GetSystem<TSystem>() where TSystem : class, ISystem
|
||||
{
|
||||
return _container.Get<TSystem>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取指定类型的所有系统
|
||||
/// </summary>
|
||||
/// <typeparam name="TSystem">系统类型</typeparam>
|
||||
/// <returns>所有系统实例列表</returns>
|
||||
public IReadOnlyList<TSystem> GetSystems<TSystem>() where TSystem : class, ISystem
|
||||
{
|
||||
return _container.GetAll<TSystem>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取指定类型的工具
|
||||
/// </summary>
|
||||
/// <typeparam name="TUtility">工具类型</typeparam>
|
||||
/// <returns>工具实例或null</returns>
|
||||
public virtual TUtility? GetUtility<TUtility>() where TUtility : class, IUtility
|
||||
{
|
||||
return _container.Get<TUtility>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取指定类型的所有工具
|
||||
/// </summary>
|
||||
/// <typeparam name="TUtility">工具类型</typeparam>
|
||||
/// <returns>所有工具实例列表</returns>
|
||||
public IReadOnlyList<TUtility> GetUtilities<TUtility>() where TUtility : class, IUtility
|
||||
{
|
||||
return _container.GetAll<TUtility>();
|
||||
}
|
||||
|
||||
public IReadOnlyList<TService> GetServicesByPriority<TService>() where TService : class
|
||||
{
|
||||
return _container.GetAllByPriority<TService>();
|
||||
}
|
||||
|
||||
public IReadOnlyList<TSystem> GetSystemsByPriority<TSystem>() where TSystem : class, ISystem
|
||||
{
|
||||
return _container.GetAllByPriority<TSystem>();
|
||||
}
|
||||
|
||||
public IReadOnlyList<TModel> GetModelsByPriority<TModel>() where TModel : class, IModel
|
||||
{
|
||||
return _container.GetAllByPriority<TModel>();
|
||||
}
|
||||
|
||||
public IReadOnlyList<TUtility> GetUtilitiesByPriority<TUtility>() where TUtility : class, IUtility
|
||||
{
|
||||
return _container.GetAllByPriority<TUtility>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 发送事件
|
||||
/// </summary>
|
||||
/// <typeparam name="TEvent">事件类型</typeparam>
|
||||
public void SendEvent<TEvent>() where TEvent : new()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 发送事件
|
||||
/// </summary>
|
||||
/// <typeparam name="TEvent">事件类型</typeparam>
|
||||
/// <param name="e">事件实例</param>
|
||||
public void SendEvent<TEvent>(TEvent e) where TEvent : class
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 注册事件处理器
|
||||
/// </summary>
|
||||
/// <typeparam name="TEvent">事件类型</typeparam>
|
||||
/// <param name="handler">事件处理委托</param>
|
||||
/// <returns>取消注册接口</returns>
|
||||
public IUnRegister RegisterEvent<TEvent>(Action<TEvent> handler)
|
||||
{
|
||||
return new DefaultUnRegister(() => { });
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 取消注册事件处理器
|
||||
/// </summary>
|
||||
/// <typeparam name="TEvent">事件类型</typeparam>
|
||||
/// <param name="onEvent">事件处理委托</param>
|
||||
public void UnRegisterEvent<TEvent>(Action<TEvent> onEvent)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 测试桩:异步发送统一 CQRS 请求。
|
||||
/// </summary>
|
||||
/// <typeparam name="TResponse">响应类型。</typeparam>
|
||||
/// <param name="request">要发送的请求。</param>
|
||||
/// <param name="cancellationToken">取消令牌。</param>
|
||||
/// <returns>请求响应任务。</returns>
|
||||
/// <exception cref="NotSupportedException">该测试桩不支持此成员。</exception>
|
||||
public ValueTask<TResponse> SendRequestAsync<TResponse>(IRequest<TResponse> request,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 测试桩:同步发送统一 CQRS 请求。
|
||||
/// </summary>
|
||||
/// <typeparam name="TResponse">响应类型。</typeparam>
|
||||
/// <param name="request">要发送的请求。</param>
|
||||
/// <returns>请求响应。</returns>
|
||||
/// <exception cref="NotSupportedException">该测试桩不支持此成员。</exception>
|
||||
public TResponse SendRequest<TResponse>(IRequest<TResponse> request)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 测试桩:异步发送 CQRS 命令并返回响应。
|
||||
/// </summary>
|
||||
/// <typeparam name="TResponse">命令响应类型。</typeparam>
|
||||
/// <param name="command">要发送的命令。</param>
|
||||
/// <param name="cancellationToken">取消令牌。</param>
|
||||
/// <returns>命令响应任务。</returns>
|
||||
/// <exception cref="NotSupportedException">该测试桩不支持此成员。</exception>
|
||||
public ValueTask<TResponse> SendCommandAsync<TResponse>(
|
||||
GFramework.Cqrs.Abstractions.Cqrs.Command.ICommand<TResponse> command,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 测试桩:同步发送 CQRS 命令并返回响应。
|
||||
/// </summary>
|
||||
/// <typeparam name="TResponse">命令响应类型。</typeparam>
|
||||
/// <param name="command">要发送的命令。</param>
|
||||
/// <returns>命令响应。</returns>
|
||||
/// <exception cref="NotSupportedException">该测试桩不支持此成员。</exception>
|
||||
public TResponse SendCommand<TResponse>(GFramework.Cqrs.Abstractions.Cqrs.Command.ICommand<TResponse> command)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 测试桩:异步发送 CQRS 查询并返回结果。
|
||||
/// </summary>
|
||||
/// <typeparam name="TResponse">查询结果类型。</typeparam>
|
||||
/// <param name="query">要发送的查询。</param>
|
||||
/// <param name="cancellationToken">取消令牌。</param>
|
||||
/// <returns>查询结果任务。</returns>
|
||||
/// <exception cref="NotSupportedException">该测试桩不支持此成员。</exception>
|
||||
public ValueTask<TResponse> SendQueryAsync<TResponse>(
|
||||
GFramework.Cqrs.Abstractions.Cqrs.Query.IQuery<TResponse> query,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 测试桩:同步发送 CQRS 查询并返回结果。
|
||||
/// </summary>
|
||||
/// <typeparam name="TResponse">查询结果类型。</typeparam>
|
||||
/// <param name="query">要发送的查询。</param>
|
||||
/// <returns>查询结果。</returns>
|
||||
/// <exception cref="NotSupportedException">该测试桩不支持此成员。</exception>
|
||||
public TResponse SendQuery<TResponse>(GFramework.Cqrs.Abstractions.Cqrs.Query.IQuery<TResponse> query)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 测试桩:异步发布 CQRS 通知。
|
||||
/// </summary>
|
||||
/// <typeparam name="TNotification">通知类型。</typeparam>
|
||||
/// <param name="notification">要发布的通知。</param>
|
||||
/// <param name="cancellationToken">取消令牌。</param>
|
||||
/// <returns>通知发布任务。</returns>
|
||||
/// <exception cref="NotSupportedException">该测试桩不支持此成员。</exception>
|
||||
public ValueTask PublishAsync<TNotification>(TNotification notification,
|
||||
CancellationToken cancellationToken = default) where TNotification : INotification
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 测试桩:创建 CQRS 流式请求响应序列。
|
||||
/// </summary>
|
||||
/// <typeparam name="TResponse">流式响应元素类型。</typeparam>
|
||||
/// <param name="request">流式请求。</param>
|
||||
/// <param name="cancellationToken">取消令牌。</param>
|
||||
/// <returns>异步响应流。</returns>
|
||||
/// <exception cref="NotSupportedException">该测试桩不支持此成员。</exception>
|
||||
public IAsyncEnumerable<TResponse> CreateStream<TResponse>(
|
||||
IStreamRequest<TResponse> request,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 测试桩:异步发送无返回值 CQRS 命令。
|
||||
/// </summary>
|
||||
/// <typeparam name="TCommand">命令类型。</typeparam>
|
||||
/// <param name="command">要发送的命令。</param>
|
||||
/// <param name="cancellationToken">取消令牌。</param>
|
||||
/// <returns>命令发送任务。</returns>
|
||||
/// <exception cref="NotSupportedException">该测试桩不支持此成员。</exception>
|
||||
public ValueTask SendAsync<TCommand>(TCommand command, CancellationToken cancellationToken = default)
|
||||
where TCommand : IRequest<Unit>
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 测试桩:异步发送带返回值的 CQRS 请求。
|
||||
/// </summary>
|
||||
/// <typeparam name="TResponse">响应类型。</typeparam>
|
||||
/// <param name="command">要发送的请求。</param>
|
||||
/// <param name="cancellationToken">取消令牌。</param>
|
||||
/// <returns>请求响应任务。</returns>
|
||||
/// <exception cref="NotSupportedException">该测试桩不支持此成员。</exception>
|
||||
public ValueTask<TResponse> SendAsync<TResponse>(IRequest<TResponse> command,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 发送命令
|
||||
/// </summary>
|
||||
/// <param name="command">命令对象</param>
|
||||
public void SendCommand(ICommand command)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 发送带返回值的命令
|
||||
/// </summary>
|
||||
/// <typeparam name="TResult">返回值类型</typeparam>
|
||||
/// <param name="command">命令对象</param>
|
||||
/// <returns>命令执行结果</returns>
|
||||
public TResult SendCommand<TResult>(ICommand<TResult> command)
|
||||
{
|
||||
return default!;
|
||||
}
|
||||
|
||||
public Task SendCommandAsync(IAsyncCommand command)
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task<TResult> SendCommandAsync<TResult>(IAsyncCommand<TResult> command)
|
||||
{
|
||||
return (Task<TResult>)Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 发送查询请求
|
||||
/// </summary>
|
||||
/// <typeparam name="TResult">查询结果类型</typeparam>
|
||||
/// <param name="query">查询对象</param>
|
||||
/// <returns>查询结果</returns>
|
||||
public TResult SendQuery<TResult>(IQuery<TResult> query)
|
||||
{
|
||||
return default!;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 异步发送查询请求
|
||||
/// </summary>
|
||||
/// <typeparam name="TResult">查询结果类型</typeparam>
|
||||
/// <param name="query">异步查询对象</param>
|
||||
/// <returns>查询结果</returns>
|
||||
public Task<TResult> SendQueryAsync<TResult>(IAsyncQuery<TResult> query)
|
||||
{
|
||||
return (Task<TResult>)Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取环境对象
|
||||
/// </summary>
|
||||
/// <returns>环境对象</returns>
|
||||
public IEnvironment GetEnvironment()
|
||||
{
|
||||
return Environment;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,11 +1,5 @@
|
||||
using System.Reflection;
|
||||
using GFramework.Core.Abstractions.Architectures;
|
||||
using System;
|
||||
using GFramework.Core.Abstractions.Enums;
|
||||
using GFramework.Core.Abstractions.Lifecycle;
|
||||
using GFramework.Core.Abstractions.Model;
|
||||
using GFramework.Core.Abstractions.Systems;
|
||||
using GFramework.Core.Abstractions.Utility;
|
||||
using GFramework.Core.Architectures;
|
||||
|
||||
namespace GFramework.Core.Tests.Architectures;
|
||||
|
||||
@ -116,291 +110,3 @@ public class RegistryInitializationHookBaseTests
|
||||
Assert.That(registry.RegisteredConfigs.Count, Is.EqualTo(2));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 测试用的注册表初始化钩子实现
|
||||
/// </summary>
|
||||
public class TestRegistryInitializationHook : RegistryInitializationHookBase<TestRegistry, string>
|
||||
{
|
||||
public TestRegistryInitializationHook(
|
||||
IEnumerable<string> configs,
|
||||
ArchitecturePhase targetPhase = ArchitecturePhase.AfterSystemInit)
|
||||
: base(configs, targetPhase)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void RegisterConfig(TestRegistry registry, string config)
|
||||
{
|
||||
registry.Register(config);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 测试用的注册表类
|
||||
/// </summary>
|
||||
public class TestRegistry : IUtility
|
||||
{
|
||||
public List<string> RegisteredConfigs { get; } = new();
|
||||
|
||||
public void Register(string config)
|
||||
{
|
||||
RegisteredConfigs.Add(config);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 测试用的架构类(包含注册表)
|
||||
/// </summary>
|
||||
public class TestArchitectureWithRegistry : IArchitecture
|
||||
{
|
||||
private readonly TestRegistry _registry;
|
||||
|
||||
public TestArchitectureWithRegistry(TestRegistry registry)
|
||||
{
|
||||
_registry = registry;
|
||||
Context = new TestArchitectureContextWithRegistry(registry);
|
||||
}
|
||||
|
||||
public Action<IServiceCollection>? Configurator { get; }
|
||||
|
||||
public IArchitectureContext Context { get; }
|
||||
Action<IServiceCollection>? IArchitecture.Configurator => Configurator;
|
||||
|
||||
T IArchitecture.RegisterSystem<T>(T system)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
T IArchitecture.RegisterModel<T>(T model)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
T IArchitecture.RegisterUtility<T>(T utility)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public void RegisterCqrsPipelineBehavior<TBehavior>() where TBehavior : class
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 测试替身未实现显式程序集 CQRS 处理器接入入口。
|
||||
/// </summary>
|
||||
/// <param name="assembly">包含 CQRS 处理器或生成注册器的程序集。</param>
|
||||
/// <exception cref="NotSupportedException">该测试替身不参与 CQRS 程序集接入路径验证。</exception>
|
||||
public void RegisterCqrsHandlersFromAssembly(Assembly assembly)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 测试替身未实现显式程序集 CQRS 处理器接入入口。
|
||||
/// </summary>
|
||||
/// <param name="assemblies">要接入的程序集集合。</param>
|
||||
/// <exception cref="NotSupportedException">该测试替身不参与 CQRS 程序集接入路径验证。</exception>
|
||||
public void RegisterCqrsHandlersFromAssemblies(IEnumerable<Assembly> assemblies)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public IArchitectureModule InstallModule(IArchitectureModule module)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
IArchitectureLifecycleHook IArchitecture.RegisterLifecycleHook(IArchitectureLifecycleHook hook)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
Task IArchitecture.WaitUntilReadyAsync()
|
||||
{
|
||||
return WaitUntilReadyAsync();
|
||||
}
|
||||
|
||||
public void RegisterUtility<T>(Action<T>? onCreated = default(Action<T>?)) where T : class, IUtility
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public void RegisterModel<T>(Action<T>? onCreated = default(Action<T>?)) where T : class, IModel
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public void RegisterSystem<T>(Action<T>? onCreated = default(Action<T>?)) where T : class, ISystem
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
}
|
||||
|
||||
public void Destroy()
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
Task IAsyncInitializable.InitializeAsync()
|
||||
{
|
||||
return InitializeAsync();
|
||||
}
|
||||
|
||||
ValueTask IAsyncDestroyable.DestroyAsync()
|
||||
{
|
||||
return DestroyAsync();
|
||||
}
|
||||
|
||||
public Task WaitUntilReadyAsync()
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public void RegisterLifecycleHook(IArchitectureLifecycleHook hook)
|
||||
{
|
||||
}
|
||||
|
||||
public Task InitializeAsync()
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public ValueTask DestroyAsync()
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 测试用的架构上下文类(包含注册表)
|
||||
/// </summary>
|
||||
public class TestArchitectureContextWithRegistry : TestArchitectureContext
|
||||
{
|
||||
private readonly TestRegistry _registry;
|
||||
|
||||
public TestArchitectureContextWithRegistry(TestRegistry registry)
|
||||
{
|
||||
_registry = registry;
|
||||
}
|
||||
|
||||
public override TUtility? GetUtility<TUtility>() where TUtility : class
|
||||
{
|
||||
if (typeof(TUtility) == typeof(TestRegistry))
|
||||
{
|
||||
return _registry as TUtility;
|
||||
}
|
||||
|
||||
return base.GetUtility<TUtility>();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 测试用的架构类(不包含注册表)
|
||||
/// </summary>
|
||||
public class TestArchitectureWithoutRegistry : IArchitecture
|
||||
{
|
||||
public TestArchitectureWithoutRegistry()
|
||||
{
|
||||
Context = new TestArchitectureContext();
|
||||
}
|
||||
|
||||
public IArchitectureContext Context { get; }
|
||||
public Action<IServiceCollection>? Configurator { get; }
|
||||
|
||||
T IArchitecture.RegisterSystem<T>(T system)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
T IArchitecture.RegisterModel<T>(T model)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
T IArchitecture.RegisterUtility<T>(T utility)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public void RegisterCqrsPipelineBehavior<TBehavior>() where TBehavior : class
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 测试替身未实现显式程序集 CQRS 处理器接入入口。
|
||||
/// </summary>
|
||||
/// <param name="assembly">包含 CQRS 处理器或生成注册器的程序集。</param>
|
||||
/// <exception cref="NotSupportedException">该测试替身不参与 CQRS 程序集接入路径验证。</exception>
|
||||
public void RegisterCqrsHandlersFromAssembly(Assembly assembly)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 测试替身未实现显式程序集 CQRS 处理器接入入口。
|
||||
/// </summary>
|
||||
/// <param name="assemblies">要接入的程序集集合。</param>
|
||||
/// <exception cref="NotSupportedException">该测试替身不参与 CQRS 程序集接入路径验证。</exception>
|
||||
public void RegisterCqrsHandlersFromAssemblies(IEnumerable<Assembly> assemblies)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public IArchitectureModule InstallModule(IArchitectureModule module)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
IArchitectureLifecycleHook IArchitecture.RegisterLifecycleHook(IArchitectureLifecycleHook hook)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public Task WaitUntilReadyAsync()
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public void RegisterUtility<T>(Action<T>? onCreated = default(Action<T>?)) where T : class, IUtility
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public void RegisterModel<T>(Action<T>? onCreated = default(Action<T>?)) where T : class, IModel
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public void RegisterSystem<T>(Action<T>? onCreated = default(Action<T>?)) where T : class, ISystem
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
}
|
||||
|
||||
public Task InitializeAsync()
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public ValueTask DestroyAsync()
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public void Destroy()
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public void RegisterLifecycleHook(IArchitectureLifecycleHook hook)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
16
GFramework.Core.Tests/Architectures/TestArchitecture.cs
Normal file
16
GFramework.Core.Tests/Architectures/TestArchitecture.cs
Normal file
@ -0,0 +1,16 @@
|
||||
using GFramework.Core.Architectures;
|
||||
|
||||
namespace GFramework.Core.Tests.Architectures;
|
||||
|
||||
/// <summary>
|
||||
/// 提供给 <see cref="GameContextTests" /> 的最小架构测试桩。
|
||||
/// </summary>
|
||||
public class TestArchitecture : Architecture
|
||||
{
|
||||
/// <summary>
|
||||
/// 保持空初始化流程,便于测试仅验证 <see cref="GameContext" /> 的上下文绑定行为。
|
||||
/// </summary>
|
||||
protected override void OnInitialize()
|
||||
{
|
||||
}
|
||||
}
|
||||
@ -9,6 +9,7 @@ namespace GFramework.Core.Tests.Architectures;
|
||||
public abstract class TestArchitectureBase : Architecture
|
||||
{
|
||||
private Action<TestArchitectureBase>? _postRegistrationHook;
|
||||
private readonly List<ArchitecturePhase> _phaseHistory = [];
|
||||
|
||||
/// <summary>
|
||||
/// 获取就绪事件是否已触发的状态
|
||||
@ -23,7 +24,7 @@ public abstract class TestArchitectureBase : Architecture
|
||||
/// <summary>
|
||||
/// 获取架构阶段历史记录列表
|
||||
/// </summary>
|
||||
public List<ArchitecturePhase> PhaseHistory { get; } = [];
|
||||
public IReadOnlyList<ArchitecturePhase> PhaseHistory => _phaseHistory;
|
||||
|
||||
/// <summary>
|
||||
/// 添加注册后钩子函数
|
||||
@ -43,6 +44,6 @@ public abstract class TestArchitectureBase : Architecture
|
||||
_postRegistrationHook?.Invoke(this);
|
||||
|
||||
// 订阅阶段变更事件以记录历史
|
||||
PhaseChanged += (_, eventArgs) => PhaseHistory.Add(eventArgs.Phase);
|
||||
PhaseChanged += (_, eventArgs) => _phaseHistory.Add(eventArgs.Phase);
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,12 @@
|
||||
namespace GFramework.Core.Tests.Architectures;
|
||||
|
||||
/// <summary>
|
||||
/// 为 <see cref="GameContextTests" /> 提供最小可用的架构上下文测试桩。
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 共享的容器解析、事件总线协作与 legacy CQRS 失败契约由 <see cref="TestArchitectureContextBase" /> 提供,
|
||||
/// 当前类型仅作为默认测试上下文命名入口,供现有测试与派生替身继续复用。
|
||||
/// </remarks>
|
||||
public class TestArchitectureContext : TestArchitectureContextBase
|
||||
{
|
||||
}
|
||||
@ -0,0 +1,451 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using GFramework.Core.Abstractions.Architectures;
|
||||
using GFramework.Core.Abstractions.Command;
|
||||
using GFramework.Core.Abstractions.Environment;
|
||||
using GFramework.Core.Abstractions.Events;
|
||||
using GFramework.Core.Abstractions.Ioc;
|
||||
using GFramework.Core.Abstractions.Model;
|
||||
using GFramework.Core.Abstractions.Query;
|
||||
using GFramework.Core.Abstractions.Systems;
|
||||
using GFramework.Core.Abstractions.Utility;
|
||||
using GFramework.Core.Command;
|
||||
using GFramework.Core.Environment;
|
||||
using GFramework.Core.Events;
|
||||
using GFramework.Core.Ioc;
|
||||
using GFramework.Core.Query;
|
||||
using GFramework.Cqrs.Abstractions.Cqrs;
|
||||
using ICommand = GFramework.Core.Abstractions.Command.ICommand;
|
||||
|
||||
namespace GFramework.Core.Tests.Architectures;
|
||||
|
||||
/// <summary>
|
||||
/// 为架构相关测试替身提供共享的 <see cref="IArchitectureContext" /> 基础实现。
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 该基类统一维护容器解析、共享 <see cref="EventBus" /> 语义,以及 legacy / CQRS 入口的显式失败契约,
|
||||
/// 以避免多个测试上下文因为并行复制实现而在后续演进中发生语义漂移。
|
||||
/// </remarks>
|
||||
public abstract class TestArchitectureContextBase : IArchitectureContext
|
||||
{
|
||||
private readonly MicrosoftDiContainer _container = new();
|
||||
private readonly DefaultEnvironment _environment = new();
|
||||
private readonly EventBus _eventBus = new();
|
||||
|
||||
/// <summary>
|
||||
/// 获取用于解析测试服务的依赖注入容器。
|
||||
/// </summary>
|
||||
public IIocContainer Container => _container;
|
||||
|
||||
/// <summary>
|
||||
/// 获取测试事件总线实例。
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 返回同一个缓存事件总线,以便 <see cref="RegisterEvent{TEvent}" />、<see cref="SendEvent{TEvent}()" /> 与
|
||||
/// <see cref="UnRegisterEvent{TEvent}" /> 在同一份订阅状态上协作。
|
||||
/// </remarks>
|
||||
public IEventBus EventBus => _eventBus;
|
||||
|
||||
/// <summary>
|
||||
/// 获取测试命令执行器实例。
|
||||
/// </summary>
|
||||
public ICommandExecutor CommandExecutor => new CommandExecutor();
|
||||
|
||||
/// <summary>
|
||||
/// 获取测试查询执行器实例。
|
||||
/// </summary>
|
||||
public IQueryExecutor QueryExecutor => new QueryExecutor();
|
||||
|
||||
/// <summary>
|
||||
/// 获取默认测试环境对象。
|
||||
/// </summary>
|
||||
public IEnvironment Environment => _environment;
|
||||
|
||||
/// <summary>
|
||||
/// 获取指定类型的服务实例。
|
||||
/// </summary>
|
||||
/// <typeparam name="TService">服务类型。</typeparam>
|
||||
/// <returns>已注册的服务实例。</returns>
|
||||
/// <exception cref="InvalidOperationException">未注册服务时抛出。</exception>
|
||||
public TService GetService<TService>() where TService : class
|
||||
{
|
||||
return _container.GetRequired<TService>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取指定类型的所有服务实例。
|
||||
/// </summary>
|
||||
/// <typeparam name="TService">服务类型。</typeparam>
|
||||
/// <returns>服务实例列表。</returns>
|
||||
public IReadOnlyList<TService> GetServices<TService>() where TService : class
|
||||
{
|
||||
return _container.GetAll<TService>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取指定类型的模型实例。
|
||||
/// </summary>
|
||||
/// <typeparam name="TModel">模型类型。</typeparam>
|
||||
/// <returns>已注册的模型实例。</returns>
|
||||
/// <exception cref="InvalidOperationException">未注册模型时抛出。</exception>
|
||||
public TModel GetModel<TModel>() where TModel : class, IModel
|
||||
{
|
||||
return _container.GetRequired<TModel>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取指定类型的所有模型实例。
|
||||
/// </summary>
|
||||
/// <typeparam name="TModel">模型类型。</typeparam>
|
||||
/// <returns>模型实例列表。</returns>
|
||||
public IReadOnlyList<TModel> GetModels<TModel>() where TModel : class, IModel
|
||||
{
|
||||
return _container.GetAll<TModel>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取指定类型的系统实例。
|
||||
/// </summary>
|
||||
/// <typeparam name="TSystem">系统类型。</typeparam>
|
||||
/// <returns>已注册的系统实例。</returns>
|
||||
/// <exception cref="InvalidOperationException">未注册系统时抛出。</exception>
|
||||
public TSystem GetSystem<TSystem>() where TSystem : class, ISystem
|
||||
{
|
||||
return _container.GetRequired<TSystem>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取指定类型的所有系统实例。
|
||||
/// </summary>
|
||||
/// <typeparam name="TSystem">系统类型。</typeparam>
|
||||
/// <returns>系统实例列表。</returns>
|
||||
public IReadOnlyList<TSystem> GetSystems<TSystem>() where TSystem : class, ISystem
|
||||
{
|
||||
return _container.GetAll<TSystem>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取指定类型的工具实例。
|
||||
/// </summary>
|
||||
/// <typeparam name="TUtility">工具类型。</typeparam>
|
||||
/// <returns>已注册的工具实例。</returns>
|
||||
/// <exception cref="InvalidOperationException">未注册工具时抛出。</exception>
|
||||
public virtual TUtility GetUtility<TUtility>() where TUtility : class, IUtility
|
||||
{
|
||||
return _container.GetRequired<TUtility>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取指定类型的所有工具实例。
|
||||
/// </summary>
|
||||
/// <typeparam name="TUtility">工具类型。</typeparam>
|
||||
/// <returns>工具实例列表。</returns>
|
||||
public IReadOnlyList<TUtility> GetUtilities<TUtility>() where TUtility : class, IUtility
|
||||
{
|
||||
return _container.GetAll<TUtility>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取指定类型的所有服务实例,并按优先级排序。
|
||||
/// </summary>
|
||||
/// <typeparam name="TService">服务类型。</typeparam>
|
||||
/// <returns>按优先级排序后的服务实例列表。</returns>
|
||||
public IReadOnlyList<TService> GetServicesByPriority<TService>() where TService : class
|
||||
{
|
||||
return _container.GetAllByPriority<TService>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取指定类型的所有系统实例,并按优先级排序。
|
||||
/// </summary>
|
||||
/// <typeparam name="TSystem">系统类型。</typeparam>
|
||||
/// <returns>按优先级排序后的系统实例列表。</returns>
|
||||
public IReadOnlyList<TSystem> GetSystemsByPriority<TSystem>() where TSystem : class, ISystem
|
||||
{
|
||||
return _container.GetAllByPriority<TSystem>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取指定类型的所有模型实例,并按优先级排序。
|
||||
/// </summary>
|
||||
/// <typeparam name="TModel">模型类型。</typeparam>
|
||||
/// <returns>按优先级排序后的模型实例列表。</returns>
|
||||
public IReadOnlyList<TModel> GetModelsByPriority<TModel>() where TModel : class, IModel
|
||||
{
|
||||
return _container.GetAllByPriority<TModel>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取指定类型的所有工具实例,并按优先级排序。
|
||||
/// </summary>
|
||||
/// <typeparam name="TUtility">工具类型。</typeparam>
|
||||
/// <returns>按优先级排序后的工具实例列表。</returns>
|
||||
public IReadOnlyList<TUtility> GetUtilitiesByPriority<TUtility>() where TUtility : class, IUtility
|
||||
{
|
||||
return _container.GetAllByPriority<TUtility>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 发送无参数事件。
|
||||
/// </summary>
|
||||
/// <typeparam name="TEvent">事件类型。</typeparam>
|
||||
public void SendEvent<TEvent>() where TEvent : new()
|
||||
{
|
||||
_eventBus.Send<TEvent>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 发送带参数事件。
|
||||
/// </summary>
|
||||
/// <typeparam name="TEvent">事件类型。</typeparam>
|
||||
/// <param name="e">事件实例。</param>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="e" /> 为 <see langword="null" />。</exception>
|
||||
public void SendEvent<TEvent>(TEvent e) where TEvent : class
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(e);
|
||||
_eventBus.Send(e);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 注册事件处理器。
|
||||
/// </summary>
|
||||
/// <typeparam name="TEvent">事件类型。</typeparam>
|
||||
/// <param name="handler">事件处理委托。</param>
|
||||
/// <returns>用于测试的事件注销句柄。</returns>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="handler" /> 为 <see langword="null" />。</exception>
|
||||
public IUnRegister RegisterEvent<TEvent>(Action<TEvent> handler)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(handler);
|
||||
return _eventBus.Register(handler);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 取消注册事件处理器。
|
||||
/// </summary>
|
||||
/// <typeparam name="TEvent">事件类型。</typeparam>
|
||||
/// <param name="onEvent">事件处理委托。</param>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="onEvent" /> 为 <see langword="null" />。</exception>
|
||||
public void UnRegisterEvent<TEvent>(Action<TEvent> onEvent)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(onEvent);
|
||||
_eventBus.UnRegister(onEvent);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 测试桩:异步发送统一 CQRS 请求。
|
||||
/// </summary>
|
||||
/// <typeparam name="TResponse">响应类型。</typeparam>
|
||||
/// <param name="request">要发送的请求。</param>
|
||||
/// <param name="cancellationToken">取消令牌。</param>
|
||||
/// <returns>请求响应任务。</returns>
|
||||
/// <exception cref="NotSupportedException">该测试桩不支持此成员。</exception>
|
||||
public ValueTask<TResponse> SendRequestAsync<TResponse>(
|
||||
IRequest<TResponse> request,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 测试桩:同步发送统一 CQRS 请求。
|
||||
/// </summary>
|
||||
/// <typeparam name="TResponse">响应类型。</typeparam>
|
||||
/// <param name="request">要发送的请求。</param>
|
||||
/// <returns>请求响应。</returns>
|
||||
/// <exception cref="NotSupportedException">该测试桩不支持此成员。</exception>
|
||||
public TResponse SendRequest<TResponse>(IRequest<TResponse> request)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 测试桩:异步发送 CQRS 命令并返回响应。
|
||||
/// </summary>
|
||||
/// <typeparam name="TResponse">命令响应类型。</typeparam>
|
||||
/// <param name="command">要发送的命令。</param>
|
||||
/// <param name="cancellationToken">取消令牌。</param>
|
||||
/// <returns>命令响应任务。</returns>
|
||||
/// <exception cref="NotSupportedException">该测试桩不支持此成员。</exception>
|
||||
public ValueTask<TResponse> SendCommandAsync<TResponse>(
|
||||
GFramework.Cqrs.Abstractions.Cqrs.Command.ICommand<TResponse> command,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 测试桩:同步发送 CQRS 命令并返回响应。
|
||||
/// </summary>
|
||||
/// <typeparam name="TResponse">命令响应类型。</typeparam>
|
||||
/// <param name="command">要发送的命令。</param>
|
||||
/// <returns>命令响应。</returns>
|
||||
/// <exception cref="NotSupportedException">该测试桩不支持此成员。</exception>
|
||||
public TResponse SendCommand<TResponse>(GFramework.Cqrs.Abstractions.Cqrs.Command.ICommand<TResponse> command)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 测试桩:异步发送 CQRS 查询并返回结果。
|
||||
/// </summary>
|
||||
/// <typeparam name="TResponse">查询结果类型。</typeparam>
|
||||
/// <param name="query">要发送的查询。</param>
|
||||
/// <param name="cancellationToken">取消令牌。</param>
|
||||
/// <returns>查询结果任务。</returns>
|
||||
/// <exception cref="NotSupportedException">该测试桩不支持此成员。</exception>
|
||||
public ValueTask<TResponse> SendQueryAsync<TResponse>(
|
||||
GFramework.Cqrs.Abstractions.Cqrs.Query.IQuery<TResponse> query,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 测试桩:同步发送 CQRS 查询并返回结果。
|
||||
/// </summary>
|
||||
/// <typeparam name="TResponse">查询结果类型。</typeparam>
|
||||
/// <param name="query">要发送的查询。</param>
|
||||
/// <returns>查询结果。</returns>
|
||||
/// <exception cref="NotSupportedException">该测试桩不支持此成员。</exception>
|
||||
public TResponse SendQuery<TResponse>(GFramework.Cqrs.Abstractions.Cqrs.Query.IQuery<TResponse> query)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 测试桩:异步发布 CQRS 通知。
|
||||
/// </summary>
|
||||
/// <typeparam name="TNotification">通知类型。</typeparam>
|
||||
/// <param name="notification">要发布的通知。</param>
|
||||
/// <param name="cancellationToken">取消令牌。</param>
|
||||
/// <returns>通知发布任务。</returns>
|
||||
/// <exception cref="NotSupportedException">该测试桩不支持此成员。</exception>
|
||||
public ValueTask PublishAsync<TNotification>(
|
||||
TNotification notification,
|
||||
CancellationToken cancellationToken = default)
|
||||
where TNotification : INotification
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 测试桩:创建 CQRS 流式请求响应序列。
|
||||
/// </summary>
|
||||
/// <typeparam name="TResponse">流式响应元素类型。</typeparam>
|
||||
/// <param name="request">流式请求。</param>
|
||||
/// <param name="cancellationToken">取消令牌。</param>
|
||||
/// <returns>异步响应流。</returns>
|
||||
/// <exception cref="NotSupportedException">该测试桩不支持此成员。</exception>
|
||||
public IAsyncEnumerable<TResponse> CreateStream<TResponse>(
|
||||
IStreamRequest<TResponse> request,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 测试桩:异步发送无返回值 CQRS 命令。
|
||||
/// </summary>
|
||||
/// <typeparam name="TCommand">命令类型。</typeparam>
|
||||
/// <param name="command">要发送的命令。</param>
|
||||
/// <param name="cancellationToken">取消令牌。</param>
|
||||
/// <returns>命令发送任务。</returns>
|
||||
/// <exception cref="NotSupportedException">该测试桩不支持此成员。</exception>
|
||||
public ValueTask SendAsync<TCommand>(TCommand command, CancellationToken cancellationToken = default)
|
||||
where TCommand : IRequest<Unit>
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 测试桩:异步发送带返回值的 CQRS 请求。
|
||||
/// </summary>
|
||||
/// <typeparam name="TResponse">响应类型。</typeparam>
|
||||
/// <param name="command">要发送的请求。</param>
|
||||
/// <param name="cancellationToken">取消令牌。</param>
|
||||
/// <returns>请求响应任务。</returns>
|
||||
/// <exception cref="NotSupportedException">该测试桩不支持此成员。</exception>
|
||||
public ValueTask<TResponse> SendAsync<TResponse>(
|
||||
IRequest<TResponse> command,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 发送旧版命令。
|
||||
/// </summary>
|
||||
/// <param name="command">命令对象。</param>
|
||||
/// <exception cref="NotSupportedException">该测试桩不支持旧版命令执行入口。</exception>
|
||||
public void SendCommand(ICommand command)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 发送旧版带返回值命令。
|
||||
/// </summary>
|
||||
/// <typeparam name="TResult">返回值类型。</typeparam>
|
||||
/// <param name="command">命令对象。</param>
|
||||
/// <returns>此方法始终抛出异常,不返回结果。</returns>
|
||||
/// <exception cref="NotSupportedException">该测试桩不支持旧版命令执行入口。</exception>
|
||||
public TResult SendCommand<TResult>(ICommand<TResult> command)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 异步发送旧版命令。
|
||||
/// </summary>
|
||||
/// <param name="command">命令对象。</param>
|
||||
/// <returns>已失败的任务。</returns>
|
||||
public Task SendCommandAsync(IAsyncCommand command)
|
||||
{
|
||||
return Task.FromException(new NotSupportedException());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 异步发送旧版带返回值命令。
|
||||
/// </summary>
|
||||
/// <typeparam name="TResult">返回值类型。</typeparam>
|
||||
/// <param name="command">命令对象。</param>
|
||||
/// <returns>已失败的任务。</returns>
|
||||
public Task<TResult> SendCommandAsync<TResult>(IAsyncCommand<TResult> command)
|
||||
{
|
||||
return Task.FromException<TResult>(new NotSupportedException());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 发送旧版查询请求。
|
||||
/// </summary>
|
||||
/// <typeparam name="TResult">查询结果类型。</typeparam>
|
||||
/// <param name="query">查询对象。</param>
|
||||
/// <returns>此方法始终抛出异常,不返回结果。</returns>
|
||||
/// <exception cref="NotSupportedException">该测试桩不支持旧版查询执行入口。</exception>
|
||||
public TResult SendQuery<TResult>(IQuery<TResult> query)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 异步发送旧版查询请求。
|
||||
/// </summary>
|
||||
/// <typeparam name="TResult">查询结果类型。</typeparam>
|
||||
/// <param name="query">异步查询对象。</param>
|
||||
/// <returns>已失败的任务。</returns>
|
||||
public Task<TResult> SendQueryAsync<TResult>(IAsyncQuery<TResult> query)
|
||||
{
|
||||
return Task.FromException<TResult>(new NotSupportedException());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取当前环境对象。
|
||||
/// </summary>
|
||||
/// <returns>默认测试环境对象。</returns>
|
||||
public IEnvironment GetEnvironment()
|
||||
{
|
||||
return Environment;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,218 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using GFramework.Core.Abstractions.Architectures;
|
||||
using GFramework.Core.Abstractions.Command;
|
||||
using GFramework.Core.Abstractions.Enums;
|
||||
using GFramework.Core.Abstractions.Query;
|
||||
|
||||
namespace GFramework.Core.Tests.Architectures;
|
||||
|
||||
/// <summary>
|
||||
/// 覆盖测试架构上下文替身的共享事件与显式失败契约。
|
||||
/// </summary>
|
||||
[TestFixture]
|
||||
public class TestArchitectureContextBehaviorTests
|
||||
{
|
||||
private static IEnumerable<TestCaseData> EventContextFactories()
|
||||
{
|
||||
yield return new TestCaseData(
|
||||
new Func<IArchitectureContext>(() => new TestArchitectureContext()))
|
||||
.SetArgDisplayNames(nameof(TestArchitectureContext));
|
||||
yield return new TestCaseData(
|
||||
new Func<IArchitectureContext>(() => new TestArchitectureContextV3()))
|
||||
.SetArgDisplayNames(nameof(TestArchitectureContextV3));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证测试上下文会把事件注册与发送委托到同一个事件总线实例。
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void RegisterEvent_And_SendEvent_Should_Use_Shared_EventBus()
|
||||
{
|
||||
var context = new TestArchitectureContext();
|
||||
var eventReceived = false;
|
||||
|
||||
context.RegisterEvent<TestEventV2>(_ => eventReceived = true);
|
||||
context.SendEvent<TestEventV2>();
|
||||
|
||||
Assert.That(eventReceived, Is.True);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证用于 ArchitectureServices 的上下文替身也会把事件注册与发送委托到同一个事件总线实例。
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void RegisterEvent_And_SendEvent_On_TestArchitectureContextV3_Should_Use_Shared_EventBus()
|
||||
{
|
||||
var context = new TestArchitectureContextV3();
|
||||
var eventReceived = false;
|
||||
|
||||
context.RegisterEvent<TestEventV2>(_ => eventReceived = true);
|
||||
context.SendEvent<TestEventV2>();
|
||||
|
||||
Assert.That(eventReceived, Is.True);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证两类架构测试替身都会对事件 API 的空参数执行显式校验。
|
||||
/// </summary>
|
||||
/// <param name="createContext">创建待验证测试上下文的工厂。</param>
|
||||
[TestCaseSource(nameof(EventContextFactories))]
|
||||
public void Event_APIs_Should_Validate_Null_Arguments(Func<IArchitectureContext> createContext)
|
||||
{
|
||||
var context = createContext();
|
||||
|
||||
Assert.That(() => context.SendEvent<TestEventV2>(null!), Throws.TypeOf<ArgumentNullException>());
|
||||
Assert.That(() => context.RegisterEvent<TestEventV2>(null!), Throws.TypeOf<ArgumentNullException>());
|
||||
Assert.That(() => context.UnRegisterEvent<TestEventV2>(null!), Throws.TypeOf<ArgumentNullException>());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证两类架构测试替身在注销事件处理器后都不会继续分发同一回调。
|
||||
/// </summary>
|
||||
/// <param name="createContext">创建待验证测试上下文的工厂。</param>
|
||||
[TestCaseSource(nameof(EventContextFactories))]
|
||||
public void UnRegisterEvent_Should_Stop_Dispatch(Func<IArchitectureContext> createContext)
|
||||
{
|
||||
var context = createContext();
|
||||
var eventReceived = false;
|
||||
|
||||
void Handler(TestEventV2 _)
|
||||
{
|
||||
eventReceived = true;
|
||||
}
|
||||
|
||||
context.RegisterEvent<TestEventV2>(Handler);
|
||||
context.UnRegisterEvent<TestEventV2>(Handler);
|
||||
context.SendEvent<TestEventV2>();
|
||||
|
||||
Assert.That(eventReceived, Is.False);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证测试上下文的旧版命令与查询入口会显式抛出未支持异常。
|
||||
/// </summary>
|
||||
[Test]
|
||||
public async Task Legacy_Entries_Should_Throw_Or_Return_Faulted_Tasks()
|
||||
{
|
||||
var context = new TestArchitectureContext();
|
||||
|
||||
Assert.That(() => context.SendCommand(new TestCommandV2()), Throws.TypeOf<NotSupportedException>());
|
||||
Assert.That(
|
||||
() => context.SendCommand(new TestCommandWithResultV2 { Result = 1 }),
|
||||
Throws.TypeOf<NotSupportedException>());
|
||||
Assert.That(() => context.SendQuery(new TestQueryV2 { Result = 1 }), Throws.TypeOf<NotSupportedException>());
|
||||
Assert.That(
|
||||
async () => await context.SendCommandAsync(new TestAsyncCommand()).ConfigureAwait(false),
|
||||
Throws.TypeOf<NotSupportedException>());
|
||||
Assert.That(
|
||||
async () => await context.SendCommandAsync(new TestAsyncCommandWithResult()).ConfigureAwait(false),
|
||||
Throws.TypeOf<NotSupportedException>());
|
||||
Assert.That(
|
||||
async () => await context.SendQueryAsync(new TestAsyncQuery()).ConfigureAwait(false),
|
||||
Throws.TypeOf<NotSupportedException>());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证用于 ArchitectureServices 的上下文替身也会把旧版入口显式标记为不支持。
|
||||
/// </summary>
|
||||
[Test]
|
||||
public async Task Legacy_Entries_On_TestArchitectureContextV3_Should_Throw_Or_Return_Faulted_Tasks()
|
||||
{
|
||||
var context = new TestArchitectureContextV3();
|
||||
|
||||
Assert.That(() => context.SendCommand(new TestCommandV2()), Throws.TypeOf<NotSupportedException>());
|
||||
Assert.That(
|
||||
() => context.SendCommand(new TestCommandWithResultV2 { Result = 1 }),
|
||||
Throws.TypeOf<NotSupportedException>());
|
||||
Assert.That(() => context.SendQuery(new TestQueryV2 { Result = 1 }), Throws.TypeOf<NotSupportedException>());
|
||||
Assert.That(
|
||||
async () => await context.SendCommandAsync(new TestAsyncCommand()).ConfigureAwait(false),
|
||||
Throws.TypeOf<NotSupportedException>());
|
||||
Assert.That(
|
||||
async () => await context.SendCommandAsync(new TestAsyncCommandWithResult()).ConfigureAwait(false),
|
||||
Throws.TypeOf<NotSupportedException>());
|
||||
Assert.That(
|
||||
async () => await context.SendQueryAsync(new TestAsyncQuery()).ConfigureAwait(false),
|
||||
Throws.TypeOf<NotSupportedException>());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证两类架构测试替身在接口视角下都会以 no-op 方式接受生命周期钩子。
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void RegisterLifecycleHook_Via_Interface_Should_Return_Original_Hook()
|
||||
{
|
||||
IArchitecture withRegistry = new TestArchitectureWithRegistry(new TestRegistry());
|
||||
IArchitecture withoutRegistry = new TestArchitectureWithoutRegistry();
|
||||
var hook = new NoOpLifecycleHook();
|
||||
|
||||
Assert.That(withRegistry.RegisterLifecycleHook(hook), Is.SameAs(hook));
|
||||
Assert.That(withoutRegistry.RegisterLifecycleHook(hook), Is.SameAs(hook));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 为旧版异步命令入口提供最小实现的测试命令。
|
||||
/// </summary>
|
||||
private sealed class TestAsyncCommand : IAsyncCommand
|
||||
{
|
||||
public Task ExecuteAsync()
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public IArchitectureContext GetContext()
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public void SetContext(IArchitectureContext context)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(context);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 为旧版异步命令入口提供最小实现的带结果测试命令。
|
||||
/// </summary>
|
||||
private sealed class TestAsyncCommandWithResult : IAsyncCommand<int>
|
||||
{
|
||||
public Task<int> ExecuteAsync()
|
||||
{
|
||||
return Task.FromResult(1);
|
||||
}
|
||||
|
||||
public IArchitectureContext GetContext()
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public void SetContext(IArchitectureContext context)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(context);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 为旧版异步查询入口提供最小实现的测试查询。
|
||||
/// </summary>
|
||||
private sealed class TestAsyncQuery : IAsyncQuery<int>
|
||||
{
|
||||
public Task<int> DoAsync()
|
||||
{
|
||||
return Task.FromResult(1);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 为生命周期钩子接口提供空实现的测试替身。
|
||||
/// </summary>
|
||||
private sealed class NoOpLifecycleHook : IArchitectureLifecycleHook
|
||||
{
|
||||
public void OnPhase(ArchitecturePhase phase, IArchitecture architecture)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(architecture);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,16 @@
|
||||
namespace GFramework.Core.Tests.Architectures;
|
||||
|
||||
/// <summary>
|
||||
/// 为 <see cref="ArchitectureServicesTests" /> 提供最小实现的架构上下文测试桩。
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 共享的容器解析、事件总线协作与 legacy CQRS 失败契约由 <see cref="TestArchitectureContextBase" /> 提供,
|
||||
/// 当前类型只补充 <see cref="ArchitectureServicesTests" /> 需要的上下文实例标识。
|
||||
/// </remarks>
|
||||
public class TestArchitectureContextV3 : TestArchitectureContextBase
|
||||
{
|
||||
/// <summary>
|
||||
/// 获取或初始化用于区分测试上下文实例的标识。
|
||||
/// </summary>
|
||||
public int Id { get; init; }
|
||||
}
|
||||
@ -0,0 +1,35 @@
|
||||
using GFramework.Core.Architectures;
|
||||
|
||||
namespace GFramework.Core.Tests.Architectures;
|
||||
|
||||
/// <summary>
|
||||
/// 为 <see cref="RegistryInitializationHookBaseTests" /> 在架构上下文中暴露 <see cref="TestRegistry" /> 的测试替身。
|
||||
/// </summary>
|
||||
public class TestArchitectureContextWithRegistry : TestArchitectureContext
|
||||
{
|
||||
private readonly TestRegistry _registry;
|
||||
|
||||
/// <summary>
|
||||
/// 使用给定测试注册表创建上下文测试替身。
|
||||
/// </summary>
|
||||
/// <param name="registry">需要通过 <see cref="GetUtility{TUtility}" /> 返回的测试注册表。</param>
|
||||
public TestArchitectureContextWithRegistry(TestRegistry registry)
|
||||
{
|
||||
_registry = registry;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 在请求 <see cref="TestRegistry" /> 时返回测试注册表,其余类型回退到基类实现。
|
||||
/// </summary>
|
||||
/// <typeparam name="TUtility">请求的工具类型。</typeparam>
|
||||
/// <returns>匹配时返回测试注册表,否则返回基类结果。</returns>
|
||||
public override TUtility GetUtility<TUtility>()
|
||||
{
|
||||
if (typeof(TUtility) == typeof(TestRegistry))
|
||||
{
|
||||
return (TUtility)(object)_registry;
|
||||
}
|
||||
|
||||
return base.GetUtility<TUtility>();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,205 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
using GFramework.Core.Abstractions.Architectures;
|
||||
using GFramework.Core.Abstractions.Lifecycle;
|
||||
using GFramework.Core.Abstractions.Model;
|
||||
using GFramework.Core.Abstractions.Systems;
|
||||
using GFramework.Core.Abstractions.Utility;
|
||||
|
||||
namespace GFramework.Core.Tests.Architectures;
|
||||
|
||||
/// <summary>
|
||||
/// 为 <see cref="RegistryInitializationHookBaseTests" /> 提供已挂接 <see cref="TestRegistry" /> 的架构测试替身。
|
||||
/// </summary>
|
||||
public class TestArchitectureWithRegistry : IArchitecture
|
||||
{
|
||||
/// <summary>
|
||||
/// 使用给定测试注册表创建架构测试替身。
|
||||
/// </summary>
|
||||
/// <param name="registry">要通过架构上下文暴露给钩子的测试注册表。</param>
|
||||
public TestArchitectureWithRegistry(TestRegistry registry)
|
||||
{
|
||||
Context = new TestArchitectureContextWithRegistry(registry);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取测试替身公开的服务配置入口。
|
||||
/// 当前切片不验证服务配置流程,因此始终保持为空。
|
||||
/// </summary>
|
||||
public Action<IServiceCollection>? Configurator { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取当前测试替身使用的架构上下文。
|
||||
/// </summary>
|
||||
public IArchitectureContext Context { get; }
|
||||
|
||||
Action<IServiceCollection>? IArchitecture.Configurator => Configurator;
|
||||
|
||||
T IArchitecture.RegisterSystem<T>(T system)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
T IArchitecture.RegisterModel<T>(T model)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
T IArchitecture.RegisterUtility<T>(T utility)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 测试替身未实现 CQRS 管道行为注册。
|
||||
/// </summary>
|
||||
/// <typeparam name="TBehavior">行为类型。</typeparam>
|
||||
/// <exception cref="NotSupportedException">该测试替身不参与 CQRS 管道配置验证。</exception>
|
||||
public void RegisterCqrsPipelineBehavior<TBehavior>() where TBehavior : class
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 测试替身未实现显式程序集 CQRS 处理器接入入口。
|
||||
/// </summary>
|
||||
/// <param name="assembly">包含 CQRS 处理器或生成注册器的程序集。</param>
|
||||
/// <exception cref="NotSupportedException">该测试替身不参与 CQRS 程序集接入路径验证。</exception>
|
||||
public void RegisterCqrsHandlersFromAssembly(Assembly assembly)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 测试替身未实现显式程序集 CQRS 处理器接入入口。
|
||||
/// </summary>
|
||||
/// <param name="assemblies">要接入的程序集集合。</param>
|
||||
/// <exception cref="NotSupportedException">该测试替身不参与 CQRS 程序集接入路径验证。</exception>
|
||||
public void RegisterCqrsHandlersFromAssemblies(IEnumerable<Assembly> assemblies)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 测试替身未实现模块安装流程。
|
||||
/// </summary>
|
||||
/// <param name="module">要安装的模块。</param>
|
||||
/// <returns>此方法始终抛出异常,不返回模块实例。</returns>
|
||||
/// <exception cref="NotSupportedException">该测试替身不参与模块安装路径验证。</exception>
|
||||
public IArchitectureModule InstallModule(IArchitectureModule module)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
IArchitectureLifecycleHook IArchitecture.RegisterLifecycleHook(IArchitectureLifecycleHook hook)
|
||||
{
|
||||
RegisterLifecycleHook(hook);
|
||||
return hook;
|
||||
}
|
||||
|
||||
Task IArchitecture.WaitUntilReadyAsync()
|
||||
{
|
||||
return WaitUntilReadyAsync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 测试替身未实现工具延迟注册入口。
|
||||
/// </summary>
|
||||
/// <typeparam name="T">工具类型。</typeparam>
|
||||
/// <param name="onCreated">工具创建后的回调。</param>
|
||||
/// <exception cref="NotSupportedException">该测试替身不参与工具注册路径验证。</exception>
|
||||
public void RegisterUtility<T>(Action<T>? onCreated = default) where T : class, IUtility
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 测试替身未实现 Model 延迟注册入口。
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Model 类型。</typeparam>
|
||||
/// <param name="onCreated">Model 创建后的回调。</param>
|
||||
/// <exception cref="NotSupportedException">该测试替身不参与 Model 注册路径验证。</exception>
|
||||
public void RegisterModel<T>(Action<T>? onCreated = default) where T : class, IModel
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 测试替身未实现 System 延迟注册入口。
|
||||
/// </summary>
|
||||
/// <typeparam name="T">System 类型。</typeparam>
|
||||
/// <param name="onCreated">System 创建后的回调。</param>
|
||||
/// <exception cref="NotSupportedException">该测试替身不参与 System 注册路径验证。</exception>
|
||||
public void RegisterSystem<T>(Action<T>? onCreated = default) where T : class, ISystem
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 初始化测试替身。
|
||||
/// 该切片只需要上下文可用,因此初始化过程保持为空实现。
|
||||
/// </summary>
|
||||
public void Initialize()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 测试替身未实现销毁路径。
|
||||
/// </summary>
|
||||
/// <exception cref="NotSupportedException">该测试替身不参与销毁路径验证。</exception>
|
||||
public void Destroy()
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
Task IAsyncInitializable.InitializeAsync()
|
||||
{
|
||||
return InitializeAsync();
|
||||
}
|
||||
|
||||
ValueTask IAsyncDestroyable.DestroyAsync()
|
||||
{
|
||||
return DestroyAsync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 测试替身未实现就绪等待流程。
|
||||
/// </summary>
|
||||
/// <returns>此方法始终抛出异常,不返回等待任务。</returns>
|
||||
/// <exception cref="NotSupportedException">该测试替身不参与就绪等待路径验证。</exception>
|
||||
public Task WaitUntilReadyAsync()
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 注册架构生命周期钩子。
|
||||
/// 当前切片不依赖生命周期钩子执行,因此保持空实现。
|
||||
/// </summary>
|
||||
/// <param name="hook">要忽略的生命周期钩子。</param>
|
||||
public void RegisterLifecycleHook(IArchitectureLifecycleHook hook)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 测试替身未实现异步初始化路径。
|
||||
/// </summary>
|
||||
/// <returns>此方法始终抛出异常,不返回初始化任务。</returns>
|
||||
/// <exception cref="NotSupportedException">该测试替身不参与异步初始化验证。</exception>
|
||||
public Task InitializeAsync()
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 测试替身未实现异步销毁路径。
|
||||
/// </summary>
|
||||
/// <returns>此方法始终抛出异常,不返回销毁任务。</returns>
|
||||
/// <exception cref="NotSupportedException">该测试替身不参与异步销毁验证。</exception>
|
||||
public ValueTask DestroyAsync()
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,188 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
using GFramework.Core.Abstractions.Architectures;
|
||||
using GFramework.Core.Abstractions.Lifecycle;
|
||||
using GFramework.Core.Abstractions.Model;
|
||||
using GFramework.Core.Abstractions.Systems;
|
||||
using GFramework.Core.Abstractions.Utility;
|
||||
using GFramework.Core.Architectures;
|
||||
|
||||
namespace GFramework.Core.Tests.Architectures;
|
||||
|
||||
/// <summary>
|
||||
/// 为 <see cref="RegistryInitializationHookBaseTests" /> 提供不包含 <see cref="TestRegistry" /> 的架构测试替身。
|
||||
/// </summary>
|
||||
public class TestArchitectureWithoutRegistry : IArchitecture
|
||||
{
|
||||
/// <summary>
|
||||
/// 创建不包含测试注册表的架构替身。
|
||||
/// </summary>
|
||||
public TestArchitectureWithoutRegistry()
|
||||
{
|
||||
Context = new TestArchitectureContext();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取测试替身公开的服务配置入口。
|
||||
/// 当前切片不验证服务配置流程,因此始终保持为空。
|
||||
/// </summary>
|
||||
public Action<IServiceCollection>? Configurator { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取当前测试替身使用的架构上下文。
|
||||
/// </summary>
|
||||
public IArchitectureContext Context { get; }
|
||||
|
||||
T IArchitecture.RegisterSystem<T>(T system)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
T IArchitecture.RegisterModel<T>(T model)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
T IArchitecture.RegisterUtility<T>(T utility)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 测试替身未实现 CQRS 管道行为注册。
|
||||
/// </summary>
|
||||
/// <typeparam name="TBehavior">行为类型。</typeparam>
|
||||
/// <exception cref="NotSupportedException">该测试替身不参与 CQRS 管道配置验证。</exception>
|
||||
public void RegisterCqrsPipelineBehavior<TBehavior>() where TBehavior : class
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 测试替身未实现显式程序集 CQRS 处理器接入入口。
|
||||
/// </summary>
|
||||
/// <param name="assembly">包含 CQRS 处理器或生成注册器的程序集。</param>
|
||||
/// <exception cref="NotSupportedException">该测试替身不参与 CQRS 程序集接入路径验证。</exception>
|
||||
public void RegisterCqrsHandlersFromAssembly(Assembly assembly)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 测试替身未实现显式程序集 CQRS 处理器接入入口。
|
||||
/// </summary>
|
||||
/// <param name="assemblies">要接入的程序集集合。</param>
|
||||
/// <exception cref="NotSupportedException">该测试替身不参与 CQRS 程序集接入路径验证。</exception>
|
||||
public void RegisterCqrsHandlersFromAssemblies(IEnumerable<Assembly> assemblies)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 测试替身未实现模块安装流程。
|
||||
/// </summary>
|
||||
/// <param name="module">要安装的模块。</param>
|
||||
/// <returns>此方法始终抛出异常,不返回模块实例。</returns>
|
||||
/// <exception cref="NotSupportedException">该测试替身不参与模块安装路径验证。</exception>
|
||||
public IArchitectureModule InstallModule(IArchitectureModule module)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
IArchitectureLifecycleHook IArchitecture.RegisterLifecycleHook(IArchitectureLifecycleHook hook)
|
||||
{
|
||||
RegisterLifecycleHook(hook);
|
||||
return hook;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 测试替身未实现就绪等待流程。
|
||||
/// </summary>
|
||||
/// <returns>此方法始终抛出异常,不返回等待任务。</returns>
|
||||
/// <exception cref="NotSupportedException">该测试替身不参与就绪等待路径验证。</exception>
|
||||
public Task WaitUntilReadyAsync()
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 测试替身未实现工具延迟注册入口。
|
||||
/// </summary>
|
||||
/// <typeparam name="T">工具类型。</typeparam>
|
||||
/// <param name="onCreated">工具创建后的回调。</param>
|
||||
/// <exception cref="NotSupportedException">该测试替身不参与工具注册路径验证。</exception>
|
||||
public void RegisterUtility<T>(Action<T>? onCreated = default) where T : class, IUtility
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 测试替身未实现 Model 延迟注册入口。
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Model 类型。</typeparam>
|
||||
/// <param name="onCreated">Model 创建后的回调。</param>
|
||||
/// <exception cref="NotSupportedException">该测试替身不参与 Model 注册路径验证。</exception>
|
||||
public void RegisterModel<T>(Action<T>? onCreated = default) where T : class, IModel
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 测试替身未实现 System 延迟注册入口。
|
||||
/// </summary>
|
||||
/// <typeparam name="T">System 类型。</typeparam>
|
||||
/// <param name="onCreated">System 创建后的回调。</param>
|
||||
/// <exception cref="NotSupportedException">该测试替身不参与 System 注册路径验证。</exception>
|
||||
public void RegisterSystem<T>(Action<T>? onCreated = default) where T : class, ISystem
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 初始化测试替身。
|
||||
/// 该切片只需要一个不含注册表的上下文,因此初始化过程保持为空实现。
|
||||
/// </summary>
|
||||
public void Initialize()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 测试替身未实现异步初始化路径。
|
||||
/// </summary>
|
||||
/// <returns>此方法始终抛出异常,不返回初始化任务。</returns>
|
||||
/// <exception cref="NotSupportedException">该测试替身不参与异步初始化验证。</exception>
|
||||
public Task InitializeAsync()
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 测试替身未实现异步销毁路径。
|
||||
/// </summary>
|
||||
/// <returns>此方法始终抛出异常,不返回销毁任务。</returns>
|
||||
/// <exception cref="NotSupportedException">该测试替身不参与异步销毁验证。</exception>
|
||||
public ValueTask DestroyAsync()
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 测试替身未实现销毁路径。
|
||||
/// </summary>
|
||||
/// <exception cref="NotSupportedException">该测试替身不参与销毁路径验证。</exception>
|
||||
public void Destroy()
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 注册架构生命周期钩子。
|
||||
/// 当前切片不依赖生命周期钩子执行,因此保持空实现。
|
||||
/// </summary>
|
||||
/// <param name="hook">要忽略的生命周期钩子。</param>
|
||||
public void RegisterLifecycleHook(IArchitectureLifecycleHook hook)
|
||||
{
|
||||
}
|
||||
}
|
||||
26
GFramework.Core.Tests/Architectures/TestRegistry.cs
Normal file
26
GFramework.Core.Tests/Architectures/TestRegistry.cs
Normal file
@ -0,0 +1,26 @@
|
||||
using System.Collections.Generic;
|
||||
using GFramework.Core.Abstractions.Utility;
|
||||
|
||||
namespace GFramework.Core.Tests.Architectures;
|
||||
|
||||
/// <summary>
|
||||
/// 为 <see cref="RegistryInitializationHookBaseTests" /> 记录注册结果的测试注册表。
|
||||
/// </summary>
|
||||
public class TestRegistry : IUtility
|
||||
{
|
||||
private readonly List<string> _registeredConfigs = [];
|
||||
|
||||
/// <summary>
|
||||
/// 获取已注册配置值的只读视图,避免将测试内部使用的列表实现暴露给调用方。
|
||||
/// </summary>
|
||||
public IReadOnlyList<string> RegisteredConfigs => _registeredConfigs;
|
||||
|
||||
/// <summary>
|
||||
/// 记录一次配置注册。
|
||||
/// </summary>
|
||||
/// <param name="config">要追加到测试结果中的配置值。</param>
|
||||
public void Register(string config)
|
||||
{
|
||||
_registeredConfigs.Add(config);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,33 @@
|
||||
using System.Collections.Generic;
|
||||
using GFramework.Core.Abstractions.Enums;
|
||||
using GFramework.Core.Architectures;
|
||||
|
||||
namespace GFramework.Core.Tests.Architectures;
|
||||
|
||||
/// <summary>
|
||||
/// 为 <see cref="RegistryInitializationHookBaseTests" /> 提供的注册表初始化钩子测试替身。
|
||||
/// </summary>
|
||||
public class TestRegistryInitializationHook : RegistryInitializationHookBase<TestRegistry, string>
|
||||
{
|
||||
/// <summary>
|
||||
/// 使用给定配置集合和目标阶段创建测试钩子。
|
||||
/// </summary>
|
||||
/// <param name="configs">测试期间要注册到目标注册表的配置值。</param>
|
||||
/// <param name="targetPhase">触发注册行为的架构阶段。</param>
|
||||
public TestRegistryInitializationHook(
|
||||
IEnumerable<string> configs,
|
||||
ArchitecturePhase targetPhase = ArchitecturePhase.AfterSystemInit)
|
||||
: base(configs, targetPhase)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将当前配置值写入测试注册表。
|
||||
/// </summary>
|
||||
/// <param name="registry">要接收配置值的测试注册表。</param>
|
||||
/// <param name="config">当前遍历到的配置值。</param>
|
||||
protected override void RegisterConfig(TestRegistry registry, string config)
|
||||
{
|
||||
registry.Register(config);
|
||||
}
|
||||
}
|
||||
@ -405,7 +405,7 @@ public class CommandCoroutineExtensionsTests
|
||||
CommandCoroutineExtensions.SendCommandAndWaitEventCoroutine<TestCommand, TestEvent>(
|
||||
contextAware,
|
||||
command,
|
||||
null,
|
||||
_ => { },
|
||||
-1.0f));
|
||||
}
|
||||
|
||||
@ -421,7 +421,7 @@ public class CommandCoroutineExtensionsTests
|
||||
// 设置上下文服务以返回null事件总线
|
||||
contextAware._mockContext
|
||||
.Setup(ctx => ctx.GetService<IEventBus>())
|
||||
.Returns((IEventBus?)null);
|
||||
.Returns((IEventBus)null!);
|
||||
|
||||
// 创建协程
|
||||
var coroutine = CommandCoroutineExtensions.SendCommandAndWaitEventCoroutine<TestCommand, TestEvent>(
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
using System;
|
||||
using GFramework.Core.Abstractions.Events;
|
||||
using GFramework.Core.Coroutine.Instructions;
|
||||
using GFramework.Core.Events;
|
||||
@ -8,25 +9,28 @@ namespace GFramework.Core.Tests.Coroutine
|
||||
[TestFixture]
|
||||
public class WaitForMultipleEventsTests
|
||||
{
|
||||
private IEventBus? _eventBus;
|
||||
|
||||
private IEventBus EventBus => _eventBus ?? throw new InvalidOperationException("EventBus has not been initialized.");
|
||||
|
||||
[SetUp]
|
||||
public void SetUp()
|
||||
{
|
||||
eventBus = new EventBus();
|
||||
_eventBus = new EventBus();
|
||||
}
|
||||
|
||||
[TearDown]
|
||||
public void TearDown()
|
||||
{
|
||||
(eventBus as IDisposable)?.Dispose();
|
||||
(EventBus as IDisposable)?.Dispose();
|
||||
_eventBus = null;
|
||||
}
|
||||
|
||||
private IEventBus eventBus;
|
||||
|
||||
[Test]
|
||||
public void Constructor_RegistersBothEventTypes()
|
||||
{
|
||||
// Arrange & Act
|
||||
var waitForMultipleEvents = new WaitForMultipleEvents<TestEvent1, TestEvent2>(eventBus);
|
||||
var waitForMultipleEvents = new WaitForMultipleEvents<TestEvent1, TestEvent2>(EventBus);
|
||||
|
||||
// Assert
|
||||
Assert.That(waitForMultipleEvents.IsDone, Is.False);
|
||||
@ -37,11 +41,11 @@ namespace GFramework.Core.Tests.Coroutine
|
||||
public async Task FirstEventWins_WhenBothEventsFired()
|
||||
{
|
||||
// Arrange
|
||||
var waitForMultipleEvents = new WaitForMultipleEvents<TestEvent1, TestEvent2>(eventBus);
|
||||
var waitForMultipleEvents = new WaitForMultipleEvents<TestEvent1, TestEvent2>(EventBus);
|
||||
|
||||
// Act
|
||||
eventBus.Send(new TestEvent1 { Data = "first_event" });
|
||||
eventBus.Send(new TestEvent2 { Data = "second_event" });
|
||||
EventBus.Send(new TestEvent1 { Data = "first_event" });
|
||||
EventBus.Send(new TestEvent2 { Data = "second_event" });
|
||||
|
||||
// Assert
|
||||
Assert.That(waitForMultipleEvents.IsDone, Is.True);
|
||||
@ -54,10 +58,10 @@ namespace GFramework.Core.Tests.Coroutine
|
||||
public async Task SecondEventWins_WhenOnlySecondEventFired()
|
||||
{
|
||||
// Arrange
|
||||
var waitForMultipleEvents = new WaitForMultipleEvents<TestEvent1, TestEvent2>(eventBus);
|
||||
var waitForMultipleEvents = new WaitForMultipleEvents<TestEvent1, TestEvent2>(EventBus);
|
||||
|
||||
// Act
|
||||
eventBus.Send(new TestEvent2 { Data = "second_event" });
|
||||
EventBus.Send(new TestEvent2 { Data = "second_event" });
|
||||
|
||||
// Assert
|
||||
Assert.That(waitForMultipleEvents.IsDone, Is.True);
|
||||
@ -70,11 +74,11 @@ namespace GFramework.Core.Tests.Coroutine
|
||||
public async Task FirstEventWins_WhenBothEventsFiredInReverseOrder()
|
||||
{
|
||||
// Arrange
|
||||
var waitForMultipleEvents = new WaitForMultipleEvents<TestEvent1, TestEvent2>(eventBus);
|
||||
var waitForMultipleEvents = new WaitForMultipleEvents<TestEvent1, TestEvent2>(EventBus);
|
||||
|
||||
// Act
|
||||
eventBus.Send(new TestEvent2 { Data = "second_event" });
|
||||
eventBus.Send(new TestEvent1 { Data = "first_event" });
|
||||
EventBus.Send(new TestEvent2 { Data = "second_event" });
|
||||
EventBus.Send(new TestEvent1 { Data = "first_event" });
|
||||
|
||||
// Assert
|
||||
Assert.That(waitForMultipleEvents.IsDone, Is.True);
|
||||
@ -88,10 +92,10 @@ namespace GFramework.Core.Tests.Coroutine
|
||||
public async Task MultipleEvents_AfterCompletion_DoNotOverrideState()
|
||||
{
|
||||
// Arrange
|
||||
var waitForMultipleEvents = new WaitForMultipleEvents<TestEvent1, TestEvent2>(eventBus);
|
||||
var waitForMultipleEvents = new WaitForMultipleEvents<TestEvent1, TestEvent2>(EventBus);
|
||||
|
||||
// Act - Fire first event
|
||||
eventBus.Send(new TestEvent1 { Data = "first_event" });
|
||||
EventBus.Send(new TestEvent1 { Data = "first_event" });
|
||||
|
||||
// Verify first event was processed
|
||||
Assert.That(waitForMultipleEvents.IsDone, Is.True);
|
||||
@ -99,7 +103,7 @@ namespace GFramework.Core.Tests.Coroutine
|
||||
Assert.That(waitForMultipleEvents.FirstEventData?.Data, Is.EqualTo("first_event"));
|
||||
|
||||
// Fire second event after completion
|
||||
eventBus.Send(new TestEvent2 { Data = "second_event" });
|
||||
EventBus.Send(new TestEvent2 { Data = "second_event" });
|
||||
|
||||
// Assert - The state should not change
|
||||
Assert.That(waitForMultipleEvents.IsDone, Is.True);
|
||||
@ -113,13 +117,13 @@ namespace GFramework.Core.Tests.Coroutine
|
||||
public async Task Disposal_PreventsFurtherEventHandling()
|
||||
{
|
||||
// Arrange
|
||||
var waitForMultipleEvents = new WaitForMultipleEvents<TestEvent1, TestEvent2>(eventBus);
|
||||
var waitForMultipleEvents = new WaitForMultipleEvents<TestEvent1, TestEvent2>(EventBus);
|
||||
|
||||
// Act - Dispose the instance
|
||||
waitForMultipleEvents.Dispose();
|
||||
|
||||
// Fire an event after disposal
|
||||
eventBus.Send(new TestEvent1 { Data = "after_disposal" });
|
||||
EventBus.Send(new TestEvent1 { Data = "after_disposal" });
|
||||
|
||||
// Assert - Event should not be processed due to disposal
|
||||
// Since we disposed, no event data should be captured
|
||||
@ -138,4 +142,4 @@ namespace GFramework.Core.Tests.Coroutine
|
||||
public string Data { get; set; } = string.Empty;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
6
GFramework.Core.Tests/Events/EmptyEvent.cs
Normal file
6
GFramework.Core.Tests/Events/EmptyEvent.cs
Normal file
@ -0,0 +1,6 @@
|
||||
namespace GFramework.Core.Tests.Events;
|
||||
|
||||
/// <summary>
|
||||
/// 表示不携带任何负载的空测试事件。
|
||||
/// </summary>
|
||||
public sealed class EmptyEvent;
|
||||
@ -1,3 +1,4 @@
|
||||
using System.Runtime.InteropServices;
|
||||
using GFramework.Core.Abstractions.Events;
|
||||
using GFramework.Core.Events;
|
||||
using Moq;
|
||||
@ -303,6 +304,7 @@ public class EventListenerScopeTests
|
||||
/// <summary>
|
||||
/// 测试用的结构体事件
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Auto)]
|
||||
private struct StructEvent
|
||||
{
|
||||
public int Id { get; set; }
|
||||
@ -318,4 +320,4 @@ public class EventListenerScopeTests
|
||||
using var scope = new EventListenerScope<TestEvent>(eventBusMock.Object);
|
||||
// 作用域内部不验证
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,8 +1,12 @@
|
||||
namespace GFramework.Core.Tests.Events;
|
||||
|
||||
/// <summary>
|
||||
/// 表示包含整型载荷的测试事件。
|
||||
/// </summary>
|
||||
public sealed class TestEvent
|
||||
{
|
||||
/// <summary>
|
||||
/// 获取初始化阶段写入的接收值。
|
||||
/// </summary>
|
||||
public int ReceivedValue { get; init; }
|
||||
}
|
||||
|
||||
public sealed class EmptyEvent;
|
||||
@ -419,44 +419,3 @@ public class LoggerTests
|
||||
Assert.That(fatalLogger.Logs[0].Level, Is.EqualTo(LogLevel.Fatal));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 测试用的日志记录器实现类,继承自AbstractLogger
|
||||
/// </summary>
|
||||
public sealed class TestLogger : AbstractLogger
|
||||
{
|
||||
private readonly List<LogEntry> _logs = new();
|
||||
|
||||
/// <summary>
|
||||
/// 初始化TestLogger的新实例
|
||||
/// </summary>
|
||||
/// <param name="name">日志记录器的名称,默认为null</param>
|
||||
/// <param name="minLevel">最小日志级别,默认为LogLevel.Info</param>
|
||||
public TestLogger(string? name = null, LogLevel minLevel = LogLevel.Info) : base(name, minLevel)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取按写入顺序保存的日志条目只读视图
|
||||
/// </summary>
|
||||
public IReadOnlyList<LogEntry> Logs => _logs;
|
||||
|
||||
/// <summary>
|
||||
/// 将日志信息写入内部存储
|
||||
/// </summary>
|
||||
/// <param name="level">日志级别</param>
|
||||
/// <param name="message">日志消息</param>
|
||||
/// <param name="exception">相关异常(可选)</param>
|
||||
protected override void Write(LogLevel level, string message, Exception? exception)
|
||||
{
|
||||
_logs.Add(new LogEntry(level, message, exception));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 表示单个日志条目的记录类型
|
||||
/// </summary>
|
||||
/// <param name="Level">日志级别</param>
|
||||
/// <param name="Message">日志消息</param>
|
||||
/// <param name="Exception">相关异常(可选)</param>
|
||||
public sealed record LogEntry(LogLevel Level, string Message, Exception? Exception);
|
||||
}
|
||||
|
||||
@ -307,8 +307,9 @@ public class LoggingConfigurationTests
|
||||
}}";
|
||||
|
||||
var config = LoggingConfigurationLoader.LoadFromJsonString(json);
|
||||
Assert.That(config.Appenders[0].Filter, Is.Not.Null);
|
||||
Assert.That(config.Appenders[0].Filter.Type, Is.EqualTo("Namespace"));
|
||||
var filter = config.Appenders[0].Filter;
|
||||
Assert.That(filter, Is.Not.Null);
|
||||
Assert.That(filter!.Type, Is.EqualTo("Namespace"));
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
||||
64
GFramework.Core.Tests/Logging/TestLogger.cs
Normal file
64
GFramework.Core.Tests/Logging/TestLogger.cs
Normal file
@ -0,0 +1,64 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using GFramework.Core.Abstractions.Logging;
|
||||
using GFramework.Core.Logging;
|
||||
|
||||
namespace GFramework.Core.Tests.Logging;
|
||||
|
||||
/// <summary>
|
||||
/// 表示供日志相关测试复用的内存日志记录器。
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 并发写入会通过内部锁串行化;<see cref="Logs" /> 每次返回快照,避免断言观察到正在被修改的可变集合。
|
||||
/// </remarks>
|
||||
public sealed class TestLogger : AbstractLogger
|
||||
{
|
||||
private readonly List<LogEntry> _logs = new();
|
||||
private readonly Lock _sync = new();
|
||||
|
||||
/// <summary>
|
||||
/// 初始化 <see cref="TestLogger" /> 的新实例。
|
||||
/// </summary>
|
||||
/// <param name="name">日志记录器的名称;未指定时沿用基类默认行为。</param>
|
||||
/// <param name="minLevel">允许写入的最小日志级别。</param>
|
||||
public TestLogger(string? name = null, LogLevel minLevel = LogLevel.Info) : base(name, minLevel)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取按写入顺序保存的日志条目快照。
|
||||
/// </summary>
|
||||
public IReadOnlyList<LogEntry> Logs
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (_sync)
|
||||
{
|
||||
return _logs.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将日志信息追加到内存列表,供断言读取。
|
||||
/// </summary>
|
||||
/// <param name="level">日志级别。</param>
|
||||
/// <param name="message">日志消息。</param>
|
||||
/// <param name="exception">相关异常;没有异常时为 <see langword="null" />。</param>
|
||||
protected override void Write(LogLevel level, string message, Exception? exception)
|
||||
{
|
||||
lock (_sync)
|
||||
{
|
||||
_logs.Add(new LogEntry(level, message, exception));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 表示单个日志条目的不可变快照。
|
||||
/// </summary>
|
||||
/// <param name="Level">日志级别。</param>
|
||||
/// <param name="Message">日志消息。</param>
|
||||
/// <param name="Exception">相关异常;没有异常时为 <see langword="null" />。</param>
|
||||
public sealed record LogEntry(LogLevel Level, string Message, Exception? Exception);
|
||||
}
|
||||
@ -1,58 +1,8 @@
|
||||
using System.IO;
|
||||
using GFramework.Core.Abstractions.Resource;
|
||||
using GFramework.Core.Resource;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace GFramework.Core.Tests.Resource;
|
||||
|
||||
/// <summary>
|
||||
/// 测试用的简单资源类
|
||||
/// </summary>
|
||||
public class TestResource
|
||||
{
|
||||
public string Content { get; set; } = string.Empty;
|
||||
public bool IsDisposed { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 测试用的资源加载器
|
||||
/// </summary>
|
||||
public class TestResourceLoader : IResourceLoader<TestResource>
|
||||
{
|
||||
private readonly Dictionary<string, string> _resourceData = new();
|
||||
|
||||
public TestResource Load(string path)
|
||||
{
|
||||
if (_resourceData.TryGetValue(path, out var content))
|
||||
{
|
||||
return new TestResource { Content = content };
|
||||
}
|
||||
|
||||
throw new FileNotFoundException($"Resource not found: {path}");
|
||||
}
|
||||
|
||||
public async Task<TestResource> LoadAsync(string path)
|
||||
{
|
||||
await Task.Delay(10).ConfigureAwait(false); // 模拟异步加载
|
||||
return Load(path);
|
||||
}
|
||||
|
||||
public void Unload(TestResource resource)
|
||||
{
|
||||
resource.IsDisposed = true;
|
||||
}
|
||||
|
||||
public bool CanLoad(string path)
|
||||
{
|
||||
return _resourceData.ContainsKey(path);
|
||||
}
|
||||
|
||||
public void AddTestData(string path, string content)
|
||||
{
|
||||
_resourceData[path] = content;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// ResourceManager 功能测试类
|
||||
/// </summary>
|
||||
|
||||
17
GFramework.Core.Tests/Resource/TestResource.cs
Normal file
17
GFramework.Core.Tests/Resource/TestResource.cs
Normal file
@ -0,0 +1,17 @@
|
||||
namespace GFramework.Core.Tests.Resource;
|
||||
|
||||
/// <summary>
|
||||
/// 表示 ResourceManager 测试使用的简单资源对象。
|
||||
/// </summary>
|
||||
public class TestResource
|
||||
{
|
||||
/// <summary>
|
||||
/// 获取或设置资源内容。
|
||||
/// </summary>
|
||||
public string Content { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// 获取或设置一个值,指示资源是否已经被测试加载器标记为已卸载。
|
||||
/// </summary>
|
||||
public bool IsDisposed { get; set; }
|
||||
}
|
||||
88
GFramework.Core.Tests/Resource/TestResourceLoader.cs
Normal file
88
GFramework.Core.Tests/Resource/TestResourceLoader.cs
Normal file
@ -0,0 +1,88 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using GFramework.Core.Abstractions.Resource;
|
||||
|
||||
namespace GFramework.Core.Tests.Resource;
|
||||
|
||||
/// <summary>
|
||||
/// 为 ResourceManager 测试提供可控数据源的资源加载器。
|
||||
/// </summary>
|
||||
public class TestResourceLoader : IResourceLoader<TestResource>
|
||||
{
|
||||
private readonly Dictionary<string, string> _resourceData = new(StringComparer.Ordinal);
|
||||
|
||||
/// <summary>
|
||||
/// 同步加载指定路径的测试资源。
|
||||
/// </summary>
|
||||
/// <param name="path">资源路径。</param>
|
||||
/// <returns>加载得到的测试资源。</returns>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="path" /> 为 <see langword="null" />。</exception>
|
||||
/// <exception cref="ArgumentException"><paramref name="path" /> 为空字符串。</exception>
|
||||
/// <exception cref="FileNotFoundException">指定路径的测试资源不存在。</exception>
|
||||
public TestResource Load(string path)
|
||||
{
|
||||
ArgumentException.ThrowIfNullOrEmpty(path);
|
||||
|
||||
if (_resourceData.TryGetValue(path, out var content))
|
||||
{
|
||||
return new TestResource { Content = content };
|
||||
}
|
||||
|
||||
throw new FileNotFoundException($"Resource not found: {path}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 异步加载指定路径的测试资源。
|
||||
/// </summary>
|
||||
/// <param name="path">资源路径。</param>
|
||||
/// <returns>加载得到的测试资源任务。</returns>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="path" /> 为 <see langword="null" />。</exception>
|
||||
/// <exception cref="ArgumentException"><paramref name="path" /> 为空字符串。</exception>
|
||||
/// <exception cref="FileNotFoundException">指定路径的测试资源不存在。</exception>
|
||||
public Task<TestResource> LoadAsync(string path)
|
||||
{
|
||||
return Task.FromResult(Load(path));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 卸载已加载的测试资源。
|
||||
/// </summary>
|
||||
/// <param name="resource">要标记为已释放的资源。</param>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="resource" /> 为 <see langword="null" />。</exception>
|
||||
public void Unload(TestResource resource)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(resource);
|
||||
resource.IsDisposed = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 判断当前加载器是否包含指定路径的测试资源。
|
||||
/// </summary>
|
||||
/// <param name="path">资源路径。</param>
|
||||
/// <returns>存在对应测试资源时返回 <see langword="true" />;否则返回 <see langword="false" />。</returns>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="path" /> 为 <see langword="null" />。</exception>
|
||||
/// <exception cref="ArgumentException"><paramref name="path" /> 为空字符串。</exception>
|
||||
public bool CanLoad(string path)
|
||||
{
|
||||
ArgumentException.ThrowIfNullOrEmpty(path);
|
||||
return _resourceData.ContainsKey(path);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 向测试加载器注册一条可返回的资源数据。
|
||||
/// </summary>
|
||||
/// <param name="path">资源路径。</param>
|
||||
/// <param name="content">资源内容。</param>
|
||||
/// <exception cref="ArgumentNullException">
|
||||
/// <paramref name="path" /> 或 <paramref name="content" /> 为 <see langword="null" />。
|
||||
/// </exception>
|
||||
/// <exception cref="ArgumentException"><paramref name="path" /> 为空字符串。</exception>
|
||||
public void AddTestData(string path, string content)
|
||||
{
|
||||
ArgumentException.ThrowIfNullOrEmpty(path);
|
||||
ArgumentNullException.ThrowIfNull(content);
|
||||
_resourceData[path] = content;
|
||||
}
|
||||
}
|
||||
@ -1,3 +1,4 @@
|
||||
using System;
|
||||
using GFramework.Core.Abstractions.Architectures;
|
||||
using GFramework.Core.Abstractions.Enums;
|
||||
using GFramework.Core.Abstractions.Utility;
|
||||
@ -33,12 +34,30 @@ public abstract class RegistryInitializationHookBase<TRegistry, TConfig> : IArch
|
||||
/// </summary>
|
||||
/// <param name="phase">当前的架构阶段</param>
|
||||
/// <param name="architecture">相关的架构实例</param>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="architecture" /> 为 <see langword="null" />。</exception>
|
||||
/// <remarks>
|
||||
/// 当目标注册表未被装入当前架构上下文时,该钩子会保持 no-op,
|
||||
/// 以便同一组配置可以安全复用于不包含该注册表的测试或裁剪场景。
|
||||
/// </remarks>
|
||||
public void OnPhase(ArchitecturePhase phase, IArchitecture architecture)
|
||||
{
|
||||
if (phase != _targetPhase) return;
|
||||
ArgumentNullException.ThrowIfNull(architecture);
|
||||
|
||||
var registry = architecture.Context.GetUtility<TRegistry>();
|
||||
if (registry == null) return;
|
||||
if (phase != _targetPhase)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
TRegistry registry;
|
||||
|
||||
try
|
||||
{
|
||||
registry = architecture.Context.GetUtility<TRegistry>();
|
||||
}
|
||||
catch (InvalidOperationException)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var config in _configs)
|
||||
{
|
||||
@ -52,4 +71,4 @@ public abstract class RegistryInitializationHookBase<TRegistry, TConfig> : IArch
|
||||
/// <param name="registry">注册表实例</param>
|
||||
/// <param name="config">配置项</param>
|
||||
protected abstract void RegisterConfig(TRegistry registry, TConfig config);
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,23 @@
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using GFramework.Cqrs.Abstractions.Cqrs;
|
||||
|
||||
namespace GFramework.Cqrs.Tests.Cqrs;
|
||||
|
||||
/// <summary>
|
||||
/// 名称排序上应先于 Zeta 处理器执行的通知处理器。
|
||||
/// </summary>
|
||||
internal sealed class AlphaDeterministicNotificationHandler : INotificationHandler<DeterministicOrderNotification>
|
||||
{
|
||||
/// <summary>
|
||||
/// 记录当前处理器已执行。
|
||||
/// </summary>
|
||||
/// <param name="notification">通知实例。</param>
|
||||
/// <param name="cancellationToken">取消令牌。</param>
|
||||
/// <returns>已完成任务。</returns>
|
||||
public ValueTask Handle(DeterministicOrderNotification notification, CancellationToken cancellationToken)
|
||||
{
|
||||
DeterministicNotificationHandlerState.InvocationOrder.Add(nameof(AlphaDeterministicNotificationHandler));
|
||||
return ValueTask.CompletedTask;
|
||||
}
|
||||
}
|
||||
82
GFramework.Cqrs.Tests/Cqrs/CapturingLoggerFactoryProvider.cs
Normal file
82
GFramework.Cqrs.Tests/Cqrs/CapturingLoggerFactoryProvider.cs
Normal file
@ -0,0 +1,82 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using GFramework.Core.Abstractions.Logging;
|
||||
using GFramework.Core.Logging;
|
||||
using GFramework.Cqrs.Tests.Logging;
|
||||
|
||||
namespace GFramework.Cqrs.Tests.Cqrs;
|
||||
|
||||
/// <summary>
|
||||
/// 为 CQRS 注册测试捕获真实启动路径中创建的日志记录器。
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 处理器注册入口会分别为测试运行时、容器和注册器创建日志器。
|
||||
/// 该提供程序统一保留这些测试日志器,以便断言警告是否经由公开入口真正发出。
|
||||
/// 并发创建日志器时会通过内部锁串行化,<see cref="Loggers" /> 每次返回快照,避免调用方观察到可变集合。
|
||||
/// </remarks>
|
||||
internal sealed class CapturingLoggerFactoryProvider : ILoggerFactoryProvider
|
||||
{
|
||||
private readonly List<TestLogger> _loggers = [];
|
||||
private LogLevel _minLevel;
|
||||
private readonly Lock _sync = new();
|
||||
|
||||
/// <summary>
|
||||
/// 使用指定的最小日志级别初始化一个新的捕获型日志工厂提供程序。
|
||||
/// </summary>
|
||||
/// <param name="minLevel">要应用到新建测试日志器的最小日志级别。</param>
|
||||
public CapturingLoggerFactoryProvider(LogLevel minLevel = LogLevel.Info)
|
||||
{
|
||||
_minLevel = minLevel;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取通过当前提供程序创建的全部测试日志器快照。
|
||||
/// </summary>
|
||||
public IReadOnlyList<TestLogger> Loggers
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (_sync)
|
||||
{
|
||||
return _loggers.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取或设置新建测试日志器的最小日志级别。
|
||||
/// </summary>
|
||||
public LogLevel MinLevel
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (_sync)
|
||||
{
|
||||
return _minLevel;
|
||||
}
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
lock (_sync)
|
||||
{
|
||||
_minLevel = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建一个测试日志器并将其纳入捕获集合。
|
||||
/// </summary>
|
||||
/// <param name="name">日志记录器名称。</param>
|
||||
/// <returns>用于后续断言的测试日志器。</returns>
|
||||
public ILogger CreateLogger(string name)
|
||||
{
|
||||
lock (_sync)
|
||||
{
|
||||
var logger = new TestLogger(name, _minLevel);
|
||||
_loggers.Add(logger);
|
||||
return logger;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -224,124 +224,8 @@ internal sealed class CqrsDispatcherCacheTests
|
||||
/// </summary>
|
||||
private static async Task DrainAsync<T>(IAsyncEnumerable<T> stream)
|
||||
{
|
||||
await foreach (var _ in stream)
|
||||
await foreach (var _ in stream.ConfigureAwait(false))
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 用于验证 request 服务类型缓存的测试请求。
|
||||
/// </summary>
|
||||
internal sealed record DispatcherCacheRequest : IRequest<int>;
|
||||
|
||||
/// <summary>
|
||||
/// 用于验证 notification 服务类型缓存的测试通知。
|
||||
/// </summary>
|
||||
internal sealed record DispatcherCacheNotification : INotification;
|
||||
|
||||
/// <summary>
|
||||
/// 用于验证 stream 服务类型缓存的测试请求。
|
||||
/// </summary>
|
||||
internal sealed record DispatcherCacheStreamRequest : IStreamRequest<int>;
|
||||
|
||||
/// <summary>
|
||||
/// 用于验证 pipeline invoker 缓存的测试请求。
|
||||
/// </summary>
|
||||
internal sealed record DispatcherPipelineCacheRequest : IRequest<int>;
|
||||
|
||||
/// <summary>
|
||||
/// 用于验证按响应类型分层 request invoker 缓存的测试请求。
|
||||
/// </summary>
|
||||
internal sealed record DispatcherStringCacheRequest : IRequest<string>;
|
||||
|
||||
/// <summary>
|
||||
/// 处理 <see cref="DispatcherCacheRequest" />。
|
||||
/// </summary>
|
||||
internal sealed class DispatcherCacheRequestHandler : IRequestHandler<DispatcherCacheRequest, int>
|
||||
{
|
||||
/// <summary>
|
||||
/// 返回固定结果,供缓存测试验证 dispatcher 请求路径。
|
||||
/// </summary>
|
||||
public ValueTask<int> Handle(DispatcherCacheRequest request, CancellationToken cancellationToken)
|
||||
{
|
||||
return ValueTask.FromResult(1);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 处理 <see cref="DispatcherCacheNotification" />。
|
||||
/// </summary>
|
||||
internal sealed class DispatcherCacheNotificationHandler : INotificationHandler<DispatcherCacheNotification>
|
||||
{
|
||||
/// <summary>
|
||||
/// 消费通知,不执行额外副作用。
|
||||
/// </summary>
|
||||
public ValueTask Handle(DispatcherCacheNotification notification, CancellationToken cancellationToken)
|
||||
{
|
||||
return ValueTask.CompletedTask;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 处理 <see cref="DispatcherCacheStreamRequest" />。
|
||||
/// </summary>
|
||||
internal sealed class DispatcherCacheStreamHandler : IStreamRequestHandler<DispatcherCacheStreamRequest, int>
|
||||
{
|
||||
/// <summary>
|
||||
/// 返回一个最小流,供缓存测试命中 stream 分发路径。
|
||||
/// </summary>
|
||||
public async IAsyncEnumerable<int> Handle(
|
||||
DispatcherCacheStreamRequest request,
|
||||
[EnumeratorCancellation] CancellationToken cancellationToken)
|
||||
{
|
||||
yield return 1;
|
||||
await Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 处理 <see cref="DispatcherPipelineCacheRequest" />。
|
||||
/// </summary>
|
||||
internal sealed class DispatcherPipelineCacheRequestHandler : IRequestHandler<DispatcherPipelineCacheRequest, int>
|
||||
{
|
||||
/// <summary>
|
||||
/// 返回固定结果,供 pipeline 缓存测试使用。
|
||||
/// </summary>
|
||||
public ValueTask<int> Handle(DispatcherPipelineCacheRequest request, CancellationToken cancellationToken)
|
||||
{
|
||||
return ValueTask.FromResult(2);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 处理 <see cref="DispatcherStringCacheRequest" />。
|
||||
/// </summary>
|
||||
internal sealed class DispatcherStringCacheRequestHandler : IRequestHandler<DispatcherStringCacheRequest, string>
|
||||
{
|
||||
/// <summary>
|
||||
/// 返回固定字符串,供按响应类型缓存测试验证 string 路径。
|
||||
/// </summary>
|
||||
public ValueTask<string> Handle(DispatcherStringCacheRequest request, CancellationToken cancellationToken)
|
||||
{
|
||||
return ValueTask.FromResult("dispatcher-cache");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 为 <see cref="DispatcherPipelineCacheRequest" /> 提供最小 pipeline 行为,
|
||||
/// 用于命中 dispatcher 的 pipeline invoker 缓存分支。
|
||||
/// </summary>
|
||||
internal sealed class DispatcherPipelineCacheBehavior : IPipelineBehavior<DispatcherPipelineCacheRequest, int>
|
||||
{
|
||||
/// <summary>
|
||||
/// 直接转发到下一个处理器。
|
||||
/// </summary>
|
||||
public ValueTask<int> Handle(
|
||||
DispatcherPipelineCacheRequest request,
|
||||
MessageHandlerDelegate<DispatcherPipelineCacheRequest, int> next,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
return next(request, cancellationToken);
|
||||
}
|
||||
}
|
||||
|
||||
@ -216,6 +216,27 @@ internal sealed class CqrsHandlerRegistrarTests
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证捕获型日志工厂在更新最小日志级别后,会将新值应用到后续创建的日志器。
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void CapturingLoggerFactoryProvider_Should_Apply_Updated_MinLevel_To_Subsequent_Loggers()
|
||||
{
|
||||
var provider = new CapturingLoggerFactoryProvider(LogLevel.Warning);
|
||||
var warningLogger = (TestLogger)provider.CreateLogger("warning");
|
||||
|
||||
provider.MinLevel = LogLevel.Debug;
|
||||
|
||||
var debugLogger = (TestLogger)provider.CreateLogger("debug");
|
||||
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(warningLogger.IsDebugEnabled(), Is.False);
|
||||
Assert.That(debugLogger.IsDebugEnabled(), Is.True);
|
||||
Assert.That(provider.Loggers, Has.Count.EqualTo(2));
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证当生成注册器提供精确 fallback 类型名时,运行时会定向补扫剩余 handlers,
|
||||
/// 而不是重新枚举整个程序集的类型列表。
|
||||
@ -614,230 +635,3 @@ internal sealed class CqrsHandlerRegistrarTests
|
||||
.GetType("GFramework.Cqrs.Internal.CqrsHandlerRegistrar", throwOnError: true)!;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 记录确定性通知处理器的实际执行顺序。
|
||||
/// </summary>
|
||||
internal static class DeterministicNotificationHandlerState
|
||||
{
|
||||
/// <summary>
|
||||
/// 获取当前测试中的通知处理器执行顺序。
|
||||
/// </summary>
|
||||
public static List<string> InvocationOrder { get; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// 重置共享的执行顺序状态。
|
||||
/// </summary>
|
||||
public static void Reset()
|
||||
{
|
||||
InvocationOrder.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 用于验证同一通知的多个处理器是否按稳定顺序执行。
|
||||
/// </summary>
|
||||
internal sealed record DeterministicOrderNotification : INotification;
|
||||
|
||||
/// <summary>
|
||||
/// 故意放在 Alpha 之前声明,用于验证注册器不会依赖源码声明顺序。
|
||||
/// </summary>
|
||||
internal sealed class ZetaDeterministicNotificationHandler : INotificationHandler<DeterministicOrderNotification>
|
||||
{
|
||||
/// <summary>
|
||||
/// 记录当前处理器已执行。
|
||||
/// </summary>
|
||||
/// <param name="notification">通知实例。</param>
|
||||
/// <param name="cancellationToken">取消令牌。</param>
|
||||
/// <returns>已完成任务。</returns>
|
||||
public ValueTask Handle(DeterministicOrderNotification notification, CancellationToken cancellationToken)
|
||||
{
|
||||
DeterministicNotificationHandlerState.InvocationOrder.Add(nameof(ZetaDeterministicNotificationHandler));
|
||||
return ValueTask.CompletedTask;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 名称排序上应先于 Zeta 处理器执行的通知处理器。
|
||||
/// </summary>
|
||||
internal sealed class AlphaDeterministicNotificationHandler : INotificationHandler<DeterministicOrderNotification>
|
||||
{
|
||||
/// <summary>
|
||||
/// 记录当前处理器已执行。
|
||||
/// </summary>
|
||||
/// <param name="notification">通知实例。</param>
|
||||
/// <param name="cancellationToken">取消令牌。</param>
|
||||
/// <returns>已完成任务。</returns>
|
||||
public ValueTask Handle(DeterministicOrderNotification notification, CancellationToken cancellationToken)
|
||||
{
|
||||
DeterministicNotificationHandlerState.InvocationOrder.Add(nameof(AlphaDeterministicNotificationHandler));
|
||||
return ValueTask.CompletedTask;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 为 CQRS 注册测试捕获真实启动路径中创建的日志记录器。
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 处理器注册入口会分别为测试运行时、容器和注册器创建日志器。
|
||||
/// 该提供程序统一保留这些测试日志器,以便断言警告是否经由公开入口真正发出。
|
||||
/// </remarks>
|
||||
internal sealed class CapturingLoggerFactoryProvider : ILoggerFactoryProvider
|
||||
{
|
||||
private readonly List<TestLogger> _loggers = [];
|
||||
|
||||
/// <summary>
|
||||
/// 使用指定的最小日志级别初始化一个新的捕获型日志工厂提供程序。
|
||||
/// </summary>
|
||||
/// <param name="minLevel">要应用到新建测试日志器的最小日志级别。</param>
|
||||
public CapturingLoggerFactoryProvider(LogLevel minLevel = LogLevel.Info)
|
||||
{
|
||||
MinLevel = minLevel;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取通过当前提供程序创建的全部测试日志器。
|
||||
/// </summary>
|
||||
public IReadOnlyList<TestLogger> Loggers => _loggers;
|
||||
|
||||
/// <summary>
|
||||
/// 获取或设置新建测试日志器的最小日志级别。
|
||||
/// </summary>
|
||||
public LogLevel MinLevel { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 创建一个测试日志器并将其纳入捕获集合。
|
||||
/// </summary>
|
||||
/// <param name="name">日志记录器名称。</param>
|
||||
/// <returns>用于后续断言的测试日志器。</returns>
|
||||
public ILogger CreateLogger(string name)
|
||||
{
|
||||
var logger = new TestLogger(name, MinLevel);
|
||||
_loggers.Add(logger);
|
||||
return logger;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 用于验证生成注册器路径的通知消息。
|
||||
/// </summary>
|
||||
internal sealed record GeneratedRegistryNotification : INotification;
|
||||
|
||||
/// <summary>
|
||||
/// 由模拟的源码生成注册器显式注册的通知处理器。
|
||||
/// </summary>
|
||||
internal sealed class GeneratedRegistryNotificationHandler : INotificationHandler<GeneratedRegistryNotification>
|
||||
{
|
||||
/// <summary>
|
||||
/// 处理生成注册器测试中的通知。
|
||||
/// </summary>
|
||||
/// <param name="notification">通知实例。</param>
|
||||
/// <param name="cancellationToken">取消令牌。</param>
|
||||
/// <returns>已完成任务。</returns>
|
||||
public ValueTask Handle(GeneratedRegistryNotification notification, CancellationToken cancellationToken)
|
||||
{
|
||||
return ValueTask.CompletedTask;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 模拟源码生成器为某个程序集生成的 CQRS 处理器注册器。
|
||||
/// </summary>
|
||||
internal sealed class GeneratedNotificationHandlerRegistry : ICqrsHandlerRegistry
|
||||
{
|
||||
/// <summary>
|
||||
/// 将测试通知处理器注册到目标服务集合。
|
||||
/// </summary>
|
||||
/// <param name="services">承载处理器映射的服务集合。</param>
|
||||
/// <param name="logger">用于记录注册诊断的日志器。</param>
|
||||
public void Register(IServiceCollection services, ILogger logger)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(services);
|
||||
ArgumentNullException.ThrowIfNull(logger);
|
||||
|
||||
services.AddTransient(
|
||||
typeof(INotificationHandler<GeneratedRegistryNotification>),
|
||||
typeof(GeneratedRegistryNotificationHandler));
|
||||
logger.Debug(
|
||||
$"Registered CQRS handler {typeof(GeneratedRegistryNotificationHandler).FullName} as {typeof(INotificationHandler<GeneratedRegistryNotification>).FullName}.");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 用于验证“生成注册器 + reflection fallback”组合路径的私有嵌套处理器容器。
|
||||
/// </summary>
|
||||
internal sealed class ReflectionFallbackNotificationContainer
|
||||
{
|
||||
/// <summary>
|
||||
/// 获取仅能通过反射补扫接入的私有嵌套处理器类型。
|
||||
/// </summary>
|
||||
public static Type ReflectionOnlyHandlerType => typeof(ReflectionOnlyGeneratedRegistryNotificationHandler);
|
||||
|
||||
private sealed class ReflectionOnlyGeneratedRegistryNotificationHandler
|
||||
: INotificationHandler<GeneratedRegistryNotification>
|
||||
{
|
||||
/// <summary>
|
||||
/// 处理测试通知。
|
||||
/// </summary>
|
||||
/// <param name="notification">通知实例。</param>
|
||||
/// <param name="cancellationToken">取消令牌。</param>
|
||||
/// <returns>已完成任务。</returns>
|
||||
public ValueTask Handle(GeneratedRegistryNotification notification, CancellationToken cancellationToken)
|
||||
{
|
||||
return ValueTask.CompletedTask;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 模拟局部生成注册器场景中,仅注册“可由生成代码直接引用”的那部分 handlers。
|
||||
/// </summary>
|
||||
internal sealed class PartialGeneratedNotificationHandlerRegistry : ICqrsHandlerRegistry
|
||||
{
|
||||
/// <summary>
|
||||
/// 将生成路径可见的通知处理器注册到目标服务集合。
|
||||
/// </summary>
|
||||
/// <param name="services">承载处理器映射的服务集合。</param>
|
||||
/// <param name="logger">用于记录注册诊断的日志器。</param>
|
||||
public void Register(IServiceCollection services, ILogger logger)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(services);
|
||||
ArgumentNullException.ThrowIfNull(logger);
|
||||
|
||||
services.AddTransient(
|
||||
typeof(INotificationHandler<GeneratedRegistryNotification>),
|
||||
typeof(GeneratedRegistryNotificationHandler));
|
||||
logger.Debug(
|
||||
$"Registered CQRS handler {typeof(GeneratedRegistryNotificationHandler).FullName} as {typeof(INotificationHandler<GeneratedRegistryNotification>).FullName}.");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 模拟生成注册器使用私有无参构造器的场景,验证运行时仍可通过缓存工厂激活它。
|
||||
/// </summary>
|
||||
internal sealed class PrivateConstructorNotificationHandlerRegistry : ICqrsHandlerRegistry
|
||||
{
|
||||
/// <summary>
|
||||
/// 初始化一个新的私有生成注册器实例。
|
||||
/// </summary>
|
||||
private PrivateConstructorNotificationHandlerRegistry()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将测试通知处理器注册到目标服务集合。
|
||||
/// </summary>
|
||||
/// <param name="services">承载处理器映射的服务集合。</param>
|
||||
/// <param name="logger">用于记录注册诊断的日志器。</param>
|
||||
public void Register(IServiceCollection services, ILogger logger)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(services);
|
||||
ArgumentNullException.ThrowIfNull(logger);
|
||||
|
||||
services.AddTransient(
|
||||
typeof(INotificationHandler<GeneratedRegistryNotification>),
|
||||
typeof(GeneratedRegistryNotificationHandler));
|
||||
logger.Debug(
|
||||
$"Registered CQRS handler {typeof(GeneratedRegistryNotificationHandler).FullName} as {typeof(INotificationHandler<GeneratedRegistryNotification>).FullName}.");
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,29 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace GFramework.Cqrs.Tests.Cqrs;
|
||||
|
||||
/// <summary>
|
||||
/// 记录确定性通知处理器的实际执行顺序。
|
||||
/// </summary>
|
||||
internal static class DeterministicNotificationHandlerState
|
||||
{
|
||||
/// <summary>
|
||||
/// 获取当前测试中的通知处理器执行顺序。
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 该集合仅供顺序测试断言使用,不提供并发安全保证。
|
||||
/// 若多个处理器在并行测试中同时写入,调用方可能观察到竞争条件或未定义顺序。
|
||||
/// </remarks>
|
||||
public static List<string> InvocationOrder { get; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// 重置共享的执行顺序状态。
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 该方法只支持在单线程测试准备阶段调用;并发调用会与 <see cref="InvocationOrder" /> 的直接写入互相竞争。
|
||||
/// </remarks>
|
||||
public static void Reset()
|
||||
{
|
||||
InvocationOrder.Clear();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,8 @@
|
||||
using GFramework.Cqrs.Abstractions.Cqrs;
|
||||
|
||||
namespace GFramework.Cqrs.Tests.Cqrs;
|
||||
|
||||
/// <summary>
|
||||
/// 用于验证同一通知的多个处理器是否按稳定顺序执行。
|
||||
/// </summary>
|
||||
internal sealed record DeterministicOrderNotification : INotification;
|
||||
@ -0,0 +1,8 @@
|
||||
using GFramework.Cqrs.Abstractions.Cqrs;
|
||||
|
||||
namespace GFramework.Cqrs.Tests.Cqrs;
|
||||
|
||||
/// <summary>
|
||||
/// 用于验证 notification 服务类型缓存的测试通知。
|
||||
/// </summary>
|
||||
internal sealed record DispatcherCacheNotification : INotification;
|
||||
@ -0,0 +1,21 @@
|
||||
using System.Threading;
|
||||
using GFramework.Cqrs.Abstractions.Cqrs;
|
||||
|
||||
namespace GFramework.Cqrs.Tests.Cqrs;
|
||||
|
||||
/// <summary>
|
||||
/// 处理 <see cref="DispatcherCacheNotification" />。
|
||||
/// </summary>
|
||||
internal sealed class DispatcherCacheNotificationHandler : INotificationHandler<DispatcherCacheNotification>
|
||||
{
|
||||
/// <summary>
|
||||
/// 消费通知,不执行额外副作用。
|
||||
/// </summary>
|
||||
/// <param name="notification">当前通知。</param>
|
||||
/// <param name="cancellationToken">取消令牌。</param>
|
||||
/// <returns>已完成任务。</returns>
|
||||
public ValueTask Handle(DispatcherCacheNotification notification, CancellationToken cancellationToken)
|
||||
{
|
||||
return ValueTask.CompletedTask;
|
||||
}
|
||||
}
|
||||
8
GFramework.Cqrs.Tests/Cqrs/DispatcherCacheRequest.cs
Normal file
8
GFramework.Cqrs.Tests/Cqrs/DispatcherCacheRequest.cs
Normal file
@ -0,0 +1,8 @@
|
||||
using GFramework.Cqrs.Abstractions.Cqrs;
|
||||
|
||||
namespace GFramework.Cqrs.Tests.Cqrs;
|
||||
|
||||
/// <summary>
|
||||
/// 用于验证 request 服务类型缓存的测试请求。
|
||||
/// </summary>
|
||||
internal sealed record DispatcherCacheRequest : IRequest<int>;
|
||||
21
GFramework.Cqrs.Tests/Cqrs/DispatcherCacheRequestHandler.cs
Normal file
21
GFramework.Cqrs.Tests/Cqrs/DispatcherCacheRequestHandler.cs
Normal file
@ -0,0 +1,21 @@
|
||||
using System.Threading;
|
||||
using GFramework.Cqrs.Abstractions.Cqrs;
|
||||
|
||||
namespace GFramework.Cqrs.Tests.Cqrs;
|
||||
|
||||
/// <summary>
|
||||
/// 处理 <see cref="DispatcherCacheRequest" />。
|
||||
/// </summary>
|
||||
internal sealed class DispatcherCacheRequestHandler : IRequestHandler<DispatcherCacheRequest, int>
|
||||
{
|
||||
/// <summary>
|
||||
/// 返回固定结果,供缓存测试验证 dispatcher 请求路径。
|
||||
/// </summary>
|
||||
/// <param name="request">当前请求。</param>
|
||||
/// <param name="cancellationToken">取消令牌。</param>
|
||||
/// <returns>固定整数结果。</returns>
|
||||
public ValueTask<int> Handle(DispatcherCacheRequest request, CancellationToken cancellationToken)
|
||||
{
|
||||
return ValueTask.FromResult(1);
|
||||
}
|
||||
}
|
||||
27
GFramework.Cqrs.Tests/Cqrs/DispatcherCacheStreamHandler.cs
Normal file
27
GFramework.Cqrs.Tests/Cqrs/DispatcherCacheStreamHandler.cs
Normal file
@ -0,0 +1,27 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using GFramework.Cqrs.Abstractions.Cqrs;
|
||||
|
||||
namespace GFramework.Cqrs.Tests.Cqrs;
|
||||
|
||||
/// <summary>
|
||||
/// 处理 <see cref="DispatcherCacheStreamRequest" />。
|
||||
/// </summary>
|
||||
internal sealed class DispatcherCacheStreamHandler : IStreamRequestHandler<DispatcherCacheStreamRequest, int>
|
||||
{
|
||||
/// <summary>
|
||||
/// 返回一个最小流,供缓存测试命中 stream 分发路径。
|
||||
/// </summary>
|
||||
/// <param name="request">当前流请求。</param>
|
||||
/// <param name="cancellationToken">取消令牌。</param>
|
||||
/// <returns>包含一个固定元素的异步流。</returns>
|
||||
public async IAsyncEnumerable<int> Handle(
|
||||
DispatcherCacheStreamRequest request,
|
||||
[EnumeratorCancellation] CancellationToken cancellationToken)
|
||||
{
|
||||
yield return 1;
|
||||
await Task.CompletedTask.ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,8 @@
|
||||
using GFramework.Cqrs.Abstractions.Cqrs;
|
||||
|
||||
namespace GFramework.Cqrs.Tests.Cqrs;
|
||||
|
||||
/// <summary>
|
||||
/// 用于验证 stream 服务类型缓存的测试请求。
|
||||
/// </summary>
|
||||
internal sealed record DispatcherCacheStreamRequest : IStreamRequest<int>;
|
||||
@ -0,0 +1,26 @@
|
||||
using System.Threading;
|
||||
using GFramework.Cqrs.Abstractions.Cqrs;
|
||||
|
||||
namespace GFramework.Cqrs.Tests.Cqrs;
|
||||
|
||||
/// <summary>
|
||||
/// 为 <see cref="DispatcherPipelineCacheRequest" /> 提供最小 pipeline 行为,
|
||||
/// 用于命中 dispatcher 的 pipeline invoker 缓存分支。
|
||||
/// </summary>
|
||||
internal sealed class DispatcherPipelineCacheBehavior : IPipelineBehavior<DispatcherPipelineCacheRequest, int>
|
||||
{
|
||||
/// <summary>
|
||||
/// 直接转发到下一个处理器。
|
||||
/// </summary>
|
||||
/// <param name="request">当前请求。</param>
|
||||
/// <param name="next">下一个处理器委托。</param>
|
||||
/// <param name="cancellationToken">取消令牌。</param>
|
||||
/// <returns>下游处理器结果。</returns>
|
||||
public ValueTask<int> Handle(
|
||||
DispatcherPipelineCacheRequest request,
|
||||
MessageHandlerDelegate<DispatcherPipelineCacheRequest, int> next,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
return next(request, cancellationToken);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,8 @@
|
||||
using GFramework.Cqrs.Abstractions.Cqrs;
|
||||
|
||||
namespace GFramework.Cqrs.Tests.Cqrs;
|
||||
|
||||
/// <summary>
|
||||
/// 用于验证 pipeline invoker 缓存的测试请求。
|
||||
/// </summary>
|
||||
internal sealed record DispatcherPipelineCacheRequest : IRequest<int>;
|
||||
@ -0,0 +1,21 @@
|
||||
using System.Threading;
|
||||
using GFramework.Cqrs.Abstractions.Cqrs;
|
||||
|
||||
namespace GFramework.Cqrs.Tests.Cqrs;
|
||||
|
||||
/// <summary>
|
||||
/// 处理 <see cref="DispatcherPipelineCacheRequest" />。
|
||||
/// </summary>
|
||||
internal sealed class DispatcherPipelineCacheRequestHandler : IRequestHandler<DispatcherPipelineCacheRequest, int>
|
||||
{
|
||||
/// <summary>
|
||||
/// 返回固定结果,供 pipeline 缓存测试使用。
|
||||
/// </summary>
|
||||
/// <param name="request">当前请求。</param>
|
||||
/// <param name="cancellationToken">取消令牌。</param>
|
||||
/// <returns>固定整数结果。</returns>
|
||||
public ValueTask<int> Handle(DispatcherPipelineCacheRequest request, CancellationToken cancellationToken)
|
||||
{
|
||||
return ValueTask.FromResult(2);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,8 @@
|
||||
using GFramework.Cqrs.Abstractions.Cqrs;
|
||||
|
||||
namespace GFramework.Cqrs.Tests.Cqrs;
|
||||
|
||||
/// <summary>
|
||||
/// 用于验证按响应类型分层 request invoker 缓存的测试请求。
|
||||
/// </summary>
|
||||
internal sealed record DispatcherStringCacheRequest : IRequest<string>;
|
||||
@ -0,0 +1,21 @@
|
||||
using System.Threading;
|
||||
using GFramework.Cqrs.Abstractions.Cqrs;
|
||||
|
||||
namespace GFramework.Cqrs.Tests.Cqrs;
|
||||
|
||||
/// <summary>
|
||||
/// 处理 <see cref="DispatcherStringCacheRequest" />。
|
||||
/// </summary>
|
||||
internal sealed class DispatcherStringCacheRequestHandler : IRequestHandler<DispatcherStringCacheRequest, string>
|
||||
{
|
||||
/// <summary>
|
||||
/// 返回固定字符串,供按响应类型缓存测试验证 string 路径。
|
||||
/// </summary>
|
||||
/// <param name="request">当前请求。</param>
|
||||
/// <param name="cancellationToken">取消令牌。</param>
|
||||
/// <returns>固定字符串结果。</returns>
|
||||
public ValueTask<string> Handle(DispatcherStringCacheRequest request, CancellationToken cancellationToken)
|
||||
{
|
||||
return ValueTask.FromResult("dispatcher-cache");
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,30 @@
|
||||
using System;
|
||||
using GFramework.Core.Abstractions.Logging;
|
||||
using GFramework.Core.Ioc;
|
||||
using GFramework.Core.Logging;
|
||||
using GFramework.Cqrs.Abstractions.Cqrs;
|
||||
|
||||
namespace GFramework.Cqrs.Tests.Cqrs;
|
||||
|
||||
/// <summary>
|
||||
/// 模拟源码生成器为某个程序集生成的 CQRS 处理器注册器。
|
||||
/// </summary>
|
||||
internal sealed class GeneratedNotificationHandlerRegistry : ICqrsHandlerRegistry
|
||||
{
|
||||
/// <summary>
|
||||
/// 将测试通知处理器注册到目标服务集合。
|
||||
/// </summary>
|
||||
/// <param name="services">承载处理器映射的服务集合。</param>
|
||||
/// <param name="logger">用于记录注册诊断的日志器。</param>
|
||||
public void Register(IServiceCollection services, ILogger logger)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(services);
|
||||
ArgumentNullException.ThrowIfNull(logger);
|
||||
|
||||
services.AddTransient(
|
||||
typeof(INotificationHandler<GeneratedRegistryNotification>),
|
||||
typeof(GeneratedRegistryNotificationHandler));
|
||||
logger.Debug(
|
||||
$"Registered CQRS handler {typeof(GeneratedRegistryNotificationHandler).FullName} as {typeof(INotificationHandler<GeneratedRegistryNotification>).FullName}.");
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,8 @@
|
||||
using GFramework.Cqrs.Abstractions.Cqrs;
|
||||
|
||||
namespace GFramework.Cqrs.Tests.Cqrs;
|
||||
|
||||
/// <summary>
|
||||
/// 用于验证生成注册器路径的通知消息。
|
||||
/// </summary>
|
||||
internal sealed record GeneratedRegistryNotification : INotification;
|
||||
@ -0,0 +1,22 @@
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using GFramework.Cqrs.Abstractions.Cqrs;
|
||||
|
||||
namespace GFramework.Cqrs.Tests.Cqrs;
|
||||
|
||||
/// <summary>
|
||||
/// 由模拟的源码生成注册器显式注册的通知处理器。
|
||||
/// </summary>
|
||||
internal sealed class GeneratedRegistryNotificationHandler : INotificationHandler<GeneratedRegistryNotification>
|
||||
{
|
||||
/// <summary>
|
||||
/// 处理生成注册器测试中的通知。
|
||||
/// </summary>
|
||||
/// <param name="notification">通知实例。</param>
|
||||
/// <param name="cancellationToken">取消令牌。</param>
|
||||
/// <returns>已完成任务。</returns>
|
||||
public ValueTask Handle(GeneratedRegistryNotification notification, CancellationToken cancellationToken)
|
||||
{
|
||||
return ValueTask.CompletedTask;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,33 @@
|
||||
using System;
|
||||
using GFramework.Core.Abstractions.Logging;
|
||||
using GFramework.Core.Ioc;
|
||||
using GFramework.Core.Logging;
|
||||
using GFramework.Cqrs.Abstractions.Cqrs;
|
||||
|
||||
namespace GFramework.Cqrs.Tests.Cqrs;
|
||||
|
||||
/// <summary>
|
||||
/// 模拟局部生成注册器场景中,仅注册“可由生成代码直接引用”的那部分 handlers。
|
||||
/// </summary>
|
||||
internal sealed class PartialGeneratedNotificationHandlerRegistry : ICqrsHandlerRegistry
|
||||
{
|
||||
/// <summary>
|
||||
/// 将生成路径可见的通知处理器注册到目标服务集合。
|
||||
/// </summary>
|
||||
/// <param name="services">承载处理器映射的服务集合。</param>
|
||||
/// <param name="logger">用于记录注册诊断的日志器。</param>
|
||||
/// <exception cref="ArgumentNullException">
|
||||
/// <paramref name="services" /> 或 <paramref name="logger" /> 为 <see langword="null" />。
|
||||
/// </exception>
|
||||
public void Register(IServiceCollection services, ILogger logger)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(services);
|
||||
ArgumentNullException.ThrowIfNull(logger);
|
||||
|
||||
services.AddTransient(
|
||||
typeof(INotificationHandler<GeneratedRegistryNotification>),
|
||||
typeof(GeneratedRegistryNotificationHandler));
|
||||
logger.Debug(
|
||||
$"Registered CQRS handler {typeof(GeneratedRegistryNotificationHandler).FullName} as {typeof(INotificationHandler<GeneratedRegistryNotification>).FullName}.");
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,37 @@
|
||||
using System;
|
||||
using GFramework.Core.Abstractions.Logging;
|
||||
using GFramework.Core.Ioc;
|
||||
using GFramework.Core.Logging;
|
||||
using GFramework.Cqrs.Abstractions.Cqrs;
|
||||
|
||||
namespace GFramework.Cqrs.Tests.Cqrs;
|
||||
|
||||
/// <summary>
|
||||
/// 模拟生成注册器使用私有无参构造器的场景,验证运行时仍可通过缓存工厂激活它。
|
||||
/// </summary>
|
||||
internal sealed class PrivateConstructorNotificationHandlerRegistry : ICqrsHandlerRegistry
|
||||
{
|
||||
/// <summary>
|
||||
/// 初始化一个新的私有生成注册器实例。
|
||||
/// </summary>
|
||||
private PrivateConstructorNotificationHandlerRegistry()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将测试通知处理器注册到目标服务集合。
|
||||
/// </summary>
|
||||
/// <param name="services">承载处理器映射的服务集合。</param>
|
||||
/// <param name="logger">用于记录注册诊断的日志器。</param>
|
||||
public void Register(IServiceCollection services, ILogger logger)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(services);
|
||||
ArgumentNullException.ThrowIfNull(logger);
|
||||
|
||||
services.AddTransient(
|
||||
typeof(INotificationHandler<GeneratedRegistryNotification>),
|
||||
typeof(GeneratedRegistryNotificationHandler));
|
||||
logger.Debug(
|
||||
$"Registered CQRS handler {typeof(GeneratedRegistryNotificationHandler).FullName} as {typeof(INotificationHandler<GeneratedRegistryNotification>).FullName}.");
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,32 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using GFramework.Cqrs.Abstractions.Cqrs;
|
||||
|
||||
namespace GFramework.Cqrs.Tests.Cqrs;
|
||||
|
||||
/// <summary>
|
||||
/// 用于验证“生成注册器 + reflection fallback”组合路径的私有嵌套处理器容器。
|
||||
/// </summary>
|
||||
internal sealed class ReflectionFallbackNotificationContainer
|
||||
{
|
||||
/// <summary>
|
||||
/// 获取仅能通过反射补扫接入的私有嵌套处理器类型。
|
||||
/// </summary>
|
||||
public static Type ReflectionOnlyHandlerType => typeof(ReflectionOnlyGeneratedRegistryNotificationHandler);
|
||||
|
||||
private sealed class ReflectionOnlyGeneratedRegistryNotificationHandler
|
||||
: INotificationHandler<GeneratedRegistryNotification>
|
||||
{
|
||||
/// <summary>
|
||||
/// 处理测试通知。
|
||||
/// </summary>
|
||||
/// <param name="notification">通知实例。</param>
|
||||
/// <param name="cancellationToken">取消令牌。</param>
|
||||
/// <returns>已完成任务。</returns>
|
||||
public ValueTask Handle(GeneratedRegistryNotification notification, CancellationToken cancellationToken)
|
||||
{
|
||||
return ValueTask.CompletedTask;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,23 @@
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using GFramework.Cqrs.Abstractions.Cqrs;
|
||||
|
||||
namespace GFramework.Cqrs.Tests.Cqrs;
|
||||
|
||||
/// <summary>
|
||||
/// 故意放在 Alpha 之前声明,用于验证注册器不会依赖源码声明顺序。
|
||||
/// </summary>
|
||||
internal sealed class ZetaDeterministicNotificationHandler : INotificationHandler<DeterministicOrderNotification>
|
||||
{
|
||||
/// <summary>
|
||||
/// 记录当前处理器已执行。
|
||||
/// </summary>
|
||||
/// <param name="notification">通知实例。</param>
|
||||
/// <param name="cancellationToken">取消令牌。</param>
|
||||
/// <returns>已完成任务。</returns>
|
||||
public ValueTask Handle(DeterministicOrderNotification notification, CancellationToken cancellationToken)
|
||||
{
|
||||
DeterministicNotificationHandlerState.InvocationOrder.Add(nameof(ZetaDeterministicNotificationHandler));
|
||||
return ValueTask.CompletedTask;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,55 @@
|
||||
# Analyzer Warning Reduction 历史归档(RP-083 ~ RP-088)
|
||||
|
||||
## 范围说明
|
||||
|
||||
- 归档区间:`RP-083` 到 `RP-088`
|
||||
- 归档原因:active trace 已累计多个已完成阶段,不再适合作为默认恢复入口
|
||||
- 当前活跃恢复点:返回 `ai-plan/public/analyzer-warning-reduction/traces/analyzer-warning-reduction-trace.md`
|
||||
|
||||
## RP-088
|
||||
|
||||
- 阶段:收敛 `PR #300` 的 open review threads 与 failed-test follow-up
|
||||
- 主结论:
|
||||
- 核对 `TestArchitectureContext*`、`RegistryInitializationHookBase`、`TestResourceLoader`、`CapturingLoggerFactoryProvider`、`PartialGeneratedNotificationHandlerRegistry` 等 review 位点
|
||||
- 新增 `TestArchitectureContextBehaviorTests.cs`,覆盖共享事件总线、旧入口失败契约与 `RegisterLifecycleHook` 接口行为
|
||||
- 受影响项目验证通过,`GFramework.Cqrs.Tests` 仍保留既有 `Mediator/*` warning 基线
|
||||
|
||||
## RP-087
|
||||
|
||||
- 阶段:按 `$gframework-batch-boot 50` 并行收敛 `Core.Tests` / `Cqrs.Tests` 低风险切片
|
||||
- 主结论:
|
||||
- 建立仓库根 non-incremental warning 基线后,并行消化 `Core.Tests` 与 `Cqrs.Tests` 的低风险 warning
|
||||
- 仓库根 warning 从 `288` 下降到 `236`
|
||||
- 剩余热点开始集中到 `Mediator/*` 与 `YamlConfigSchemaValidator*`
|
||||
|
||||
## RP-086
|
||||
|
||||
- 阶段:收敛 `PR #298` 的 CodeRabbit nitpick follow-up
|
||||
- 主结论:
|
||||
- 修复测试辅助类型的可维护性 nitpick
|
||||
- `GFramework.Core.Tests` 定向验证通过
|
||||
- 剩余 warning 仍集中在既有热点文件
|
||||
|
||||
## RP-085
|
||||
|
||||
- 阶段:按 `$gframework-batch-boot 100` 并行消化 `GFramework.Core.Tests` 低风险 `MA0048`
|
||||
- 主结论:
|
||||
- 四波次并行拆分 `GFramework.Core.Tests` 测试辅助类型
|
||||
- 仓库根 warning 从 `353` 下降到 `288`
|
||||
- active footprint 接近阈值后主动收口
|
||||
|
||||
## RP-084
|
||||
|
||||
- 阶段:收敛 `PR #297` 的 CodeRabbit follow-up
|
||||
- 主结论:
|
||||
- 校正 `YamlConfigLoader` 取消语义与若干 XML 文档问题
|
||||
- 新增定向回归测试覆盖取消异常路径
|
||||
- 相关构建与测试全部通过
|
||||
|
||||
## RP-083
|
||||
|
||||
- 阶段:修复 `YamlConfigLoader` 单文件 warning,并拆分 `MicrosoftDiContainerTests` 的辅助类型
|
||||
- 主结论:
|
||||
- 从仓库根基线出发完成单文件 warning 修复与两组测试辅助类型拆分
|
||||
- 仓库根 warning 从 `397` 下降到 `353`
|
||||
- 后续工作切入点转向 `ArchitectureContextTests.cs` / `AsyncQueryExecutorTests.cs`
|
||||
@ -6,57 +6,38 @@
|
||||
|
||||
## 当前恢复点
|
||||
|
||||
- 恢复点编号:`ANALYZER-WARNING-REDUCTION-RP-086`
|
||||
- 当前阶段:`Phase 86`
|
||||
- 恢复点编号:`ANALYZER-WARNING-REDUCTION-RP-092`
|
||||
- 当前阶段:`Phase 92`
|
||||
- 当前焦点:
|
||||
- `2026-04-27` 已按 `$gframework-batch-boot 100` 连续执行多波 `MA0048` 小切片,当前以 `GFramework.Core.Tests` 的测试辅助类型拆分为主
|
||||
- `2026-04-27` 已按 `$gframework-pr-review` 收敛 `PR #298` 的有效 nitpick,修复测试辅助类型的只读暴露、线程安全、空安全与文档一致性问题
|
||||
- 本轮已完成 `ArchitectureContextTests`、`AsyncQueryExecutorTests`、`CommandExecutorTests`、`StateTests`、`StateMachineTests`、`StateMachineSystemTests`、`ArchitectureModulesBehaviorTests`、`ArchitectureAdditionalCqrsHandlersTests`、`QueryCoroutineExtensionsTests`、`ObjectPoolTests`、`AbstractContextUtilityTests` 等低风险单文件切片
|
||||
- 当前仓库根权威基线已从 `353 Warning(s)` / `279` 个唯一位点下降到 `288 Warning(s)` / `214` 个唯一位点
|
||||
- 当前分支下一波更适合转向 `GameContextTests.cs`、`ArchitectureServicesTests.cs`、`RegistryInitializationHookBaseTests.cs` 这类仍在 `GFramework.Core.Tests` 内、但已混入 `CS8766` / `MA0016` 的小型混合切片
|
||||
- `2026-04-28` 复核 `PR #300` 最新 open threads:代码类线程已与当前工作树对齐,仅剩 `ai-plan/public/analyzer-warning-reduction/todos/analyzer-warning-reduction-tracking.md` 的文件计数与验证口径漂移仍然成立
|
||||
- 已将 tracking 文档修正为与 `6cc87a9...HEAD` 的实际变更规模一致,并与 trace 中记录的 `dotnet build`、定向 `dotnet test`、`git diff --check` 验证口径保持一致
|
||||
- `dotnet format --verify-no-changes` 的 `GFramework.Core.Tests` 既有 `FINALNEWLINE`、`CHARSET`、`WHITESPACE` 基线仍保持独立,不与当前 `ai-plan` 同步修复混提
|
||||
|
||||
## 当前活跃事实
|
||||
|
||||
- 当前 `origin/main` 基线提交为 `7cfdd2c`(`2026-04-27T16:59:57+08:00`)。
|
||||
- 当前 `origin/main` 基线提交为 `6cc87a9`(`2026-04-27T20:28:50+08:00`)。
|
||||
- 当前直接验证结果:
|
||||
- `dotnet build GFramework.Core.Tests/GFramework.Core.Tests.csproj -c Release`
|
||||
- 最新结果:成功;`28 Warning(s)`、`0 Error(s)`;当前 warning 来自 `GameContextTests.cs`、`ArchitectureServicesTests.cs`、`RegistryInitializationHookBaseTests.cs` 等既有热点
|
||||
- `dotnet test GFramework.Core.Tests/GFramework.Core.Tests.csproj -c Release --no-build`
|
||||
- 最新结果:成功;`1610` 通过、`0` 失败
|
||||
- `dotnet clean`
|
||||
- 最新结果:成功;已刷新仓库根 non-incremental 基线
|
||||
- `dotnet build`
|
||||
- 最新结果:成功;`288 Warning(s)`、`0 Error(s)`,唯一位点 `214`
|
||||
- `dotnet build GFramework.Game/GFramework.Game.csproj -c Release`
|
||||
- 最新结果:成功;`0 Warning(s)`、`0 Error(s)`
|
||||
- `dotnet test GFramework.Game.Tests/GFramework.Game.Tests.csproj -c Release --filter "FullyQualifiedName~YamlConfigLoaderTests.ReadYamlAsync_Should_Preserve_OperationCanceledException_When_Cancellation_Is_Requested"`
|
||||
- 最新结果:成功;`1` 通过、`0` 失败
|
||||
- `dotnet test GFramework.Core.Tests/GFramework.Core.Tests.csproj -c Release --filter "FullyQualifiedName~MicrosoftDiContainerTests.GetAllByPriority_Should_Sort_By_Priority_Ascending"`
|
||||
- 最新结果:成功;`1` 通过、`0` 失败
|
||||
- `dotnet format GFramework.sln --verify-no-changes --include GFramework.Game/Config/YamlConfigLoader.cs GFramework.Game.Tests/Config/YamlConfigLoaderTests.cs GFramework.Core.Tests/Ioc/IMixedService.cs GFramework.Core.Tests/Ioc/IPrioritizedService.cs GFramework.Core.Tests/Ioc/PrioritizedService.cs GFramework.Core.Tests/Query/TestAsyncQueryWithExceptionV4.cs`
|
||||
- 最新结果:成功;本次 PR follow-up 改动文件无需额外格式化
|
||||
- `dotnet test GFramework.Core.Tests/GFramework.Core.Tests.csproj -c Release --no-build --filter "FullyQualifiedName~ArchitectureServicesTests|FullyQualifiedName~ContextAwareServiceExtensionsTests|FullyQualifiedName~TestArchitectureContextBehaviorTests|FullyQualifiedName~RegistryInitializationHookBaseTests|FullyQualifiedName~ArchitectureContextTests"`
|
||||
- 最新结果:成功;`67` 通过、`0` 失败
|
||||
- 当前批次摘要:
|
||||
- 本轮通过多批并行 worker 共完成 `20+` 个 `GFramework.Core.Tests` 文件的测试辅助类型拆分,集中消化纯 `MA0048` warning 热点
|
||||
- 本轮停止时共享工作树共有 `61` 个变更条目,仍低于 `$gframework-batch-boot 100` 的文件停止线
|
||||
- 本轮仓库根权威 warning 已从开始时的 `353` 下降到 `288`,且 `GFramework.Core.Tests` 受影响项目的 Release 构建已恢复到 `0 Warning(s)` / `0 Error(s)`
|
||||
- 当前建议保留到下一波次的候选:
|
||||
- `GFramework.Core.Tests/Architectures/GameContextTests.cs` 的 `4` 个 `CS8766` 与 `2` 个 `MA0048`
|
||||
- `GFramework.Core.Tests/Architectures/ArchitectureServicesTests.cs` 的 `4` 个 `CS8766` 与 `1` 个 `MA0048`
|
||||
- `GFramework.Core.Tests/Architectures/RegistryInitializationHookBaseTests.cs` 的 `1` 个 `MA0016` 与 `5` 个 `MA0048`
|
||||
- `GFramework.Game/Config/YamlConfigSchemaValidator.cs` 与 `YamlConfigSchemaValidator.ObjectKeywords.cs` 的高耦合 warning 热点
|
||||
- 当前分支相对 `6cc87a9...HEAD` 包含 `18` 个已修改文件与 `38` 个新增文件(合计 `56` 个变更文件),分别位于 `GFramework.Core.Tests`、`GFramework.Cqrs.Tests`、`GFramework.Core`、`.agents/skills/gframework-pr-review/` 与 `ai-plan/public/analyzer-warning-reduction`
|
||||
- 本轮没有触碰 `Mediator/*`、`YamlConfigSchemaValidator*` 或 `GFramework.Core.Tests` 的整项目格式基线波次
|
||||
|
||||
## 当前风险
|
||||
|
||||
- `GFramework.Cqrs.Tests/Mediator/*` 仍有 `47` / `44` / `34` 个唯一 warning 位点,属于高 changed-file 风险的 `MA0048` 大波次。
|
||||
- 缓解措施:优先继续处理 `6-7` 个 warning 的小文件切片,避免一次性推高文件数。
|
||||
- `GameContextTests.cs`、`ArchitectureServicesTests.cs` 这类混合 `CS8766` / `MA0048` 文件不再适合继续用“纯拆分”模式批量下发。
|
||||
- 缓解措施:下一波由主线程先局部修正可空签名,再决定是否继续并行拆分。
|
||||
- `YamlConfigSchemaValidator*` 仍然聚集多类高耦合 warning。
|
||||
- 缓解措施:继续把它们留在独立波次,不与测试项目的低风险拆分混提。
|
||||
- GitHub PR 上的 open threads 可能仍显示为未关闭,因为当前只同步了 `ai-plan` 文档,尚未推送新的 head 供审查机器人重新折叠线程。
|
||||
- 缓解措施:推送本次 `ai-plan` 同步提交后重新执行 `$gframework-pr-review`,以最新 head 再核对 thread 状态。
|
||||
- `dotnet format GFramework.Core.Tests/GFramework.Core.Tests.csproj --verify-no-changes` 当前会命中项目内大量历史格式诊断。
|
||||
- 缓解措施:本轮只记录为现存基线,不把 `PR #300` 的 review follow-up 扩展成整项目格式清理。
|
||||
- `GFramework.Game/Config/YamlConfigSchemaValidator*` 仍然是仓库根 warning 热点,但与本轮 review 修复无交集。
|
||||
- 缓解措施:继续保持为独立高耦合波次。
|
||||
|
||||
## 活跃文档
|
||||
|
||||
- 当前轮次归档:
|
||||
- [analyzer-warning-reduction-history-rp083-rp088.md](../archive/traces/analyzer-warning-reduction-history-rp083-rp088.md)
|
||||
- [analyzer-warning-reduction-history-rp074-rp078.md](../archive/todos/analyzer-warning-reduction-history-rp074-rp078.md)
|
||||
- [analyzer-warning-reduction-history-rp042-rp048.md](../archive/todos/analyzer-warning-reduction-history-rp042-rp048.md)
|
||||
- 历史跟踪归档:
|
||||
@ -72,11 +53,12 @@
|
||||
## 验证说明
|
||||
|
||||
- 权威验证结果统一维护在“当前活跃事实”。
|
||||
- `GFramework.Core.Tests` 项目级 Release 构建已在本轮清零,但仓库根 non-incremental 构建仍保留大量既有 warning。
|
||||
- warning reduction 的仓库级真值只以同轮 `dotnet clean` 后的 `dotnet build` 为准。
|
||||
- `GFramework.Core.Tests` 的当前受影响项目 Release 构建已清零,并通过对应定向测试回归。
|
||||
- `git diff --check` 结果为空,说明本轮新增改动没有引入新的尾随空格或冲突标记。
|
||||
- warning reduction 的仓库级真值以同轮 `dotnet build`、定向 `dotnet test` 与 `git diff --check` 为准,并与 trace 中的验证里程碑保持一致。
|
||||
|
||||
## 下一步建议
|
||||
|
||||
1. 提交本轮多批 `MA0048` warning reduction 与 `ai-plan` 同步。
|
||||
2. 下一波由主线程先处理 `GameContextTests.cs` / `ArchitectureServicesTests.cs` 的 `CS8766`,再决定是否继续拆分剩余 `MA0048`。
|
||||
3. 继续将 `YamlConfigSchemaValidator*` 与 `GFramework.Cqrs.Tests/Mediator/*` 作为独立高风险波次处理。
|
||||
1. 提交本轮 `PR #300` nitpick follow-up、技能规则更新与 `ai-plan` 同步。
|
||||
2. 推送后重新执行 `$gframework-pr-review`,确认 `ai-plan` 相关 thread 是否随最新 head 自动收口。
|
||||
3. 若要清理 `dotnet format` 基线,另开 `GFramework.Core.Tests` 格式治理切片,不与当前 PR review 修复混提。
|
||||
|
||||
@ -1,147 +1,59 @@
|
||||
# Analyzer Warning Reduction 追踪
|
||||
|
||||
## 2026-04-27 — RP-086
|
||||
## 2026-04-28 — RP-092
|
||||
|
||||
### 阶段:收敛 PR #298 的 CodeRabbit nitpick follow-up
|
||||
### 阶段:复核 `PR #300` 的 open threads,并只修正当前分支仍然成立的 `ai-plan` 漂移
|
||||
|
||||
- 触发背景:
|
||||
- 用户再次执行 `$gframework-pr-review` 后,要求按 `PR #298` 的 nitpick comments 收敛仍然适用的问题
|
||||
- 复核 PR 真值后确认当前分支无 failed checks、无 open review threads,但仍有一批测试辅助类型的可维护性 nitpick 值得本地落地
|
||||
- 用户要求恢复当前 `$gframework-pr-review` 任务,继续以 PR head 上的开放线程为准做 triage
|
||||
- 主线程实施:
|
||||
- 校验 `TestStateMachineSystemV5`、`ComplexQuery`、`TrackingPipelineBehavior`、`TestEnvironment`、`TestContextUtilityV1/V2` 等改动后,分别修复可变内部状态暴露、`_context!` 空抑制、静态计数器非原子递增、`new Register(...)` 测试辅助入口和生命周期标记公开 setter 问题
|
||||
- 统一更新 `TestQueryV2`、`TestCommandWithResultV2`、`TestAsyncQueryInput`、`TestAsyncQueryResult*` 的 XML 文档,使 `init` 属性语义与文档一致
|
||||
- 将三倍结果属性从 `DoubleValue` 更名为 `TripleValue`,同步更新 `TestAsyncComplexQuery*` 与相关断言,避免名称与 `* 3` 的行为不一致
|
||||
- 精简 active tracking,移除重复的 `GFramework.Core.Tests` Release build 记录,并把该项目的当前真值修正为 `28 Warning(s)`
|
||||
- 重新读取 `fetch_current_pr_review.py --json-output /tmp/current-pr-review.json` 的 latest head open threads
|
||||
- 逐条对照本地文件后确认:`TestArchitectureContextBehaviorTests`、`TestArchitectureWithRegistry`、`TestResourceLoader`、`PartialGeneratedNotificationHandlerRegistry` 相关 CodeRabbit 线程在当前工作树上都已匹配修复,仅线程状态尚未随新 head 折叠
|
||||
- 继续核对 `RegistryInitializationHookBaseTests.OnPhase_Should_Not_Throw_When_Registry_Not_Found`,确认当前实现 `RegistryInitializationHookBase.OnPhase` 已在缺少注册表时保持 no-op,定向回归测试通过
|
||||
- 修正 `analyzer-warning-reduction-tracking.md` 中仍然成立的两处漂移:
|
||||
- 将文件计数更新为相对 `6cc87a9...HEAD` 的实际规模:`18` 个已修改文件、`38` 个新增文件、合计 `56` 个变更文件
|
||||
- 将验证口径统一为 trace 已记录的 `dotnet build`、定向 `dotnet test`、`git diff --check`
|
||||
- 验证里程碑:
|
||||
- `dotnet build GFramework.Core.Tests/GFramework.Core.Tests.csproj -c Release`
|
||||
- 结果:成功;`28 Warning(s)`、`0 Error(s)`
|
||||
- `dotnet test GFramework.Core.Tests/GFramework.Core.Tests.csproj -c Release --no-build`
|
||||
- 结果:成功;`1610` 通过、`0` 失败
|
||||
- 当前结论:
|
||||
- `PR #298` 中仍然适用的低风险 nitpick 已完成收敛,且没有为当前 touched files 引入新的构建 warning 或测试回归
|
||||
- `GFramework.Core.Tests` 的剩余 warning 仍集中在 `GameContextTests.cs`、`ArchitectureServicesTests.cs`、`RegistryInitializationHookBaseTests.cs` 等既有热点,不属于本轮 nitpick follow-up 新引入问题
|
||||
- 下一步:
|
||||
1. 提交本轮 `PR #298` nitpick follow-up 与 `ai-plan` 同步。
|
||||
2. 回到 `GameContextTests.cs` / `ArchitectureServicesTests.cs` 的 `CS8766` warning reduction 主线。
|
||||
- `dotnet test GFramework.Core.Tests/GFramework.Core.Tests.csproj -c Release --filter "FullyQualifiedName~RegistryInitializationHookBaseTests.OnPhase_Should_Not_Throw_When_Registry_Not_Found|FullyQualifiedName~TestArchitectureContextBehaviorTests"`
|
||||
- 结果:成功;`10` 通过、`0` 失败
|
||||
- `git diff --check`
|
||||
- 结果:成功;无新增 whitespace / conflict-marker 问题
|
||||
|
||||
## 2026-04-27 — RP-085
|
||||
## 2026-04-28 — RP-091
|
||||
|
||||
### 阶段:按 `$gframework-batch-boot 100` 并行消化 `GFramework.Core.Tests` 低风险 `MA0048`
|
||||
### 阶段:收口 `PR #300` 的共享测试基础设施 nitpick,并升级 PR-review triage 规则
|
||||
|
||||
- 触发背景:
|
||||
- 用户要求以仓库根 non-incremental 构建 warning 为准,并在上下文可控前提下把小切片分派给多个 subagent 并行处理
|
||||
- 本轮开始时,当前分支与 `origin/main@7cfdd2c` 无提交差异,适合从纯 `MA0048` 单文件切片起步
|
||||
- 用户追问 `TestArchitectureContext` / `TestArchitectureContextV3` 的共享基础设施 nitpick 是否已经处理完成
|
||||
- 同时要求把“本地验证后仍然成立的 nitpick 不能默认降级为可选项”写入 `AGENTS.md` 或 `$gframework-pr-review`
|
||||
- 主线程实施:
|
||||
- 执行权威基线:`dotnet clean` + 仓库根 `dotnet build`
|
||||
- 初始结果:`353 Warning(s)`、唯一位点 `279`
|
||||
- 分四波次并行下发 `GFramework.Core.Tests` 小切片,累计完成 `20+` 个文件的测试辅助类型拆分
|
||||
- 主线程持续复核共享工作树、处理并发编译阻断,并在每一轮后复跑 `GFramework.Core.Tests` Release 构建
|
||||
- 在工作树达到 `61` 个变更条目时主动停止扩批,保留对 `100` 文件停止线的充分余量
|
||||
- 代表性已落地切片:
|
||||
- `ArchitectureContextTests.cs`
|
||||
- `AsyncQueryExecutorTests.cs`
|
||||
- `CommandExecutorTests.cs`
|
||||
- `StateTests.cs`
|
||||
- `StateMachineTests.cs`
|
||||
- `StateMachineSystemTests.cs`
|
||||
- `ArchitectureModulesBehaviorTests.cs`
|
||||
- `ArchitectureAdditionalCqrsHandlersTests.cs`
|
||||
- `QueryCoroutineExtensionsTests.cs`
|
||||
- `ObjectPoolTests.cs`
|
||||
- `AbstractContextUtilityTests.cs`
|
||||
- `EnvironmentTests.cs`
|
||||
- `EventBusTests.cs`
|
||||
- `ContextAwareTests.cs`
|
||||
- 新增 `TestArchitectureContextBase`,把容器解析、共享 `EventBus` 行为,以及 legacy / CQRS 失败契约统一收敛到一处
|
||||
- 将 `TestArchitectureContext` 与 `TestArchitectureContextV3` 收窄为薄包装类型,只保留各自的命名入口与 `Id` 差异
|
||||
- 更新 `.agents/skills/gframework-pr-review/SKILL.md`,明确要求:latest-head `Nitpick comment` 一旦本地验证仍成立且指向真实漂移/回归风险,就必须作为 actionable review input 处理,而不是默认视作可选
|
||||
- 验证里程碑:
|
||||
- `dotnet build GFramework.Core.Tests/GFramework.Core.Tests.csproj -c Release`
|
||||
- 结果:成功;`0 Warning(s)`、`0 Error(s)`
|
||||
- `dotnet clean`
|
||||
- 结果:成功
|
||||
- `dotnet build`
|
||||
- 结果:成功;`288 Warning(s)`、`0 Error(s)`,唯一位点 `214`
|
||||
- 当前结论:
|
||||
- 本轮主要收益来自 `GFramework.Core.Tests` 内的纯 `MA0048` 大范围收敛
|
||||
- 仓库根权威 warning 已从 `353` 降到 `288`,唯一位点从 `279` 降到 `214`
|
||||
- 下一波不再适合继续盲目平铺纯拆分,因为剩余 `GFramework.Core.Tests` 热点已开始混入 `CS8766` / `MA0016`
|
||||
- 下一步:
|
||||
1. 提交本轮 warning reduction 与 `ai-plan` 同步。
|
||||
2. 下一波优先由主线程处理 `GameContextTests.cs` / `ArchitectureServicesTests.cs` 的混合 warning。
|
||||
3. 保持 `YamlConfigSchemaValidator*` 与 `GFramework.Cqrs.Tests/Mediator/*` 为独立高风险波次。
|
||||
|
||||
## 2026-04-27 — RP-084
|
||||
|
||||
### 阶段:收敛 PR #297 的 CodeRabbit follow-up
|
||||
|
||||
- 触发背景:
|
||||
- 用户执行 `$gframework-pr-review`,要求以当前分支对应 PR 为准,提取并核对 AI review / check 信号
|
||||
- `fetch_current_pr_review.py` 返回 PR `#297` 的最新 head review 中仍有 `3` 个 open threads,另有 `2` 个 folded nitpick 仍然适用
|
||||
- 主线程实施:
|
||||
- 校验 `GFramework.Game/Config/YamlConfigLoader.cs` 后,保留 `ReadYamlAsync` 的原始取消语义,并把 `IntegerTryParseDelegate<T>` 第一个参数改为 `string?`
|
||||
- 校验 `GFramework.Core.Tests/Ioc/*` 与 `Query/TestAsyncQueryWithExceptionV4.cs` 后,补齐缺失 XML 文档,让 `IPrioritizedService` 继承 `IMixedService` 复用 `Name` 契约,并补上 `<returns>` 文档
|
||||
- 新增 `YamlConfigLoaderTests.ReadYamlAsync_Should_Preserve_OperationCanceledException_When_Cancellation_Is_Requested`,用反射直接命中私有读取路径,稳定回归本次取消语义修复
|
||||
- 用 `dotnet format --verify-no-changes --include ...` 清理并验证本次改动文件的格式状态
|
||||
- 验证里程碑:
|
||||
- `dotnet test GFramework.Game.Tests/GFramework.Game.Tests.csproj -c Release --filter "FullyQualifiedName~YamlConfigLoaderTests.ReadYamlAsync_Should_Preserve_OperationCanceledException_When_Cancellation_Is_Requested"`
|
||||
- 结果:成功;`1` 通过、`0` 失败
|
||||
- `dotnet test GFramework.Core.Tests/GFramework.Core.Tests.csproj -c Release --filter "FullyQualifiedName~MicrosoftDiContainerTests.GetAllByPriority_Should_Sort_By_Priority_Ascending"`
|
||||
- 结果:成功;`1` 通过、`0` 失败
|
||||
- `dotnet build GFramework.Game/GFramework.Game.csproj -c Release`
|
||||
- 结果:成功;`0 Warning(s)`、`0 Error(s)`
|
||||
- `dotnet build GFramework.Core.Tests/GFramework.Core.Tests.csproj -c Release`
|
||||
- 结果:成功;`0 Warning(s)`、`0 Error(s)`
|
||||
- `dotnet format GFramework.sln --verify-no-changes --include GFramework.Game/Config/YamlConfigLoader.cs GFramework.Game.Tests/Config/YamlConfigLoaderTests.cs GFramework.Core.Tests/Ioc/IMixedService.cs GFramework.Core.Tests/Ioc/IPrioritizedService.cs GFramework.Core.Tests/Ioc/PrioritizedService.cs GFramework.Core.Tests/Query/TestAsyncQueryWithExceptionV4.cs`
|
||||
- 结果:成功
|
||||
- 当前结论:
|
||||
- PR `#297` 当前仍然有效的 CodeRabbit open threads 与 folded nitpick 已在本地全部核对并收敛
|
||||
- 当前恢复点完成后,分支可以回到 `ArchitectureContextTests.cs` / `AsyncQueryExecutorTests.cs` / `YamlConfigSchemaValidator*` 的 warning reduction 主线
|
||||
- 下一步:
|
||||
1. 提交本轮 PR review follow-up。
|
||||
2. 继续执行下一波 `MA0048` 小切片,优先避免一次性进入 `Mediator*` 的高 changed-file 风险波次。
|
||||
|
||||
## 2026-04-27 — RP-083
|
||||
|
||||
### 阶段:修复 `YamlConfigLoader` 单文件 warning,并拆分 `MicrosoftDiContainerTests` 的辅助类型
|
||||
|
||||
- 触发背景:
|
||||
- 用户执行 `$gframework-batch-boot 50`,要求先拿仓库根构建 warning,再按 bounded slice 分派给不同 subagent 并持续推进
|
||||
- 当前分支在本轮开始时与 `origin/main@b6a9fef` 零提交差异,适合从低风险 warning slice 起步
|
||||
- 主线程实施:
|
||||
- 先执行 non-incremental 仓库根基线:`dotnet clean` + `dotnet build`,得到 `397 Warning(s)` / `316` 个唯一位点
|
||||
- 主线程修复 `GFramework.Game/Config/YamlConfigLoader.cs` 的 `MA0051`、`MA0002` 与 `MA0158`
|
||||
- 接受一个 worker batch:将 `GFramework.Core.Tests/Ioc/MicrosoftDiContainerTests.cs` 末尾的 `10` 个测试辅助接口/类拆分到 `Ioc/` 同目录独立文件
|
||||
- 接受第二波 worker 的已落地结果:将 `GFramework.Core.Tests/Query/AbstractAsyncQueryTests.cs` 末尾的 `7` 个测试辅助类型拆分到 `Query/` 同目录独立文件
|
||||
- 启动 `ArchitectureContextTests.cs` 候选 worker,但在共享工作树落地前主动停止,以避免本轮上下文与 review 面积继续膨胀
|
||||
- 验证里程碑:
|
||||
- `dotnet build GFramework.Game/GFramework.Game.csproj -c Release`
|
||||
- 结果:成功;`111 Warning(s)`、`0 Error(s)`
|
||||
- 观察:构建输出未再报告 `GFramework.Game/Config/YamlConfigLoader.cs`
|
||||
- `dotnet build GFramework.Core.Tests/GFramework.Core.Tests.csproj -c Release`
|
||||
- 结果:成功;`0 Warning(s)`、`0 Error(s)`
|
||||
- `dotnet clean`
|
||||
- 结果:成功;刷新最终 non-incremental 仓库根 warning 基线
|
||||
- `dotnet build`
|
||||
- 结果:成功;`353 Warning(s)`、`0 Error(s)`,唯一位点 `279`
|
||||
- 观察:构建输出未再报告 `GFramework.Game/Config/YamlConfigLoader.cs`、`GFramework.Core.Tests/Ioc/MicrosoftDiContainerTests.cs` 与 `GFramework.Core.Tests/Query/AbstractAsyncQueryTests.cs`
|
||||
- 当前结论:
|
||||
- 本轮已完成一个主线程单文件 slice 和两个 worker 拆分 slice;仓库根 non-incremental warning 从 `397` 降到 `353`
|
||||
- 当前共享工作树 footprint 为 `22` 个 changed files,仍低于 `$gframework-batch-boot 50` 的停止线
|
||||
- 下一波更适合继续处理 `7` 个 `MA0048` 的小文件,而不是立即进入 `Mediator*` 或 `YamlConfigSchemaValidator*` 的高耦合热点
|
||||
- `dotnet test GFramework.Core.Tests/GFramework.Core.Tests.csproj -c Release --no-build --filter "FullyQualifiedName~ArchitectureServicesTests|FullyQualifiedName~ContextAwareServiceExtensionsTests|FullyQualifiedName~TestArchitectureContextBehaviorTests|FullyQualifiedName~RegistryInitializationHookBaseTests|FullyQualifiedName~ArchitectureContextTests"`
|
||||
- 结果:成功;`67` 通过、`0` 失败
|
||||
- `git diff --check`
|
||||
- 结果:成功;无新增 whitespace / conflict-marker 问题
|
||||
|
||||
## 活跃风险
|
||||
|
||||
- `GFramework.Cqrs.Tests/Mediator/*` 的 `MA0048` 位点密度很高,一次性拆分会迅速推高 changed-file 数。
|
||||
- 缓解措施:下一波优先继续拿 `7` warning 级别的小切片。
|
||||
- `YamlConfigSchemaValidator*` 仍然聚集多类高耦合 warning。
|
||||
- 缓解措施:继续维持为独立波次,不与测试项目拆分混提。
|
||||
- GitHub PR 上的 open threads 在本地提交前仍可能显示为未关闭。
|
||||
- 缓解措施:以当前工作树和定向验证作为真值,推送后再让 PR 线程重新比对最新 head。
|
||||
- `GFramework.Core.Tests` 项目当前存在独立于本轮改动的 `dotnet format` 基线。
|
||||
- 缓解措施:保持为后续单独格式治理切片,不在当前 PR review follow-up 中扩写。
|
||||
|
||||
## 下一步
|
||||
|
||||
1. 完成本轮 `YamlConfigLoader.cs`、`MicrosoftDiContainerTests.cs` 与 `ai-plan` 的提交。
|
||||
2. 下一波优先从 `ArchitectureContextTests.cs` 或 `AsyncQueryExecutorTests.cs` 继续拆分纯 `MA0048`。
|
||||
1. 提交本轮 `ai-plan` 同步修复,使 PR head 能重新折叠文档相关线程。
|
||||
2. 推送后重新执行 `$gframework-pr-review`,确认剩余 open threads 是否已经下降。
|
||||
|
||||
## 历史归档指针
|
||||
|
||||
- 最新 trace 归档:
|
||||
- [analyzer-warning-reduction-history-rp083-rp088.md](../archive/traces/analyzer-warning-reduction-history-rp083-rp088.md)
|
||||
- [analyzer-warning-reduction-history-rp073-rp078.md](../archive/traces/analyzer-warning-reduction-history-rp073-rp078.md)
|
||||
- [analyzer-warning-reduction-history-rp062-rp071.md](../archive/traces/analyzer-warning-reduction-history-rp062-rp071.md)
|
||||
- 历史 todo 归档:
|
||||
|
||||
@ -0,0 +1,135 @@
|
||||
# Documentation Full Coverage Governance Validation History (RP-041 to RP-048)
|
||||
|
||||
## 2026-04-28 / RP-048 active tracking follow-up
|
||||
|
||||
### PR review 抓取
|
||||
|
||||
- `python3 .agents/skills/gframework-pr-review/scripts/fetch_current_pr_review.py --format json --json-output /tmp/current-pr-review.json`
|
||||
- 结果:通过;PR `#299` 处于 `OPEN`,latest head review 有 `1` 条 `CodeRabbit` open thread 与 `1` 条 nitpick,`Greptile` / `Gemini Code Assist` 当前无 open thread,测试汇总为 `2159 passed`,仅剩 `Title check` inconclusive。
|
||||
|
||||
### 站点构建
|
||||
|
||||
- `bun run build`(工作目录:`docs/`)
|
||||
- 结果:通过;active tracking 收口与时间线归档瘦身后站点仍可构建,仅保留既有大 chunk warning。
|
||||
|
||||
## 2026-04-28 / RP-048
|
||||
|
||||
### PR review 抓取
|
||||
|
||||
- `python3 .agents/skills/gframework-pr-review/scripts/fetch_current_pr_review.py --format json --json-output /tmp/current-pr-review.json`
|
||||
- 结果:通过;PR `#299` 处于 `OPEN`,latest head review 有 `3` 条 `CodeRabbit` open thread,`Greptile` / `Gemini Code Assist` 当前无 open thread,测试汇总为 `2159 passed`,仅剩 `Title check` inconclusive。
|
||||
|
||||
### 页面校验
|
||||
|
||||
- `bash .agents/skills/gframework-doc-refresh/scripts/validate-all.sh docs/zh-CN/abstractions/index.md`
|
||||
- 结果:通过。
|
||||
- `bash .agents/skills/gframework-doc-refresh/scripts/validate-all.sh docs/zh-CN/core/lifecycle.md`
|
||||
- 结果:通过。
|
||||
- `bash .agents/skills/gframework-doc-refresh/scripts/validate-all.sh docs/zh-CN/tutorials/state-machine-tutorial.md`
|
||||
- 结果:通过。
|
||||
- `bash .agents/skills/gframework-doc-refresh/scripts/validate-all.sh docs/zh-CN/tutorials/resource-management.md`
|
||||
- 结果:通过。
|
||||
- `bash .agents/skills/gframework-doc-refresh/scripts/validate-all.sh docs/zh-CN/tutorials/save-system.md`
|
||||
- 结果:通过。
|
||||
- `bash .agents/skills/gframework-doc-refresh/scripts/validate-all.sh docs/zh-CN/tutorials/pause-system.md`
|
||||
- 结果:通过。
|
||||
- `bash .agents/skills/gframework-doc-refresh/scripts/validate-all.sh docs/zh-CN/tutorials/large-project-organization.md`
|
||||
- 结果:通过。
|
||||
- `bash .agents/skills/gframework-doc-refresh/scripts/validate-all.sh docs/zh-CN/troubleshooting.md`
|
||||
- 结果:通过。
|
||||
|
||||
### 站点构建
|
||||
|
||||
- `bun run build`(工作目录:`docs/`)
|
||||
- 结果:通过;站点仍可构建,仅保留既有大 chunk warning。
|
||||
|
||||
## 2026-04-27 / RP-045 到 RP-047
|
||||
|
||||
### 第 1 批次 reader-facing 入口收口
|
||||
|
||||
- `bash .agents/skills/gframework-doc-refresh/scripts/validate-all.sh docs/zh-CN/source-generators/index.md`
|
||||
- `bash .agents/skills/gframework-doc-refresh/scripts/validate-all.sh docs/zh-CN/game/index.md`
|
||||
- `bash .agents/skills/gframework-doc-refresh/scripts/validate-all.sh docs/zh-CN/api-reference/index.md`
|
||||
- `bash .agents/skills/gframework-doc-refresh/scripts/validate-all.sh docs/zh-CN/godot/setting.md`
|
||||
- `bash .agents/skills/gframework-doc-refresh/scripts/validate-all.sh docs/zh-CN/abstractions/index.md`
|
||||
- `bun run build`(工作目录:`docs/`)
|
||||
- 结果:通过;第 1 批次 5 个入口页校验与站点构建通过。
|
||||
|
||||
### 第 2 批次去内部参考路径暴露
|
||||
|
||||
- `bash .agents/skills/gframework-doc-refresh/scripts/validate-all.sh docs/zh-CN/game/ui.md`
|
||||
- `bash .agents/skills/gframework-doc-refresh/scripts/validate-all.sh docs/zh-CN/godot/signal.md`
|
||||
- `bash .agents/skills/gframework-doc-refresh/scripts/validate-all.sh docs/zh-CN/source-generators/godot-project-generator.md`
|
||||
- `bash .agents/skills/gframework-doc-refresh/scripts/validate-all.sh docs/zh-CN/source-generators/get-node-generator.md`
|
||||
- `bash .agents/skills/gframework-doc-refresh/scripts/validate-all.sh docs/zh-CN/source-generators/bind-node-signal-generator.md`
|
||||
- `bash .agents/skills/gframework-doc-refresh/scripts/validate-all.sh docs/zh-CN/source-generators/auto-register-exported-collections-generator.md`
|
||||
- `bun run build`(工作目录:`docs/`)
|
||||
- 结果:通过;第 2 批次 6 个公开页面校验与站点构建通过。
|
||||
|
||||
### 第 3、4 批次旧入口/旧文档对比收口
|
||||
|
||||
- `bash .agents/skills/gframework-doc-refresh/scripts/validate-all.sh docs/zh-CN/core/query.md`
|
||||
- `bash .agents/skills/gframework-doc-refresh/scripts/validate-all.sh docs/zh-CN/core/command.md`
|
||||
- `bash .agents/skills/gframework-doc-refresh/scripts/validate-all.sh docs/zh-CN/core/context.md`
|
||||
- `bash .agents/skills/gframework-doc-refresh/scripts/validate-all.sh docs/zh-CN/game/scene.md`
|
||||
- `bash .agents/skills/gframework-doc-refresh/scripts/validate-all.sh docs/zh-CN/game/ui.md`
|
||||
- `bash .agents/skills/gframework-doc-refresh/scripts/validate-all.sh docs/zh-CN/godot/ui.md`
|
||||
- `bash .agents/skills/gframework-doc-refresh/scripts/validate-all.sh docs/zh-CN/source-generators/priority-generator.md`
|
||||
- `bash .agents/skills/gframework-doc-refresh/scripts/validate-all.sh docs/zh-CN/core/lifecycle.md`
|
||||
- `bash .agents/skills/gframework-doc-refresh/scripts/validate-all.sh docs/zh-CN/godot/scene.md`
|
||||
- `bash .agents/skills/gframework-doc-refresh/scripts/validate-all.sh docs/zh-CN/source-generators/context-aware-generator.md`
|
||||
- `bun run build`(工作目录:`docs/`)
|
||||
- 结果:通过;第 3、4 批次 10 个公开页面校验与站点构建通过。
|
||||
|
||||
### PR review follow-up 与 README / landing 补充验证
|
||||
|
||||
- `python3 .agents/skills/gframework-pr-review/scripts/fetch_current_pr_review.py --json-output /tmp/current-pr-review.json`
|
||||
- `bash .agents/skills/gframework-doc-refresh/scripts/validate-links.sh GFramework.Game.SourceGenerators/README.md GFramework.Game/README.md`
|
||||
- `bash .agents/skills/gframework-doc-refresh/scripts/validate-all.sh docs/zh-CN/tutorials/godot-integration.md`
|
||||
- `bash .agents/skills/gframework-doc-refresh/scripts/validate-all.sh docs/zh-CN/godot/extensions.md`
|
||||
- `bash .agents/skills/gframework-doc-refresh/scripts/validate-links.sh GFramework.Core.Abstractions/README.md GFramework.Game.Abstractions/README.md GFramework.Game.SourceGenerators/README.md GFramework.Ecs.Arch.Abstractions/README.md`
|
||||
- `bash .agents/skills/gframework-doc-refresh/scripts/validate-all.sh docs/zh-CN/getting-started`
|
||||
- `bash .agents/skills/gframework-doc-refresh/scripts/validate-all.sh docs/zh-CN/game/config-system.md`
|
||||
- `bash .agents/skills/gframework-doc-refresh/scripts/validate-all.sh docs/zh-CN/tutorials/basic`
|
||||
- `bash .agents/skills/gframework-doc-refresh/scripts/validate-links.sh GFramework.Game/README.md GFramework.Game.Abstractions/README.md GFramework.Godot/README.md GFramework.Cqrs.Abstractions/README.md GFramework.Ecs.Arch/README.md`
|
||||
- `bash .agents/skills/gframework-doc-refresh/scripts/validate-all.sh docs/zh-CN/tutorials/index.md`
|
||||
- `bash .agents/skills/gframework-doc-refresh/scripts/validate-all.sh docs/zh-CN/godot/ui.md`
|
||||
- `bash .agents/skills/gframework-doc-refresh/scripts/validate-all.sh docs/zh-CN/godot/scene.md`
|
||||
- `bash .agents/skills/gframework-doc-refresh/scripts/validate-all.sh docs/zh-CN/godot/signal.md`
|
||||
- `bash .agents/skills/gframework-doc-refresh/scripts/validate-all.sh docs/zh-CN/abstractions`
|
||||
- `bash .agents/skills/gframework-doc-refresh/scripts/validate-all.sh docs/zh-CN/source-generators`
|
||||
- `bash .agents/skills/gframework-doc-refresh/scripts/validate-all.sh docs/zh-CN/core/index.md`
|
||||
- `bash .agents/skills/gframework-doc-refresh/scripts/validate-all.sh docs/zh-CN/game/index.md`
|
||||
- `bash .agents/skills/gframework-doc-refresh/scripts/validate-all.sh docs/zh-CN/api-reference/index.md`
|
||||
- `bash .agents/skills/gframework-doc-refresh/scripts/validate-all.sh docs/zh-CN/getting-started/quick-start.md`
|
||||
- `bash .agents/skills/gframework-doc-refresh/scripts/validate-all.sh docs/zh-CN/core/cqrs.md`
|
||||
- `bash .agents/skills/gframework-doc-refresh/scripts/validate-all.sh docs/zh-CN/game/scene.md`
|
||||
- `bash .agents/skills/gframework-doc-refresh/scripts/validate-all.sh docs/zh-CN/game/ui.md`
|
||||
- `bash .agents/skills/gframework-doc-refresh/scripts/validate-all.sh docs/zh-CN/ecs/arch.md`
|
||||
- `bash .agents/skills/gframework-doc-refresh/scripts/validate-all.sh docs/zh-CN/tutorials/godot-integration.md`
|
||||
- `bash .agents/skills/gframework-doc-refresh/scripts/validate-all.sh docs/zh-CN/game/setting.md`
|
||||
- `bash .agents/skills/gframework-doc-refresh/scripts/validate-all.sh docs/zh-CN/game/serialization.md`
|
||||
- `bash .agents/skills/gframework-doc-refresh/scripts/validate-all.sh docs/zh-CN/godot/index.md`
|
||||
- `bash .agents/skills/gframework-doc-refresh/scripts/validate-all.sh docs/zh-CN/godot/architecture.md`
|
||||
- `bash .agents/skills/gframework-doc-refresh/scripts/validate-all.sh docs/zh-CN/godot/storage.md`
|
||||
- `bash .agents/skills/gframework-doc-refresh/scripts/validate-all.sh docs/zh-CN/godot/logging.md`
|
||||
- `bash .agents/skills/gframework-doc-refresh/scripts/validate-all.sh docs/zh-CN/godot/setting.md`
|
||||
- `bash .agents/skills/gframework-doc-refresh/scripts/validate-all.sh docs/zh-CN/godot/extensions.md`
|
||||
- `bash .agents/skills/gframework-doc-refresh/scripts/validate-all.sh docs/zh-CN/core/architecture.md`
|
||||
- `bun run build`(工作目录:`docs/`)
|
||||
- 结果:通过;相关 README、landing page 与 review follow-up 页面校验通过,站点构建通过。
|
||||
|
||||
## 2026-04-25 到 2026-04-26 / carry-over
|
||||
|
||||
### 代表性验证命令
|
||||
|
||||
- `python3 .agents/skills/gframework-pr-review/scripts/fetch_current_pr_review.py --json-output /tmp/gframework-current-pr-review.json`
|
||||
- `bash .agents/skills/gframework-doc-refresh/scripts/validate-links.sh GFramework.Core/README.md GFramework.Ecs.Arch/README.md GFramework.Game/README.md`
|
||||
- `bash .agents/skills/gframework-doc-refresh/scripts/validate-all.sh docs/zh-CN`
|
||||
- `bash .agents/skills/gframework-doc-refresh/scripts/validate-all.sh docs/zh-CN/tutorials`
|
||||
- `bash .agents/skills/gframework-doc-refresh/scripts/validate-all.sh docs/zh-CN/troubleshooting.md`
|
||||
- `dotnet build GFramework.csproj -c Release`
|
||||
- `bun run build`(工作目录:`docs/`)
|
||||
- `bun run test`(工作目录:`tools/gframework-config-tool/`)
|
||||
- `bun run package:vsix`(工作目录:`tools/gframework-config-tool/`)
|
||||
- 结果:通过;更早一轮的 README、导航、教程、排障、工具 README 与元包校验结果仍由这些命令覆盖,详细结论可结合 trace 中的 RP-041 到 RP-047 条目与 Git 历史继续追溯。
|
||||
@ -0,0 +1,55 @@
|
||||
# Documentation Full Coverage Governance Trace History (RP-041 to RP-048)
|
||||
|
||||
## Scope
|
||||
|
||||
- 该归档记录 `RP-041` 到 `RP-048` 从 active trace 迁出的阶段性时间线,保留每轮恢复点、主要决策与停止条件。
|
||||
- 逐命令验证明细继续保存在:
|
||||
`ai-plan/public/documentation-full-coverage-governance/archive/todos/documentation-full-coverage-governance-validation-history-rp-041-to-rp-048-2026-04-28.md`
|
||||
|
||||
## 2026-04-28 / RP-048
|
||||
|
||||
- 通过 `$gframework-pr-review` 重新抓取 PR `#299` 后确认:latest head review 只剩 `1` 条 `CodeRabbit` open thread 与 `1` 条 nitpick,且都指向 active tracking 文档,而非公开文档页面本身。
|
||||
- 本地复核确认此前对抽象层入口语义化链接、生命周期入口示例与教程 / 排障页的修正已不再构成当前 remote latest-head review 的剩余阻塞。
|
||||
- 因此本轮收敛为 active tracking 收口:补齐 `RP-048` 下一步,并将 `RP-045` 到 `RP-047` 的逐命令历史从 active trace 下沉到 archive。
|
||||
|
||||
## 2026-04-27 / RP-047
|
||||
|
||||
- 第 `2` 批次提交 `2d2cd0c` 后,branch diff 相对 `origin/main` 为 `13` files / `124` lines,仍明显低于 `$gframework-batch-boot 50` 的 stop condition。
|
||||
- 最后一批集中收口 `core/query.md`、`core/command.md`、`core/context.md`、`game/scene.md`、`godot/ui.md`、`source-generators/priority-generator.md`、`core/lifecycle.md`、`game/ui.md`、`godot/scene.md`、`source-generators/context-aware-generator.md` 中依赖“旧文档 / 旧入口”对比的句式。
|
||||
- 在这里停止,不是因为达到阈值,而是因为剩余命中已转成需要人工判断是否保留迁移边界或 README 交叉引用的问题。
|
||||
|
||||
## 2026-04-27 / RP-046
|
||||
|
||||
- 第 `1` 批次提交 `c56260b` 后,branch diff 相对 `origin/main` 为 `7` files / `68` lines,仍远低于 stop condition。
|
||||
- 本轮继续处理 `docs/zh-CN/game/ui.md`、`godot/signal.md` 以及 `4` 个 source-generators 专题页,把直接暴露 `ai-libs/CoreGrid` 路径的公开说明改写为项目侧常见实现或典型入口组织方式。
|
||||
- 决策上继续保持单页文字级边界,不把 `ai-libs/**` 当成公开导航或消费者说明,并在每个稳定批次校验通过后立即提交,以便精确计量 branch diff。
|
||||
|
||||
## 2026-04-27 / RP-045
|
||||
|
||||
- 通过 `$gframework-batch-boot 50` 从零 diff 状态重新进入,沿用显式 `--git-dir` / `--work-tree` 绑定,确认当前分支仍为 `docs/sdk-update-documentation`,baseline 固定到最新本地 `origin/main` `7cfdd2c`。
|
||||
- 第 `1` 批次先处理 `docs/zh-CN/source-generators/index.md`、`game/index.md`、`api-reference/index.md`、`godot/setting.md`、`abstractions/index.md` 的 reader-facing 标题、描述与导航措辞,不改示例代码,也不扩栏目结构。
|
||||
- 当轮下一候选批次被限定为继续清理 `ai-libs/CoreGrid` 与“旧文档”式指向表达,保持风险最低且适合批处理。
|
||||
|
||||
## 2026-04-27 / RP-044
|
||||
|
||||
- 本轮从 `$gframework-pr-review` 重新进入,抓取 PR `#296` 后确认 latest reviewed commit 为 `5778782df05e22dd24dc95189dd768458afb8537`,共有 `4` 条 open thread。
|
||||
- 接受的仍成立问题集中在 `GFramework.Game.SourceGenerators/README.md` 的表头语义、`GFramework.Game/README.md` 的重复 `storage.md` 链接,以及 `docs/zh-CN/tutorials/godot-integration.md`、`docs/zh-CN/godot/extensions.md` 的 reader-facing 措辞收口。
|
||||
- 唯一 failed check `Title check` 被明确排除在仓库文件修复范围之外,本轮只做 review 指向文件的最小修正并同步 tracking / trace。
|
||||
|
||||
## 2026-04-27 / RP-043
|
||||
|
||||
- 在提交 `docs(reader-facing): 统一站内入口与公开术语` 后,branch diff 已到 `46` changed files,接近 `$gframework-batch-boot 50` 的停止线。
|
||||
- 因此最后只接受 `10` 个还没进入 branch diff 的文件,包括 `tutorials/godot-integration.md`、`game/setting.md`、`game/serialization.md`、`godot/index.md`、`godot/architecture.md`、`godot/storage.md`、`godot/logging.md`、`godot/setting.md`、`godot/extensions.md`、`core/architecture.md`。
|
||||
- 这些修正统一把 `旧文档`、`ai-libs`、`.Wait()`、`family` 等维护 / 内部口吻改成当前采用指导,并在验证通过后立即停止扩批。
|
||||
|
||||
## 2026-04-27 / RP-042
|
||||
|
||||
- 用户明确允许在 stop condition 内循环推进,并接受使用 subagent 降低主线程上下文压力;主线程保留实现与验证,把热点识别交给 `3` 个 explorer。
|
||||
- 接受的结论主要是:入口页应统一为 reader-facing 骨架;GitHub blob README 不应继续作为公开主导航;多个 README 与 Godot 页面还残留 `ai-libs`、`family`、`seam`、`ReadMe.md` 等对外不友好的表述。
|
||||
- 基于这些结论,连续落地了入口页改写、README / Godot 页面去内部口吻、以及 GitHub blob README 外链回归站内入口等 `3` 组低风险切片。
|
||||
|
||||
## 2026-04-27 / RP-041
|
||||
|
||||
- 这一轮用于提交后重算 branch diff,并确认在继续补一批新文件后,工作树已经来到 `46` changed files,足够触发“接近 stop condition 时收口”的策略。
|
||||
- 因此最后批次刻意限制在 `10` 个尚未进入 branch diff 的文件,并保持全部为 reader-facing 文案修正,不扩大到新结构或示例体系重写。
|
||||
- 验证通过后,将后续恢复建议切换为“下一轮从 PR review 或剩余未触达细页重新开一轮”,而不是在同一轮继续堆文件数。
|
||||
@ -12,17 +12,20 @@
|
||||
|
||||
## 当前恢复点
|
||||
|
||||
- 恢复点编号:`DOCUMENTATION-FULL-COVERAGE-GOV-RP-044`
|
||||
- 恢复点编号:`DOCUMENTATION-FULL-COVERAGE-GOV-RP-048`
|
||||
- 当前阶段:`Phase 5 - Governance Maintenance`
|
||||
- 当前焦点:
|
||||
- 继续以最新 `origin/main`(`617e0bf`,`2026-04-26 12:17:15 +08:00`)作为 baseline,当前批处理 stop condition 仍是 branch diff vs baseline 接近 `50` changed files
|
||||
- 本轮从 `$gframework-pr-review` 重新抓取当前 PR `#296`,确认 latest reviewed commit 为 `5778782df05e22dd24dc95189dd768458afb8537`,剩余 open thread 都落在 reader-facing 文案与 README 导航收口上
|
||||
- 当前工作树相对 `origin/main` 的 tracked diff 仍接近 `50` files;因此本轮只接受 latest-head review 中仍成立的 4 条低风险修正,不再扩新栏目或新专题页
|
||||
- 已确认 `Title check` 的 inconclusive 仅是 GitHub PR 标题元数据提示,不属于仓库文件内可修复范围;本轮只处理本地仍成立的文档线程
|
||||
- 按 `$gframework-pr-review` 复核 PR `#299` 的 latest-head review,并收口当前仍成立的 active tracking 问题:补齐 `RP-048` 下一步,以及把 `RP-045` 到 `RP-047` 的逐命令历史迁入归档
|
||||
- 本轮通过 `$gframework-batch-boot 50` 重新进入后确认 `HEAD == origin/main`,当前已提交 branch diff 为 `0` files / `0` lines,因此可以从新的低风险文档批次重新累计阈值
|
||||
- 当前已完成 4 个低风险批次,并在本轮额外完成 1 次 review-driven 收口:入口页 reader-facing 标题统一、内部参考路径去暴露,以及 `Core` / `Game` / `Godot` / `source-generators` 多个页面中“旧文档式对比”提示的直接契约化改写
|
||||
- 第 2 个已提交批次结束时,branch diff 相对 `origin/main` 为 `13` files / `124` lines;本轮最后一批提交后仍会明显低于 `50` 文件 stop condition
|
||||
- 本轮已确认 `Architecture` 只暴露 `OnInitialize()`,`AbstractArchitecture` 通过 `InstallModules()` 暴露模块注册入口,而组件级 `OnInit()` 仍然是当前有效生命周期
|
||||
- 当前建议在本轮停止:active tracking 收口完成并推送后,只需重新抓取 PR `#299`,确认剩余项是否只剩 GitHub 侧 `Title check` 元数据提示
|
||||
|
||||
## 当前状态摘要
|
||||
|
||||
- `Core`、`Ecs.Arch`、`Cqrs`、`Game`、`Godot` 五个模块族当前都已有 README / landing / topic / API 参考层级的已验证入口。
|
||||
- `2026-04-28` 已重新抓取 PR `#299` 并复核 latest-head review:remote 当前只剩 `1` 条 `CodeRabbit` open thread 与 `1` 条 nitpick,且都指向 active tracking 文档;`Greptile` / `Gemini Code Assist` 当前无 open thread,测试汇总为 `2159 passed`,`Title check` 仍是 PR 元数据问题。
|
||||
- `2026-04-25` 已重新抓取 PR `#290` 并确认:latest reviewed commit 为 `54b8e5770af9ab3c8a86a396ffa4794fe4bb5181`,open thread 聚焦在 `docs/.vitepress/config.mts` 的侧栏重复 / 标签不一致,以及 `GFramework.Core`、`GFramework.Ecs.Arch`、`GFramework.Game` README 的 reader-facing 表格残留治理字段。
|
||||
- `2026-04-25` `docs/.vitepress/config.mts` 已保留 `source-generators` 栏目自有子页导航,但不再让 `api-reference` 侧栏重复跳回 `core`、`game`、`godot`、`ecs` 等独立栏目入口。
|
||||
- `2026-04-25` `GFramework.Core/README.md`、`GFramework.Ecs.Arch/README.md`、`GFramework.Game/README.md` 当前把 XML 阅读表统一收敛为“代表类型 + 阅读重点”,不再暴露日期、覆盖计数或 `已覆盖` 这类治理式字段。
|
||||
@ -35,6 +38,9 @@
|
||||
- `2026-04-27` `GFramework.Core.Abstractions/README.md`、`GFramework.Game.Abstractions/README.md`、`GFramework.Game.SourceGenerators/README.md`、`GFramework.Ecs.Arch.Abstractions/README.md` 当前都已把 XML 阅读入口改写为“代表类型 + 阅读重点”,不再暴露覆盖计数、日期或 `已覆盖` 这类治理字段。
|
||||
- `2026-04-27` `docs/zh-CN/game/config-system.md` 与 `docs/zh-CN/tutorials/basic/index.md` 已把维护者 / 指挥式措辞改成中性的采用建议与阅读入口,避免公开页面继续暴露内部决策口吻。
|
||||
- `2026-04-27` `docs/zh-CN/getting-started/index.md`、`core/index.md`、`game/index.md`、`api-reference/index.md`、`source-generators/index.md` 已统一收敛为“适用场景 / 起步路线 / 继续阅读”式 reader-facing 入口,不再把 GitHub blob README 或治理说明当作主导航。
|
||||
- `2026-04-27` 新一轮 batch boot 第 1 批次已进一步收口 `docs/zh-CN/source-generators/index.md`、`game/index.md`、`api-reference/index.md`、`godot/setting.md`、`abstractions/index.md` 的标题与导航口吻,去掉 `family`、自我指涉标题、原始 `README.md` 文件名提示和“先理解…”式栏目标题。
|
||||
- `2026-04-27` 新一轮 batch boot 第 2 批次已把 `docs/zh-CN/game/ui.md`、`godot/signal.md`、`source-generators/godot-project-generator.md`、`get-node-generator.md`、`bind-node-signal-generator.md`、`auto-register-exported-collections-generator.md` 中直接暴露 `ai-libs/CoreGrid` 的路径型说明改成项目侧常见实现说明。
|
||||
- `2026-04-27` 新一轮 batch boot 第 3、4 批次已把 `core/query.md`、`core/command.md`、`core/context.md`、`core/lifecycle.md`、`game/scene.md`、`game/ui.md`、`godot/ui.md`、`godot/scene.md`、`source-generators/priority-generator.md`、`context-aware-generator.md` 中依赖“旧文档/旧入口”对比的句式改成直接陈述当前契约与推荐入口。
|
||||
- `2026-04-27` `GFramework.Game/README.md`、`GFramework.Game.Abstractions/README.md`、`GFramework.Godot/README.md`、`GFramework.Cqrs.Abstractions/README.md`、`GFramework.Ecs.Arch/README.md` 已收口 `ai-libs`、`family`、`seam`、`ReadMe.md` 等内部化或文件名式表述。
|
||||
- `2026-04-27` `docs/zh-CN` 当前已清空所有指向 `github.com/GeWuYou/GFramework/blob/main/.../README.md` 的公开外链,相关入口统一回到站内栏目页、专题页或 API 导航。
|
||||
- `2026-04-27` `docs/zh-CN/tutorials/godot-integration.md`、`game/setting.md`、`game/serialization.md`、`godot/index.md`、`godot/architecture.md`、`godot/storage.md`、`godot/logging.md`、`godot/setting.md`、`godot/extensions.md`、`core/architecture.md` 已把 `旧文档` / `ai-libs` / `.Wait()` / `family` 这类维护与内部语气改写成当前采用说明。
|
||||
@ -58,7 +64,8 @@
|
||||
`MSB4276` / `MSB4018`;这是已知环境阻塞,不属于本轮文档回归。
|
||||
- 当前 WSL 会话里 `git.exe` 可解析但不能执行,应继续使用显式 `--git-dir` / `--work-tree` 绑定作为默认 Git 策略。
|
||||
- `dotnet build GFramework.csproj -c Release` 当前仍会输出仓库既有 analyzer warnings(如 `MA0158`、`MA0051`、`MA0004`);本轮仅修改文档与 package metadata,不扩展到 warning 清理。
|
||||
- PR `#296` 当前 review 线程仍主要来自 CodeRabbit 与 Greptile,对 reader-facing 文案和文档入口连通性要求较细;本轮提交后仍需重新抓取 latest-head review,确认 open thread 是否已自动关闭。
|
||||
- 当前 batch boot 已从 `origin/main` 零 diff 状态重新起步并完成 4 个低风险文案批次;剩余命中更偏向是否保留迁移说明的编辑判断,不再适合继续按同一批处理模式机械推进。
|
||||
- PR `#299` 在 remote latest-head review 中仍显示 `1` 条 open thread 与 `1` 条 nitpick,直到本轮 `ai-plan` 改动推送后才会反映关闭状态;`Title check` 仍需要直接修改 GitHub 上的 PR 标题。
|
||||
|
||||
## 归档指针
|
||||
|
||||
@ -72,174 +79,23 @@
|
||||
`ai-plan/public/documentation-full-coverage-governance/archive/traces/documentation-full-coverage-governance-trace-history-through-rp-016.md`
|
||||
- 时间线归档(`RP-023` 到 `RP-025`):
|
||||
`ai-plan/public/documentation-full-coverage-governance/archive/traces/documentation-full-coverage-governance-trace-history-rp-023-to-rp-025-2026-04-24.md`
|
||||
- 时间线归档(`RP-041` 到 `RP-048`):
|
||||
`ai-plan/public/documentation-full-coverage-governance/archive/traces/documentation-full-coverage-governance-trace-history-rp-041-to-rp-048-2026-04-28.md`
|
||||
- 验证历史归档(`RP-041` 到 `RP-048`):
|
||||
`ai-plan/public/documentation-full-coverage-governance/archive/todos/documentation-full-coverage-governance-validation-history-rp-041-to-rp-048-2026-04-28.md`
|
||||
|
||||
## 最新验证
|
||||
|
||||
- `2026-04-27` `bash .agents/skills/gframework-doc-refresh/scripts/validate-links.sh GFramework.Game.SourceGenerators/README.md GFramework.Game/README.md`
|
||||
- 结果:通过;本轮 2 个 README 的 reader-facing 表格与导航去重调整后链接目标有效。
|
||||
- `2026-04-27` `bash .agents/skills/gframework-doc-refresh/scripts/validate-all.sh docs/zh-CN/tutorials/godot-integration.md`
|
||||
- 结果:通过;Godot 集成教程的措辞收口后页面 frontmatter、链接与代码块校验均通过。
|
||||
- `2026-04-27` `bash .agents/skills/gframework-doc-refresh/scripts/validate-all.sh docs/zh-CN/godot/extensions.md`
|
||||
- 结果:通过;Godot 扩展页去自我指涉表述后页面 frontmatter、链接与代码块校验均通过。
|
||||
- `2026-04-27` `bun run build`(工作目录:`docs/`)
|
||||
- 结果:通过;本轮 PR `#296` review 收口后的站点仍可构建,仅保留既有大 chunk warning。
|
||||
- `2026-04-27` `python3 .agents/skills/gframework-pr-review/scripts/fetch_current_pr_review.py --json-output /tmp/current-pr-review.json`
|
||||
- 结果:通过;PR `#296` 处于 `OPEN`,latest head review 共有 `4` 条 open thread,其中 `3` 条文档问题与 `1` 条措辞 nitpick 在本地复核后仍成立;测试汇总为 `2156 passed`,仅剩 `Title check` inconclusive。
|
||||
- `2026-04-25` `python3 .agents/skills/gframework-pr-review/scripts/fetch_current_pr_review.py --json-output /tmp/gframework-current-pr-review.json`
|
||||
- 结果:通过;PR `#290` 处于 `OPEN`,latest head commit `54b8e5770af9ab3c8a86a396ffa4794fe4bb5181` 有 `2` 条 open thread(CodeRabbit `1`、Greptile `1`),测试汇总为 `2156 passed`,无 failed checks。
|
||||
- `2026-04-25` `python3 .agents/skills/gframework-pr-review/scripts/fetch_current_pr_review.py --json-output /tmp/gframework-current-pr-review.json`
|
||||
- 结果:通过;PR `#292` 处于 `OPEN`,latest head commit `b96565ffa367bade30f44c2d4e8955143fbff85e` 有 `2` 条 CodeRabbit open thread,测试汇总为 `2156 passed`,无 failed tests;另有 `Title check` inconclusive,属于 PR 标题元数据问题,不是仓库文件阻塞。
|
||||
- `2026-04-25` `bash .agents/skills/gframework-doc-refresh/scripts/validate-links.sh GFramework.Core/README.md GFramework.Ecs.Arch/README.md GFramework.Game/README.md`
|
||||
- 结果:通过;本轮 3 个模块 README 调整后链接目标仍然有效。
|
||||
- `2026-04-27` `bash .agents/skills/gframework-doc-refresh/scripts/validate-links.sh GFramework.Core.Abstractions/README.md GFramework.Game.Abstractions/README.md GFramework.Game.SourceGenerators/README.md GFramework.Ecs.Arch.Abstractions/README.md`
|
||||
- 结果:通过;4 个公开模块 README 的 reader-facing 改写后链接目标仍然有效。
|
||||
- `2026-04-27` `bash .agents/skills/gframework-doc-refresh/scripts/validate-all.sh docs/zh-CN/getting-started`
|
||||
- 结果:通过;`installation.md` 更新后 `getting-started` 栏目的 frontmatter、链接与代码块校验均通过。
|
||||
- `2026-04-27` `bash .agents/skills/gframework-doc-refresh/scripts/validate-all.sh docs/zh-CN/game/config-system.md`
|
||||
- 结果:通过;`config-system.md` 的工具形态建议改写后页面校验通过。
|
||||
- `2026-04-27` `bash .agents/skills/gframework-doc-refresh/scripts/validate-all.sh docs/zh-CN/tutorials/basic`
|
||||
- 结果:通过;基础教程入口的阅读路径改写后栏目校验通过。
|
||||
- `2026-04-27` `bash .agents/skills/gframework-doc-refresh/scripts/validate-links.sh GFramework.Game/README.md GFramework.Game.Abstractions/README.md GFramework.Godot/README.md GFramework.Cqrs.Abstractions/README.md GFramework.Ecs.Arch/README.md`
|
||||
- 结果:通过;本轮 5 个模块 README 的 reader-facing 术语与入口改写后链接目标有效。
|
||||
- `2026-04-27` `bash .agents/skills/gframework-doc-refresh/scripts/validate-all.sh docs/zh-CN/tutorials/index.md`
|
||||
- 结果:通过;教程页受众表述改写后页面校验通过。
|
||||
- `2026-04-27` `bash .agents/skills/gframework-doc-refresh/scripts/validate-all.sh docs/zh-CN/godot/ui.md`
|
||||
- 结果:通过;Godot UI 页的接法示例与 reader-facing 术语改写后页面校验通过。
|
||||
- `2026-04-27` `bash .agents/skills/gframework-doc-refresh/scripts/validate-all.sh docs/zh-CN/godot/scene.md`
|
||||
- 结果:通过;Godot 场景页的接法示例与 reader-facing 术语改写后页面校验通过。
|
||||
- `2026-04-27` `bash .agents/skills/gframework-doc-refresh/scripts/validate-all.sh docs/zh-CN/godot/signal.md`
|
||||
- 结果:通过;信号页切回站内生成器入口后页面校验通过。
|
||||
- `2026-04-27` `bash .agents/skills/gframework-doc-refresh/scripts/validate-all.sh docs/zh-CN/abstractions`
|
||||
- 结果:通过;3 个抽象层页改回站内入口后栏目校验通过。
|
||||
- `2026-04-27` `bash .agents/skills/gframework-doc-refresh/scripts/validate-all.sh docs/zh-CN/source-generators`
|
||||
- 结果:通过;生成器栏目及受影响专题页改回站内入口后栏目校验通过。
|
||||
- `2026-04-27` `bash .agents/skills/gframework-doc-refresh/scripts/validate-all.sh docs/zh-CN/core/index.md`
|
||||
- 结果:通过;Core 入口页 reader-facing 改写后页面校验通过。
|
||||
- `2026-04-27` `bash .agents/skills/gframework-doc-refresh/scripts/validate-all.sh docs/zh-CN/game/index.md`
|
||||
- 结果:通过;Game 入口页 reader-facing 改写后页面校验通过。
|
||||
- `2026-04-27` `bash .agents/skills/gframework-doc-refresh/scripts/validate-all.sh docs/zh-CN/api-reference/index.md`
|
||||
- 结果:通过;API 入口页导航改写后页面校验通过。
|
||||
- `2026-04-27` `bash .agents/skills/gframework-doc-refresh/scripts/validate-all.sh docs/zh-CN/getting-started/quick-start.md`
|
||||
- 结果:通过;快速开始页切回站内安装入口后页面校验通过。
|
||||
- `2026-04-27` `bash .agents/skills/gframework-doc-refresh/scripts/validate-all.sh docs/zh-CN/core/cqrs.md`
|
||||
- 结果:通过;CQRS 页继续阅读入口改写后页面校验通过。
|
||||
- `2026-04-27` `bash .agents/skills/gframework-doc-refresh/scripts/validate-all.sh docs/zh-CN/game/scene.md`
|
||||
- 结果:通过;Game 场景页相关推荐改回站内入口后页面校验通过。
|
||||
- `2026-04-27` `bash .agents/skills/gframework-doc-refresh/scripts/validate-all.sh docs/zh-CN/game/ui.md`
|
||||
- 结果:通过;Game UI 页相关推荐改回站内入口后页面校验通过。
|
||||
- `2026-04-27` `bash .agents/skills/gframework-doc-refresh/scripts/validate-all.sh docs/zh-CN/ecs/arch.md`
|
||||
- 结果:通过;ECS Arch 页入口改写后页面校验通过。
|
||||
- `2026-04-27` `bash .agents/skills/gframework-doc-refresh/scripts/validate-all.sh docs/zh-CN/tutorials/godot-integration.md`
|
||||
- 结果:通过;Godot 集成教程的接线口吻改写后页面校验通过。
|
||||
- `2026-04-27` `bash .agents/skills/gframework-doc-refresh/scripts/validate-all.sh docs/zh-CN/game/setting.md`
|
||||
- 结果:通过;设置系统页初始化语义改写后页面校验通过。
|
||||
- `2026-04-27` `bash .agents/skills/gframework-doc-refresh/scripts/validate-all.sh docs/zh-CN/game/serialization.md`
|
||||
- 结果:通过;序列化页生命周期说明改写后页面校验通过。
|
||||
- `2026-04-27` `bash .agents/skills/gframework-doc-refresh/scripts/validate-all.sh docs/zh-CN/godot/index.md`
|
||||
- 结果:通过;Godot landing page 的采用说明改写后页面校验通过。
|
||||
- `2026-04-27` `bash .agents/skills/gframework-doc-refresh/scripts/validate-all.sh docs/zh-CN/godot/architecture.md`
|
||||
- 结果:通过;Godot 架构页异步初始化口吻改写后页面校验通过。
|
||||
- `2026-04-27` `bash .agents/skills/gframework-doc-refresh/scripts/validate-all.sh docs/zh-CN/godot/storage.md`
|
||||
- 结果:通过;Godot 存储页示例口吻改写后页面校验通过。
|
||||
- `2026-04-27` `bash .agents/skills/gframework-doc-refresh/scripts/validate-all.sh docs/zh-CN/godot/logging.md`
|
||||
- 结果:通过;Godot 日志页 provider 接线说明改写后页面校验通过。
|
||||
- `2026-04-27` `bash .agents/skills/gframework-doc-refresh/scripts/validate-all.sh docs/zh-CN/godot/setting.md`
|
||||
- 结果:通过;Godot 设置页 applicator 接线口吻改写后页面校验通过。
|
||||
- `2026-04-27` `bash .agents/skills/gframework-doc-refresh/scripts/validate-all.sh docs/zh-CN/godot/extensions.md`
|
||||
- 结果:通过;Godot 扩展页边界说明改写后页面校验通过。
|
||||
- `2026-04-27` `bash .agents/skills/gframework-doc-refresh/scripts/validate-all.sh docs/zh-CN/core/architecture.md`
|
||||
- 结果:通过;Core 架构页旧初始化入口改写后页面校验通过。
|
||||
- `2026-04-27` `bun run build`(工作目录:`docs/`)
|
||||
- 结果:通过;本轮 README、安装页与公开文案改写后站点仍可构建,仅保留既有大 chunk warning。
|
||||
- `2026-04-25` `bun run build`(工作目录:`docs/`)
|
||||
- 结果:通过;移除 `api-reference` 侧栏重复项并统一 `source-generators` 标签后站点仍可正常构建,仅保留既有大 chunk warning。
|
||||
- `2026-04-25` `bash .agents/skills/gframework-doc-refresh/scripts/validate-links.sh README.md GFramework.Core/README.md GFramework.Core.Abstractions/README.md GFramework.Game/README.md GFramework.Game.Abstractions/README.md GFramework.Game.SourceGenerators/README.md GFramework.Ecs.Arch/README.md GFramework.Ecs.Arch.Abstractions/README.md`
|
||||
- 结果:通过;根 README 与本轮触达的模块 README 链接目标有效。
|
||||
- `2026-04-25` `bash .agents/skills/gframework-doc-refresh/scripts/validate-all.sh docs/zh-CN/tutorials`
|
||||
- 结果:通过;本轮新增触达的 10 个教程页与其余教程页 frontmatter、链接、代码块校验均通过。
|
||||
- `2026-04-25` `bash .agents/skills/gframework-doc-refresh/scripts/validate-all.sh docs/zh-CN/best-practices`
|
||||
- 结果:通过;`index.md` 与 `architecture-patterns.md` 的代码块标记补齐后栏目验证通过。
|
||||
- `2026-04-25` `bash .agents/skills/gframework-doc-refresh/scripts/validate-all.sh docs/zh-CN/troubleshooting.md`
|
||||
- 结果:通过;错误输出与完整错误信息块补齐为 `text` 后页面验证通过。
|
||||
- `2026-04-25` `bash .agents/skills/gframework-doc-refresh/scripts/validate-all.sh docs/zh-CN/contributing.md`
|
||||
- 结果:通过;嵌套 fenced 示例已改写为转义围栏文本,`docs/zh-CN/contributing.md` 不再保留代码块语言警告。
|
||||
- `2026-04-25` `bash .agents/skills/gframework-doc-refresh/scripts/validate-all.sh docs/zh-CN`
|
||||
- 结果:通过;当前 `docs/zh-CN` 全量 frontmatter、链接与代码块校验均通过,不再保留既有代码块语言警告。
|
||||
- `2026-04-25` `bun run build`(工作目录:`docs/`)
|
||||
- 结果:通过;`contributing.md` 的 Mermaid 示例改写后站点仍可正常构建,仅保留既有大 chunk warning。
|
||||
- `2026-04-25` `bash .agents/skills/gframework-doc-refresh/scripts/validate-all.sh docs/zh-CN/source-generators`
|
||||
- 结果:通过;`source-generators` 栏目触达页 frontmatter、链接与代码块校验均通过。
|
||||
- `2026-04-25` `bash .agents/skills/gframework-doc-refresh/scripts/validate-all.sh docs/zh-CN/game`
|
||||
- 结果:通过;新增 `config-tool.md` 与 `Game` 栏目触达页 frontmatter、链接与代码块校验均通过。
|
||||
- `2026-04-25` `bash .agents/skills/gframework-doc-refresh/scripts/validate-all.sh docs/zh-CN/core/cqrs.md`
|
||||
- 结果:通过;`CQRS` 页补充 `Request` / stream 变体与协程入口后链接和代码块校验通过。
|
||||
- `2026-04-25` `bash .agents/skills/gframework-doc-refresh/scripts/validate-all.sh docs/zh-CN/index.md`
|
||||
- 结果:通过;首页 hero actions 与 feature 文案更新后 frontmatter、代码块校验通过。
|
||||
- `2026-04-25` `bash .agents/skills/gframework-doc-refresh/scripts/validate-links.sh README.md tools/gframework-config-tool/README.md GFramework.SourceGenerators.Common/README.md GFramework.Core.SourceGenerators.Abstractions/README.md GFramework.Godot.SourceGenerators.Abstractions/README.md`
|
||||
- 结果:通过;根 README、config tool README 与新增 3 个 support README 的链接目标有效。
|
||||
- `2026-04-25` `dotnet build GFramework.csproj -c Release`
|
||||
- 结果:通过;元包工程与聚合依赖可编译,输出 `357` 条既有 analyzer warnings,无新增错误。
|
||||
- `2026-04-25` `bun run build`(工作目录:`docs/`)
|
||||
- 结果:通过;meta-package / config tool / source-generators / CQRS 多批次文档更新后站点仍可构建,仅保留既有大 chunk warning。
|
||||
- `2026-04-25` `bash .agents/skills/gframework-doc-refresh/scripts/validate-all.sh docs/zh-CN/godot/resource.md`
|
||||
- 结果:通过;`Godot` 资源页剩余 bare opening fence 已补齐语言标记。
|
||||
- `2026-04-25` `bun run build`(工作目录:`docs/`)
|
||||
- 结果:通过;本轮导航补齐、README reader-facing 改写与教程 / 排障 / 资源页代码块语言标记更新后站点仍可构建,仅保留既有大 chunk warning。
|
||||
- `2026-04-25` `bash .agents/skills/gframework-doc-refresh/scripts/validate-links.sh GFramework.Core/README.md`
|
||||
- 结果:通过;README 链接目标有效。
|
||||
- `2026-04-25` `bash .agents/skills/gframework-doc-refresh/scripts/validate-links.sh GFramework.Core.SourceGenerators/README.md`
|
||||
- 结果:通过;README 链接目标有效。
|
||||
- `2026-04-25` `bash .agents/skills/gframework-doc-refresh/scripts/validate-links.sh GFramework.Cqrs.SourceGenerators/README.md`
|
||||
- 结果:通过;README 链接目标有效。
|
||||
- `2026-04-25` `bash .agents/skills/gframework-doc-refresh/scripts/validate-links.sh GFramework.Ecs.Arch/README.md`
|
||||
- 结果:通过;README 链接目标有效。
|
||||
- `2026-04-25` `bash .agents/skills/gframework-doc-refresh/scripts/validate-links.sh GFramework.Game.SourceGenerators/README.md`
|
||||
- 结果:通过;README 链接目标有效。
|
||||
- `2026-04-25` `rg -n '\\[[^\\]]*(README\\.md|\\.md|\\.md/|/zh-CN/[^\\]]*)\\]\\([^)]*\\)' GFramework.Core/README.md GFramework.Core.SourceGenerators/README.md GFramework.Cqrs.SourceGenerators/README.md GFramework.Ecs.Arch/README.md GFramework.Game.SourceGenerators/README.md`
|
||||
- 结果:无命中;本轮 5 个 README 已无可见路径式 / 文件名式 Markdown 链接标签残留。
|
||||
- `2026-04-25` `bash .agents/skills/gframework-doc-refresh/scripts/validate-all.sh docs/zh-CN/core`
|
||||
- 结果:通过;`Core` 栏目本轮触达页面的 frontmatter、链接与代码块校验均通过。
|
||||
- `2026-04-25` `bash .agents/skills/gframework-doc-refresh/scripts/validate-all.sh docs/zh-CN/tutorials/basic`
|
||||
- 结果:通过;基础教程栏目本轮触达页面的 frontmatter、链接与代码块校验均通过。
|
||||
- `2026-04-25` `bun run build`(工作目录:`docs/`)
|
||||
- 结果:通过;README 标签修正与 `Core` / 基础教程代码块语言标记补齐后站点仍可构建,仅保留既有大 chunk warning。
|
||||
- `2026-04-24` `python3 .agents/skills/gframework-pr-review/scripts/fetch_current_pr_review.py --json-output /tmp/current-pr-review.json`
|
||||
- 结果:通过;PR `#284` 处于 `OPEN`,latest head commit `77540c07f0890cc05b10a849722c87b8bed8f561` 有 `3` 条 CodeRabbit 与 `1` 条 Greptile open thread,测试汇总为 `2156 passed`,仅剩 `Title check` 的 inconclusive PR 元数据提示。
|
||||
- `2026-04-24` `rg -n --pcre2 '\\]\\(/zh-CN/[^)]+(?<!\\.md)\\)' docs/zh-CN/troubleshooting.md`
|
||||
- 结果:当前无命中;`/zh-CN/core/architecture` 与 `/zh-CN/faq` 已统一补成显式 `.md` 链接。
|
||||
- `2026-04-24` `bun run build`(工作目录:`docs/`)
|
||||
- 结果:通过;文档标题本地化、站内链接修正与 `ai-plan` 归档瘦身落地后站点仍可正常构建,仅保留既有大 chunk warning。
|
||||
- `2026-04-24` `python3 - <<'PY' ...`(扫描 `docs/zh-CN/**/*.md` frontmatter 是否缺 `title` / `description`)
|
||||
- 结果:通过;当前带 frontmatter 的 `docs/zh-CN` 页面已无 `title` / `description` 缺口。
|
||||
- `2026-04-24` `bun run build`(工作目录:`docs/`)
|
||||
- 结果:通过;首页与基础教程 metadata 补齐后站点仍可正常构建,仅保留既有大 chunk warning。
|
||||
- `2026-04-24` `python3 - <<'PY' ...`(扫描 `docs/zh-CN/**/*.md` 中以 `./`、`../`、`/zh-CN/` 开头且未带扩展名的 Markdown 链接)
|
||||
- 结果:通过;当前 `docs/zh-CN` 站内 Markdown 链接已无缺失扩展名的命中。
|
||||
- `2026-04-24` `bun run build`(工作目录:`docs/`)
|
||||
- 结果:通过;`25` 个页面的站内链接补齐为显式 `.md` / `index.md` 后站点仍可正常构建,仅保留既有大 chunk warning。
|
||||
- `2026-04-24` `bun run build`(工作目录:`docs/`)
|
||||
- 结果:通过;模块 README、中文落地页 reader-facing 文档入口对齐,以及 `docs/index.md` metadata 调整后站点仍可正常构建,仅保留既有大 chunk warning。
|
||||
- `2026-04-24` `python3 - <<'PY' ...`(扫描 `docs/zh-CN/**/*.md` 中纯英文 `title`)
|
||||
- 结果:通过;经过三轮标题本地化后,仅剩 `CQRS` 与 `GFramework` 两个品牌/缩写型标题。
|
||||
- `2026-04-24` `bun run build`(工作目录:`docs/`)
|
||||
- 结果:通过;根 `README.md` reader-friendly 链接标签修正与 `docs/zh-CN` 多页标题本地化落地后站点仍可正常构建,仅保留既有大 chunk warning。
|
||||
- `2026-04-25` `python3 .agents/skills/gframework-doc-refresh/scripts/scan_module_evidence.py Core`
|
||||
- 结果:通过;技能仍能正常解析 `Core` 模块证据面,说明新增的 reader-facing 输出约束未破坏模块扫描主流程。
|
||||
- `2026-04-25` `python3 .agents/skills/gframework-pr-review/scripts/fetch_current_pr_review.py --json-output /tmp/current-pr-review.json`
|
||||
- 结果:通过;PR `#287` 处于 `OPEN`,latest head commit `8209d7a29f35d969fca6258b9817da9b33a203a3` 仅剩
|
||||
`1` 条 Greptile open thread,无 failed checks,测试汇总为 `2156 passed`。
|
||||
- `2026-04-25` `bun run build`(工作目录:`docs/`)
|
||||
- 结果:通过;`docs/zh-CN/api-reference/index.md` 的站内入口标签统一为语义化写法后站点仍可正常构建,仅保留既有大 chunk warning。
|
||||
- `2026-04-26` `bun run test`(工作目录:`tools/gframework-config-tool/`)
|
||||
- 结果:通过;`122` 个测试全部通过,说明 README 收口没有影响该工具现有测试面。
|
||||
- `2026-04-26` `bun run package:vsix`(工作目录:`tools/gframework-config-tool/`)
|
||||
- 结果:通过;成功生成 `gframework-config-tool-0.0.3.vsix`,满足本轮工具模块的最小 build validation。
|
||||
- `2026-04-28` `python3 .agents/skills/gframework-pr-review/scripts/fetch_current_pr_review.py --format json --json-output /tmp/current-pr-review.json`
|
||||
- 结果:通过;PR `#299` 处于 `OPEN`,latest head review 有 `1` 条 `CodeRabbit` open thread 与 `1` 条 nitpick,`Greptile` / `Gemini Code Assist` 当前无 open thread,测试汇总为 `2159 passed`,仅剩 `Title check` inconclusive。
|
||||
- `2026-04-28` `bun run build`(工作目录:`docs/`)
|
||||
- 结果:通过;active tracking 收口与时间线归档瘦身后站点仍可构建,仅保留既有大 chunk warning。
|
||||
- `2026-04-27` 到 `2026-04-28` 的详细逐命令验证历史已迁入:
|
||||
`ai-plan/public/documentation-full-coverage-governance/archive/todos/documentation-full-coverage-governance-validation-history-rp-041-to-rp-048-2026-04-28.md`
|
||||
|
||||
## 下一步
|
||||
|
||||
1. 提交当前接近阈值的稳定批次后,优先重新抓取 `$gframework-pr-review` 或在新一轮里按 `46 / 50` 的 branch diff 重新评估是否还适合继续扩批。
|
||||
2. 若后续还要继续文档治理,优先复核尚未触达的 `Game` persistence、Godot runtime 细页与少量残余 `ai-libs` 口吻,而不是继续扩大同一轮 review 面。
|
||||
3. 若后续分支继续调整 `Game` persistence runtime、README 或公共 API,优先复核 `docs/zh-CN/game/data.md`、
|
||||
`storage.md`、`serialization.md`、`setting.md` 与 landing page 是否仍保持同一套职责边界。
|
||||
4. 若后续分支继续调整 `Godot` generator 接法,优先复核 `GFramework.Godot.SourceGenerators/README.md`、
|
||||
`docs/zh-CN/tutorials/godot-integration.md` 与相关专题页是否仍保持一致。
|
||||
1. 推送本轮提交后,优先重新抓取 `$gframework-pr-review`,确认 PR `#299` 仅剩标题元数据提示或已全部清空。
|
||||
2. 若 remote review 清空后仍继续文档治理,优先人工复核尚未触达的 `Game` persistence、Godot runtime 细页与少量残余迁移边界表述,而不是继续按关键词机械扩批。
|
||||
3. 若后续分支继续调整 `Game` persistence runtime、README 或公共 API,优先复核 `docs/zh-CN/game/data.md`、`storage.md`、`serialization.md`、`setting.md` 与 landing page 是否仍保持同一套职责边界。
|
||||
4. 若后续分支继续调整 `Godot` generator 接法,优先复核 `GFramework.Godot.SourceGenerators/README.md`、`docs/zh-CN/tutorials/godot-integration.md` 与相关专题页是否仍保持一致。
|
||||
|
||||
@ -1,7 +1,48 @@
|
||||
# Documentation Full Coverage Governance Trace
|
||||
|
||||
## 2026-04-28
|
||||
|
||||
### 当前恢复点:RP-048
|
||||
|
||||
- 本轮按 `$gframework-pr-review` 抓取当前 PR `#299`,确认 latest head review 当前只剩 `1` 条 `CodeRabbit` open thread 与 `1` 条 nitpick;两者都指向 active tracking 文档本身,`Greptile` 与 `Gemini Code Assist` 当前无 open thread,测试汇总为 `2159 passed`,另有 `Title check` inconclusive。
|
||||
- 本地复核后确认:此前针对 `docs/zh-CN/abstractions/index.md`、`docs/zh-CN/core/lifecycle.md` 与相关教程 / 排障页的 review follow-up 已不再是当前 remote latest-head review 的剩余阻塞项。
|
||||
- 当前仍需收口的只剩两件事:为 `RP-048` 补齐明确的“下一步”段落,以及把 `RP-045` 到 `RP-047` 的逐命令时间线从 active trace 下沉到归档文件。
|
||||
|
||||
### 当前决策(RP-048)
|
||||
|
||||
- 本轮限定只修改 `ai-plan/public/documentation-full-coverage-governance` 下的 tracking / trace 文档,不再扩展到已经收口的公开文档页面。
|
||||
- active trace 只保留当前恢复点、验证结论、下一步与归档指针;`RP-041` 到 `RP-048` 的阶段细节转入专门的 trace archive,逐命令验证继续保留在 validation history archive。
|
||||
- 不把 `Title check` 当成仓库文件修复项;本轮完成后只需要在提交推送后重新抓取 PR review,确认 remote 线程状态是否清空。
|
||||
|
||||
### 当前验证(RP-048)
|
||||
|
||||
- PR review 抓取:
|
||||
- `python3 .agents/skills/gframework-pr-review/scripts/fetch_current_pr_review.py --format json --json-output /tmp/current-pr-review.json`
|
||||
- 结果:通过;PR `#299` 处于 `OPEN`,latest head review 有 `1` 条 `CodeRabbit` open thread 与 `1` 条 nitpick,`Greptile` / `Gemini Code Assist` 当前无 open thread,测试汇总为 `2159 passed`,仅剩 `Title check` inconclusive。
|
||||
- 站点构建:
|
||||
- `bun run build`(工作目录:`docs/`)
|
||||
- 结果:通过;active tracking 收口与时间线归档瘦身后站点仍可构建,仅保留既有大 chunk warning。
|
||||
- 详细时间线归档:
|
||||
- `ai-plan/public/documentation-full-coverage-governance/archive/traces/documentation-full-coverage-governance-trace-history-rp-041-to-rp-048-2026-04-28.md`
|
||||
- 详细验证归档:
|
||||
- `ai-plan/public/documentation-full-coverage-governance/archive/todos/documentation-full-coverage-governance-validation-history-rp-041-to-rp-048-2026-04-28.md`
|
||||
|
||||
### 下一步(RP-048)
|
||||
|
||||
1. 提交本轮 active tracking 收口改动,并将提交推送到 PR `#299`。
|
||||
2. 推送后重新抓取 `$gframework-pr-review`,确认 latest-head review 是否只剩 `Title check` 或已全部清空。
|
||||
3. 若仍有新的文档 review 线程,继续按 latest-head review 精确收口,不恢复关键词驱动的机械扩批。
|
||||
|
||||
## 2026-04-27
|
||||
|
||||
### 已归档历史(RP-041 到 RP-047)
|
||||
|
||||
- `RP-045` 到 `RP-047` 的 batch boot 逐阶段时间线、branch diff 计量与 review follow-up 决策,已迁入专门的 trace archive,避免 active trace 继续保留逐命令历史。
|
||||
- 对应的页面校验、README 链接校验与站点构建命令,继续保留在 validation history archive 中,供后续追溯。
|
||||
- 归档路径:
|
||||
- `ai-plan/public/documentation-full-coverage-governance/archive/traces/documentation-full-coverage-governance-trace-history-rp-041-to-rp-048-2026-04-28.md`
|
||||
- `ai-plan/public/documentation-full-coverage-governance/archive/todos/documentation-full-coverage-governance-validation-history-rp-041-to-rp-048-2026-04-28.md`
|
||||
|
||||
### 当前恢复点:RP-044
|
||||
|
||||
- 本轮从 `$gframework-pr-review` 重新进入,继续沿用显式 `--git-dir` / `--work-tree` 绑定确认当前分支仍为 `docs/sdk-update-documentation`,并通过 `python3 .agents/skills/gframework-pr-review/scripts/fetch_current_pr_review.py --json-output /tmp/current-pr-review.json` 抓取当前 PR `#296`。
|
||||
|
||||
@ -21,9 +21,9 @@ description: GFramework 各抽象层模块的阅读入口与使用边界。
|
||||
|
||||
## 使用建议
|
||||
|
||||
- 如果你只是想直接使用框架功能,优先从对应运行时模块的 `README.md` 和栏目页开始。
|
||||
- 如果你只是想直接使用框架功能,优先从对应运行时模块说明和栏目页开始。
|
||||
- 只有在明确需要“契约层而非实现层”时,才单独依赖 `*.Abstractions` 包。
|
||||
- 抽象层页面会解释接口分组与职责;实际安装与接入路径,仍应以运行时模块 README 与 `getting-started` 为主。
|
||||
- 抽象层页面会解释接口分组与职责;实际安装与接入路径,仍应以对应运行时模块说明和[快速开始](../getting-started/)为主。
|
||||
|
||||
## 当前边界
|
||||
|
||||
|
||||
@ -5,7 +5,7 @@ description: GFramework 的 API 阅读入口,按模块映射 README、专题
|
||||
|
||||
# API 参考
|
||||
|
||||
这页不是签名索引,而是“先看哪个模块入口、再回哪里读 XML 文档”的导航页。
|
||||
本页聚焦“先看哪个模块入口、再回哪里读 XML 文档”的 API 阅读导航。
|
||||
|
||||
最常见的阅读顺序是:
|
||||
|
||||
@ -15,14 +15,14 @@ description: GFramework 的 API 阅读入口,按模块映射 README、专题
|
||||
|
||||
## 阅读顺序
|
||||
|
||||
### 想确认“该装哪个包、先看哪类 API”
|
||||
### 安装与选包入口
|
||||
|
||||
先读站内入口页:
|
||||
|
||||
- 入门入口:[入门指南](../getting-started/index.md)
|
||||
- 安装与选包:[安装配置](../getting-started/installation.md)
|
||||
|
||||
### 想确认“这个功能属于哪个模块”
|
||||
### 模块定位入口
|
||||
|
||||
按下面的模块映射进入对应入口:
|
||||
|
||||
|
||||
@ -20,7 +20,7 @@ description: 说明 GFramework.Core.Command 旧命令体系的兼容定位、可
|
||||
- `AbstractCommand<TInput, TResult>`
|
||||
- 有输入、有返回值
|
||||
|
||||
注意一个和旧文档不同的点:泛型命令现在通过构造函数接收输入,而不是依赖 `Input` 可写属性。
|
||||
当前泛型命令通过构造函数接收输入,而不是依赖 `Input` 可写属性。
|
||||
|
||||
## 无输入命令
|
||||
|
||||
|
||||
@ -18,14 +18,14 @@ description: 说明 IArchitectureContext 与 ArchitectureContext 的统一上下
|
||||
|
||||
## 先记住一个事实
|
||||
|
||||
如果你还在找旧文档里的这些属性:
|
||||
如果你正在寻找这些属性式总线入口:
|
||||
|
||||
- `CommandBus`
|
||||
- `QueryBus`
|
||||
- `EventBus`
|
||||
- `Container`
|
||||
|
||||
那说明你看到的是旧写法。当前推荐入口是方法,不是这些属性式总线。
|
||||
当前公开入口是方法,不是这些属性式总线。
|
||||
|
||||
## 组件访问
|
||||
|
||||
|
||||
@ -89,7 +89,9 @@ protected override void OnInitialize()
|
||||
|
||||
## 组件自己的生命周期
|
||||
|
||||
大多数组件不需要手写 `Initialize()`;继承框架基类即可:
|
||||
大多数组件不需要手写 `Initialize()`;继承框架基类即可。
|
||||
|
||||
这里的 `OnInit()` 是组件级入口,用来初始化单个 `Model` / `System`;上面的 `OnInitialize()` 则用于架构级注册:
|
||||
|
||||
```csharp
|
||||
public sealed class PlayerModel : AbstractModel
|
||||
@ -159,7 +161,7 @@ architecture.PhaseChanged += (_, args) =>
|
||||
## 推荐做法
|
||||
|
||||
- 新代码优先使用 `InitializeAsync()` / `DestroyAsync()`
|
||||
- 把注册逻辑放在 `OnInitialize()`,不要沿用旧文档里的 `Init()`
|
||||
- 把架构级注册逻辑放在 `OnInitialize()`;组件自己的初始化仍然使用 `OnInit()`,不要再尝试重写 `Init()` 这类旧入口
|
||||
- 让 `Utility` 承载底层能力,让 `Model` 承载状态,再让 `System` 消费两者
|
||||
- 跨组件阶段逻辑优先写成 `IArchitectureLifecycleHook`
|
||||
|
||||
|
||||
@ -18,7 +18,7 @@ description: 说明 GFramework.Core.Query 旧查询体系的兼容定位、可
|
||||
- `AbstractQuery<TInput, TResult>`
|
||||
- 带输入查询
|
||||
|
||||
与旧文档不同,带输入查询现在通过构造函数接收输入,不再依赖 `Input` 属性赋值。
|
||||
当前带输入查询通过构造函数接收输入,不再依赖 `Input` 属性赋值。
|
||||
|
||||
## 无输入查询
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
---
|
||||
title: Game 模块
|
||||
description: GFramework.Game family 的运行时入口、采用顺序与源码阅读导航。
|
||||
description: GFramework.Game 运行时模块的入口、采用顺序与源码阅读导航。
|
||||
---
|
||||
|
||||
# Game 模块
|
||||
|
||||
@ -76,7 +76,7 @@ description: 说明 GFramework.Game 场景路由的当前入口、项目侧接
|
||||
- `PopAsync`
|
||||
- 对栈顶执行离开检查,通过后退出并卸载它,再从 `ISceneRoot` 移除,然后恢复新的栈顶。
|
||||
|
||||
当前还有两个容易被旧文档误导的点:
|
||||
当前还有两个容易混淆的点:
|
||||
|
||||
- `SceneRouterBase` 默认不允许同一个 `sceneKey` 在栈中重复存在;内部会先做 `Contains(sceneKey)` 检查
|
||||
- 框架不会替你实现“场景键 -> 具体场景实例”的注册逻辑;这仍然是 `ISceneFactory` 或项目注册表的职责
|
||||
|
||||
@ -113,7 +113,7 @@ description: 说明 GFramework.Game UI 路由当前的页面栈、层级 UI、
|
||||
- `Page` 层属于栈语义,用 `PushAsync` / `ReplaceAsync` / `PopAsync`
|
||||
- `Overlay`、`Modal`、`Toast`、`Topmost` 属于层级语义,用 `Show` / `Hide` / `Resume`
|
||||
|
||||
`Show(..., UiLayer.Page)` 在当前实现里会直接抛异常,因此旧文档里那种“所有 UI 都统一通过 Show 进入”的写法不再准确。
|
||||
`Show(..., UiLayer.Page)` 在当前实现里会直接抛异常;`Page` 层应通过 `PushAsync` / `ReplaceAsync` / `PopAsync` 进入。
|
||||
|
||||
### 输入不是页面自己抢,而是 router 先仲裁
|
||||
|
||||
@ -211,7 +211,7 @@ public sealed class GameUiRouter : UiRouterBase
|
||||
- `AddUiPage(IUiPageBehavior child, UiLayer layer, int orderInLayer = 0)`
|
||||
- `RemoveUiPage(IUiPageBehavior child)`
|
||||
|
||||
当前 `ai-libs/` 的参考实现就是在项目自己的 `CanvasLayer` 上为每个 `UiLayer` 建独立容器,再在 `_Ready()` 时执行
|
||||
一种常见的项目侧实现方式,是在自己的 `CanvasLayer` 上为每个 `UiLayer` 建独立容器,再在 `_Ready()` 时执行
|
||||
`_uiRouter.BindRoot(this)`。
|
||||
|
||||
### 4. 装配 router 与 factory
|
||||
|
||||
@ -290,8 +290,7 @@ await sceneRouter.PopAsync();
|
||||
|
||||
### 没有 `GodotSceneRouter`
|
||||
|
||||
仓库当前不存在 `GodotSceneRouter` 类型。旧文档里把它写成默认入口是失真的;实际入口仍然是项目侧继承
|
||||
`SceneRouterBase` 的 router。
|
||||
仓库当前不存在 `GodotSceneRouter` 类型;实际入口仍然是项目侧继承 `SceneRouterBase` 的 router。
|
||||
|
||||
### 没有自动注册所有场景
|
||||
|
||||
|
||||
@ -133,12 +133,12 @@ await settingsSystem.Apply<GodotAudioSettings>();
|
||||
|
||||
## 什么时候应该改看别的入口
|
||||
|
||||
### 先理解设置模型和仓库
|
||||
### 相关设置模型
|
||||
|
||||
如果你想先理解 `ISettingsData`、`IResetApplyAbleSettings`、`SettingsModel`、`SettingsSystem` 与设置迁移,先看
|
||||
[Game 设置系统](../game/setting.md)。
|
||||
|
||||
### 先理解设置如何被持久化
|
||||
### 相关持久化入口
|
||||
|
||||
如果你关注的是统一设置文件、备份、数据位置和底层存储实现,应该回到:
|
||||
|
||||
|
||||
@ -120,7 +120,7 @@ private void OnStartPressed()
|
||||
- 运行时选出的 signal 名称
|
||||
- 需要临时追加监听的 dock、panel、overlay
|
||||
|
||||
`ai-libs/CoreGrid` 当前就有这类用法:
|
||||
项目侧常见也会这样写:
|
||||
|
||||
```csharp
|
||||
_quitConfirmDialog.Signal("Confirmed")
|
||||
|
||||
@ -68,7 +68,7 @@ Godot runtime 的页面行为包装基类。它把 `IUiPageBehavior` 的这些
|
||||
- `UiLayer.Toast` -> `ToastLayerUiPageBehavior<T>`
|
||||
- `UiLayer.Topmost` -> `TopmostLayerUiPageBehavior<T>`
|
||||
|
||||
几个容易被旧文档写偏的默认语义如下:
|
||||
几个容易混淆的默认语义如下:
|
||||
|
||||
- `Page`
|
||||
- 不可重入,阻断输入
|
||||
@ -319,7 +319,7 @@ uiRouter.Hide(handle, UiLayer.Modal);
|
||||
|
||||
### 没有 `GodotUiRouter`
|
||||
|
||||
仓库当前没有这个类型。旧文档把它写成默认入口是不准确的;真实入口仍然是项目侧的 `UiRouterBase` 派生类。
|
||||
仓库当前没有这个类型;真实入口仍然是项目侧的 `UiRouterBase` 派生类。
|
||||
|
||||
### UI 工厂不会自动补 behavior
|
||||
|
||||
|
||||
@ -161,7 +161,7 @@ public List<IntConfig>? Values { get; } = new();
|
||||
|
||||
## 真实采用路径
|
||||
|
||||
`ai-libs/CoreGrid/global/GameEntryPoint.cs` 是当前最直接的消费者参考:
|
||||
一个典型的项目侧入口会这样组织导出集合:
|
||||
|
||||
- `UiPageConfigs`
|
||||
- `GameSceneConfigs`
|
||||
|
||||
@ -130,7 +130,7 @@ private void OnAnyButtonPressed()
|
||||
|
||||
当前生成器会为每个特性都生成一条 `+=` 和一条 `-=`。
|
||||
|
||||
`ai-libs/CoreGrid` 里的 `GameplayHud`、`PauseMenu` 和 `OptionBrowser` 都在大量使用这种声明式绑定方式。
|
||||
项目侧的 HUD、菜单和选项面板节点,通常都会大量使用这种声明式绑定方式。
|
||||
|
||||
## 与 GetNode 的协作边界
|
||||
|
||||
|
||||
@ -102,7 +102,7 @@ public partial class PlayerController : IController
|
||||
- 不维护共享 provider
|
||||
- 默认直接回退到 `GameContext.GetFirstArchitectureContext()`
|
||||
|
||||
因此,旧文档里把两条路径混写成“只是写法不同”已经不准确。
|
||||
因此,这两条路径不是“只是写法不同”,而是共享 provider 策略和实例缓存边界都不同。
|
||||
|
||||
## 何时使用 `[ContextAware]`
|
||||
|
||||
|
||||
@ -179,7 +179,7 @@ public override void _Ready()
|
||||
|
||||
先注入节点,再绑定事件;否则 `BindNodeSignal` 对应的字段还没完成解析。
|
||||
|
||||
这也是 `ai-libs/CoreGrid` 里项目侧节点类的实际用法。
|
||||
这也是项目侧节点类的常见接法。
|
||||
|
||||
## 什么时候适合用 `[GetNode]`
|
||||
|
||||
|
||||
@ -200,8 +200,8 @@ AutoLoad 名称也遵循同样的冲突处理策略。
|
||||
- 节点字段注入:`[GetNode]`
|
||||
- 节点 CLR event 订阅:`[BindNodeSignal]`
|
||||
|
||||
在 `ai-libs/CoreGrid` 中,这三类能力是并行使用的:`project.godot` 负责 AutoLoad / Input Action,具体 UI 或场景节点再通过
|
||||
`[GetNode]` 和 `[BindNodeSignal]` 处理。
|
||||
在一个同时使用这些生成器的项目里,这三类能力通常并行存在:`project.godot` 负责 AutoLoad / Input Action,具体 UI
|
||||
或场景节点再通过 `[GetNode]` 和 `[BindNodeSignal]` 处理。
|
||||
|
||||
## 诊断与约束
|
||||
|
||||
|
||||
@ -70,7 +70,7 @@ GFramework 当前发布的生成器包是:
|
||||
- 再根据 attribute 或 diagnostics 回到对应专题页
|
||||
- 只有在排查生成失败原因时,才继续下钻到这些共享支撑目录
|
||||
|
||||
## 这个栏目怎么读
|
||||
## 阅读路线
|
||||
|
||||
### Core 侧通用生成器
|
||||
|
||||
|
||||
@ -68,7 +68,7 @@ var handlers = container.GetAllByPriority<IMyHandler>();
|
||||
- `this.GetModelsByPriority<TModel>()`
|
||||
- `this.GetUtilitiesByPriority<TUtility>()`
|
||||
|
||||
这比旧文档里反复出现的 `this.GetAllByPriority<T>()` 更贴近当前公开扩展方法。
|
||||
当前公开扩展方法就是按角色拆分的这些 API,而不是泛化的 `this.GetAllByPriority<T>()`。
|
||||
|
||||
## 最小接入示例
|
||||
|
||||
|
||||
@ -150,7 +150,7 @@ if (!arch.IsInitialized)
|
||||
// ✅ 更好:使用单例模式
|
||||
public class GameArchitecture : Architecture<GameArchitecture>
|
||||
{
|
||||
protected override void Init()
|
||||
protected override void OnInitialize()
|
||||
{
|
||||
// 注册组件
|
||||
}
|
||||
@ -176,7 +176,7 @@ InvalidOperationException: No service for type 'IPlayerService' has been registe
|
||||
// ❌ 错误:未注册服务
|
||||
public class GameArchitecture : Architecture
|
||||
{
|
||||
protected override void Init()
|
||||
protected override void OnInitialize()
|
||||
{
|
||||
// 忘记注册 IPlayerService
|
||||
}
|
||||
@ -187,7 +187,7 @@ var service = arch.GetService<IPlayerService>(); // 抛出异常
|
||||
// ✅ 正确:先注册服务
|
||||
public class GameArchitecture : Architecture
|
||||
{
|
||||
protected override void Init()
|
||||
protected override void OnInitialize()
|
||||
{
|
||||
// 注册服务
|
||||
RegisterService<IPlayerService, PlayerService>();
|
||||
@ -216,14 +216,14 @@ NullReferenceException: Object reference not set to an instance of an object
|
||||
|
||||
```csharp
|
||||
// ❌ 错误:SystemB 依赖 ModelA,但 ModelA 后注册
|
||||
protected override void Init()
|
||||
protected override void OnInitialize()
|
||||
{
|
||||
RegisterSystem(new SystemB()); // SystemB.OnInit() 中访问 ModelA 失败
|
||||
RegisterModel(new ModelA());
|
||||
}
|
||||
|
||||
// ✅ 正确:先注册依赖项
|
||||
protected override void Init()
|
||||
protected override void OnInitialize()
|
||||
{
|
||||
// 1. 先注册 Model
|
||||
RegisterModel(new ModelA());
|
||||
@ -306,7 +306,7 @@ var model = arch.GetModel<PlayerModel>(); // 抛出异常
|
||||
// ✅ 正确:先注册再获取
|
||||
public class GameArchitecture : Architecture
|
||||
{
|
||||
protected override void Init()
|
||||
protected override void OnInitialize()
|
||||
{
|
||||
RegisterModel(new PlayerModel()); // 注册模型
|
||||
}
|
||||
@ -523,7 +523,7 @@ arch.RegisterEvent<GameEvent>(OnGameEvent);
|
||||
3. **检查事件总线是否正确注册**:
|
||||
|
||||
```csharp
|
||||
protected override void Init()
|
||||
protected override void OnInitialize()
|
||||
{
|
||||
// 确保注册了事件总线
|
||||
RegisterSystem(new EventBusModule());
|
||||
@ -825,7 +825,7 @@ else
|
||||
// ✅ 使用资源管理器
|
||||
public class GameArchitecture : Architecture
|
||||
{
|
||||
protected override void Init()
|
||||
protected override void OnInitialize()
|
||||
{
|
||||
RegisterSystem(new ResourceManager());
|
||||
}
|
||||
@ -1153,7 +1153,7 @@ public partial class Player : Node
|
||||
// ✅ 使用 Godot 模块
|
||||
public class GameArchitecture : AbstractArchitecture
|
||||
{
|
||||
protected override void Init()
|
||||
protected override void InstallModules()
|
||||
{
|
||||
// 注册 Godot 模块
|
||||
this.RegisterGodotModule<PlayerModule>();
|
||||
@ -1176,7 +1176,7 @@ await uiRouter.PushAsync("MainMenu"); // 失败:页面未注册
|
||||
// ✅ 正确:先注册 UI 页面
|
||||
public class GameArchitecture : Architecture
|
||||
{
|
||||
protected override void Init()
|
||||
protected override void OnInitialize()
|
||||
{
|
||||
var uiRegistry = new GodotUiRegistry();
|
||||
uiRegistry.Register("MainMenu", "res://ui/main_menu.tscn", UiLayer.Page);
|
||||
@ -1467,7 +1467,7 @@ _coroutineQueue.Enqueue(MyCoroutine());
|
||||
// 使用 GFramework 日志系统
|
||||
public class GameArchitecture : Architecture
|
||||
{
|
||||
protected override void Init()
|
||||
protected override void OnInitialize()
|
||||
{
|
||||
// 注册日志工厂
|
||||
RegisterUtility<ILoggerFactory>(new GodotLoggerFactory());
|
||||
@ -1554,7 +1554,7 @@ public class EventTracer : AbstractSystem
|
||||
|
||||
// 在架构中启用事件追踪
|
||||
#if DEBUG
|
||||
protected override void Init()
|
||||
protected override void OnInitialize()
|
||||
{
|
||||
RegisterSystem(new EventTracer());
|
||||
}
|
||||
@ -1679,7 +1679,7 @@ System.Collections.Generic.KeyNotFoundException: 未找到类型为 'PlayerModel
|
||||
// 在架构中注册组件
|
||||
public class GameArchitecture : Architecture
|
||||
{
|
||||
protected override void Init()
|
||||
protected override void OnInitialize()
|
||||
{
|
||||
RegisterModel(new PlayerModel());
|
||||
RegisterSystem(new PlayerSystem());
|
||||
@ -1745,7 +1745,7 @@ var model = this.GetModel<PlayerModel>(); // 返回 null
|
||||
model.Health.Value = 100; // 抛出异常
|
||||
|
||||
// ✅ 正确:先注册
|
||||
protected override void Init()
|
||||
protected override void OnInitialize()
|
||||
{
|
||||
RegisterModel(new PlayerModel());
|
||||
}
|
||||
@ -1814,20 +1814,20 @@ System.ArgumentException: An item with the same key has already been added. Key:
|
||||
|
||||
```csharp
|
||||
// ❌ 错误:重复注册
|
||||
protected override void Init()
|
||||
protected override void OnInitialize()
|
||||
{
|
||||
RegisterModel(new PlayerModel());
|
||||
RegisterModel(new PlayerModel()); // 重复注册
|
||||
}
|
||||
|
||||
// ✅ 正确:只注册一次
|
||||
protected override void Init()
|
||||
protected override void OnInitialize()
|
||||
{
|
||||
RegisterModel(new PlayerModel());
|
||||
}
|
||||
|
||||
// ✅ 如果需要多个实例,使用不同的键
|
||||
protected override void Init()
|
||||
protected override void OnInitialize()
|
||||
{
|
||||
RegisterModel(new PlayerModel(), "Player1");
|
||||
RegisterModel(new PlayerModel(), "Player2");
|
||||
|
||||
@ -162,7 +162,7 @@ namespace MyGame.Core.Architecture
|
||||
{
|
||||
public static IArchitecture Interface { get; private set; }
|
||||
|
||||
protected override void Init()
|
||||
protected override void OnInitialize()
|
||||
{
|
||||
Interface = this;
|
||||
|
||||
@ -857,7 +857,7 @@ namespace MyGame.Core.Architecture
|
||||
{
|
||||
public static IArchitecture Interface { get; private set; }
|
||||
|
||||
protected override void Init()
|
||||
protected override void OnInitialize()
|
||||
{
|
||||
Interface = this;
|
||||
|
||||
|
||||
@ -38,7 +38,7 @@ namespace MyGame
|
||||
{
|
||||
public static IArchitecture Interface { get; private set; }
|
||||
|
||||
protected override void Init()
|
||||
protected override void OnInitialize()
|
||||
{
|
||||
Interface = this;
|
||||
|
||||
@ -631,7 +631,7 @@ namespace MyGame
|
||||
{
|
||||
public static IArchitecture Interface { get; private set; }
|
||||
|
||||
protected override void Init()
|
||||
protected override void OnInitialize()
|
||||
{
|
||||
Interface = this;
|
||||
|
||||
|
||||
@ -220,7 +220,7 @@ namespace MyGame
|
||||
{
|
||||
public static IArchitecture Interface { get; private set; }
|
||||
|
||||
protected override void Init()
|
||||
protected override void OnInitialize()
|
||||
{
|
||||
Interface = this;
|
||||
|
||||
|
||||
@ -671,7 +671,7 @@ namespace MyGame
|
||||
{
|
||||
public static IArchitecture Interface { get; private set; }
|
||||
|
||||
protected override void Init()
|
||||
protected override void OnInitialize()
|
||||
{
|
||||
Interface = this;
|
||||
|
||||
|
||||
@ -356,7 +356,7 @@ namespace MyGame
|
||||
{
|
||||
public static IArchitecture Interface { get; private set; }
|
||||
|
||||
protected override void Init()
|
||||
protected override void OnInitialize()
|
||||
{
|
||||
Interface = this;
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user