From f276683137ef0c1d3969b22ad2cfd0de611100c2 Mon Sep 17 00:00:00 2001 From: Kosta Petan Date: Thu, 5 Dec 2024 16:11:27 +0100 Subject: [PATCH] grpc tests WIP --- .gitignore | 3 +- .../GrpcGatewayService.cs | 2 +- .../GrpcGatewayServiceTests.cs | 69 ++++++++++++++++ .../GrpcGatewayTests.cs | 2 +- .../Helpers/Grpc/TestAsyncStreamReader.cs | 63 +++++++++++++++ .../Helpers/Grpc/TestServerCallContext.cs | 73 +++++++++++++++++ .../Helpers/Grpc/TestServerStreamWriter.cs | 81 +++++++++++++++++++ .../{ => Orleans}/ClusterCollection.cs | 2 +- .../Helpers/{ => Orleans}/ClusterFixture.cs | 2 +- 9 files changed, 292 insertions(+), 5 deletions(-) create mode 100644 dotnet/test/Microsoft.AutoGen.Runtime.Grpc.Tests/GrpcGatewayServiceTests.cs create mode 100644 dotnet/test/Microsoft.AutoGen.Runtime.Grpc.Tests/Helpers/Grpc/TestAsyncStreamReader.cs create mode 100644 dotnet/test/Microsoft.AutoGen.Runtime.Grpc.Tests/Helpers/Grpc/TestServerCallContext.cs create mode 100644 dotnet/test/Microsoft.AutoGen.Runtime.Grpc.Tests/Helpers/Grpc/TestServerStreamWriter.cs rename dotnet/test/Microsoft.AutoGen.Runtime.Grpc.Tests/Helpers/{ => Orleans}/ClusterCollection.cs (79%) rename dotnet/test/Microsoft.AutoGen.Runtime.Grpc.Tests/Helpers/{ => Orleans}/ClusterFixture.cs (84%) diff --git a/.gitignore b/.gitignore index bd419b14d7c7..808eb8b9b937 100644 --- a/.gitignore +++ b/.gitignore @@ -195,4 +195,5 @@ samples/apps/autogen-studio/autogenstudio/models/test/ notebook/coding # dotnet artifacts -artifacts \ No newline at end of file +artifacts +/dotnet/.NCrunch_AutoGen/StoredText 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 16e63e16bb93..142d125954ca 100644 --- a/dotnet/src/Microsoft.AutoGen/Microsoft.AutoGen.Runtime.Grpc/GrpcGatewayService.cs +++ b/dotnet/src/Microsoft.AutoGen/Microsoft.AutoGen.Runtime.Grpc/GrpcGatewayService.cs @@ -7,7 +7,7 @@ namespace Microsoft.AutoGen.Runtime.Grpc; // gRPC service which handles communication between the agent worker and the cluster. -internal sealed class GrpcGatewayService : AgentRpc.AgentRpcBase +public sealed class GrpcGatewayService : AgentRpc.AgentRpcBase { private readonly GrpcGateway Gateway; public GrpcGatewayService(GrpcGateway gateway) diff --git a/dotnet/test/Microsoft.AutoGen.Runtime.Grpc.Tests/GrpcGatewayServiceTests.cs b/dotnet/test/Microsoft.AutoGen.Runtime.Grpc.Tests/GrpcGatewayServiceTests.cs new file mode 100644 index 000000000000..773440a65146 --- /dev/null +++ b/dotnet/test/Microsoft.AutoGen.Runtime.Grpc.Tests/GrpcGatewayServiceTests.cs @@ -0,0 +1,69 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// GrpcGatewayServiceTests.cs + +using FluentAssertions; +using Microsoft.AutoGen.Abstractions; +using Microsoft.AutoGen.Runtime.Grpc.Tests.Helpers.Grpc; +using Microsoft.AutoGen.Runtime.Grpc.Tests.Helpers.Orleans; +using Microsoft.Extensions.Logging; +using Moq; + +namespace Microsoft.AutoGen.Runtime.Grpc.Tests; +[Collection(ClusterCollection.Name)] +public class GrpcGatewayServiceTests +{ + private readonly ClusterFixture _fixture; + + public GrpcGatewayServiceTests(ClusterFixture fixture) + { + _fixture = fixture; + } + // Test broadcast Event + [Fact] + public async Task Test_OpenChannel() + { + var logger = Mock.Of>(); + var gateway = new GrpcGateway(_fixture.Cluster.Client, logger); + var service = new GrpcGatewayService(gateway); + var callContext = TestServerCallContext.Create(); + var requestStream = new TestAsyncStreamReader(callContext); + var responseStream = new TestServerStreamWriter(callContext); + + await service.OpenChannel(requestStream, responseStream, callContext); + + requestStream.AddMessage(new Message { }); + + requestStream.Complete(); + + responseStream.Complete(); + + var responseMessage = await responseStream.ReadNextAsync(); + responseMessage.Should().NotBeNull(); + } + + [Fact] + public async Task Test_SaveState() + { + var logger = Mock.Of>(); + var gateway = new GrpcGateway(_fixture.Cluster.Client, logger); + var service = new GrpcGatewayService(gateway); + var callContext = TestServerCallContext.Create(); + + var response = await service.SaveState(new AgentState { }, callContext); + + response.Should().NotBeNull(); + } + + [Fact] + public async Task Test_GetState() + { + var logger = Mock.Of>(); + var gateway = new GrpcGateway(_fixture.Cluster.Client, logger); + var service = new GrpcGatewayService(gateway); + var callContext = TestServerCallContext.Create(); + + var response = await service.GetState(new AgentId { }, callContext); + + response.Should().NotBeNull(); + } +} diff --git a/dotnet/test/Microsoft.AutoGen.Runtime.Grpc.Tests/GrpcGatewayTests.cs b/dotnet/test/Microsoft.AutoGen.Runtime.Grpc.Tests/GrpcGatewayTests.cs index 5ef8ce9b04de..2cb1a094c939 100644 --- a/dotnet/test/Microsoft.AutoGen.Runtime.Grpc.Tests/GrpcGatewayTests.cs +++ b/dotnet/test/Microsoft.AutoGen.Runtime.Grpc.Tests/GrpcGatewayTests.cs @@ -2,7 +2,7 @@ // GrpcGatewayTests.cs using Microsoft.AutoGen.Abstractions; -using Microsoft.AutoGen.Runtime.Grpc.Tests.Helpers; +using Microsoft.AutoGen.Runtime.Grpc.Tests.Helpers.Orleans; using Microsoft.Extensions.Logging; using Moq; diff --git a/dotnet/test/Microsoft.AutoGen.Runtime.Grpc.Tests/Helpers/Grpc/TestAsyncStreamReader.cs b/dotnet/test/Microsoft.AutoGen.Runtime.Grpc.Tests/Helpers/Grpc/TestAsyncStreamReader.cs new file mode 100644 index 000000000000..88520ff58aa6 --- /dev/null +++ b/dotnet/test/Microsoft.AutoGen.Runtime.Grpc.Tests/Helpers/Grpc/TestAsyncStreamReader.cs @@ -0,0 +1,63 @@ +#pragma warning disable IDE0073 +// Copyright 2019 The gRPC Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.Threading.Channels; +using Grpc.Core; + +namespace Microsoft.AutoGen.Runtime.Grpc.Tests.Helpers.Grpc; + +public class TestAsyncStreamReader : IAsyncStreamReader where T : class +{ + private readonly Channel _channel; + private readonly ServerCallContext _serverCallContext; + + public T Current { get; private set; } = null!; + + public TestAsyncStreamReader(ServerCallContext serverCallContext) + { + _channel = Channel.CreateUnbounded(); + _serverCallContext = serverCallContext; + } + + public void AddMessage(T message) + { + if (!_channel.Writer.TryWrite(message)) + { + throw new InvalidOperationException("Unable to write message."); + } + } + + public void Complete() + { + _channel.Writer.Complete(); + } + + public async Task MoveNext(CancellationToken cancellationToken) + { + _serverCallContext.CancellationToken.ThrowIfCancellationRequested(); + + if (await _channel.Reader.WaitToReadAsync(cancellationToken) && + _channel.Reader.TryRead(out var message)) + { + Current = message; + return true; + } + else + { + Current = null!; + return false; + } + } +} diff --git a/dotnet/test/Microsoft.AutoGen.Runtime.Grpc.Tests/Helpers/Grpc/TestServerCallContext.cs b/dotnet/test/Microsoft.AutoGen.Runtime.Grpc.Tests/Helpers/Grpc/TestServerCallContext.cs new file mode 100644 index 000000000000..47f25155602d --- /dev/null +++ b/dotnet/test/Microsoft.AutoGen.Runtime.Grpc.Tests/Helpers/Grpc/TestServerCallContext.cs @@ -0,0 +1,73 @@ +#pragma warning disable IDE0073 +// Copyright 2019 The gRPC Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Grpc.Core; + +namespace Microsoft.AutoGen.Runtime.Grpc.Tests.Helpers.Grpc; + +public class TestServerCallContext : ServerCallContext +{ + private readonly Metadata _requestHeaders; + private readonly CancellationToken _cancellationToken; + private readonly Metadata _responseTrailers; + private readonly AuthContext _authContext; + private readonly Dictionary _userState; + private WriteOptions? _writeOptions; + + public Metadata? ResponseHeaders { get; private set; } + + private TestServerCallContext(Metadata requestHeaders, CancellationToken cancellationToken) + { + _requestHeaders = requestHeaders; + _cancellationToken = cancellationToken; + _responseTrailers = new Metadata(); + _authContext = new AuthContext(string.Empty, new Dictionary>()); + _userState = new Dictionary(); + } + + protected override string MethodCore => "MethodName"; + protected override string HostCore => "HostName"; + protected override string PeerCore => "PeerName"; + protected override DateTime DeadlineCore { get; } + protected override Metadata RequestHeadersCore => _requestHeaders; + protected override CancellationToken CancellationTokenCore => _cancellationToken; + protected override Metadata ResponseTrailersCore => _responseTrailers; + protected override Status StatusCore { get; set; } + protected override WriteOptions? WriteOptionsCore { get => _writeOptions; set { _writeOptions = value; } } + protected override AuthContext AuthContextCore => _authContext; + + protected override ContextPropagationToken CreatePropagationTokenCore(ContextPropagationOptions? options) + { + throw new NotImplementedException(); + } + + protected override Task WriteResponseHeadersAsyncCore(Metadata responseHeaders) + { + if (ResponseHeaders != null) + { + throw new InvalidOperationException("Response headers have already been written."); + } + + ResponseHeaders = responseHeaders; + return Task.CompletedTask; + } + + protected override IDictionary UserStateCore => _userState; + + public static TestServerCallContext Create(Metadata? requestHeaders = null, CancellationToken cancellationToken = default) + { + return new TestServerCallContext(requestHeaders ?? new Metadata(), cancellationToken); + } +} diff --git a/dotnet/test/Microsoft.AutoGen.Runtime.Grpc.Tests/Helpers/Grpc/TestServerStreamWriter.cs b/dotnet/test/Microsoft.AutoGen.Runtime.Grpc.Tests/Helpers/Grpc/TestServerStreamWriter.cs new file mode 100644 index 000000000000..6a72f315bdcc --- /dev/null +++ b/dotnet/test/Microsoft.AutoGen.Runtime.Grpc.Tests/Helpers/Grpc/TestServerStreamWriter.cs @@ -0,0 +1,81 @@ +#pragma warning disable IDE0073 +// Copyright 2019 The gRPC Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.Threading.Channels; +using Grpc.Core; + +namespace Microsoft.AutoGen.Runtime.Grpc.Tests.Helpers.Grpc; + +public class TestServerStreamWriter : IServerStreamWriter where T : class +{ + private readonly ServerCallContext _serverCallContext; + private readonly Channel _channel; + + public WriteOptions? WriteOptions { get; set; } + + public TestServerStreamWriter(ServerCallContext serverCallContext) + { + _channel = Channel.CreateUnbounded(); + + _serverCallContext = serverCallContext; + } + + public void Complete() + { + _channel.Writer.Complete(); + } + + public IAsyncEnumerable ReadAllAsync() + { + return _channel.Reader.ReadAllAsync(); + } + + public async Task ReadNextAsync() + { + if (await _channel.Reader.WaitToReadAsync()) + { + _channel.Reader.TryRead(out var message); + return message; + } + else + { + return null; + } + } + + public Task WriteAsync(T message, CancellationToken cancellationToken) + { + if (cancellationToken.IsCancellationRequested) + { + return Task.FromCanceled(cancellationToken); + } + if (_serverCallContext.CancellationToken.IsCancellationRequested) + { + return Task.FromCanceled(_serverCallContext.CancellationToken); + } + + if (!_channel.Writer.TryWrite(message)) + { + throw new InvalidOperationException("Unable to write message."); + } + + return Task.CompletedTask; + } + + public Task WriteAsync(T message) + { + return WriteAsync(message, CancellationToken.None); + } +} diff --git a/dotnet/test/Microsoft.AutoGen.Runtime.Grpc.Tests/Helpers/ClusterCollection.cs b/dotnet/test/Microsoft.AutoGen.Runtime.Grpc.Tests/Helpers/Orleans/ClusterCollection.cs similarity index 79% rename from dotnet/test/Microsoft.AutoGen.Runtime.Grpc.Tests/Helpers/ClusterCollection.cs rename to dotnet/test/Microsoft.AutoGen.Runtime.Grpc.Tests/Helpers/Orleans/ClusterCollection.cs index 3493ba1d58fa..d61dc7b21c50 100644 --- a/dotnet/test/Microsoft.AutoGen.Runtime.Grpc.Tests/Helpers/ClusterCollection.cs +++ b/dotnet/test/Microsoft.AutoGen.Runtime.Grpc.Tests/Helpers/Orleans/ClusterCollection.cs @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // ClusterCollection.cs -namespace Microsoft.AutoGen.Runtime.Grpc.Tests.Helpers; +namespace Microsoft.AutoGen.Runtime.Grpc.Tests.Helpers.Orleans; [CollectionDefinition(Name)] public sealed class ClusterCollection : ICollectionFixture diff --git a/dotnet/test/Microsoft.AutoGen.Runtime.Grpc.Tests/Helpers/ClusterFixture.cs b/dotnet/test/Microsoft.AutoGen.Runtime.Grpc.Tests/Helpers/Orleans/ClusterFixture.cs similarity index 84% rename from dotnet/test/Microsoft.AutoGen.Runtime.Grpc.Tests/Helpers/ClusterFixture.cs rename to dotnet/test/Microsoft.AutoGen.Runtime.Grpc.Tests/Helpers/Orleans/ClusterFixture.cs index f3a3cf2ccf40..494814bfd324 100644 --- a/dotnet/test/Microsoft.AutoGen.Runtime.Grpc.Tests/Helpers/ClusterFixture.cs +++ b/dotnet/test/Microsoft.AutoGen.Runtime.Grpc.Tests/Helpers/Orleans/ClusterFixture.cs @@ -3,7 +3,7 @@ using Orleans.TestingHost; -namespace Microsoft.AutoGen.Runtime.Grpc.Tests.Helpers; +namespace Microsoft.AutoGen.Runtime.Grpc.Tests.Helpers.Orleans; public sealed class ClusterFixture : IDisposable {