|
- // Copyright (c) Microsoft Corporation. All rights reserved.
- // GrpcGatewayServiceTests.cs
-
- using FluentAssertions;
- using Microsoft.AutoGen.Contracts;
- using Microsoft.AutoGen.Core;
- using Microsoft.AutoGen.Protobuf;
- using Microsoft.AutoGen.RuntimeGateway.Grpc.Tests.Helpers.Grpc;
- using Microsoft.AutoGen.RuntimeGateway.Grpc.Tests.Helpers.Orleans;
- using Microsoft.Extensions.Logging;
- using Moq;
- using NewMessageReceived = Tests.Events.NewMessageReceived;
-
- namespace Microsoft.AutoGen.RuntimeGateway.Grpc.Tests;
- [Collection(ClusterCollection.Name)]
- [Trait("Category", "UnitV2")]
- public class GrpcGatewayServiceTests
- {
- private readonly ClusterFixture _fixture;
-
- public GrpcGatewayServiceTests(ClusterFixture fixture)
- {
- _fixture = fixture;
- }
- [Fact]
- public async Task Test_OpenChannel()
- {
- var logger = Mock.Of<ILogger<GrpcGateway>>();
- var gateway = new GrpcGateway(_fixture.Cluster.Client, logger);
- var service = new GrpcGatewayService(gateway);
- var client = new TestGrpcClient<Message>();
-
- gateway._workers.Count.Should().Be(0);
- var task = OpenChannel(service, client);
- gateway._workers.Count.Should().Be(1);
- client.Dispose();
- await task;
- }
-
- [Fact]
- public async Task Test_ControlChannel_Pass_Message_Through_Gateway()
- {
- var logger = Mock.Of<ILogger<GrpcGateway>>();
- var gateway = new GrpcGateway(_fixture.Cluster.Client, logger);
- var service = new GrpcGatewayService(gateway);
-
- var controlClient = new TestGrpcClient<ControlMessage>();
- var controlTask = OpenConrolChannel(service, controlClient);
- gateway._controlWorkers.Count.Should().Be(1);
-
- var testMessage = new ControlMessage
- {
- RpcId = "123",
- Destination = "agentid=PBAgent", // Target the registered agent
- RespondTo = "", // Empty string means it's a request
- RpcMessage = Google.Protobuf.WellKnownTypes.Any.Pack(new SaveStateRequest { AgentId = new Protobuf.AgentId() })
- };
-
- controlClient.AddMessage(testMessage);
-
- // Step 4: Verify that the agent receives the control message
- var receivedMessage = await controlClient.ReadNext();
- receivedMessage.Should().NotBeNull();
- receivedMessage.RpcId.Should().Be("123");
-
- // Cleanup
- controlClient.Dispose();
- await controlTask;
- }
-
- [Fact]
- public async Task Test_Message_Exchange_Through_Gateway()
- {
- var logger = Mock.Of<ILogger<GrpcGateway>>();
- var gateway = new GrpcGateway(_fixture.Cluster.Client, logger);
- var service = new GrpcGatewayService(gateway);
- var client = new TestGrpcClient<Message>();
- var task = OpenChannel(service: service, client);
- await service.RegisterAgent(await CreateRegistrationRequest(service, typeof(PBAgent)), client.CallContext);
- await service.RegisterAgent(await CreateRegistrationRequest(service, typeof(GMAgent)), client.CallContext);
-
- //var inputEvent = new NewMessageReceived { Message = $"Start-{client.CallContext.Peer}" }.ToCloudEvent("gh-gh-gh", "gh-gh-gh");
- var newMessage = new NewMessageReceived { Message = $"Start-{client.CallContext.Peer}" };
- var eventType = GetFullName(typeof(NewMessageReceived));
- var inputEvent = CloudEventExtensions.CreateCloudEvent(
- Google.Protobuf.WellKnownTypes.Any.Pack(newMessage),
- new TopicId(eventType, "gh-gh-gh"),
- eventType,
- null,
- Guid.NewGuid().ToString());
-
- client.AddMessage(new Message { CloudEvent = inputEvent });
- var newMessageReceived = await client.ReadNext();
- newMessageReceived!.CloudEvent.Type.Should().Be(GetFullName(typeof(NewMessageReceived)));
- newMessageReceived.CloudEvent.Source.Should().Be("gh-gh-gh");
- var secondMessage = await client.ReadNext();
- secondMessage!.CloudEvent.Type.Should().Be(GetFullName(typeof(NewMessageReceived)));
-
- // Simulate an agent, by publishing a new message in the request stream
- //var helloEvent = new Hello { Message = $"Hello test-{client.CallContext.Peer}" }.ToCloudEvent("gh-gh-gh", "gh-gh-gh");
- var hello = new Hello { Message = $"Hello test-{client.CallContext.Peer}" };
- var eventTypeHello = GetFullName(typeof(Hello));
- var helloEvent = CloudEventExtensions.CreateCloudEvent(
- Google.Protobuf.WellKnownTypes.Any.Pack(message: hello),
- new TopicId(eventTypeHello, "gh-gh-gh"),
- eventTypeHello,
- null,
- Guid.NewGuid().ToString()
- );
- client.AddMessage(new Message { CloudEvent = helloEvent });
- var helloMessageReceived = await client.ReadNext();
- helloMessageReceived!.CloudEvent.Type.Should().Be(GetFullName(typeof(Hello)));
- helloMessageReceived.CloudEvent.Source.Should().Be("gh-gh-gh");
- client.Dispose();
- await task;
- }
-
- [Fact]
- public async Task Test_RegisterAgent_Should_Succeed()
- {
- var logger = Mock.Of<ILogger<GrpcGateway>>();
- var gateway = new GrpcGateway(_fixture.Cluster.Client, logger);
- var service = new GrpcGatewayService(gateway);
- var client = new TestGrpcClient<Message>();
- var task = OpenChannel(service: service, client);
- var response = await service.RegisterAgent(await CreateRegistrationRequest(service, typeof(PBAgent)), client.CallContext);
- response.GetType().Should().Be(typeof(RegisterAgentTypeResponse));
- client.Dispose();
- await task;
- }
-
- private async Task<RegisterAgentTypeRequest> CreateRegistrationRequest(GrpcGatewayService service, Type type)
- {
- var registration = new RegisterAgentTypeRequest
- {
- Type = type.Name,
- };
- var assembly = type.Assembly;
- var eventTypes = ReflectionHelper.GetAgentsMetadata(assembly);
- var events = eventTypes.GetEventsForAgent(type)?.ToList();
- var topics = eventTypes.GetTopicsForAgent(type)?.ToList();
- var topicsPrefix = eventTypes.GetTopicsPrefixForAgent(type)?.ToList();
- if (events is not null && topics is not null) { events.AddRange(topics); }
- var client = new TestGrpcClient<Message>();
-
- if (events != null)
- {
- foreach (var e in events)
- {
- var subscriptionRequest = new AddSubscriptionRequest
- {
- Subscription = new Subscription
- {
- Id = Guid.NewGuid().ToString(),
- TypeSubscription = new Protobuf.TypeSubscription
- {
- AgentType = type.Name,
- TopicType = type.Name + "." + e
- }
- }
-
- };
- await service.AddSubscription(subscriptionRequest, client.CallContext);
- }
- }
- var topicTypes = type.GetCustomAttributes(typeof(TypeSubscriptionAttribute), true).Cast<TypeSubscriptionAttribute>().Select(t => t.Topic).ToList();
- if (topicTypes != null)
- {
- foreach (var topicType in topicTypes)
- {
- var subscriptionRequest = new AddSubscriptionRequest
- {
- Subscription = new Subscription
- {
- Id = Guid.NewGuid().ToString(),
- TypeSubscription = new Protobuf.TypeSubscription
- {
- AgentType = type.Name,
- TopicType = topicType
- }
- }
-
- };
- await service.AddSubscription(subscriptionRequest, client.CallContext);
- }
- }
- var topicPrefixTypes = type.GetCustomAttributes(typeof(TypePrefixSubscriptionAttribute), true).Cast<TypePrefixSubscriptionAttribute>().Select(t => t.Topic).ToList();
- if (topicPrefixTypes != null)
- {
- foreach (var topicType in topicPrefixTypes)
- {
- var subscriptionRequest = new AddSubscriptionRequest
- {
- Subscription = new Subscription
- {
- Id = Guid.NewGuid().ToString(),
- TypePrefixSubscription = new Protobuf.TypePrefixSubscription
- {
- AgentType = type.Name,
- TopicTypePrefix = topicType
- }
- }
-
- };
- await service.AddSubscription(subscriptionRequest, client.CallContext);
- }
- }
- return registration;
- }
-
- private Task OpenChannel(GrpcGatewayService service, TestGrpcClient<Message> client)
- {
- return service.OpenChannel(client.RequestStream, client.ResponseStream, client.CallContext);
- }
- private Task OpenConrolChannel(GrpcGatewayService service, TestGrpcClient<ControlMessage> client)
- {
- return service.OpenControlChannel(client.RequestStream, client.ResponseStream, client.CallContext);
- }
- private string GetFullName(Type type)
- {
- return ReflectionHelper.GetMessageDescriptor(type)!.FullName;
- }
- /// duplicate code here because I could not get InternalsVisibleTo to work
- internal static class Constants
- {
- public const string DATA_CONTENT_TYPE_PROTOBUF_VALUE = "application/x-protobuf";
- public const string DATA_CONTENT_TYPE_JSON_VALUE = "application/json";
- public const string DATA_CONTENT_TYPE_TEXT_VALUE = "text/plain";
-
- public const string DATA_CONTENT_TYPE_ATTR = "datacontenttype";
- public const string DATA_SCHEMA_ATTR = "dataschema";
- public const string AGENT_SENDER_TYPE_ATTR = "agagentsendertype";
- public const string AGENT_SENDER_KEY_ATTR = "agagentsenderkey";
-
- public const string MESSAGE_KIND_ATTR = "agmsgkind";
- public const string MESSAGE_KIND_VALUE_PUBLISH = "publish";
- public const string MESSAGE_KIND_VALUE_RPC_REQUEST = "rpc_request";
- public const string MESSAGE_KIND_VALUE_RPC_RESPONSE = "rpc_response";
- }
- internal static class CloudEventExtensions
- {
- // Convert an ISubscrptionDefinition to a Protobuf Subscription
- internal static CloudEvent CreateCloudEvent(Google.Protobuf.WellKnownTypes.Any payload, TopicId topic, string dataType, Contracts.AgentId? sender, string messageId)
- {
- var attributes = new Dictionary<string, CloudEvent.Types.CloudEventAttributeValue>
- {
- {
- Constants.DATA_CONTENT_TYPE_ATTR, new CloudEvent.Types.CloudEventAttributeValue { CeString = Constants.DATA_CONTENT_TYPE_PROTOBUF_VALUE }
- },
- {
- Constants.DATA_SCHEMA_ATTR, new CloudEvent.Types.CloudEventAttributeValue { CeString = dataType }
- },
- {
- Constants.MESSAGE_KIND_ATTR, new CloudEvent.Types.CloudEventAttributeValue { CeString = Constants.MESSAGE_KIND_VALUE_PUBLISH }
- }
- };
-
- if (sender != null)
- {
- var senderNonNull = (Contracts.AgentId)sender;
- attributes.Add(Constants.AGENT_SENDER_TYPE_ATTR, new CloudEvent.Types.CloudEventAttributeValue { CeString = senderNonNull.Type });
- attributes.Add(Constants.AGENT_SENDER_KEY_ATTR, new CloudEvent.Types.CloudEventAttributeValue { CeString = senderNonNull.Key });
- }
-
- return new CloudEvent
- {
- ProtoData = payload,
- Type = topic.Type,
- Source = topic.Source,
- Id = messageId,
- Attributes = { attributes }
- };
-
- }
- }
- }
|