diff --git a/Directory.Build.props b/Directory.Build.props new file mode 100644 index 000000000..1674327de --- /dev/null +++ b/Directory.Build.props @@ -0,0 +1,7 @@ + + + true + 7.3 + $(NoWarn);CS1591 + + diff --git a/MagicOnion.sln b/MagicOnion.sln index 4826460c1..3cc281676 100644 --- a/MagicOnion.sln +++ b/MagicOnion.sln @@ -9,8 +9,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{7ACC27E8 EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "sandbox", "sandbox", "{7682EFFC-681C-4DCC-B5E7-D8449E42DAC9}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MagicOnion.Tests", "tests\MagicOnion.Tests\MagicOnion.Tests.csproj", "{879C8453-8995-4D9A-964F-2FEA2B84FF1A}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MagicOnion", "src\MagicOnion\MagicOnion.csproj", "{C79CE0BF-ED4C-47BE-822E-E82CF83FC68A}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "nuget", "nuget", "{7F61607D-5772-4E41-9D37-04E6AE7E47BA}" @@ -25,8 +23,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "nuget", "nuget", "{7F61607D EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MagicOnion.HttpGateway", "src\MagicOnion.HttpGateway\MagicOnion.HttpGateway.csproj", "{FCE03661-803E-4629-944F-45DB6B444320}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MagicOnion.Tests.NetCore", "tests\MagicOnion.Tests.NetCore\MagicOnion.Tests.NetCore.csproj", "{A0E9C1EA-6E7F-4CF1-81D2-21EAC6ACAE67}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sandbox.NetCoreServer", "sandbox\Sandbox.NetCoreServer\Sandbox.NetCoreServer.csproj", "{8174DFB5-0C46-440C-A225-C3A9851C2700}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DynamicCodeDumper", "sandbox\DynamicCodeDumper\DynamicCodeDumper.csproj", "{A2156D81-1A67-4EAC-8A74-3E459385E4A0}" @@ -43,16 +39,14 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MagicOnion.Hosting", "src\M EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MagicOnion.Hosting.Tests", "tests\MagicOnion.Hosting.Tests\MagicOnion.Hosting.Tests.csproj", "{5A381446-409B-4532-8B1A-877D996F7575}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MagicOnion.NetCoreTests", "tests\MagicOnion.NetCoreTests\MagicOnion.NetCoreTests.csproj", "{9130778E-4B00-436C-BD1F-1DFDF95AD2CC}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {879C8453-8995-4D9A-964F-2FEA2B84FF1A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {879C8453-8995-4D9A-964F-2FEA2B84FF1A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {879C8453-8995-4D9A-964F-2FEA2B84FF1A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {879C8453-8995-4D9A-964F-2FEA2B84FF1A}.Release|Any CPU.Build.0 = Release|Any CPU {C79CE0BF-ED4C-47BE-822E-E82CF83FC68A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {C79CE0BF-ED4C-47BE-822E-E82CF83FC68A}.Debug|Any CPU.Build.0 = Debug|Any CPU {C79CE0BF-ED4C-47BE-822E-E82CF83FC68A}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -61,10 +55,6 @@ Global {FCE03661-803E-4629-944F-45DB6B444320}.Debug|Any CPU.Build.0 = Debug|Any CPU {FCE03661-803E-4629-944F-45DB6B444320}.Release|Any CPU.ActiveCfg = Release|Any CPU {FCE03661-803E-4629-944F-45DB6B444320}.Release|Any CPU.Build.0 = Release|Any CPU - {A0E9C1EA-6E7F-4CF1-81D2-21EAC6ACAE67}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {A0E9C1EA-6E7F-4CF1-81D2-21EAC6ACAE67}.Debug|Any CPU.Build.0 = Debug|Any CPU - {A0E9C1EA-6E7F-4CF1-81D2-21EAC6ACAE67}.Release|Any CPU.ActiveCfg = Release|Any CPU - {A0E9C1EA-6E7F-4CF1-81D2-21EAC6ACAE67}.Release|Any CPU.Build.0 = Release|Any CPU {8174DFB5-0C46-440C-A225-C3A9851C2700}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {8174DFB5-0C46-440C-A225-C3A9851C2700}.Debug|Any CPU.Build.0 = Debug|Any CPU {8174DFB5-0C46-440C-A225-C3A9851C2700}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -93,15 +83,17 @@ Global {5A381446-409B-4532-8B1A-877D996F7575}.Debug|Any CPU.Build.0 = Debug|Any CPU {5A381446-409B-4532-8B1A-877D996F7575}.Release|Any CPU.ActiveCfg = Release|Any CPU {5A381446-409B-4532-8B1A-877D996F7575}.Release|Any CPU.Build.0 = Release|Any CPU + {9130778E-4B00-436C-BD1F-1DFDF95AD2CC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9130778E-4B00-436C-BD1F-1DFDF95AD2CC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9130778E-4B00-436C-BD1F-1DFDF95AD2CC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9130778E-4B00-436C-BD1F-1DFDF95AD2CC}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution - {879C8453-8995-4D9A-964F-2FEA2B84FF1A} = {7ACC27E8-8FBE-4807-B91F-B89AF3CFF7E0} {C79CE0BF-ED4C-47BE-822E-E82CF83FC68A} = {1987061F-8970-4018-8D58-6932961C9EB4} {FCE03661-803E-4629-944F-45DB6B444320} = {1987061F-8970-4018-8D58-6932961C9EB4} - {A0E9C1EA-6E7F-4CF1-81D2-21EAC6ACAE67} = {7ACC27E8-8FBE-4807-B91F-B89AF3CFF7E0} {8174DFB5-0C46-440C-A225-C3A9851C2700} = {7682EFFC-681C-4DCC-B5E7-D8449E42DAC9} {A2156D81-1A67-4EAC-8A74-3E459385E4A0} = {7682EFFC-681C-4DCC-B5E7-D8449E42DAC9} {5637ED33-7EB8-4C30-94EE-947C58FDE363} = {1987061F-8970-4018-8D58-6932961C9EB4} @@ -109,6 +101,7 @@ Global {A1DB4A40-10FE-61E7-9ADF-1D16330A9BF3} = {5C86A5C9-ECE1-4FB9-B48C-A9DCF9C3FECC} {72B2FD95-57B6-400B-95D0-96E19EBCF44B} = {1987061F-8970-4018-8D58-6932961C9EB4} {5A381446-409B-4532-8B1A-877D996F7575} = {7ACC27E8-8FBE-4807-B91F-B89AF3CFF7E0} + {9130778E-4B00-436C-BD1F-1DFDF95AD2CC} = {7ACC27E8-8FBE-4807-B91F-B89AF3CFF7E0} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {D5B2E7E3-B727-40A1-BE68-7BAC9B9DE2FE} diff --git a/nuget/MagicOnion.Hosting.nuspec b/nuget/MagicOnion.Hosting.nuspec index a908c5ddd..782f95c92 100644 --- a/nuget/MagicOnion.Hosting.nuspec +++ b/nuget/MagicOnion.Hosting.nuspec @@ -2,7 +2,7 @@ MagicOnion.Hosting - 2.0.4 + 2.0.5 MagicOnion.Hosting neuecc neuecc @@ -13,7 +13,7 @@ gRPC, HTTP2 - + diff --git a/nuget/MagicOnion.HttpGateway.nuspec b/nuget/MagicOnion.HttpGateway.nuspec index 555bba51f..fdf862767 100644 --- a/nuget/MagicOnion.HttpGateway.nuspec +++ b/nuget/MagicOnion.HttpGateway.nuspec @@ -2,7 +2,7 @@ MagicOnion.HttpGateway - 2.0.4 + 2.0.5 MagicOnion.HttpGateway neuecc neuecc @@ -13,7 +13,7 @@ gRPC, HTTP2 - + diff --git a/nuget/MagicOnion.Redis.nuspec b/nuget/MagicOnion.Redis.nuspec index 2845622a0..7f72d9c0e 100644 --- a/nuget/MagicOnion.Redis.nuspec +++ b/nuget/MagicOnion.Redis.nuspec @@ -2,7 +2,7 @@ MagicOnion.Redis - 2.0.4 + 2.0.5 MagicOnion neuecc neuecc @@ -13,7 +13,7 @@ gRPC, HTTP2 - + diff --git a/nuget/MagicOnion.nuspec b/nuget/MagicOnion.nuspec index 7199128b7..272a77867 100644 --- a/nuget/MagicOnion.nuspec +++ b/nuget/MagicOnion.nuspec @@ -2,7 +2,7 @@ MagicOnion - 2.0.4 + 2.0.5 MagicOnion neuecc neuecc @@ -13,7 +13,7 @@ gRPC, HTTP2 - + @@ -21,7 +21,7 @@ - + @@ -29,7 +29,7 @@ - + @@ -39,7 +39,7 @@ - + diff --git a/nuget/push.bat b/nuget/push.bat index 5c3632412..8e9a2b50e 100644 --- a/nuget/push.bat +++ b/nuget/push.bat @@ -1,4 +1,4 @@ -nuget push MagicOnion.2.0.4.nupkg -Source https://www.nuget.org/api/v2/package -nuget push MagicOnion.HttpGateway.2.0.4.nupkg -Source https://www.nuget.org/api/v2/package -nuget push MagicOnion.Redis.2.0.4.nupkg -Source https://www.nuget.org/api/v2/package -nuget push MagicOnion.Hosting.2.0.4.nupkg -Source https://www.nuget.org/api/v2/package \ No newline at end of file +nuget push MagicOnion.2.0.5.nupkg -Source https://www.nuget.org/api/v2/package +nuget push MagicOnion.HttpGateway.2.0.5.nupkg -Source https://www.nuget.org/api/v2/package +nuget push MagicOnion.Redis.2.0.5.nupkg -Source https://www.nuget.org/api/v2/package +nuget push MagicOnion.Hosting.2.0.5.nupkg -Source https://www.nuget.org/api/v2/package \ No newline at end of file diff --git a/sandbox/Sandbox.NetCoreServer/Sandbox.NetCoreServer.csproj b/sandbox/Sandbox.NetCoreServer/Sandbox.NetCoreServer.csproj index 31a66e2c6..5a603b18d 100644 --- a/sandbox/Sandbox.NetCoreServer/Sandbox.NetCoreServer.csproj +++ b/sandbox/Sandbox.NetCoreServer/Sandbox.NetCoreServer.csproj @@ -11,7 +11,7 @@ - + diff --git a/src/MagicOnion.Client.Unity/Assets/Scripts/ChatShare.cs b/src/MagicOnion.Client.Unity/Assets/Scripts/ChatShare.cs index 0929bfce6..7765a0685 100644 --- a/src/MagicOnion.Client.Unity/Assets/Scripts/ChatShare.cs +++ b/src/MagicOnion.Client.Unity/Assets/Scripts/ChatShare.cs @@ -94,7 +94,7 @@ public class GamingHubClient : IGamingHubReceiver public async Task ConnectAsync(Channel grpcChannel, string roomName, string playerName) { - var client = StreamingHubClient.Connect(grpcChannel, this); + client = StreamingHubClient.Connect(grpcChannel, this); var roomPlayers = await client.JoinAsync(roomName, playerName, Vector3.zero, Quaternion.identity); foreach (var player in roomPlayers) diff --git a/src/MagicOnion.Hosting/MagicOnion.Hosting.csproj b/src/MagicOnion.Hosting/MagicOnion.Hosting.csproj index eb647808f..6337a0ae4 100644 --- a/src/MagicOnion.Hosting/MagicOnion.Hosting.csproj +++ b/src/MagicOnion.Hosting/MagicOnion.Hosting.csproj @@ -1,4 +1,4 @@ - + Library @@ -7,8 +7,11 @@ - + + + + diff --git a/src/MagicOnion.HttpGateway/MagicOnion.HttpGateway.csproj b/src/MagicOnion.HttpGateway/MagicOnion.HttpGateway.csproj index ff13690f0..2ef666c83 100644 --- a/src/MagicOnion.HttpGateway/MagicOnion.HttpGateway.csproj +++ b/src/MagicOnion.HttpGateway/MagicOnion.HttpGateway.csproj @@ -33,7 +33,7 @@ - + diff --git a/src/MagicOnion/MagicOnion.csproj b/src/MagicOnion/MagicOnion.csproj index 4b46774d5..3200dc19b 100644 --- a/src/MagicOnion/MagicOnion.csproj +++ b/src/MagicOnion/MagicOnion.csproj @@ -67,7 +67,7 @@ - + diff --git a/src/MagicOnion/_AssemblyInfo.cs b/src/MagicOnion/_AssemblyInfo.cs index 74a22b6d0..ccd74d7a4 100644 --- a/src/MagicOnion/_AssemblyInfo.cs +++ b/src/MagicOnion/_AssemblyInfo.cs @@ -14,5 +14,5 @@ [assembly: Guid("cbfa1f05-746b-47e1-8432-d709f88ce24e")] -[assembly: AssemblyVersion("2.0.4")] -[assembly: AssemblyFileVersion("2.0.4")] +[assembly: AssemblyVersion("2.0.5")] +[assembly: AssemblyFileVersion("2.0.5")] diff --git a/tests/MagicOnion.Hosting.Tests/TestService.cs b/tests/MagicOnion.Hosting.Tests/TestService.cs index 7e2484d65..4b739d910 100644 --- a/tests/MagicOnion.Hosting.Tests/TestService.cs +++ b/tests/MagicOnion.Hosting.Tests/TestService.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1998 + using System; using MagicOnion; using MagicOnion.Server; @@ -15,4 +17,5 @@ public async UnaryResult Sum(int x, int y) return x + y; } } -} \ No newline at end of file +} +#pragma warning restore CS1998 diff --git a/tests/MagicOnion.NetCoreTests/MagicOnion.NetCoreTests.csproj b/tests/MagicOnion.NetCoreTests/MagicOnion.NetCoreTests.csproj new file mode 100644 index 000000000..1cdabe736 --- /dev/null +++ b/tests/MagicOnion.NetCoreTests/MagicOnion.NetCoreTests.csproj @@ -0,0 +1,22 @@ + + + + netcoreapp2.1 + + false + + + + + + + + + + + + + + + + diff --git a/tests/MagicOnion.NetCoreTests/Tests/ArgumentPatternTest.cs b/tests/MagicOnion.NetCoreTests/Tests/ArgumentPatternTest.cs new file mode 100644 index 000000000..f1fd6d048 --- /dev/null +++ b/tests/MagicOnion.NetCoreTests/Tests/ArgumentPatternTest.cs @@ -0,0 +1,369 @@ +using Grpc.Core; +using FluentAssertions; +using MagicOnion.Client; +using MagicOnion.Server; +using MessagePack; +using System; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Xunit; +using Xunit.Abstractions; + +namespace MagicOnion.Tests +{ + [MessagePackObject] + public class MyRequest + { + [Key(0)] + public virtual int Id { get; set; } + [Key(1)] + public virtual string Data { get; set; } + } + + [MessagePackObject] + public struct MyStructRequest + { + [Key(0)] + public int X; + [Key(1)] + public int Y; + + public MyStructRequest(int x, int y) + { + this.X = x; + this.Y = y; + } + } + + [MessagePackObject] + public struct MyStructResponse + { + [Key(0)] + public int X; + [Key(1)] + public int Y; + + public MyStructResponse(int x, int y) + { + this.X = x; + this.Y = y; + } + } + + + [MessagePackObject] + public class MyResponse + { + [Key(0)] + public virtual int Id { get; set; } + [Key(1)] + public virtual string Data { get; set; } + } + + + public interface IArgumentPattern : IService + { + UnaryResult Unary1(int x, int y, string z = "unknown"); + UnaryResult Unary2(MyRequest req); + UnaryResult Unary3(); + UnaryResult Unary4(); + UnaryResult Unary5(MyStructRequest req); + + + Task> ServerStreamingResult1(int x, int y, string z = "unknown"); + Task> ServerStreamingResult2(MyRequest req); + Task> ServerStreamingResult3(); + Task> ServerStreamingResult4(); + Task> ServerStreamingResult5(MyStructRequest req); + } + + public class ArgumentPattern : ServiceBase, IArgumentPattern + { + + public UnaryResult Unary1(int x, int y, string z = "unknown") + { + return UnaryResult(new MyResponse + { + Id = x + y, + Data = z + }); + } + + public UnaryResult Unary2(MyRequest req) + { + return UnaryResult(new MyResponse + { + Id = req.Id, + Data = req.Data + }); + } + + public UnaryResult Unary3() + { + return UnaryResult(new MyResponse + { + Id = -1, + Data = "NoArg" + }); + } + + public UnaryResult Unary4() + { + return UnaryResult(Nil.Default); + } + + public UnaryResult Unary5(MyStructRequest req) + { + return UnaryResult(new MyStructResponse + { + X = req.X, + Y = req.Y + }); + } + + + public async Task> ServerStreamingResult1(int x, int y, string z = "unknown") + { + var stream = GetServerStreamingContext(); + await stream.WriteAsync(new MyResponse { Id = x + y, Data = z }); + return stream.Result(); + } + + public async Task> ServerStreamingResult2(MyRequest req) + { + var stream = GetServerStreamingContext(); + await stream.WriteAsync(new MyResponse { Id = req.Id, Data = req.Data }); + return stream.Result(); + } + + public async Task> ServerStreamingResult3() + { + var stream = GetServerStreamingContext(); + await stream.WriteAsync(new MyResponse { Id = -1, Data = "empty" }); + return stream.Result(); + } + + public async Task> ServerStreamingResult4() + { + var stream = GetServerStreamingContext(); + await stream.WriteAsync(Nil.Default); + return stream.Result(); + } + + public async Task> ServerStreamingResult5(MyStructRequest req) + { + var stream = GetServerStreamingContext(); + await stream.WriteAsync(new MyStructResponse { X = req.X, Y = req.Y }); + return stream.Result(); + } + } + + [Collection(nameof(AllAssemblyGrpcServerFixture))] + public class ArgumentPatternTest + { + ITestOutputHelper logger; + Channel channel; + + public ArgumentPatternTest(ITestOutputHelper logger, ServerFixture server) + { + this.logger = logger; + this.channel = server.DefaultChannel; + } + + [Fact] + public async Task Unary1() + { + { + var client = MagicOnionClient.Create(channel); + + var result = await client.Unary1(10, 20, "hogehoge"); + + result.Id.Should().Be(30); + result.Data.Should().Be("hogehoge"); + } + { + var client = new ArgumentPatternManualClient(channel); + + var result = await client.Unary1(10, 20, "__omit_last_argument__"); + + result.Id.Should().Be(30); + result.Data.Should().Be("unknown"); + } + } + + [Fact] + public async Task Unary2() + { + var client = MagicOnionClient.Create(channel); + + var result = await client.Unary2(new MyRequest { Id = 30, Data = "huga" }); + + result.Id.Should().Be(30); + result.Data.Should().Be("huga"); + } + + [Fact] + public async Task Unary3() + { + var client = MagicOnionClient.Create(channel); + + var result = await client.Unary3(); + + result.Id.Should().Be(-1); + result.Data.Should().Be("NoArg"); + } + + [Fact] + public async Task Unary4() + { + var client = MagicOnionClient.Create(channel); + + var result = await client.Unary4(); + result.Should().Be(Nil.Default); + } + + [Fact] + public async Task Unary5() + { + var client = MagicOnionClient.Create(channel); + + var result = await client.Unary5(new MyStructRequest(999, 9999)); + result.X.Should().Be(999); + result.Y.Should().Be(9999); + } + + [Fact] + public async Task ServerStreaming() + { + var client = MagicOnionClient.Create(channel); + + { + var callResult = await client.ServerStreamingResult1(10, 100, "aaa"); + var result = await callResult.ResponseStream.AsAsyncEnumerable().First(); + result.Id.Should().Be(110); + result.Data.Should().Be("aaa"); + } + + { + var callResult = await client.ServerStreamingResult2(new MyRequest { Id = 999, Data = "zzz" }); + var result = await callResult.ResponseStream.AsAsyncEnumerable().First(); + result.Id.Should().Be(999); + result.Data.Should().Be("zzz"); + } + + { + var callResult = await client.ServerStreamingResult3(); + var result = await callResult.ResponseStream.AsAsyncEnumerable().First(); + result.Id.Should().Be(-1); + result.Data.Should().Be("empty"); + } + + { + var callResult = await client.ServerStreamingResult4(); + var result = await callResult.ResponseStream.AsAsyncEnumerable().First(); + result.Should().Be(Nil.Default); + } + + { + var callResult = await client.ServerStreamingResult5(new MyStructRequest { X = 9, Y = 100 }); + var result = await callResult.ResponseStream.AsAsyncEnumerable().First(); + result.X.Should().Be(9); + result.Y.Should().Be(100); + } + } + + [Ignore] // client is not service. + class ArgumentPatternManualClient : IArgumentPattern + { + readonly CallInvoker invoker; + + public ArgumentPatternManualClient(Channel channel) + { + this.invoker = new DefaultCallInvoker(channel); + } + + public Task> ServerStreamingResult1(int x, int y, string z = "unknown") + { + throw new NotImplementedException(); + } + + public Task> ServerStreamingResult2(MyRequest req) + { + throw new NotImplementedException(); + } + + public Task> ServerStreamingResult3() + { + throw new NotImplementedException(); + } + + public Task> ServerStreamingResult4() + { + throw new NotImplementedException(); + } + + public Task> ServerStreamingResult5(MyStructRequest req) + { + throw new NotImplementedException(); + } + + public UnaryResult Unary1(int x, int y, string z = "unknown") + { + var tuple = new DynamicArgumentTuple(x, y); + + var method = new Method(MethodType.Unary, "IArgumentPattern", "Unary1", MagicOnionMarshallers.ThroughMarshaller, MagicOnionMarshallers.ThroughMarshaller); + var request = LZ4MessagePackSerializer.Serialize(tuple); + + var callResult = invoker.AsyncUnaryCall(method, null, default(CallOptions), request); + + return new UnaryResult(callResult, MessagePackSerializer.DefaultResolver); + } + + public UnaryResult Unary2(MyRequest req) + { + throw new NotImplementedException(); + } + + public UnaryResult Unary3() + { + throw new NotImplementedException(); + } + + public UnaryResult Unary4() + { + throw new NotImplementedException(); + } + + public UnaryResult Unary5(MyStructRequest req) + { + throw new NotImplementedException(); + } + + public IArgumentPattern WithCancellationToken(CancellationToken cancellationToken) + { + throw new NotImplementedException(); + } + + public IArgumentPattern WithDeadline(DateTime deadline) + { + throw new NotImplementedException(); + } + + public IArgumentPattern WithHeaders(Metadata headers) + { + throw new NotImplementedException(); + } + + public IArgumentPattern WithHost(string host) + { + throw new NotImplementedException(); + } + + public IArgumentPattern WithOptions(CallOptions option) + { + throw new NotImplementedException(); + } + } + } +} diff --git a/tests/MagicOnion.NetCoreTests/Tests/ClientConfigTest.cs b/tests/MagicOnion.NetCoreTests/Tests/ClientConfigTest.cs new file mode 100644 index 000000000..6e30efae7 --- /dev/null +++ b/tests/MagicOnion.NetCoreTests/Tests/ClientConfigTest.cs @@ -0,0 +1,148 @@ +using Grpc.Core; +using FluentAssertions; +using MagicOnion.Client; +using MagicOnion.Server; +using MessagePack; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Xunit; +using Xunit.Abstractions; + +namespace MagicOnion.Tests +{ + [MessagePackObject] + public class SerializableCallContext + { + [Key(0)] + public virtual DateTime Deadline { get; set; } + [Key(1)] + public virtual string Host { get; set; } + [Key(2)] + public virtual string Method { get; set; } + [Key(3)] + public virtual string Peer { get; set; } + [Key(4)] + public virtual Dictionary RequestHeaders { get; set; } + [Key(5)] + public virtual Dictionary ResponseTrailers { get; set; } + } + + public interface IConfigurationChange : IService + { + UnaryResult ReturnContext(); + } + + public class ConfigurationChange : ServiceBase, IConfigurationChange + { + public UnaryResult ReturnContext() + { + var context = this.Context.CallContext; + var result = new SerializableCallContext + { + Deadline = context.Deadline, + Host = context.Host, + Method = context.Method, + Peer = context.Peer, + RequestHeaders = context.RequestHeaders.ToDictionary(x => x.Key, x => x.Value), + ResponseTrailers = context.ResponseTrailers.ToDictionary(x => x.Key, x => x.Value), + }; + + return UnaryResult(result); + } + } + + [Collection(nameof(AllAssemblyGrpcServerFixture))] + public class ClientConfigTest + { + ITestOutputHelper logger; + IConfigurationChange client; + + public ClientConfigTest(ITestOutputHelper logger, ServerFixture server) + { + this.logger = logger; + this.client = server.CreateClient(); + } + + /* + protected string host; + protected CallOptions option; + protected CallInvoker callInvoker; + */ + + //[Fact] + //public async Task WithHost() + //{ + // (client.Should().Be( .AsDynamic().host as string).IsNull(); + + // var hostChange = client.WithHost("newhost"); + // (hostChange.AsDynamic().host as string).Should().Be("newhost"); + + // var serverContext = await hostChange.ReturnContext(); + // serverContext.Host.Should().Be("newhost"); + //} + + //[Fact] + //public void WithCancellation() + //{ + // var cts = new CancellationTokenSource(); + // var tk = cts.Token; + // var opt = (CallOptions)client.AsDynamic().option; + // opt.CancellationToken.IsNot(tk); + + // var change = client.WithCancellationToken(tk); + // opt = (CallOptions)change.AsDynamic().option; + + // opt.CancellationToken.Should().Be(tk); + //} + + //[Fact] + //public async Task WithDeadline() + //{ + // var now = DateTime.UtcNow.AddMinutes(10); + // var opt = (CallOptions)client.AsDynamic().option; + // opt.Deadline.IsNull(); + + // var change = client.WithDeadline(now); + // opt = (CallOptions)change.AsDynamic().option; + // opt.Deadline.Should().Be(now); + + // var context = await change.ReturnContext(); + // context.Deadline.ToString().Should().Be(now.ToString()); // almost same:) + //} + + //[Fact] + //public async Task WithHeaders() + //{ + // var meta = new Metadata(); + // meta.Add("test", "aaaaa"); + + // var opt = (CallOptions)client.AsDynamic().option; + // opt.Headers.IsNull(); + + // var change = client.WithHeaders(meta); + // opt = (CallOptions)change.AsDynamic().option; + // opt.Headers[0].Key.Should().Be("test"); + // opt.Headers[0].Value.Should().Be("aaaaa"); + + // var context = await change.ReturnContext(); + // context.RequestHeaders["test"].Should().Be("aaaaa"); + //} + + + //[Fact] + //public void WithOption() + //{ + // var opt = (CallOptions)client.AsDynamic().option; + // opt.WriteOptions.IsNull(); + + // var change = client.WithOptions(new CallOptions(writeOptions: new WriteOptions(WriteFlags.BufferHint))); + // opt = (CallOptions)change.AsDynamic().option; + // opt.WriteOptions.Flags.Should().Be(WriteFlags.BufferHint); + //} + + } +} diff --git a/tests/MagicOnion.NetCoreTests/Tests/FilterTest.cs b/tests/MagicOnion.NetCoreTests/Tests/FilterTest.cs new file mode 100644 index 000000000..db347dedc --- /dev/null +++ b/tests/MagicOnion.NetCoreTests/Tests/FilterTest.cs @@ -0,0 +1,214 @@ +using System; +using FluentAssertions; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using MagicOnion.Server; +using Xunit; +using Grpc.Core; +using MagicOnion.Client; +using Xunit.Abstractions; + +namespace MagicOnion.Tests +{ + [Flags] + public enum FilterCalledStatus + { + Begin = 1, + Catch = 2, + Finally = 4 + } + + public class SimpleFilter1 : MagicOnionFilterAttribute + { + public SimpleFilter1() : this(null) + { + + } + + public SimpleFilter1(Func next) : base(next) + { + } + + public async override ValueTask Invoke(ServiceContext context) + { + try + { + (context.Items["list"] as List).Add(nameof(SimpleFilter1)); + context.Items[nameof(SimpleFilter1)] = FilterCalledStatus.Begin; + await Next(context); + } + catch + { + context.Items[nameof(SimpleFilter1)] = (FilterCalledStatus)context.Items[nameof(SimpleFilter1)] | FilterCalledStatus.Catch; + throw; + } + finally + { + context.Items[nameof(SimpleFilter1)] = (FilterCalledStatus)context.Items[nameof(SimpleFilter1)] | FilterCalledStatus.Finally; + } + } + } + + public class MultiFilter2 : MagicOnionFilterAttribute + { + public int X { get; private set; } + public int Y { get; private set; } + public int Z { get; set; } + + public MultiFilter2(int x, int y) + : base(null) + { + this.X = x; + this.Y = y; + } + + public MultiFilter2(Func next) : base(next) + { + } + + public override async ValueTask Invoke(ServiceContext context) + { + try + { + (context.Items["list"] as List).Add(nameof(MultiFilter2)); + context.Items[nameof(MultiFilter2)] = FilterCalledStatus.Begin; + context.Items[nameof(MultiFilter2) + "xyz"] = Tuple.Create(X, Y, Z); + await Next(context); + } + catch + { + context.Items[nameof(MultiFilter2)] = (FilterCalledStatus)context.Items[nameof(MultiFilter2)] | FilterCalledStatus.Catch; + throw; + } + finally + { + context.Items[nameof(MultiFilter2)] = (FilterCalledStatus)context.Items[nameof(MultiFilter2)] | FilterCalledStatus.Finally; + } + } + } + + public class MoreThanFilter3 : MagicOnionFilterAttribute + { + readonly string msg; + + public MoreThanFilter3(string msg) + : base(null) + { + this.msg = msg; + } + + public MoreThanFilter3(Func next) : base(next) + { + } + + public async override ValueTask Invoke(ServiceContext context) + { + try + { + (context.Items["list"] as List).Add(nameof(MoreThanFilter3)); + context.Items[nameof(MoreThanFilter3)] = FilterCalledStatus.Begin; + context.Items[nameof(MoreThanFilter3) + "msg"] = msg; + await Next(context); + } + catch + { + context.Items[nameof(MoreThanFilter3)] = (FilterCalledStatus)context.Items[nameof(MoreThanFilter3)] | FilterCalledStatus.Catch; + throw; + } + finally + { + context.Items[nameof(MoreThanFilter3)] = (FilterCalledStatus)context.Items[nameof(MoreThanFilter3)] | FilterCalledStatus.Finally; + } + } + } + + public class DumpFilter : MagicOnionFilterAttribute + { + public DumpFilter() : base(null) + { + + } + + public DumpFilter(Func next) : base(next) + { + } + + public override async ValueTask Invoke(ServiceContext context) + { + try + { + context.Items["list"] = new List(); + (context.Items["list"] as List).Add(nameof(DumpFilter)); + await Next(context); + } + catch + { + } + finally + { + var calls = string.Join(", ", (context.Items["list"] as List)); + var dict = string.Join(", ", context.Items.Where(x => x.Key != "list").OrderBy(x => x.Key).Select(x => x.Key + ", " + x.Value.ToString())); + SetStatusCode(context, (StatusCode)999, calls + " : " + dict); + } + } + } + + public interface IFilterTester : IService + { + UnaryResult A(); + UnaryResult B(); + UnaryResult C(); + } + + [DumpFilter(Order = int.MinValue)] + [MoreThanFilter3("put-class", Order = 244)] + public class FilterTester : ServiceBase, IFilterTester + { + [SimpleFilter1(Order = 10)] + public UnaryResult A() + { + return UnaryResult(0); + } + + [SimpleFilter1(Order = 300)] + [MultiFilter2(99, 30, Z = 4595, Order = 200)] + public UnaryResult B() + { + return UnaryResult(999); + } + + [SimpleFilter1(Order = int.MinValue)] + public UnaryResult C() + { + throw new Exception("C-Exception"); + } + } + + + [Collection(nameof(AllAssemblyGrpcServerFixture))] + public class FilterTest + { + IFilterTester client; + + public FilterTest(ITestOutputHelper logger, ServerFixture server) + { + this.client = server.CreateClient(); + } + + + [Fact] + public void Filter() + { + Assert.Throws(() => client.A().GetAwaiter().GetResult()).Status.Detail + .Should().Be("DumpFilter, SimpleFilter1, MoreThanFilter3 : MoreThanFilter3, Begin, Finally, MoreThanFilter3msg, put-class, SimpleFilter1, Begin, Finally"); + + Assert.Throws(() => client.B().GetAwaiter().GetResult()).Status.Detail + .Should().Be("DumpFilter, MultiFilter2, MoreThanFilter3, SimpleFilter1 : MoreThanFilter3, Begin, Finally, MoreThanFilter3msg, put-class, MultiFilter2, Begin, Finally, MultiFilter2xyz, (99, 30, 4595), SimpleFilter1, Begin, Finally"); + + Assert.Throws(() => client.C().GetAwaiter().GetResult()).Status.Detail + .Should().Be("DumpFilter, SimpleFilter1, MoreThanFilter3 : MoreThanFilter3, Begin, Catch, Finally, MoreThanFilter3msg, put-class, SimpleFilter1, Begin, Catch, Finally"); + } + } +} diff --git a/tests/MagicOnion.NetCoreTests/Tests/PartialDefinitionTest.cs b/tests/MagicOnion.NetCoreTests/Tests/PartialDefinitionTest.cs new file mode 100644 index 000000000..bbccf56af --- /dev/null +++ b/tests/MagicOnion.NetCoreTests/Tests/PartialDefinitionTest.cs @@ -0,0 +1,62 @@ +using System; +using System.Threading.Tasks; +using Grpc.Core; +using MagicOnion.Client; +using MagicOnion.Server; +using Xunit; +using Xunit.Abstractions; +using FluentAssertions; + +namespace MagicOnion.Tests +{ + // note, do not allow partial definition. + + //public interface IPartialDefinition : IService, IPartialDefinition2 + //{ + // UnaryResult Unary1(int x, int y); + //} + + + //public interface IPartialDefinition2 + //{ + // UnaryResult Unary2(); + //} + + + //public class CombinedDefinition : ServiceBase, IPartialDefinition2 + //{ + // public UnaryResult Unary1(int x, int y) + // => this.UnaryResult(x + y); + + // public UnaryResult Unary2() + // => this.UnaryResult(100); + //} + + + //[Collection(nameof(AllAssemblyGrpcServerFixture))] + //public class PartialDefinitionTest + //{ + // ITestOutputHelper logger; + // IPartialDefinition client; + + // public PartialDefinitionTest(ITestOutputHelper logger, ServerFixture server) + // { + // this.logger = logger; + // this.client = server.CreateClient(); + // } + + // [Fact] + // public async Task Unary1() + // { + // var r = await client.Unary1(10, 20); + // r.Should().Be(30); + // } + + // [Fact] + // public async Task Unary2() + // { + // var r = await client.Unary2(); + // r.Should().Be(100); + // } + //} +} \ No newline at end of file diff --git a/tests/MagicOnion.NetCoreTests/Tests/ReturnStatusTest.cs b/tests/MagicOnion.NetCoreTests/Tests/ReturnStatusTest.cs new file mode 100644 index 000000000..2acba4d43 --- /dev/null +++ b/tests/MagicOnion.NetCoreTests/Tests/ReturnStatusTest.cs @@ -0,0 +1,130 @@ +using Grpc.Core; +using MagicOnion.Client; +using MagicOnion.Server; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using FluentAssertions; +using Xunit; +using Xunit.Abstractions; + +namespace MagicOnion.Tests +{ + public interface IReturnStatus : IService + { + // 1 is exception + UnaryResult Unary1(); + ClientStreamingResult ClientStreaming1(); + ServerStreamingResult Serverstreaming1(); + DuplexStreamingResult DuplexStreaming1(); + + // 2 is direct return + UnaryResult Unary2(); + ClientStreamingResult ClientStreaming2(); + ServerStreamingResult Serverstreaming2(); + DuplexStreamingResult DuplexStreaming2(); + + // others + UnaryResult CustomThrow(int code); + } + + public class ReturnStatus : ServiceBase, IReturnStatus + { + public ClientStreamingResult ClientStreaming1() + { + throw new ReturnStatusException(Grpc.Core.StatusCode.Aborted, "a"); + } + + public ClientStreamingResult ClientStreaming2() + { + return GetClientStreamingContext().ReturnStatus(Grpc.Core.StatusCode.InvalidArgument, "b"); + } + + public DuplexStreamingResult DuplexStreaming1() + { + throw new ReturnStatusException(Grpc.Core.StatusCode.Aborted, "a"); + } + + public DuplexStreamingResult DuplexStreaming2() + { + return GetDuplexStreamingContext().ReturnStatus(Grpc.Core.StatusCode.InvalidArgument, "b"); + } + + public ServerStreamingResult Serverstreaming1() + { + throw new ReturnStatusException(Grpc.Core.StatusCode.Aborted, "a"); + } + + public ServerStreamingResult Serverstreaming2() + { + return GetServerStreamingContext().ReturnStatus(Grpc.Core.StatusCode.InvalidArgument, "b"); + } + + public UnaryResult Unary1() + { + throw new ReturnStatusException(Grpc.Core.StatusCode.Aborted, "a"); + } + + public UnaryResult Unary2() + { + return ReturnStatus(Grpc.Core.StatusCode.InvalidArgument, "b"); + } + + public UnaryResult CustomThrow(int code) + { + return ReturnStatus((StatusCode)code, null); + } + } + + [Collection(nameof(AllAssemblyGrpcServerFixture))] + public class ReturnStatusTest + { + ITestOutputHelper logger; + IReturnStatus client; + + public ReturnStatusTest(ITestOutputHelper logger, ServerFixture server) + { + this.logger = logger; + this.client = server.CreateClient(); + } + + //[Fact] + //public void CheckException() + //{ + // Assert.Throws(() => client.Unary1().ResponseAsync.GetAwaiter().GetResult()) + // .Should().Be(x => x.Status.StatusCode == StatusCode.Aborted && x.Status.Detail == "a"); + // Assert.Throws(() => client.ClientStreaming1().ResponseAsync.GetAwaiter().GetResult()) + // .Should().Be(x => x.Status.StatusCode == StatusCode.Aborted && x.Status.Detail == "a"); + // Assert.Throws(() => client.Serverstreaming1().ResponseStream.MoveNext().GetAwaiter().GetResult()) + // .Should().Be(x => x.Status.StatusCode == StatusCode.Aborted && x.Status.Detail == "a"); + // Assert.Throws(() => client.DuplexStreaming1().ResponseStream.MoveNext().GetAwaiter().GetResult()) + // .Should().Be(x => x.Status.StatusCode == StatusCode.Aborted && x.Status.Detail == "a"); + //} + + //[Fact] + //public void CheckDirect() + //{ + // Assert.Throws(() => client.Unary2().ResponseAsync.GetAwaiter().GetResult()) + // .Should().Be(x => x.Status.StatusCode == StatusCode.InvalidArgument && x.Status.Detail == "b"); + // Assert.Throws(() => client.ClientStreaming2().ResponseAsync.GetAwaiter().GetResult()) + // .Should().Be(x => x.Status.StatusCode == StatusCode.InvalidArgument && x.Status.Detail == "b"); + // Assert.Throws(() => client.Serverstreaming2().ResponseStream.MoveNext().GetAwaiter().GetResult()) + // .Should().Be(x => x.Status.StatusCode == StatusCode.InvalidArgument && x.Status.Detail == "b"); + // Assert.Throws(() => client.DuplexStreaming2().ResponseStream.MoveNext().GetAwaiter().GetResult()) + // .Should().Be(x => x.Status.StatusCode == StatusCode.InvalidArgument && x.Status.Detail == "b"); + //} + + [Fact] + public void CheckCustomThrow() + { + var ex = Assert.Throws(() => + { + client.CustomThrow(123).GetAwaiter().GetResult(); + }); + + ex.Status.StatusCode.Should().Be((StatusCode)123); + } + } +} diff --git a/tests/MagicOnion.NetCoreTests/Tests/ServerErrorTest.cs b/tests/MagicOnion.NetCoreTests/Tests/ServerErrorTest.cs new file mode 100644 index 000000000..c77d62b7a --- /dev/null +++ b/tests/MagicOnion.NetCoreTests/Tests/ServerErrorTest.cs @@ -0,0 +1,86 @@ +using Grpc.Core; +using MagicOnion.Client; +using MagicOnion.Server; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using FluentAssertions; +using System.Threading.Tasks; +using Xunit; + +namespace MagicOnion.Tests +{ + public interface IOverloadMethod : IService + { + UnaryResult Hoge(int x); + UnaryResult Hoge(int x, int y); + } + + [Ignore] + public class OverloadService : ServiceBase, IOverloadMethod + { + public UnaryResult Hoge(int x) + { + return UnaryResult(0); + } + + public UnaryResult Hoge(int x, int y) + { + return UnaryResult(0); + } + } + + public interface IArgumentMethod : IService + { + UnaryResult Hoge(int x); + ServerStreamingResult Huga(int x); + ClientStreamingResult Tako(int x); + DuplexStreamingResult Nano(int x); + } + + public class ArgumentMethodService : ServiceBase, IArgumentMethod + { + public UnaryResult Hoge(int x) + { + throw new NotImplementedException(); + } + + public ServerStreamingResult Huga(int x) + { + throw new NotImplementedException(); + } + + [Ignore] + public DuplexStreamingResult Nano(int x) + { + throw new NotImplementedException(); + } + + [Ignore] + public ClientStreamingResult Tako(int x) + { + throw new NotImplementedException(); + } + } + + public class ServerErrorTest + { + //[Fact] + //public async Task Moge() + //{ + // var service = MagicOnionEngine.BuildServerServiceDefinition(true); + + // var server = new global::Grpc.Core.Server + // { + // Services = { service }, + // Ports = { new ServerPort("localhost", 12345, ServerCredentials.Insecure) } + // }; + + // server.Start(); + + // //var channel = new Channel("localhost:12345", ChannelCredentials.Insecure); + // //await MagicOnionClient.Create(channel).Hoge(0); + //} + } +} diff --git a/tests/MagicOnion.NetCoreTests/Tests/SimpleTest.cs b/tests/MagicOnion.NetCoreTests/Tests/SimpleTest.cs new file mode 100644 index 000000000..18ae6ab2d --- /dev/null +++ b/tests/MagicOnion.NetCoreTests/Tests/SimpleTest.cs @@ -0,0 +1,212 @@ +using Grpc.Core; +using MagicOnion.Client; +using MagicOnion.Server; +using System; +using System.Collections.Generic; +using FluentAssertions; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Xunit; +using Xunit.Abstractions; + +namespace MagicOnion.Tests +{ + public interface ISimpleTest : IService + { + UnaryResult Unary1(int x, int y); + Task> Unary1Task(int x, int y); + UnaryResult Unary2(int x, int y); + + ClientStreamingResult ClientStreaming1(); + Task> ClientStreaming1Task(); + + ServerStreamingResult Serverstreaming1(int x, int y, int z); + Task> ServerStreaming1Task(int x, int y, int z); + + DuplexStreamingResult DuplexStreaming1(); + Task> DuplexStreaming1Task(); + } + + public class UnaryTestImpl : ServiceBase, ISimpleTest + { + public ClientStreamingResult ClientStreaming1() + { + var streaming = GetClientStreamingContext(); + + var list = new List(); + // no listen from client... + return streaming.Result("finished:" + string.Join(", ", list)); + } + + public async Task> ClientStreaming1Task() + { + var streaming = GetClientStreamingContext(); + + var list = new List(); + await streaming.ForEachAsync(x => + { + list.Add(x); + }); + + return streaming.Result("finished:" + string.Join(", ", list)); + } + + public DuplexStreamingResult DuplexStreaming1() + { + var stream = GetDuplexStreamingContext(); + return stream.Result(); + } + + public async Task> DuplexStreaming1Task() + { + var stream = GetDuplexStreamingContext(); + + var l = new List(); + + while (await stream.MoveNext()) + { + l.Add(stream.Current); + await stream.WriteAsync(string.Join(", ", l)); + } + + return stream.Result(); + } + + public ServerStreamingResult Serverstreaming1(int x, int y, int z) + { + var stream = GetServerStreamingContext(); + + // no write? + return stream.Result(); + } + + public async Task> ServerStreaming1Task(int x, int y, int z) + { + var stream = GetServerStreamingContext(); + + var acc = 0; + for (int i = 0; i < z; i++) + { + acc = acc + x + y; + await stream.WriteAsync(acc.ToString()); + } + + return stream.Result(); + } + + public UnaryResult Unary1(int x, int y) + { + return UnaryResult(x + y); + } + + public async Task> Unary1Task(int x, int y) + { + await Task.Yield(); + return UnaryResult(x + y); + } + +#pragma warning disable CS1998 + + public async UnaryResult Unary2(int x, int y) + { + return x + y; + } + +#pragma warning restore CS1998 + + } + + [Collection(nameof(AllAssemblyGrpcServerFixture))] + public class SimpleTest + { + ITestOutputHelper logger; + Channel channel; + + public SimpleTest(ITestOutputHelper logger, ServerFixture server) + { + this.logger = logger; + this.channel = server.DefaultChannel; + } + + [Fact] + public async Task Unary() + { + var client = MagicOnionClient.Create(channel); + + var r = await client.Unary1(10, 20); + r.Should().Be(30); + + var r2 = await await client.Unary1Task(1000, 2000); + r2.Should().Be(3000); + } + + [Fact] + public async Task ClientStreaming() + { + var client = MagicOnionClient.Create(channel); + { + var r = await client.ClientStreaming1Task(); + await r.RequestStream.WriteAsync(10); + await r.RequestStream.WriteAsync(20); + await r.RequestStream.WriteAsync(30); + await r.RequestStream.CompleteAsync(); + + var result = await r.ResponseAsync; + result.Should().Be("finished:10, 20, 30"); + } + { + var r = client.ClientStreaming1(); + var result = await r.ResponseAsync; + result.Should().Be("finished:"); + } + } + + [Fact] + public async Task ServerStreaming() + { + var client = MagicOnionClient.Create(channel); + { + var r = await client.ServerStreaming1Task(10, 20, 3); + await r.ResponseStream.MoveNext(); + r.ResponseStream.Current.Should().Be("30"); + await r.ResponseStream.MoveNext(); + r.ResponseStream.Current.Should().Be("60"); + await r.ResponseStream.MoveNext(); + r.ResponseStream.Current.Should().Be("90"); + } + { + var r = client.Serverstreaming1(100, 200, 3); + (await r.ResponseStream.MoveNext()).Should().BeFalse(); + } + } + + [Fact] + public async Task DuplexStreaming() + { + var client = MagicOnionClient.Create(channel); + { + var r = await client.DuplexStreaming1Task(); + + await r.RequestStream.WriteAsync(1000); + await r.ResponseStream.MoveNext(); + r.ResponseStream.Current.Should().Be("1000"); + + await r.RequestStream.WriteAsync(2000); + await r.ResponseStream.MoveNext(); + r.ResponseStream.Current.Should().Be("1000, 2000"); + + await r.RequestStream.WriteAsync(3000); + await r.ResponseStream.MoveNext(); + r.ResponseStream.Current.Should().Be("1000, 2000, 3000"); + + await r.RequestStream.CompleteAsync(); + (await r.ResponseStream.MoveNext()).Should().BeFalse(); + } + { + var r = client.DuplexStreaming1(); + (await r.ResponseStream.MoveNext()).Should().BeFalse(); + } + } + } +} diff --git a/tests/MagicOnion.NetCoreTests/Tests/StreamingHubTest.cs b/tests/MagicOnion.NetCoreTests/Tests/StreamingHubTest.cs new file mode 100644 index 000000000..c62996592 --- /dev/null +++ b/tests/MagicOnion.NetCoreTests/Tests/StreamingHubTest.cs @@ -0,0 +1,486 @@ +#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously + +using Grpc.Core; +using MagicOnion.Client; +using MagicOnion.Server.Hubs; +using FluentAssertions; +using MessagePack; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Xunit; +using Xunit.Abstractions; + +namespace MagicOnion.Tests +{ + public interface IMessageReceiver + { + Task ZeroArgument(); + Task OneArgument(int x); + Task MoreArgument(int x, string y, double z); + void VoidZeroArgument(); + void VoidOneArgument(int x); + void VoidMoreArgument(int x, string y, double z); + Task OneArgument2(TestObject x); + void VoidOneArgument2(TestObject x); + Task OneArgument3(TestObject[] x); + void VoidOneArgument3(TestObject[] x); + } + + public interface ITestHub : IStreamingHub + { + Task ZeroArgument(); + Task OneArgument(int x); + Task MoreArgument(int x, string y, double z); + + Task RetrunZeroArgument(); + Task RetrunOneArgument(int x); + Task RetrunMoreArgument(int x, string y, double z); + + Task OneArgument2(TestObject x); + Task RetrunOneArgument2(TestObject x); + + Task OneArgument3(TestObject[] x); + Task RetrunOneArgument3(TestObject[] x); + } + + [MessagePackObject] + public class TestObject + { + [Key(0)] + public int X { get; set; } + [Key(1)] + public int Y { get; set; } + [Key(2)] + public int Z { get; set; } + } + + public class TestHub : StreamingHubBase, ITestHub + { + IGroup group; + + protected override async ValueTask OnConnecting() + { + group = await Group.AddAsync("global"); + } + + protected override async ValueTask OnDisconnected() + { + if (group != null) await group.RemoveAsync(Context); + } + + + public async Task MoreArgument(int x, string y, double z) + { + Broadcast(group).VoidMoreArgument(x, y, z); + await Broadcast(group).MoreArgument(x, y, z); + } + + public async Task OneArgument(int x) + { + Broadcast(group).VoidOneArgument(x); + await Broadcast(group).OneArgument(x); + } + + public async Task OneArgument2(TestObject x) + { + Broadcast(group).VoidOneArgument2(x); + await Broadcast(group).OneArgument2(x); + } + + public async Task OneArgument3(TestObject[] x) + { + Broadcast(group).VoidOneArgument3(x); + await Broadcast(group).OneArgument3(x); + } + + public Task RetrunMoreArgument(int x, string y, double z) + { + return Task.FromResult(z); + } + + public async Task RetrunOneArgument(int x) + { + return x.ToString(); + } + + public async Task RetrunOneArgument2(TestObject x) + { + return x; + } + + public async Task RetrunOneArgument3(TestObject[] x) + { + return x; + } + + public async Task RetrunZeroArgument() + { + return 1000; + } + + public async Task ZeroArgument() + { + Broadcast(group).VoidZeroArgument(); + await Broadcast(group).ZeroArgument(); + } + } + + [Collection(nameof(AllAssemblyGrpcServerFixture))] + public class BasicStreamingHubTest : IMessageReceiver, IDisposable + { + ITestOutputHelper logger; + Channel channel; + ITestHub client; + + public BasicStreamingHubTest(ITestOutputHelper logger, ServerFixture server) + { + this.logger = logger; + this.channel = server.DefaultChannel; + } + + [Fact] + public async Task ZeroArgument() + { + client = StreamingHubClient.Connect(channel, this); + await client.ZeroArgument(); + await voidZeroTask.Task; + await zeroTask.Task; + // ok, pass. + + await client.DisposeAsync(); + } + + [Fact] + public async Task OneArgument() + { + var client = StreamingHubClient.Connect(channel, this); + await client.OneArgument(100); + var x = await oneTask.Task; + var y = await voidoneTask.Task; + x.Should().Be(100); + y.Should().Be(100); + await client.DisposeAsync(); + } + + [Fact] + public async Task MoreArgument() + { + var client = StreamingHubClient.Connect(channel, this); + await client.MoreArgument(100, "foo", 10.3); + var x = await moreTask.Task; + var y = await voidmoreTask.Task; + x.Should().Be((100, "foo", 10.3)); + y.Should().Be((100, "foo", 10.3)); + await client.DisposeAsync(); + } + + [Fact] + public async Task RetrunZeroArgument() + { + var client = StreamingHubClient.Connect(channel, this); + var v = await client.RetrunZeroArgument(); + v.Should().Be(1000); + await client.DisposeAsync(); + } + [Fact] + public async Task RetrunOneArgument() + { + var client = StreamingHubClient.Connect(channel, this); + var v = await client.RetrunZeroArgument(); + v.Should().Be(1000); + await client.DisposeAsync(); + } + [Fact] + public async Task RetrunMoreArgument() + { + var client = StreamingHubClient.Connect(channel, this); + var v = await client.RetrunMoreArgument(10, "foo", 30.4); + v.Should().Be(30.4); + await client.DisposeAsync(); + } + + [Fact] + public async Task OneArgument2() + { + var client = StreamingHubClient.Connect(channel, this); + await client.OneArgument2(new TestObject() { X = 10, Y = 99, Z = 100 }); + { + var v = await one2Task.Task; + v.X.Should().Be(10); + v.Y.Should().Be(99); + v.Z.Should().Be(100); + } + { + var v = await voidone2Task.Task; + v.X.Should().Be(10); + v.Y.Should().Be(99); + v.Z.Should().Be(100); + } + await client.DisposeAsync(); + } + [Fact] + public async Task RetrunOneArgument2() + { + var client = StreamingHubClient.Connect(channel, this); + var v = await client.RetrunOneArgument2(new TestObject() { X = 10, Y = 99, Z = 100 }); + v.X.Should().Be(10); + v.Y.Should().Be(99); + v.Z.Should().Be(100); + await client.DisposeAsync(); + } + + [Fact] + public async Task OneArgument3() + { + var client = StreamingHubClient.Connect(channel, this); + await client.OneArgument3(new[] + { + new TestObject() { X = 10, Y = 99, Z = 100 }, + new TestObject() { X = 5, Y = 39, Z = 200 }, + new TestObject() { X = 4, Y = 59, Z = 300 }, + }); + { + var v = await one3Task.Task; + + v[0].X.Should().Be(10); + v[0].Y.Should().Be(99); + v[0].Z.Should().Be(100); + + v[1].X.Should().Be(5); + v[1].Y.Should().Be(39); + v[1].Z.Should().Be(200); + + v[2].X.Should().Be(4); + v[2].Y.Should().Be(59); + v[2].Z.Should().Be(300); + } + { + var v = await voidone3Task.Task; + + v[0].X.Should().Be(10); + v[0].Y.Should().Be(99); + v[0].Z.Should().Be(100); + + v[1].X.Should().Be(5); + v[1].Y.Should().Be(39); + v[1].Z.Should().Be(200); + + v[2].X.Should().Be(4); + v[2].Y.Should().Be(59); + v[2].Z.Should().Be(300); + } + await client.DisposeAsync(); + } + [Fact] + public async Task RetrunOneArgument3() + { + var client = StreamingHubClient.Connect(channel, this); + var v = await client.RetrunOneArgument3(new[] + { + new TestObject() { X = 10, Y = 99, Z = 100 }, + new TestObject() { X = 5, Y = 39, Z = 200 }, + new TestObject() { X = 4, Y = 59, Z = 300 }, + }); + + v[0].X.Should().Be(10); + v[0].Y.Should().Be(99); + v[0].Z.Should().Be(100); + + v[1].X.Should().Be(5); + v[1].Y.Should().Be(39); + v[1].Z.Should().Be(200); + + v[2].X.Should().Be(4); + v[2].Y.Should().Be(59); + v[2].Z.Should().Be(300); + await client.DisposeAsync(); + } + + + + TaskCompletionSource<(int, string, double)> moreTask = new TaskCompletionSource<(int, string, double)>(); + async Task IMessageReceiver.MoreArgument(int x, string y, double z) + { + moreTask.TrySetResult((x, y, z)); + } + + TaskCompletionSource oneTask = new TaskCompletionSource(); + async Task IMessageReceiver.OneArgument(int x) + { + oneTask.TrySetResult(x); + } + + TaskCompletionSource one2Task = new TaskCompletionSource(); + async Task IMessageReceiver.OneArgument2(TestObject x) + { + one2Task.TrySetResult(x); + } + + TaskCompletionSource one3Task = new TaskCompletionSource(); + async Task IMessageReceiver.OneArgument3(TestObject[] x) + { + one3Task.TrySetResult(x); + } + + TaskCompletionSource<(int, string, double)> voidmoreTask = new TaskCompletionSource<(int, string, double)>(); + void IMessageReceiver.VoidMoreArgument(int x, string y, double z) + { + voidmoreTask.TrySetResult((x, y, z)); + } + + TaskCompletionSource voidoneTask = new TaskCompletionSource(); + void IMessageReceiver.VoidOneArgument(int x) + { + voidoneTask.TrySetResult(x); + } + + TaskCompletionSource voidone2Task = new TaskCompletionSource(); + void IMessageReceiver.VoidOneArgument2(TestObject x) + { + voidone2Task.TrySetResult(x); + } + + TaskCompletionSource voidone3Task = new TaskCompletionSource(); + void IMessageReceiver.VoidOneArgument3(TestObject[] x) + { + voidone3Task.TrySetResult(x); + } + + TaskCompletionSource voidZeroTask = new TaskCompletionSource(); + void IMessageReceiver.VoidZeroArgument() + { + voidZeroTask.TrySetResult(null); + } + + TaskCompletionSource zeroTask = new TaskCompletionSource(); + async Task IMessageReceiver.ZeroArgument() + { + zeroTask.TrySetResult(null); + } + + public void Dispose() + { + if (client != null) + { + client.DisposeAsync().Wait(); + } + } + } + + + public interface IEmptyReceiver + { + } + + public interface IMoreCheckHub : IStreamingHub + { + Task ReceiveExceptionAsync(); + Task StatusCodeAsync(); + Task FilterCheckAsync(); + } + + public class MoreCheckHub : StreamingHubBase, IMoreCheckHub + { + public Task ReceiveExceptionAsync() + { + throw new Exception("foo"); + } + + public async Task StatusCodeAsync() + { + throw new ReturnStatusException((StatusCode)99, "foo bar baz"); + } + + [StreamingHubTestFilter] + public async Task FilterCheckAsync() + { + + } + } + + public class StreamingHubTestFilterAttribute : StreamingHubFilterAttribute + { + public static bool calledBefore; + public static bool calledAfter; + + public StreamingHubTestFilterAttribute(Func next) : base(next) { } + public StreamingHubTestFilterAttribute() : base(null) { } + + public override async ValueTask Invoke(StreamingHubContext context) + { + context.Items["HubFilter1_AF"] = "BeforeOK"; + await Next.Invoke(context); + context.Items["HubFilter1_BF"] = "AfterOK"; + } + } + + + [Collection(nameof(AllAssemblyGrpcServerFixture))] + public class MoreCheckHubTest : IEmptyReceiver, IDisposable + { + ITestOutputHelper logger; + Channel channel; + IMoreCheckHub client; + + public MoreCheckHubTest(ITestOutputHelper logger, ServerFixture server) + { + this.logger = logger; + this.channel = server.DefaultChannel; + } + + [Fact] + public async Task ReceiveEx() + { + client = StreamingHubClient.Connect(channel, this); + + var ex = Assert.Throws(() => + { + client.ReceiveExceptionAsync().GetAwaiter().GetResult(); + }); + + ex.StatusCode.Should().Be(StatusCode.Internal); + logger.WriteLine(ex.ToString()); + + await client.DisposeAsync(); + } + + [Fact] + public async Task StatusCodeEx() + { + client = StreamingHubClient.Connect(channel, this); + + var ex = Assert.Throws(() => + { + client.StatusCodeAsync().GetAwaiter().GetResult(); + }); + + ex.StatusCode.Should().Be((StatusCode)99); + logger.WriteLine(ex.Status.Detail); + logger.WriteLine(ex.ToString()); + + await client.DisposeAsync(); + } + + [Fact] + public async Task Filter() + { + client = StreamingHubClient.Connect(channel, this); + await client.FilterCheckAsync(); + } + + + public void Dispose() + { + if (client != null) + { + client.DisposeAsync().Wait(); + } + } + } +} + + +#pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously diff --git a/tests/MagicOnion.NetCoreTests/_ServerFixture.cs b/tests/MagicOnion.NetCoreTests/_ServerFixture.cs new file mode 100644 index 000000000..56145f1fd --- /dev/null +++ b/tests/MagicOnion.NetCoreTests/_ServerFixture.cs @@ -0,0 +1,87 @@ +using Grpc.Core; +using System.Reflection; +using MagicOnion.Client; +using MagicOnion.Server; +using System; +using System.Security.Cryptography; +using System.Threading.Tasks; +using Xunit; + +namespace MagicOnion.Tests +{ + public static class RandomProvider + { + [ThreadStatic] + static Random random; + + public static Random ThreadRandom + { + get + { + if (random == null) + { + using (var rng = RandomNumberGenerator.Create()) + { + var buffer = new byte[sizeof(int)]; + rng.GetBytes(buffer); + var seed = BitConverter.ToInt32(buffer, 0); + random = new Random(seed); + } + } + + return random; + } + } + } + + public class ServerFixture : IDisposable + { + Grpc.Core.Server server; + public ServerPort ServerPort { get; private set; } + public Channel DefaultChannel { get; private set; } + + public ServerFixture() + { + var options = new MagicOnionOptions { IsReturnExceptionStackTraceInErrorDetail = true }; + var service = MagicOnionEngine.BuildServerServiceDefinition(new[] { typeof(ServerFixture).GetTypeInfo().Assembly }, options); + + var port = RandomProvider.ThreadRandom.Next(10000, 30000); + var serverPort = new ServerPort("localhost", port, ServerCredentials.Insecure); + + server = new global::Grpc.Core.Server + { + Services = { service.ServerServiceDefinition }, + Ports = { serverPort } + }; + + server.Start(); + + ServerPort = serverPort; + DefaultChannel = new Channel(serverPort.Host, serverPort.Port, ChannelCredentials.Insecure); + } + + public T CreateClient() + where T : IService + { + return MagicOnionClient.Create(DefaultChannel); + } + + public TStreamingHub CreateStreamingHubClient(TReceiver receiver) + where TStreamingHub : IStreamingHub + { + return StreamingHubClient.Connect(DefaultChannel, receiver); + } + + public void Dispose() + { + DefaultChannel.ShutdownAsync().Wait(); + server.ShutdownAsync().Wait(); + } + } + + [CollectionDefinition(nameof(AllAssemblyGrpcServerFixture))] + public class AllAssemblyGrpcServerFixture : ICollectionFixture + { + + } +} \ No newline at end of file diff --git a/tests/MagicOnion.Tests.NetCore/MagicOnion.Tests.NetCore.csproj b/tests/MagicOnion.Tests.NetCore/MagicOnion.Tests.NetCore.csproj deleted file mode 100644 index d85603707..000000000 --- a/tests/MagicOnion.Tests.NetCore/MagicOnion.Tests.NetCore.csproj +++ /dev/null @@ -1,35 +0,0 @@ - - - - netcoreapp2.0 - false - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/tests/MagicOnion.Tests.NetCore/ReflectionExtensions.cs b/tests/MagicOnion.Tests.NetCore/ReflectionExtensions.cs deleted file mode 100644 index e16b75914..000000000 --- a/tests/MagicOnion.Tests.NetCore/ReflectionExtensions.cs +++ /dev/null @@ -1,144 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Reflection; -using System.Linq; -using System.Text; - -// Internal, Global. -internal static class ReflectionExtensions -{ - public static bool IsNullable(this System.Reflection.TypeInfo type) - { - return type.IsGenericType && type.GetGenericTypeDefinition() == typeof(System.Nullable<>); - } - - public static bool IsPublic(this System.Reflection.TypeInfo type) - { - return type.IsPublic; - } - - public static bool IsConstructedGenericType(this System.Reflection.TypeInfo type) - { - return type.AsType().IsConstructedGenericType; - } - - public static MethodInfo GetGetMethod(this PropertyInfo propInfo) - { - return propInfo.GetMethod; - } - - public static MethodInfo GetSetMethod(this PropertyInfo propInfo) - { - return propInfo.SetMethod; - } - - public static bool IsEnum(this Type type) - { - return type.GetTypeInfo().IsEnum; - } - - public static bool IsAbstract(this Type type) - { - return type.GetTypeInfo().IsAbstract; - } - - public static Type[] GetGenericArguments(this Type type) - { - return type.GetTypeInfo().GetGenericArguments(); - } - - public static ConstructorInfo[] GetConstructors(this Type type) - { - return type.GetTypeInfo().GetConstructors(); - } - - public static MethodInfo[] GetMethods(this Type type, BindingFlags flags) - { - return type.GetTypeInfo().GetMethods(flags); - } - - public static T GetCustomAttribute(this Type type, bool inherit) - where T : Attribute - { - return type.GetTypeInfo().GetCustomAttribute(inherit); - } - - public static IEnumerable GetCustomAttributes(this Type type, bool inherit) - where T : Attribute - { - return type.GetTypeInfo().GetCustomAttributes(inherit); - } - - public static IEnumerable GetCustomAttributes(this Type type, bool inherit) - { - return type.GetTypeInfo().GetCustomAttributes(inherit).Cast(); - } - - public static bool IsAssignableFrom(this Type type, Type c) - { - return type.GetTypeInfo().IsAssignableFrom(c); - } - - public static PropertyInfo GetProperty(this Type type, string name) - { - return type.GetTypeInfo().GetProperty(name); - } - - public static PropertyInfo GetProperty(this Type type, string name, BindingFlags flags) - { - return type.GetTypeInfo().GetProperty(name, flags); - } - - public static FieldInfo GetField(this Type type, string name, BindingFlags flags) - { - return type.GetTypeInfo().GetField(name, flags); - } - - public static FieldInfo[] GetFields(this Type type, BindingFlags flags) - { - return type.GetTypeInfo().GetFields(flags); - } - - public static Type GetInterface(this Type type, string name) - { - return type.GetTypeInfo().GetInterface(name); - } - - public static Type[] GetInterfaces(this Type type) - { - return type.GetTypeInfo().GetInterfaces(); - } - - public static MethodInfo GetMethod(this Type type, string name) - { - return type.GetTypeInfo().GetMethod(name); - } - - public static MethodInfo GetMethod(this Type type, string name, BindingFlags flags) - { - return type.GetTypeInfo().GetMethod(name, flags); - } - - public static MethodInfo[] GetMethods(this Type type) - { - return type.GetTypeInfo().GetMethods(); - } - - public static ConstructorInfo GetConstructor(this Type type, Type[] types) - { - return type.GetTypeInfo().GetConstructor(types); - } - - public static ConstructorInfo GetConstructor(this Type type, BindingFlags bindingAttr, object dymmy1, Type[] types, object dummy2) - { - return type.GetTypeInfo().GetConstructors(bindingAttr).First(x => - { - return x.GetParameters().Select(y => y.ParameterType).SequenceEqual(types); - }); - } - - public static object InvokeMember(this Type type, string name, BindingFlags invokeAttr, object dummy, object target, object[] args) - { - return type.GetTypeInfo().GetMethod(name, invokeAttr).Invoke(target, args); - } -} \ No newline at end of file diff --git a/tests/MagicOnion.Tests/MagicOnion.Tests.csproj b/tests/MagicOnion.Tests/MagicOnion.Tests.csproj index 11ae22a03..9e891d538 100644 --- a/tests/MagicOnion.Tests/MagicOnion.Tests.csproj +++ b/tests/MagicOnion.Tests/MagicOnion.Tests.csproj @@ -34,7 +34,10 @@ - ..\..\packages\Grpc.Core.1.18.0\lib\net45\Grpc.Core.dll + ..\..\packages\Grpc.Core.1.19.0\lib\net45\Grpc.Core.dll + + + ..\..\packages\Grpc.Core.Api.1.19.0\lib\net45\Grpc.Core.Api.dll ..\..\packages\MessagePack.1.7.3.4\lib\net45\MessagePack.dll @@ -114,9 +117,9 @@ This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - + - +