|
2 | 2 | using System.Collections.Generic; |
3 | 3 | using System.Threading; |
4 | 4 | using System.Threading.Tasks; |
| 5 | +using Google.Protobuf; |
5 | 6 | using Grpc.Core; |
6 | 7 | using NSubstitute; |
7 | 8 | using NSubstitute.ExceptionExtensions; |
@@ -301,6 +302,105 @@ public static IEnumerable<object[]> ResolveValueDataLossData() |
301 | 302 | }; |
302 | 303 | } |
303 | 304 |
|
| 305 | + [Theory] |
| 306 | + [MemberData(nameof(ResolveValueFlagdMetadata))] |
| 307 | + internal async Task ResolveValueAsync_AddsFlagMetadata<T>(Func<RpcResolver, Task<ResolutionDetails<T>>> act, |
| 308 | + Action<Service.ServiceClient, Google.Protobuf.WellKnownTypes.Struct> setup) |
| 309 | + { |
| 310 | + // Arrange |
| 311 | + var mockGrpcClient = Substitute.For<Service.ServiceClient>(); |
| 312 | + |
| 313 | + var setupMetadata = new Google.Protobuf.WellKnownTypes.Struct() |
| 314 | + { |
| 315 | + Fields = |
| 316 | + { |
| 317 | + { "key1", Google.Protobuf.WellKnownTypes.Value.ForString("value1") }, |
| 318 | + { "key2", Google.Protobuf.WellKnownTypes.Value.ForString(string.Empty) }, |
| 319 | + { "key3", Google.Protobuf.WellKnownTypes.Value.ForBool(true) }, |
| 320 | + { "key4", Google.Protobuf.WellKnownTypes.Value.ForBool(false) }, |
| 321 | + { "key5", Google.Protobuf.WellKnownTypes.Value.ForNumber(1) }, |
| 322 | + { "key6", Google.Protobuf.WellKnownTypes.Value.ForNumber(3.14) }, |
| 323 | + { "key7", Google.Protobuf.WellKnownTypes.Value.ForNumber(-0.531921) }, |
| 324 | + { "key8", Google.Protobuf.WellKnownTypes.Value.ForList(Google.Protobuf.WellKnownTypes.Value.ForString("1"), Google.Protobuf.WellKnownTypes.Value.ForString("2")) }, |
| 325 | + { "key9", Google.Protobuf.WellKnownTypes.Value.ForNull() }, |
| 326 | + { "key10", Google.Protobuf.WellKnownTypes.Value.ForStruct(new Google.Protobuf.WellKnownTypes.Struct() |
| 327 | + { |
| 328 | + Fields = { { "innerkey", Google.Protobuf.WellKnownTypes.Value.ForBool(true) } } |
| 329 | + }) }, |
| 330 | + } |
| 331 | + }; |
| 332 | + |
| 333 | + setup(mockGrpcClient, setupMetadata); |
| 334 | + |
| 335 | + var config = new FlagdConfig(); |
| 336 | + var resolver = new RpcResolver(mockGrpcClient, config, null); |
| 337 | + |
| 338 | + // Act |
| 339 | + var value = await act(resolver); |
| 340 | + |
| 341 | + // Assert |
| 342 | + var metadata = value.FlagMetadata; |
| 343 | + Assert.NotNull(metadata); |
| 344 | + Assert.Equal("value1", metadata.GetString("key1")); |
| 345 | + Assert.Equal(string.Empty, metadata.GetString("key2")); |
| 346 | + Assert.True(metadata.GetBool("key3")); |
| 347 | + Assert.False(metadata.GetBool("key4")); |
| 348 | + Assert.Equal(1, metadata.GetInt("key5")); |
| 349 | + Assert.Equal(3.14, metadata.GetDouble("key6")); |
| 350 | + Assert.Equal(-0.531921, metadata.GetDouble("key7")); |
| 351 | + Assert.Null(metadata.GetString("key8")); |
| 352 | + Assert.Null(metadata.GetString("key9")); |
| 353 | + Assert.Null(metadata.GetString("key10")); |
| 354 | + } |
| 355 | + |
| 356 | + public static IEnumerable<object[]> ResolveValueFlagdMetadata() |
| 357 | + { |
| 358 | + const string flagKey = "test-key"; |
| 359 | + |
| 360 | + yield return new object[] |
| 361 | + { |
| 362 | + new Func<RpcResolver, Task<ResolutionDetails<bool>>>(r => r.ResolveBooleanValueAsync(flagKey, false)), |
| 363 | + new Action<Service.ServiceClient, Google.Protobuf.WellKnownTypes.Struct>((client, metadata) => client.ResolveBooleanAsync(Arg.Any<ResolveBooleanRequest>()) |
| 364 | + .Returns(CreateRpcResponse(new ResolveBooleanResponse() { Value = true, Variant = "true", Reason = "TARGETING_MATCH", Metadata = metadata }))) |
| 365 | + }; |
| 366 | + yield return new object[] |
| 367 | + { |
| 368 | + new Func<RpcResolver, Task<ResolutionDetails<string>>>(r => r.ResolveStringValueAsync(flagKey, "def")), |
| 369 | + new Action<Service.ServiceClient, Google.Protobuf.WellKnownTypes.Struct>((client, metadata) => client.ResolveStringAsync(Arg.Any<ResolveStringRequest>()) |
| 370 | + .Returns(CreateRpcResponse(new ResolveStringResponse() { Value = "one", Variant = "default", Reason = "TARGETING_MATCH", Metadata = metadata }))) |
| 371 | + }; |
| 372 | + yield return new object[] |
| 373 | + { |
| 374 | + new Func<RpcResolver, Task<ResolutionDetails<int>>>(r => r.ResolveIntegerValueAsync(flagKey, 3)), |
| 375 | + new Action<Service.ServiceClient, Google.Protobuf.WellKnownTypes.Struct>((client, metadata) => client.ResolveIntAsync(Arg.Any<ResolveIntRequest>()) |
| 376 | + .Returns(CreateRpcResponse(new ResolveIntResponse() { Value = 1, Variant = "one", Reason = "TARGETING_MATCH", Metadata = metadata }))) |
| 377 | + }; |
| 378 | + yield return new object[] |
| 379 | + { |
| 380 | + new Func<RpcResolver, Task<ResolutionDetails<double>>>(r => r.ResolveDoubleValueAsync(flagKey, 3.5)), |
| 381 | + new Action<Service.ServiceClient, Google.Protobuf.WellKnownTypes.Struct>((client, metadata) => client.ResolveFloatAsync(Arg.Any<ResolveFloatRequest>()) |
| 382 | + .Returns(CreateRpcResponse(new ResolveFloatResponse() { Value = 1.61, Variant = "one", Reason = "TARGETING_MATCH", Metadata = metadata }))) |
| 383 | + }; |
| 384 | + yield return new object[] |
| 385 | + { |
| 386 | + new Func<RpcResolver, Task<ResolutionDetails<Value>>>(r => r.ResolveStructureValueAsync(flagKey, new Value(Structure.Builder().Set("value1", true).Build()))), |
| 387 | + new Action<Service.ServiceClient, Google.Protobuf.WellKnownTypes.Struct>((client, metadata) => client.ResolveObjectAsync(Arg.Any<ResolveObjectRequest>()) |
| 388 | + .Returns(CreateRpcResponse(new ResolveObjectResponse() |
| 389 | + { |
| 390 | + Value = new Google.Protobuf.WellKnownTypes.Struct(), |
| 391 | + Variant = "one", |
| 392 | + Reason = "TARGETING_MATCH", |
| 393 | + Metadata = metadata |
| 394 | + }))) |
| 395 | + }; |
| 396 | + } |
| 397 | + |
| 398 | + private static AsyncUnaryCall<T> CreateRpcResponse<T>(T resp) |
| 399 | + where T : IMessage<T>, IBufferMessage |
| 400 | + { |
| 401 | + return new AsyncUnaryCall<T>(Task.FromResult(resp), Task.FromResult(Grpc.Core.Metadata.Empty), () => Status.DefaultSuccess, () => Grpc.Core.Metadata.Empty, () => { }); |
| 402 | + } |
| 403 | + |
304 | 404 | private static Service.ServiceClient SetupGrpcStream(List<EventStreamResponse> responses) |
305 | 405 | { |
306 | 406 | var mockGrpcClient = Substitute.For<Service.ServiceClient>(); |
|
0 commit comments