Skip to content

Commit 7553f1e

Browse files
authored
Merge pull request #1 from Byteology/initial-code-push
Initial code push
2 parents bdd3196 + 824e04e commit 7553f1e

21 files changed

+1100
-10
lines changed

.editorconfig

+4
Original file line numberDiff line numberDiff line change
@@ -26,3 +26,7 @@ dotnet_naming_rule.private_fields_underscore_camel_case.severity = suggestion
2626

2727
dotnet_diagnostic.IDE0003.severity = suggestion
2828
dotnet_diagnostic.CA1825.severity = none
29+
30+
dotnet_diagnostic.S1186.severity = none # does not allow empty methods
31+
dotnet_diagnostic.S3011.severity = none # does not allow reflection of private members
32+
dotnet_diagnostic.S3267.severity = none # Loops should be simplified by LINQ

.github/workflows/push-action.yml

+16-5
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ jobs:
99
build-and-pack:
1010
runs-on: ubuntu-latest
1111
env:
12-
project: Byteology.TypedHttpClients
12+
project_path: Byteology.TypedHttpClients
13+
sonar_project_key: Byteology_typed-http-clients
1314

1415
steps:
1516
- name: Setup .NET
@@ -19,22 +20,32 @@ jobs:
1920

2021
- name: Checkout
2122
uses: actions/checkout@v2
22-
23+
24+
- name: Sonarqube Begin
25+
run: |
26+
dotnet tool install --global dotnet-sonarscanner
27+
dotnet sonarscanner begin /o:byteology /k:${sonar_project_key} /d:sonar.login=${{ secrets.SONAR_TOKEN }} /s:$GITHUB_WORKSPACE/SonarQube.Analysis.xml
28+
2329
- name: Restore
2430
run: dotnet restore
2531

2632
- name: Build
2733
run: dotnet build --configuration Release --no-restore
2834

2935
- name: Test
30-
run: dotnet test --no-build --configuration Release --verbosity normal
36+
run: dotnet test --no-build --configuration Release --verbosity normal --settings coverlet.runsettings --logger:trx
3137

38+
- name: Sonarqube End
39+
run: dotnet sonarscanner end /d:sonar.login="${{ secrets.SONAR_TOKEN }}"
40+
env:
41+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
42+
3243
- name: Pack
3344
if: ${{ github.ref == 'refs/heads/master' }}
3445
run: |
35-
version=$(grep -oP '(?<=<Version>).*?(?=</Version>)' ./${project}/*.csproj)
46+
version=$(grep -oP '(?<=<Version>).*?(?=</Version>)' ./${project_path}/*.csproj)
3647
version="${version}-rc-${{ github.run_number }}"
37-
dotnet pack ${project} -c Release /property:Version=${version} -o ./out --no-restore --no-build
48+
dotnet pack ${project_path} -c Release /property:Version=${version} -o ./out --no-restore --no-build
3849
dotnet nuget push ./out/*.nupkg \
3950
-s https://nuget.pkg.github.com/Byteology/index.json -k ${{ secrets.GITHUB_TOKEN }} \
4051
--skip-duplicate --no-symbols true

Byteology.TypedHttpClients.Tests/Byteology.TypedHttpClients.Tests.csproj

+6-2
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,13 @@
77
</PropertyGroup>
88

99
<ItemGroup>
10-
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.7.1" />
10+
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.11.0" />
1111
<PackageReference Include="xunit" Version="2.4.1" />
1212
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3">
1313
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
1414
<PrivateAssets>all</PrivateAssets>
1515
</PackageReference>
16-
<PackageReference Include="coverlet.collector" Version="1.3.0">
16+
<PackageReference Include="coverlet.collector" Version="3.1.0">
1717
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
1818
<PrivateAssets>all</PrivateAssets>
1919
</PackageReference>
@@ -23,4 +23,8 @@
2323
</PackageReference>
2424
</ItemGroup>
2525

26+
<ItemGroup>
27+
<ProjectReference Include="..\Byteology.TypedHttpClients\Byteology.TypedHttpClients.csproj" />
28+
</ItemGroup>
29+
2630
</Project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,240 @@
1+
using Byteology.TypedHttpClients.Tests.Mocks;
2+
using Byteology.TypedHttpClients.Tests.TestServices;
3+
using System;
4+
using System.Net;
5+
using System.Net.Http;
6+
using System.Threading.Tasks;
7+
using Xunit;
8+
9+
namespace Byteology.TypedHttpClients.Tests
10+
{
11+
public class JsonHttpClientTests
12+
{
13+
[Fact]
14+
public void Success()
15+
{
16+
// Arrange
17+
using HttpClient httpClient = getHttpClient(HttpStatusCode.OK, null);
18+
ITestService service = new JsonHttpClient<ITestService>(httpClient).Endpoints;
19+
20+
// Act
21+
Task result = service.NoResultActionAsync();
22+
23+
// Assert
24+
Assert.True(result.IsCompletedSuccessfully);
25+
}
26+
27+
[Fact]
28+
public void Success_Generic()
29+
{
30+
// Arrange
31+
TestServiceResult response = new ();
32+
using HttpClient httpClient = getHttpClient(HttpStatusCode.OK, new StringContent(response.ToString()));
33+
ITestService service = new JsonHttpClient<ITestService>(httpClient).Endpoints;
34+
35+
// Act
36+
TestServiceResult result = service.ActionAsync().Result;
37+
38+
// Assert
39+
Assert.Equal(response, result);
40+
}
41+
42+
[Fact]
43+
public void Error()
44+
{
45+
// Arrange
46+
using HttpClient httpClient = getHttpClient(HttpStatusCode.Forbidden, null);
47+
ITestService service = new JsonHttpClient<ITestService>(httpClient).Endpoints;
48+
49+
// Act
50+
Task result = service.NoResultActionAsync();
51+
52+
// Assert
53+
Assert.ThrowsAny<Exception>(() => result.Wait());
54+
}
55+
56+
[Fact]
57+
public void Error_Generic()
58+
{
59+
// Arrange
60+
using HttpClient httpClient = getHttpClient(HttpStatusCode.Forbidden, null);
61+
ITestService service = new JsonHttpClient<ITestService>(httpClient).Endpoints;
62+
63+
// Act
64+
Task<TestServiceResult> result = service.ActionAsync();
65+
66+
// Assert
67+
Assert.ThrowsAny<Exception>(() => result.Wait());
68+
}
69+
70+
[Fact]
71+
public void Uri_Simple()
72+
{
73+
// Arrange
74+
string expectedUrl = "https://example.com/simpleuri";
75+
void assertUri(HttpRequestMessage m) => Assert.Equal(expectedUrl, m.RequestUri?.ToString());
76+
77+
using HttpClient httpClient = getHttpClient(HttpStatusCode.OK, null, assertUri);
78+
ITestService service = new JsonHttpClient<ITestService>(httpClient).Endpoints;
79+
80+
// Act
81+
Task result = service.SimpleUriAsync();
82+
result.Wait();
83+
84+
// Assert
85+
Assert.True(result.IsCompletedSuccessfully);
86+
}
87+
88+
[Fact]
89+
public void Uri_Simple_NoDash()
90+
{
91+
// Arrange
92+
static void assertUri(HttpRequestMessage m) => Assert.Equal("https://example.com/simpleuri", m.RequestUri?.ToString());
93+
94+
using HttpClient httpClient = getHttpClient(HttpStatusCode.OK, null, assertUri);
95+
ITestService service = new JsonHttpClient<ITestService>(httpClient).Endpoints;
96+
97+
// Act
98+
Task result = service.SimpleUriNoDashAsync();
99+
result.Wait();
100+
101+
// Assert
102+
Assert.True(result.IsCompletedSuccessfully);
103+
}
104+
105+
[Theory]
106+
[InlineData("test")]
107+
[InlineData(5)]
108+
[InlineData(5.8f)]
109+
[InlineData(true)]
110+
[InlineData(null)]
111+
public void Uri_Param(object param)
112+
{
113+
// Arrange
114+
string expectedUrl = $"https://example.com/paramuri/{param}";
115+
void assertUri(HttpRequestMessage m) => Assert.Equal(expectedUrl, m.RequestUri?.ToString());
116+
117+
using HttpClient httpClient = getHttpClient(HttpStatusCode.OK, null, assertUri);
118+
ITestService service = new JsonHttpClient<ITestService>(httpClient).Endpoints;
119+
120+
// Act
121+
Task result = service.ParamUriAsync(param);
122+
result.Wait();
123+
124+
// Assert
125+
Assert.True(result.IsCompletedSuccessfully);
126+
}
127+
128+
[Fact]
129+
public void Uri_Query()
130+
{
131+
// Arrange
132+
string expectedUrl = "https://example.com/query?i=5&s=asdf&b=True&f=5.4&n=&a=1&a=2&a=3&a=";
133+
void assertUri(HttpRequestMessage m) => Assert.Equal(expectedUrl, m.RequestUri?.ToString());
134+
135+
using HttpClient httpClient = getHttpClient(HttpStatusCode.OK, null, assertUri);
136+
ITestService service = new JsonHttpClient<ITestService>(httpClient).Endpoints;
137+
138+
// Act
139+
Task result = service.QueryAsync(5, "asdf", true, 5.4f, null, new int?[] { 1, 2, 3, null });
140+
result.Wait();
141+
142+
// Assert
143+
Assert.True(result.IsCompletedSuccessfully);
144+
}
145+
146+
[Fact]
147+
public void Body()
148+
{
149+
// Arrange
150+
string expectedUrl = "https://example.com/actionbody";
151+
TestServiceResult body = new();
152+
void assertUri(HttpRequestMessage m)
153+
{
154+
Assert.Equal(expectedUrl, m.RequestUri?.ToString());
155+
Assert.Equal(body.ToString(), m.Content.ReadAsStringAsync().Result);
156+
}
157+
158+
using HttpClient httpClient = getHttpClient(HttpStatusCode.OK, null, assertUri);
159+
ITestService service = new JsonHttpClient<ITestService>(httpClient).Endpoints;
160+
161+
// Act
162+
Task result = service.BodyActionAsync(body);
163+
result.Wait();
164+
165+
// Assert
166+
Assert.True(result.IsCompletedSuccessfully);
167+
}
168+
169+
[Fact]
170+
public void Verb()
171+
{
172+
// Arrange
173+
static void assertUri(HttpRequestMessage m) => Assert.Equal("VERB", m.Method.Method);
174+
175+
using HttpClient httpClient = getHttpClient(HttpStatusCode.OK, null, assertUri);
176+
ITestService service = new JsonHttpClient<ITestService>(httpClient).Endpoints;
177+
178+
// Act
179+
Task result = service.VerbAsync();
180+
result.Wait();
181+
182+
// Assert
183+
Assert.True(result.IsCompletedSuccessfully);
184+
}
185+
186+
[Fact]
187+
public void Invalid_OutParam()
188+
{
189+
// Arrange
190+
using HttpClient httpClient = getHttpClient(HttpStatusCode.OK, null);
191+
ITestService service = new JsonHttpClient<ITestService>(httpClient).Endpoints;
192+
193+
// Act & Assert
194+
Assert.ThrowsAny<Exception>(() => service.OutParamAsync(out int p).Wait());
195+
}
196+
197+
[Fact]
198+
public void Invalid_MultipleBody()
199+
{
200+
// Arrange
201+
using HttpClient httpClient = getHttpClient(HttpStatusCode.OK, null);
202+
ITestService service = new JsonHttpClient<ITestService>(httpClient).Endpoints;
203+
204+
// Act & Assert
205+
Assert.ThrowsAny<Exception>(() => service.MultipleBodyAsync(1, 2).Wait());
206+
}
207+
208+
[Fact]
209+
public void Invalid_NotDecorated()
210+
{
211+
// Arrange
212+
using HttpClient httpClient = getHttpClient(HttpStatusCode.OK, null);
213+
ITestService service = new JsonHttpClient<ITestService>(httpClient).Endpoints;
214+
215+
// Act & Assert
216+
Assert.ThrowsAny<Exception>(() => service.NotDecoratedAsync().Wait());
217+
}
218+
219+
[Fact]
220+
public void Invalid_NotAsync()
221+
{
222+
// Arrange
223+
using HttpClient httpClient = getHttpClient(HttpStatusCode.OK, null);
224+
ITestService service = new JsonHttpClient<ITestService>(httpClient).Endpoints;
225+
226+
// Act & Assert
227+
Assert.ThrowsAny<Exception>(() => service.NotAsync());
228+
}
229+
230+
private static HttpClient getHttpClient(
231+
HttpStatusCode statusCode,
232+
HttpContent content,
233+
Action<HttpRequestMessage> onBeforeSend = null)
234+
{
235+
HttpClient httpClient = MockHttpClientFactory.Create(statusCode, content, onBeforeSend);
236+
httpClient.BaseAddress = new Uri("https://example.com");
237+
return httpClient;
238+
}
239+
}
240+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
using System;
2+
using System.Net;
3+
using System.Net.Http;
4+
using System.Threading;
5+
using System.Threading.Tasks;
6+
7+
namespace Byteology.TypedHttpClients.Tests.Mocks
8+
{
9+
internal static class MockHttpClientFactory
10+
{
11+
public static HttpClient Create(HttpStatusCode statusCode, HttpContent content, Action<HttpRequestMessage> onBeforeSend = null)
12+
{
13+
MessageHandler handler = new (statusCode, content, onBeforeSend);
14+
return new HttpClient(handler, true);
15+
}
16+
17+
private class MessageHandler : HttpMessageHandler
18+
{
19+
private readonly HttpStatusCode _statusCode;
20+
private readonly HttpContent _content;
21+
private readonly Action<HttpRequestMessage> _onBeforeSend;
22+
23+
public MessageHandler(HttpStatusCode statusCode, HttpContent content, Action<HttpRequestMessage> onBeforeSend = null)
24+
{
25+
_statusCode = statusCode;
26+
_content = content;
27+
_onBeforeSend = onBeforeSend;
28+
}
29+
30+
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
31+
{
32+
_onBeforeSend?.Invoke(request);
33+
34+
return Task.FromResult(
35+
new HttpResponseMessage()
36+
{
37+
StatusCode = _statusCode,
38+
Content = _content
39+
});
40+
}
41+
}
42+
}
43+
}

0 commit comments

Comments
 (0)