From f17f9f3da6a1de91b8a7ab0b15a03d59d3e91c81 Mon Sep 17 00:00:00 2001 From: gewuyou <95328647+GeWuYou@users.noreply.github.com> Date: Thu, 30 Apr 2026 13:14:29 +0800 Subject: [PATCH] =?UTF-8?q?test(cqrs):=20=E8=A1=A5=E5=85=85=20stream=20inv?= =?UTF-8?q?oker=20provider=20=E7=94=9F=E6=88=90=E6=96=AD=E8=A8=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 stream invoker provider runtime fixture,复用 request provider 测试风格锁定 descriptor 和静态 invoker 形状 - 补充 Phase 8 stream invoker provider 回归测试骨架,并暂时以 Ignore 挂起等待主线程生成实现落地 --- .../Cqrs/CqrsHandlerRegistryGeneratorTests.cs | 166 ++++++++++++++++++ 1 file changed, 166 insertions(+) diff --git a/GFramework.SourceGenerators.Tests/Cqrs/CqrsHandlerRegistryGeneratorTests.cs b/GFramework.SourceGenerators.Tests/Cqrs/CqrsHandlerRegistryGeneratorTests.cs index 9961a139..ad8a93f7 100644 --- a/GFramework.SourceGenerators.Tests/Cqrs/CqrsHandlerRegistryGeneratorTests.cs +++ b/GFramework.SourceGenerators.Tests/Cqrs/CqrsHandlerRegistryGeneratorTests.cs @@ -1790,6 +1790,112 @@ public class CqrsHandlerRegistryGeneratorTests } """; + private const string StreamInvokerProviderSource = """ + using System; + using System.Collections.Generic; + using System.Reflection; + using System.Threading; + using System.Threading.Tasks; + + namespace Microsoft.Extensions.DependencyInjection + { + public interface IServiceCollection { } + + public static class ServiceCollectionServiceExtensions + { + public static void AddTransient(IServiceCollection services, Type serviceType, Type implementationType) { } + } + } + + namespace GFramework.Core.Abstractions.Logging + { + public interface ILogger + { + void Debug(string msg); + } + } + + namespace GFramework.Cqrs.Abstractions.Cqrs + { + public interface IRequest { } + public interface INotification { } + public interface IStreamRequest { } + + public interface IRequestHandler where TRequest : IRequest + { + ValueTask Handle(TRequest request, CancellationToken cancellationToken); + } + + public interface INotificationHandler where TNotification : INotification { } + + public interface IStreamRequestHandler where TRequest : IStreamRequest + { + IAsyncEnumerable Handle(TRequest request, CancellationToken cancellationToken); + } + } + + namespace GFramework.Cqrs + { + public interface ICqrsHandlerRegistry + { + void Register(Microsoft.Extensions.DependencyInjection.IServiceCollection services, GFramework.Core.Abstractions.Logging.ILogger logger); + } + + public interface ICqrsStreamInvokerProvider + { + bool TryGetDescriptor(Type requestType, Type responseType, out CqrsStreamInvokerDescriptor? descriptor); + } + + public interface IEnumeratesCqrsStreamInvokerDescriptors + { + IReadOnlyList GetDescriptors(); + } + + public sealed class CqrsStreamInvokerDescriptor + { + public CqrsStreamInvokerDescriptor(Type handlerType, MethodInfo invokerMethod) { } + } + + public sealed class CqrsStreamInvokerDescriptorEntry + { + public CqrsStreamInvokerDescriptorEntry(Type requestType, Type responseType, CqrsStreamInvokerDescriptor descriptor) + { + RequestType = requestType; + ResponseType = responseType; + Descriptor = descriptor; + } + + public Type RequestType { get; } + + public Type ResponseType { get; } + + public CqrsStreamInvokerDescriptor Descriptor { get; } + } + + [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] + public sealed class CqrsHandlerRegistryAttribute : Attribute + { + public CqrsHandlerRegistryAttribute(Type registryType) { } + } + } + + namespace TestApp + { + using GFramework.Cqrs.Abstractions.Cqrs; + + public sealed record VisibleStream(int Count) : IStreamRequest; + + public sealed class VisibleStreamHandler : IStreamRequestHandler + { + public async IAsyncEnumerable Handle(VisibleStream request, CancellationToken cancellationToken) + { + yield return request.Count; + await Task.CompletedTask; + } + } + } + """; + /// /// 验证生成器会为当前程序集中的 request、notification 和 stream 处理器生成稳定顺序的注册器。 /// @@ -2387,6 +2493,66 @@ public class CqrsHandlerRegistryGeneratorTests }); } + /// + /// 验证当 runtime 暴露 stream invoker provider 契约时,生成器会让 generated registry 同时发射 + /// stream invoker 描述符与对应的开放静态 invoker 方法。 + /// + [Test] + [Ignore("Enable after generated stream invoker provider / descriptor emission lands in Phase 8.")] + public void Emits_Stream_Invoker_Provider_Metadata_When_Runtime_Contract_Is_Available() + { + var execution = ExecuteGenerator(StreamInvokerProviderSource); + var inputCompilationErrors = execution.InputCompilationDiagnostics + .Where(static diagnostic => diagnostic.Severity == DiagnosticSeverity.Error) + .ToArray(); + var generatedCompilationErrors = execution.GeneratedCompilationDiagnostics + .Where(static diagnostic => diagnostic.Severity == DiagnosticSeverity.Error) + .ToArray(); + var generatorErrors = execution.GeneratorDiagnostics + .Where(static diagnostic => diagnostic.Severity == DiagnosticSeverity.Error) + .ToArray(); + Assert.Multiple(() => + { + Assert.That(inputCompilationErrors, Is.Empty); + Assert.That(generatedCompilationErrors, Is.Empty); + Assert.That(generatorErrors, Is.Empty); + Assert.That(execution.GeneratedSources, Has.Length.EqualTo(1)); + Assert.That(execution.GeneratedSources[0].filename, Is.EqualTo("CqrsHandlerRegistry.g.cs")); + var generatedSource = execution.GeneratedSources[0].content; + Assert.That( + generatedSource, + Does.Contain( + "internal sealed class __GFrameworkGeneratedCqrsHandlerRegistry : global::GFramework.Cqrs.ICqrsHandlerRegistry, global::GFramework.Cqrs.ICqrsStreamInvokerProvider, global::GFramework.Cqrs.IEnumeratesCqrsStreamInvokerDescriptors")); + Assert.That( + generatedSource, + Does.Contain( + "new global::GFramework.Cqrs.CqrsStreamInvokerDescriptorEntry(typeof(global::TestApp.VisibleStream), typeof(int),")); + Assert.That( + generatedSource, + Does.Contain( + "new global::GFramework.Cqrs.CqrsStreamInvokerDescriptor(typeof(global::GFramework.Cqrs.Abstractions.Cqrs.IStreamRequestHandler), typeof(__GFrameworkGeneratedCqrsHandlerRegistry).GetMethod(nameof(InvokeStreamHandler0), global::System.Reflection.BindingFlags.NonPublic | global::System.Reflection.BindingFlags.Static)!)")); + Assert.That( + generatedSource, + Does.Contain( + "public bool TryGetDescriptor(global::System.Type requestType, global::System.Type responseType, out global::GFramework.Cqrs.CqrsStreamInvokerDescriptor? descriptor)")); + Assert.That( + generatedSource, + Does.Contain( + "private static object InvokeStreamHandler0(object handler, object request, global::System.Threading.CancellationToken cancellationToken)")); + Assert.That( + generatedSource, + Does.Contain( + "var typedHandler = (global::GFramework.Cqrs.Abstractions.Cqrs.IStreamRequestHandler)handler;")); + Assert.That( + generatedSource, + Does.Contain("return typedHandler.Handle(typedRequest, cancellationToken);")); + Assert.That( + generatedSource, + Does.Contain( + "public global::System.Collections.Generic.IReadOnlyList GetDescriptors()")); + }); + } + /// /// 验证日志字符串转义会覆盖换行、反斜杠和双引号,避免生成代码中的字符串字面量被意外截断。 ///