You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

GrpcGatewayServiceTests.cs 10 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241
  1. // Copyright (c) Microsoft Corporation. All rights reserved.
  2. // GrpcGatewayServiceTests.cs
  3. using FluentAssertions;
  4. using Microsoft.AutoGen.Contracts;
  5. using Microsoft.AutoGen.Core;
  6. using Microsoft.AutoGen.Protobuf;
  7. using Microsoft.AutoGen.RuntimeGateway.Grpc.Tests.Helpers.Grpc;
  8. using Microsoft.AutoGen.RuntimeGateway.Grpc.Tests.Helpers.Orleans;
  9. using Microsoft.Extensions.Logging;
  10. using Moq;
  11. using NewMessageReceived = Tests.Events.NewMessageReceived;
  12. namespace Microsoft.AutoGen.RuntimeGateway.Grpc.Tests;
  13. [Collection(ClusterCollection.Name)]
  14. [Trait("Category", "UnitV2")]
  15. public class GrpcGatewayServiceTests
  16. {
  17. private readonly ClusterFixture _fixture;
  18. public GrpcGatewayServiceTests(ClusterFixture fixture)
  19. {
  20. _fixture = fixture;
  21. }
  22. [Fact]
  23. public async Task Test_OpenChannel()
  24. {
  25. var logger = Mock.Of<ILogger<GrpcGateway>>();
  26. var gateway = new GrpcGateway(_fixture.Cluster.Client, logger);
  27. var service = new GrpcGatewayService(gateway);
  28. var client = new TestGrpcClient();
  29. gateway._workers.Count.Should().Be(0);
  30. var task = OpenChannel(service, client);
  31. gateway._workers.Count.Should().Be(1);
  32. client.Dispose();
  33. await task;
  34. }
  35. [Fact]
  36. public async Task Test_Message_Exchange_Through_Gateway()
  37. {
  38. var logger = Mock.Of<ILogger<GrpcGateway>>();
  39. var gateway = new GrpcGateway(_fixture.Cluster.Client, logger);
  40. var service = new GrpcGatewayService(gateway);
  41. var client = new TestGrpcClient();
  42. var task = OpenChannel(service: service, client);
  43. await service.RegisterAgent(await CreateRegistrationRequest(service, typeof(PBAgent)), client.CallContext);
  44. await service.RegisterAgent(await CreateRegistrationRequest(service, typeof(GMAgent)), client.CallContext);
  45. //var inputEvent = new NewMessageReceived { Message = $"Start-{client.CallContext.Peer}" }.ToCloudEvent("gh-gh-gh", "gh-gh-gh");
  46. var newMessage = new NewMessageReceived { Message = $"Start-{client.CallContext.Peer}" };
  47. var eventType = GetFullName(typeof(NewMessageReceived));
  48. var inputEvent = CloudEventExtensions.CreateCloudEvent(
  49. Google.Protobuf.WellKnownTypes.Any.Pack(newMessage),
  50. new TopicId(eventType, "gh-gh-gh"),
  51. eventType,
  52. null,
  53. Guid.NewGuid().ToString());
  54. client.AddMessage(new Message { CloudEvent = inputEvent });
  55. var newMessageReceived = await client.ReadNext();
  56. newMessageReceived!.CloudEvent.Type.Should().Be(GetFullName(typeof(NewMessageReceived)));
  57. newMessageReceived.CloudEvent.Source.Should().Be("gh-gh-gh");
  58. var secondMessage = await client.ReadNext();
  59. secondMessage!.CloudEvent.Type.Should().Be(GetFullName(typeof(NewMessageReceived)));
  60. // Simulate an agent, by publishing a new message in the request stream
  61. //var helloEvent = new Hello { Message = $"Hello test-{client.CallContext.Peer}" }.ToCloudEvent("gh-gh-gh", "gh-gh-gh");
  62. var hello = new Hello { Message = $"Hello test-{client.CallContext.Peer}" };
  63. var eventTypeHello = GetFullName(typeof(Hello));
  64. var helloEvent = CloudEventExtensions.CreateCloudEvent(
  65. Google.Protobuf.WellKnownTypes.Any.Pack(message: hello),
  66. new TopicId(eventTypeHello, "gh-gh-gh"),
  67. eventTypeHello,
  68. null,
  69. Guid.NewGuid().ToString()
  70. );
  71. client.AddMessage(new Message { CloudEvent = helloEvent });
  72. var helloMessageReceived = await client.ReadNext();
  73. helloMessageReceived!.CloudEvent.Type.Should().Be(GetFullName(typeof(Hello)));
  74. helloMessageReceived.CloudEvent.Source.Should().Be("gh-gh-gh");
  75. client.Dispose();
  76. await task;
  77. }
  78. [Fact]
  79. public async Task Test_RegisterAgent_Should_Succeed()
  80. {
  81. var logger = Mock.Of<ILogger<GrpcGateway>>();
  82. var gateway = new GrpcGateway(_fixture.Cluster.Client, logger);
  83. var service = new GrpcGatewayService(gateway);
  84. var client = new TestGrpcClient();
  85. var task = OpenChannel(service: service, client);
  86. var response = await service.RegisterAgent(await CreateRegistrationRequest(service, typeof(PBAgent)), client.CallContext);
  87. response.GetType().Should().Be(typeof(RegisterAgentTypeResponse));
  88. client.Dispose();
  89. await task;
  90. }
  91. private async Task<RegisterAgentTypeRequest> CreateRegistrationRequest(GrpcGatewayService service, Type type)
  92. {
  93. var registration = new RegisterAgentTypeRequest
  94. {
  95. Type = type.Name,
  96. };
  97. var assembly = type.Assembly;
  98. var eventTypes = ReflectionHelper.GetAgentsMetadata(assembly);
  99. var events = eventTypes.GetEventsForAgent(type)?.ToList();
  100. var topics = eventTypes.GetTopicsForAgent(type)?.ToList();
  101. var topicsPrefix = eventTypes.GetTopicsPrefixForAgent(type)?.ToList();
  102. if (events is not null && topics is not null) { events.AddRange(topics); }
  103. var client = new TestGrpcClient();
  104. if (events != null)
  105. {
  106. foreach (var e in events)
  107. {
  108. var subscriptionRequest = new AddSubscriptionRequest
  109. {
  110. Subscription = new Subscription
  111. {
  112. Id = Guid.NewGuid().ToString(),
  113. TypeSubscription = new Protobuf.TypeSubscription
  114. {
  115. AgentType = type.Name,
  116. TopicType = type.Name + "." + e
  117. }
  118. }
  119. };
  120. await service.AddSubscription(subscriptionRequest, client.CallContext);
  121. }
  122. }
  123. var topicTypes = type.GetCustomAttributes(typeof(TypeSubscriptionAttribute), true).Cast<TypeSubscriptionAttribute>().Select(t => t.Topic).ToList();
  124. if (topicTypes != null)
  125. {
  126. foreach (var topicType in topicTypes)
  127. {
  128. var subscriptionRequest = new AddSubscriptionRequest
  129. {
  130. Subscription = new Subscription
  131. {
  132. Id = Guid.NewGuid().ToString(),
  133. TypeSubscription = new Protobuf.TypeSubscription
  134. {
  135. AgentType = type.Name,
  136. TopicType = topicType
  137. }
  138. }
  139. };
  140. await service.AddSubscription(subscriptionRequest, client.CallContext);
  141. }
  142. }
  143. var topicPrefixTypes = type.GetCustomAttributes(typeof(TypePrefixSubscriptionAttribute), true).Cast<TypePrefixSubscriptionAttribute>().Select(t => t.Topic).ToList();
  144. if (topicPrefixTypes != null)
  145. {
  146. foreach (var topicType in topicPrefixTypes)
  147. {
  148. var subscriptionRequest = new AddSubscriptionRequest
  149. {
  150. Subscription = new Subscription
  151. {
  152. Id = Guid.NewGuid().ToString(),
  153. TypePrefixSubscription = new Protobuf.TypePrefixSubscription
  154. {
  155. AgentType = type.Name,
  156. TopicTypePrefix = topicType
  157. }
  158. }
  159. };
  160. await service.AddSubscription(subscriptionRequest, client.CallContext);
  161. }
  162. }
  163. return registration;
  164. }
  165. private Task OpenChannel(GrpcGatewayService service, TestGrpcClient client)
  166. {
  167. return service.OpenChannel(client.RequestStream, client.ResponseStream, client.CallContext);
  168. }
  169. private string GetFullName(Type type)
  170. {
  171. return ReflectionHelper.GetMessageDescriptor(type)!.FullName;
  172. }
  173. /// duplicate code here because I could not get InternalsVisibleTo to work
  174. internal static class Constants
  175. {
  176. public const string DATA_CONTENT_TYPE_PROTOBUF_VALUE = "application/x-protobuf";
  177. public const string DATA_CONTENT_TYPE_JSON_VALUE = "application/json";
  178. public const string DATA_CONTENT_TYPE_TEXT_VALUE = "text/plain";
  179. public const string DATA_CONTENT_TYPE_ATTR = "datacontenttype";
  180. public const string DATA_SCHEMA_ATTR = "dataschema";
  181. public const string AGENT_SENDER_TYPE_ATTR = "agagentsendertype";
  182. public const string AGENT_SENDER_KEY_ATTR = "agagentsenderkey";
  183. public const string MESSAGE_KIND_ATTR = "agmsgkind";
  184. public const string MESSAGE_KIND_VALUE_PUBLISH = "publish";
  185. public const string MESSAGE_KIND_VALUE_RPC_REQUEST = "rpc_request";
  186. public const string MESSAGE_KIND_VALUE_RPC_RESPONSE = "rpc_response";
  187. }
  188. internal static class CloudEventExtensions
  189. {
  190. // Convert an ISubscrptionDefinition to a Protobuf Subscription
  191. internal static CloudEvent CreateCloudEvent(Google.Protobuf.WellKnownTypes.Any payload, TopicId topic, string dataType, Contracts.AgentId? sender, string messageId)
  192. {
  193. var attributes = new Dictionary<string, CloudEvent.Types.CloudEventAttributeValue>
  194. {
  195. {
  196. Constants.DATA_CONTENT_TYPE_ATTR, new CloudEvent.Types.CloudEventAttributeValue { CeString = Constants.DATA_CONTENT_TYPE_PROTOBUF_VALUE }
  197. },
  198. {
  199. Constants.DATA_SCHEMA_ATTR, new CloudEvent.Types.CloudEventAttributeValue { CeString = dataType }
  200. },
  201. {
  202. Constants.MESSAGE_KIND_ATTR, new CloudEvent.Types.CloudEventAttributeValue { CeString = Constants.MESSAGE_KIND_VALUE_PUBLISH }
  203. }
  204. };
  205. if (sender != null)
  206. {
  207. var senderNonNull = (Contracts.AgentId)sender;
  208. attributes.Add(Constants.AGENT_SENDER_TYPE_ATTR, new CloudEvent.Types.CloudEventAttributeValue { CeString = senderNonNull.Type });
  209. attributes.Add(Constants.AGENT_SENDER_KEY_ATTR, new CloudEvent.Types.CloudEventAttributeValue { CeString = senderNonNull.Key });
  210. }
  211. return new CloudEvent
  212. {
  213. ProtoData = payload,
  214. Type = topic.Type,
  215. Source = topic.Source,
  216. Id = messageId,
  217. Attributes = { attributes }
  218. };
  219. }
  220. }
  221. }