diff --git a/dotnet/Directory.Packages.props b/dotnet/Directory.Packages.props index e6ada46abd48..5c41c338159c 100644 --- a/dotnet/Directory.Packages.props +++ b/dotnet/Directory.Packages.props @@ -78,6 +78,7 @@ + diff --git a/dotnet/src/Microsoft.AutoGen/Microsoft.AutoGen.Core.Grpc/GrpcAgentWorker.cs b/dotnet/src/Microsoft.AutoGen/Microsoft.AutoGen.Core.Grpc/GrpcAgentWorker.cs index 177310c470e2..5f58e1a92a2d 100644 --- a/dotnet/src/Microsoft.AutoGen/Microsoft.AutoGen.Core.Grpc/GrpcAgentWorker.cs +++ b/dotnet/src/Microsoft.AutoGen/Microsoft.AutoGen.Core.Grpc/GrpcAgentWorker.cs @@ -203,17 +203,14 @@ private async ValueTask RegisterAgentTypeAsync(string type, Type agentType, Canc // TODO: Reimplement registration as RPC call _logger.LogInformation($"{cancellationToken.ToString}"); // TODO: remove this - //await WriteChannelAsync(new Message - //{ - // RegisterAgentTypeRequest = new RegisterAgentTypeRequest - // { - // Type = type, - // RequestId = Guid.NewGuid().ToString(), - // //TopicTypes = { topicTypes }, - // //StateType = state?.Name, - // Events = { events } - // } - //}, cancellationToken).ConfigureAwait(false); + var response = await _client.RegisterAgentAsync(new RegisterAgentTypeRequest + { + Type = type, + RequestId = Guid.NewGuid().ToString(), + //TopicTypes = { topicTypes }, + //StateType = state?.Name, + Events = { events } + }, null, null, cancellationToken); } } diff --git a/dotnet/src/Microsoft.AutoGen/Microsoft.AutoGen.Runtime.Grpc/GrpcGateway.cs b/dotnet/src/Microsoft.AutoGen/Microsoft.AutoGen.Runtime.Grpc/GrpcGateway.cs index d8ddca87421a..ed2b2ed3b3bd 100644 --- a/dotnet/src/Microsoft.AutoGen/Microsoft.AutoGen.Runtime.Grpc/GrpcGateway.cs +++ b/dotnet/src/Microsoft.AutoGen/Microsoft.AutoGen.Runtime.Grpc/GrpcGateway.cs @@ -17,6 +17,7 @@ public sealed class GrpcGateway : BackgroundService, IGateway private readonly ConcurrentDictionary _agentState = new(); private readonly IRegistryGrain _gatewayRegistry; private readonly IGateway _reference; + // The agents supported by each worker process. private readonly ConcurrentDictionary> _supportedAgentTypes = []; internal readonly ConcurrentDictionary _workers = new(); @@ -35,33 +36,6 @@ public GrpcGateway(IClusterClient clusterClient, ILogger logger) _reference = clusterClient.CreateObjectReference(this); _gatewayRegistry = clusterClient.GetGrain(0); } - public async ValueTask BroadcastEvent(CloudEvent evt) - { - var tasks = new List(); - foreach (var (key, connection) in _supportedAgentTypes) - { - if (_agentsToEventsMap.TryGetValue(key, out var events) && events.Contains(evt.Type)) - { - tasks.Add(SendMessageAsync(connection[0], evt, default)); - } - } - await Task.WhenAll(tasks).ConfigureAwait(false); - } - //intetionally not static so can be called by some methods implemented in base class - internal async Task SendMessageAsync(GrpcWorkerConnection connection, CloudEvent cloudEvent, CancellationToken cancellationToken = default) - { - await connection.ResponseStream.WriteAsync(new Message { CloudEvent = cloudEvent }, cancellationToken).ConfigureAwait(false); - } - private void DispatchResponse(GrpcWorkerConnection connection, RpcResponse response) - { - if (!_pendingRequests.TryRemove((connection, response.RequestId), out var completion)) - { - _logger.LogWarning("Received response for unknown request."); - return; - } - // Complete the request. - completion.SetResult(response); - } protected override async Task ExecuteAsync(CancellationToken stoppingToken) { while (!stoppingToken.IsCancellationRequested) @@ -85,7 +59,32 @@ protected override async Task ExecuteAsync(CancellationToken stoppingToken) _logger.LogWarning(exception, "Error removing worker from registry."); } } - //new is intentional... + + internal Task ConnectToWorkerProcess(IAsyncStreamReader requestStream, IServerStreamWriter responseStream, ServerCallContext context) + { + _logger.LogInformation("Received new connection from {Peer}.", context.Peer); + var workerProcess = new GrpcWorkerConnection(this, requestStream, responseStream, context); + _workers[workerProcess] = workerProcess; + return workerProcess.Completion; + } + + public async ValueTask BroadcastEvent(CloudEvent evt) + { + var tasks = new List(); + foreach (var (key, connection) in _supportedAgentTypes) + { + if (_agentsToEventsMap.TryGetValue(key, out var events) && events.Contains(evt.Type)) + { + tasks.Add(SendMessageAsync(connection[0], evt, default)); + } + } + await Task.WhenAll(tasks).ConfigureAwait(false); + } + //intetionally not static so can be called by some methods implemented in base class + internal async Task SendMessageAsync(GrpcWorkerConnection connection, CloudEvent cloudEvent, CancellationToken cancellationToken = default) + { + await connection.ResponseStream.WriteAsync(new Message { CloudEvent = cloudEvent }, cancellationToken).ConfigureAwait(false); + } internal async Task OnReceivedMessageAsync(GrpcWorkerConnection connection, Message message) { _logger.LogInformation("Received message {Message} from connection {Connection}.", message, connection); @@ -101,32 +100,20 @@ internal async Task OnReceivedMessageAsync(GrpcWorkerConnection connection, Mess await DispatchEventAsync(message.CloudEvent); break; default: - // if it wasn't recognized return bad request - await RespondBadRequestAsync(connection, $"Unknown message type for message '{message}'."); - break; + throw new RpcException(new Status(StatusCode.InvalidArgument, $"Unknown message type for message '{message}'.")); }; } - private async ValueTask RespondBadRequestAsync(GrpcWorkerConnection connection, string error) + private void DispatchResponse(GrpcWorkerConnection connection, RpcResponse response) { - throw new RpcException(new Status(StatusCode.InvalidArgument, error)); + if (!_pendingRequests.TryRemove((connection, response.RequestId), out var completion)) + { + _logger.LogWarning("Received response for unknown request."); + return; + } + // Complete the request. + completion.SetResult(response); } - private async ValueTask RegisterAgentTypeAsync(GrpcWorkerConnection connection, RegisterAgentTypeRequest msg) - { - connection.AddSupportedType(msg.Type); - _supportedAgentTypes.GetOrAdd(msg.Type, _ => []).Add(connection); - _agentsToEventsMap.TryAdd(msg.Type, new HashSet(msg.Events)); - - await _gatewayRegistry.RegisterAgentType(msg.Type, _reference).ConfigureAwait(true); - } - private async ValueTask DispatchEventAsync(CloudEvent evt) - { - await BroadcastEvent(evt).ConfigureAwait(false); - /* - var topic = _clusterClient.GetStreamProvider("agents").GetStream(StreamId.Create(evt.Namespace, evt.Type)); - await topic.OnNextAsync(evt.ToEvent()); - */ - } private async ValueTask DispatchRequestAsync(GrpcWorkerConnection connection, RpcRequest request) { var requestId = request.RequestId; @@ -151,6 +138,24 @@ await InvokeRequestDelegate(connection, request, async request => //} } + private async ValueTask DispatchEventAsync(CloudEvent evt) + { + await BroadcastEvent(evt).ConfigureAwait(false); + /* + var topic = _clusterClient.GetStreamProvider("agents").GetStream(StreamId.Create(evt.Namespace, evt.Type)); + await topic.OnNextAsync(evt.ToEvent()); + */ + } + + private async ValueTask RegisterAgentTypeAsync(GrpcWorkerConnection connection, RegisterAgentTypeRequest msg) + { + connection.AddSupportedType(msg.Type); + _supportedAgentTypes.GetOrAdd(msg.Type, _ => []).Add(connection); + _agentsToEventsMap.TryAdd(msg.Type, new HashSet(msg.Events)); + + await _gatewayRegistry.RegisterAgentType(msg.Type, _reference).ConfigureAwait(true); + } + private static async Task InvokeRequestDelegate(GrpcWorkerConnection connection, RpcRequest request, Func> func) { try @@ -164,13 +169,7 @@ private static async Task InvokeRequestDelegate(GrpcWorkerConnection connection, await connection.ResponseStream.WriteAsync(new Message { Response = new RpcResponse { RequestId = request.RequestId, Error = ex.Message } }).ConfigureAwait(false); } } - internal Task ConnectToWorkerProcess(IAsyncStreamReader requestStream, IServerStreamWriter responseStream, ServerCallContext context) - { - _logger.LogInformation("Received new connection from {Peer}.", context.Peer); - var workerProcess = new GrpcWorkerConnection(this, requestStream, responseStream, context); - _workers[workerProcess] = workerProcess; - return workerProcess.Completion; - } + public async ValueTask StoreAsync(AgentState value) { var agentId = value.AgentId ?? throw new ArgumentNullException(nameof(value.AgentId)); @@ -205,6 +204,7 @@ internal void OnRemoveWorkerProcess(GrpcWorkerConnection workerProcess) } } } + public async ValueTask InvokeRequest(RpcRequest request, CancellationToken cancellationToken = default) { var agentId = (request.Target.Type, request.Target.Key); @@ -233,8 +233,13 @@ public async ValueTask InvokeRequest(RpcRequest request, Cancellati return response; } - async ValueTask IGateway.InvokeRequest(RpcRequest request) + public ValueTask RegisterAgentTypeAsync(RegisterAgentTypeRequest request) + { + throw new NotImplementedException(); + } + + public ValueTask InvokeRequest(RpcRequest request) { - return await InvokeRequest(request).ConfigureAwait(false); + throw new NotImplementedException(); } } diff --git a/dotnet/src/Microsoft.AutoGen/Microsoft.AutoGen.Runtime.Grpc/GrpcGatewayService.cs b/dotnet/src/Microsoft.AutoGen/Microsoft.AutoGen.Runtime.Grpc/GrpcGatewayService.cs index a2985c95f7bb..16e63e16bb93 100644 --- a/dotnet/src/Microsoft.AutoGen/Microsoft.AutoGen.Runtime.Grpc/GrpcGatewayService.cs +++ b/dotnet/src/Microsoft.AutoGen/Microsoft.AutoGen.Runtime.Grpc/GrpcGatewayService.cs @@ -43,4 +43,16 @@ public override async Task SaveState(AgentState request, Serv Success = true // TODO: Implement error handling }; } + + public override Task AddSubscription(AddSubscriptionRequest request, ServerCallContext context) + { + // TODO: This should map to Orleans Streaming explicit api + return base.AddSubscription(request, context); + } + + public override async Task RegisterAgent(RegisterAgentTypeRequest request, ServerCallContext context) + { + // TODO: This should add the agent to registry + return await Gateway.RegisterAgentTypeAsync(request); + } } diff --git a/dotnet/src/Microsoft.AutoGen/Microsoft.AutoGen.Runtime.Grpc/AgentWorkerHostingExtensions.cs b/dotnet/src/Microsoft.AutoGen/Microsoft.AutoGen.Runtime.Grpc/GrpcRuntimeHostingExtensions.cs similarity index 51% rename from dotnet/src/Microsoft.AutoGen/Microsoft.AutoGen.Runtime.Grpc/AgentWorkerHostingExtensions.cs rename to dotnet/src/Microsoft.AutoGen/Microsoft.AutoGen.Runtime.Grpc/GrpcRuntimeHostingExtensions.cs index 4c3c889ca9d6..bfb67f39281d 100644 --- a/dotnet/src/Microsoft.AutoGen/Microsoft.AutoGen.Runtime.Grpc/AgentWorkerHostingExtensions.cs +++ b/dotnet/src/Microsoft.AutoGen/Microsoft.AutoGen.Runtime.Grpc/GrpcRuntimeHostingExtensions.cs @@ -1,5 +1,5 @@ // Copyright (c) Microsoft Corporation. All rights reserved. -// AgentWorkerHostingExtensions.cs +// GrpcRuntimeHostingExtensions.cs using System.Diagnostics; using Microsoft.AspNetCore.Builder; @@ -11,30 +11,28 @@ namespace Microsoft.AutoGen.Runtime.Grpc; -public static class AgentWorkerHostingExtensions +public static class GrpcRuntimeHostingExtensions { - public static WebApplicationBuilder AddAgentService(this WebApplicationBuilder builder, bool inMemoryOrleans = false, bool useGrpc = true) + public static WebApplicationBuilder AddGrpcRuntime(this WebApplicationBuilder builder, bool inMemoryOrleans = false) { + // TODO: allow for configuration of Orleans, for state and streams builder.AddOrleans(inMemoryOrleans); builder.Services.TryAddSingleton(DistributedContextPropagator.Current); - if (useGrpc) + builder.WebHost.ConfigureKestrel(serverOptions => { - builder.WebHost.ConfigureKestrel(serverOptions => + // TODO: make port configurable + serverOptions.ListenAnyIP(5001, listenOptions => { - // TODO: make port configurable - serverOptions.ListenAnyIP(5001, listenOptions => - { - listenOptions.Protocols = HttpProtocols.Http2; - listenOptions.UseHttps(); - }); + listenOptions.Protocols = HttpProtocols.Http2; + listenOptions.UseHttps(); }); + }); - builder.Services.AddGrpc(); - builder.Services.AddSingleton(); - builder.Services.AddSingleton(sp => (IHostedService)sp.GetRequiredService()); - } + builder.Services.AddGrpc(); + builder.Services.AddSingleton(); + builder.Services.AddSingleton(sp => (IHostedService)sp.GetRequiredService()); return builder; } diff --git a/dotnet/src/Microsoft.AutoGen/Microsoft.AutoGen.Runtime.Grpc/IGateway.cs b/dotnet/src/Microsoft.AutoGen/Microsoft.AutoGen.Runtime.Grpc/IGateway.cs index 16784b64fcc9..9ea0fdb076fd 100644 --- a/dotnet/src/Microsoft.AutoGen/Microsoft.AutoGen.Runtime.Grpc/IGateway.cs +++ b/dotnet/src/Microsoft.AutoGen/Microsoft.AutoGen.Runtime.Grpc/IGateway.cs @@ -11,4 +11,5 @@ public interface IGateway : IGrainObserver ValueTask BroadcastEvent(CloudEvent evt); ValueTask StoreAsync(AgentState value); ValueTask ReadAsync(AgentId agentId); + ValueTask RegisterAgentTypeAsync(RegisterAgentTypeRequest request); } diff --git a/dotnet/src/Microsoft.AutoGen/Microsoft.Autogen.AgentHost/Program.cs b/dotnet/src/Microsoft.AutoGen/Microsoft.Autogen.AgentHost/Program.cs index 4c406429a9a2..01e08410cff1 100644 --- a/dotnet/src/Microsoft.AutoGen/Microsoft.Autogen.AgentHost/Program.cs +++ b/dotnet/src/Microsoft.AutoGen/Microsoft.Autogen.AgentHost/Program.cs @@ -6,7 +6,7 @@ var builder = WebApplication.CreateBuilder(args); -builder.AddAgentService(inMemoryOrleans: true, useGrpc: true); +builder.AddGrpcRuntime(inMemoryOrleans: true); var app = builder.Build(); diff --git a/dotnet/test/Microsoft.AutoGen.Core.Tests/HandleInterfaceTest.cs b/dotnet/test/Microsoft.AutoGen.Core.Tests/HandleInterfaceTests.cs similarity index 93% rename from dotnet/test/Microsoft.AutoGen.Core.Tests/HandleInterfaceTest.cs rename to dotnet/test/Microsoft.AutoGen.Core.Tests/HandleInterfaceTests.cs index 14694543f196..8cf74a74bf3b 100644 --- a/dotnet/test/Microsoft.AutoGen.Core.Tests/HandleInterfaceTest.cs +++ b/dotnet/test/Microsoft.AutoGen.Core.Tests/HandleInterfaceTests.cs @@ -1,5 +1,5 @@ // Copyright (c) Microsoft Corporation. All rights reserved. -// HandleInterfaceTest.cs +// HandleInterfaceTests.cs using FluentAssertions; using Tests.Events; @@ -7,7 +7,7 @@ namespace Microsoft.AutoGen.Core.Tests; -public class HandleInterfaceTest +public class HandleInterfaceTests { [Fact] public void Can_Get_Handler_Methods_From_Class() diff --git a/dotnet/test/Microsoft.AutoGen.Core.Tests/ClusterFixtureCollection.cs b/dotnet/test/Microsoft.AutoGen.Core.Tests/Helpers/ClusterFixtureCollection.cs similarity index 86% rename from dotnet/test/Microsoft.AutoGen.Core.Tests/ClusterFixtureCollection.cs rename to dotnet/test/Microsoft.AutoGen.Core.Tests/Helpers/ClusterFixtureCollection.cs index 4981d779904f..5f643410d8be 100644 --- a/dotnet/test/Microsoft.AutoGen.Core.Tests/ClusterFixtureCollection.cs +++ b/dotnet/test/Microsoft.AutoGen.Core.Tests/Helpers/ClusterFixtureCollection.cs @@ -3,7 +3,7 @@ using Xunit; -namespace Microsoft.AutoGen.Core.Tests; +namespace Microsoft.AutoGen.Core.Tests.Helpers; [CollectionDefinition(Name)] public sealed class ClusterFixtureCollection : ICollectionFixture diff --git a/dotnet/test/Microsoft.AutoGen.Core.Tests/InMemoryAgentRuntimeFixture.cs b/dotnet/test/Microsoft.AutoGen.Core.Tests/Helpers/InMemoryAgentRuntimeFixture.cs similarity index 96% rename from dotnet/test/Microsoft.AutoGen.Core.Tests/InMemoryAgentRuntimeFixture.cs rename to dotnet/test/Microsoft.AutoGen.Core.Tests/Helpers/InMemoryAgentRuntimeFixture.cs index abf5dcadfc52..66d54c938840 100644 --- a/dotnet/test/Microsoft.AutoGen.Core.Tests/InMemoryAgentRuntimeFixture.cs +++ b/dotnet/test/Microsoft.AutoGen.Core.Tests/Helpers/InMemoryAgentRuntimeFixture.cs @@ -8,7 +8,7 @@ using Microsoft.Extensions.Logging; using Moq; -namespace Microsoft.AutoGen.Core.Tests; +namespace Microsoft.AutoGen.Core.Tests.Helpers; public sealed class InMemoryAgentRuntimeFixture : IDisposable { diff --git a/dotnet/test/Microsoft.AutoGen.Core.Tests/InMemoryAgentTests.cs b/dotnet/test/Microsoft.AutoGen.Core.Tests/InMemoryAgentTests.cs index 12ea850566cb..7e71342fc71d 100644 --- a/dotnet/test/Microsoft.AutoGen.Core.Tests/InMemoryAgentTests.cs +++ b/dotnet/test/Microsoft.AutoGen.Core.Tests/InMemoryAgentTests.cs @@ -4,6 +4,7 @@ using FluentAssertions; using Google.Protobuf.Reflection; using Microsoft.AutoGen.Abstractions; +using Microsoft.AutoGen.Core.Tests.Helpers; using Microsoft.Extensions.DependencyInjection; using Tests.Events; using Xunit; diff --git a/dotnet/test/Microsoft.AutoGen.Runtime.Grpc.Tests/Class1.cs b/dotnet/test/Microsoft.AutoGen.Runtime.Grpc.Tests/Class1.cs deleted file mode 100644 index 59d7f60b7172..000000000000 --- a/dotnet/test/Microsoft.AutoGen.Runtime.Grpc.Tests/Class1.cs +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Class1.cs - -namespace Microsoft.AutoGen.Runtime.Grpc.Tests; - -public class Class1 -{ - -} diff --git a/dotnet/test/Microsoft.AutoGen.Runtime.Grpc.Tests/GrpcGatewayTests.cs b/dotnet/test/Microsoft.AutoGen.Runtime.Grpc.Tests/GrpcGatewayTests.cs new file mode 100644 index 000000000000..5ef8ce9b04de --- /dev/null +++ b/dotnet/test/Microsoft.AutoGen.Runtime.Grpc.Tests/GrpcGatewayTests.cs @@ -0,0 +1,119 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// GrpcGatewayTests.cs + +using Microsoft.AutoGen.Abstractions; +using Microsoft.AutoGen.Runtime.Grpc.Tests.Helpers; +using Microsoft.Extensions.Logging; +using Moq; + +namespace Microsoft.AutoGen.Runtime.Grpc.Tests; + +[Collection(ClusterCollection.Name)] +public class GrpcGatewayTests +{ + private readonly ClusterFixture _fixture; + + public GrpcGatewayTests(ClusterFixture fixture) + { + _fixture = fixture; + } + // Test broadcast Event + [Fact] + public async Task TestBroadcastEvent() + { + var logger = Mock.Of>(); + var gateway = new GrpcGateway(_fixture.Cluster.Client, logger); + var evt = new CloudEvent { + Type = "TestType", + }; + + // 1. Register Agent + // 2. Broadcast Event + // 3. + await gateway.BroadcastEvent(evt); + //var registry = fixture.Registry; + //var subscriptions = fixture.Subscriptions; + //var gateway = new Gateway(registry, subscriptions); + //var agentId = new AgentId(1, "TestAgent"); + //var worker = new GatewayWorker(agentId, gateway); + //await registry.AddWorker(worker); + //var agentType = "TestAgent"; + //var topic = "TestTopic"; + //await subscriptions.Subscribe(agentType, topic); + //var message = new Message(agentId, topic, new byte[] { 1, 2, 3 }); + //await gateway.BroadcastEvent(message); + //var receivedMessage = await worker.ReceiveMessage(); + //Assert.Equal(message, receivedMessage); + //await registry.RemoveWorker(worker); + } + + // Test StoreAsync + [Fact] + public async Task TestStoreAsync() + { + var logger = Mock.Of>(); + var gateway = new GrpcGateway(_fixture.Cluster.Client, logger); + var agentState = new AgentState + { + AgentId = new AgentId + { + Type = "TestType", + Key = "TestKey", + }, + }; + await gateway.StoreAsync(agentState); + //var registry = fixture.Registry; + //var gateway = new Gateway(registry, subscriptions); + //var agentId = new AgentId(1, "TestAgent"); + //var worker = new GatewayWorker(agentId, gateway); + //await registry.AddWorker(worker); + //var agentState = new AgentState(agentId, new byte[] { 1, 2, 3 }); + //await gateway.StoreAsync(agentState); + //var receivedAgentState = await worker.ReceiveState(); + //Assert.Equal(agentState, receivedAgentState); + //await registry.RemoveWorker(worker); + } + + // Test ReadAsync + [Fact] + public async Task TestReadAsync() + { + var logger = Mock.Of>(); + var gateway = new GrpcGateway(_fixture.Cluster.Client, logger); + var agentId = new AgentId + { + Type = "TestType", + Key = "TestKey", + }; + var _ = await gateway.ReadAsync(agentId); + //var registry = fixture.Registry; + //var gateway = new Gateway(registry, subscriptions); + //var agentId = new AgentId(1, "TestAgent"); + //var worker = new GatewayWorker(agentId, gateway); + //await registry.AddWorker(worker); + //var agentState = new AgentState(agentId, new byte[] { 1, 2, 3 }); + //await worker.SendState(agentState); + //var receivedAgentState = await gateway.ReadAsync(agentId); + //Assert.Equal(agentState, receivedAgentState); + //await registry.RemoveWorker(worker); + } + + // Test RegisterAgentTypeAsync + [Fact] + public async Task TestRegisterAgentTypeAsync() + { + var logger = Mock.Of>(); + var gateway = new GrpcGateway(_fixture.Cluster.Client, logger); + var request = new RegisterAgentTypeRequest + { + Type = "TestType", + }; + var _ = await gateway.RegisterAgentTypeAsync(request); + //var registry = fixture.Registry; + //var gateway = new Gateway(registry, subscriptions); + //var agentType = "TestAgent"; + //var request = new RegisterAgentTypeRequest(agentType); + //var response = await gateway.RegisterAgentTypeAsync(request); + //Assert.Equal(agentType, response.AgentType); + } +} diff --git a/dotnet/test/Microsoft.AutoGen.Runtime.Grpc.Tests/Helpers/ClusterCollection.cs b/dotnet/test/Microsoft.AutoGen.Runtime.Grpc.Tests/Helpers/ClusterCollection.cs new file mode 100644 index 000000000000..3493ba1d58fa --- /dev/null +++ b/dotnet/test/Microsoft.AutoGen.Runtime.Grpc.Tests/Helpers/ClusterCollection.cs @@ -0,0 +1,10 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// ClusterCollection.cs + +namespace Microsoft.AutoGen.Runtime.Grpc.Tests.Helpers; + +[CollectionDefinition(Name)] +public sealed class ClusterCollection : ICollectionFixture +{ + public const string Name = nameof(ClusterCollection); +} diff --git a/dotnet/test/Microsoft.AutoGen.Runtime.Grpc.Tests/Helpers/ClusterFixture.cs b/dotnet/test/Microsoft.AutoGen.Runtime.Grpc.Tests/Helpers/ClusterFixture.cs new file mode 100644 index 000000000000..f3a3cf2ccf40 --- /dev/null +++ b/dotnet/test/Microsoft.AutoGen.Runtime.Grpc.Tests/Helpers/ClusterFixture.cs @@ -0,0 +1,15 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// ClusterFixture.cs + +using Orleans.TestingHost; + +namespace Microsoft.AutoGen.Runtime.Grpc.Tests.Helpers; + +public sealed class ClusterFixture : IDisposable +{ + public TestCluster Cluster { get; } = new TestClusterBuilder().Build(); + + public ClusterFixture() => Cluster.Deploy(); + + void IDisposable.Dispose() => Cluster.StopAllSilos(); +} diff --git a/dotnet/test/Microsoft.AutoGen.Runtime.Grpc.Tests/Microsoft.AutoGen.Runtime.Grpc.Tests.csproj b/dotnet/test/Microsoft.AutoGen.Runtime.Grpc.Tests/Microsoft.AutoGen.Runtime.Grpc.Tests.csproj index 80fccfe5e9a2..0a0df26b158a 100644 --- a/dotnet/test/Microsoft.AutoGen.Runtime.Grpc.Tests/Microsoft.AutoGen.Runtime.Grpc.Tests.csproj +++ b/dotnet/test/Microsoft.AutoGen.Runtime.Grpc.Tests/Microsoft.AutoGen.Runtime.Grpc.Tests.csproj @@ -7,6 +7,11 @@ True + + + + +