1
+ using Grpc . Core ;
2
+ using ProtoBuf ;
3
+ using ProtoBuf . Grpc . Client ;
4
+ using ProtoBuf . Grpc . Configuration ;
5
+ using ProtoBuf . Grpc . Server ;
6
+ using ProtoBuf . Meta ;
7
+ using System ;
8
+ using System . Buffers ;
9
+ using System . Collections . Generic ;
10
+ using System . IO ;
11
+ using System . Runtime . InteropServices ;
12
+ using System . Runtime . Serialization ;
13
+ using System . Threading . Tasks ;
14
+ using ProtoBuf . Grpc ;
15
+ using Xunit ;
16
+ using Xunit . Abstractions ;
17
+
18
+ #nullable disable
19
+
20
+ namespace protobuf_net . Grpc . Test . Integration
21
+ {
22
+ public class ClientProxyTests : IClassFixture < ClientProxyTests . ClientProxyTestsServerFixture >
23
+ {
24
+
25
+ [ DataContract ]
26
+ public class MyRequest
27
+ {
28
+ [ DataMember ( Order = 1 ) ]
29
+ public int Id { get ; set ; }
30
+ }
31
+
32
+ [ DataContract ]
33
+ public class MyResponse
34
+ {
35
+ [ DataMember ( Order = 1 ) ]
36
+ public string ? Value { get ; set ; }
37
+ }
38
+
39
+
40
+ /// <summary>
41
+ /// An interface which is not marked with [Service] attribute.
42
+ /// Its methods' proxy-implementations are expected to throw unsupported exception.
43
+ /// </summary>
44
+ public interface INotAService
45
+ {
46
+ ValueTask < MyResponse > NotAServiceUnary ( MyRequest request , CallContext callContext = default ) ;
47
+ }
48
+
49
+ [ ServiceInheritable ]
50
+ public interface ISomeInheritableBaseGenericService < in TGenericRequest , TGenericResult >
51
+ {
52
+ ValueTask < TGenericResult > BaseGenericUnary ( TGenericRequest request , CallContext callContext = default ) ;
53
+ }
54
+
55
+ [ Service ]
56
+ public interface IMyDerivedService : ISomeInheritableBaseGenericService < MyRequest , MyResponse > , INotAService
57
+ {
58
+ ValueTask < MyResponse > Derived ( MyRequest request , CallContext callContext = default ) ;
59
+ }
60
+
61
+ #pragma warning disable IDE0079 // Remove unnecessary suppression
62
+ #pragma warning disable IDE0051 , IDE0052 // "unused" things; they are, but it depends on the TFM
63
+ private readonly ITestOutputHelper _log ;
64
+ private readonly ClientProxyTestsServerFixture _server ;
65
+ private void Log ( string message ) => _log ? . WriteLine ( message ) ;
66
+ #pragma warning restore IDE0051 , IDE0052
67
+ #pragma warning restore IDE0079 // Remove unnecessary suppression
68
+
69
+ private int Port => _server . Port ;
70
+
71
+ public ClientProxyTests ( ClientProxyTestsServerFixture server , ITestOutputHelper log )
72
+ {
73
+ _server = server ;
74
+ _log = log ;
75
+ GrpcClientFactory . AllowUnencryptedHttp2 = true ;
76
+ }
77
+
78
+ public class ClientProxyTestsServerFixture : IMyDerivedService , IAsyncDisposable
79
+ {
80
+ public int Port { get ; } = PortManager . GetNextPort ( ) ;
81
+
82
+ public async ValueTask DisposeAsync ( )
83
+ {
84
+ if ( _server != null )
85
+ await _server . KillAsync ( ) ;
86
+ }
87
+
88
+ private readonly Server ? _server ;
89
+ public ClientProxyTestsServerFixture ( )
90
+ {
91
+ _server = new Server
92
+ {
93
+ Ports = { new ServerPort ( "localhost" , Port , ServerCredentials . Insecure ) }
94
+ } ;
95
+ _server . Services . AddCodeFirst ( this ) ;
96
+ _server . Start ( ) ;
97
+ }
98
+
99
+ public async ValueTask < MyResponse > NotAServiceUnary ( MyRequest request , CallContext callContext = default )
100
+ {
101
+ // we expect this won't be called through GRPC
102
+ return await Task . FromResult ( new MyResponse ( ) ) ;
103
+ }
104
+
105
+ public async ValueTask < MyResponse > Derived ( MyRequest request , CallContext callContext = default )
106
+ {
107
+ return await Task . FromResult ( new MyResponse ( ) ) ;
108
+ }
109
+
110
+ public async ValueTask < MyResponse > BaseGenericUnary ( MyRequest request , CallContext callContext = default )
111
+ {
112
+ return await Task . FromResult ( new MyResponse ( ) ) ;
113
+ }
114
+ }
115
+
116
+
117
+ #if ! ( NET461 || NET472 )
118
+ [ Fact ]
119
+ public async Task ClientProxyTests_WhenCalledToDerivedInterfaceMethod_NoException ( )
120
+ {
121
+ using var http = global ::Grpc . Net . Client . GrpcChannel . ForAddress ( $ "http://localhost:{ Port } ") ;
122
+ var client = http . CreateGrpcService < IMyDerivedService > ( ) ;
123
+ var obj = await client . Derived ( new MyRequest ( ) ) ;
124
+ }
125
+
126
+ [ Fact ]
127
+ public async Task ClientProxyTests_WhenCalledToBaseInheritableMethod_NoException ( )
128
+ {
129
+ using var http = global ::Grpc . Net . Client . GrpcChannel . ForAddress ( $ "http://localhost:{ Port } ") ;
130
+ var client = http . CreateGrpcService < IMyDerivedService > ( ) ;
131
+ var obj = await client . BaseGenericUnary ( new MyRequest ( ) ) ;
132
+ }
133
+
134
+
135
+ [ Fact ]
136
+ public async Task ClientProxyTests_WhenCalledToBaseNonInheritableMethod_ThrowsUnsupportedException ( )
137
+ {
138
+ using var http = global ::Grpc . Net . Client . GrpcChannel . ForAddress ( $ "http://localhost:{ Port } ") ;
139
+ var client = http . CreateGrpcService < IMyDerivedService > ( ) ;
140
+
141
+ await Assert . ThrowsAsync < NotSupportedException > ( async ( ) =>
142
+ {
143
+ // we expect an exception from the client proxy
144
+ var obj = await client . NotAServiceUnary ( new MyRequest ( ) ) ;
145
+ } ) ;
146
+ }
147
+ #endif
148
+ }
149
+
150
+ }
0 commit comments