Skip to content

Commit 7c354b4

Browse files
Add HttpMessageHandlerTest
1 parent 8786cbf commit 7c354b4

10 files changed

+523
-0
lines changed

HttpClientToCurlGenerator.sln

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HttpRequestMessageToCurlTes
3131
EndProject
3232
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HttpClientToCurl.Sample.InSpecific", "examples\HttpClientToCurl.Sample.InSpecific\HttpClientToCurl.Sample.InSpecific.csproj", "{9D56718F-C9E6-4C45-926D-97599072DA35}"
3333
EndProject
34+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HttpMessageHandlerTest", "tests\HttpMessageHandlerTest\HttpMessageHandlerTest.csproj", "{EF2591EB-8810-433B-BAD6-A41540801342}"
35+
EndProject
3436
Global
3537
GlobalSection(SolutionConfigurationPlatforms) = preSolution
3638
Debug|Any CPU = Debug|Any CPU
@@ -69,6 +71,10 @@ Global
6971
{9D56718F-C9E6-4C45-926D-97599072DA35}.Debug|Any CPU.Build.0 = Debug|Any CPU
7072
{9D56718F-C9E6-4C45-926D-97599072DA35}.Release|Any CPU.ActiveCfg = Release|Any CPU
7173
{9D56718F-C9E6-4C45-926D-97599072DA35}.Release|Any CPU.Build.0 = Release|Any CPU
74+
{EF2591EB-8810-433B-BAD6-A41540801342}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
75+
{EF2591EB-8810-433B-BAD6-A41540801342}.Debug|Any CPU.Build.0 = Debug|Any CPU
76+
{EF2591EB-8810-433B-BAD6-A41540801342}.Release|Any CPU.ActiveCfg = Release|Any CPU
77+
{EF2591EB-8810-433B-BAD6-A41540801342}.Release|Any CPU.Build.0 = Release|Any CPU
7278
EndGlobalSection
7379
GlobalSection(SolutionProperties) = preSolution
7480
HideSolutionNode = FALSE
@@ -82,6 +88,7 @@ Global
8288
{8CC76F1F-5845-D81E-5E9A-113F913A444B} = {E36BF269-7F5D-4DE7-99B0-14567F9CD6B3}
8389
{69E31075-F14E-1DE2-1D6E-D934A5C0480F} = {E36BF269-7F5D-4DE7-99B0-14567F9CD6B3}
8490
{9D56718F-C9E6-4C45-926D-97599072DA35} = {A8574DB9-8411-4F81-A82E-F97AD00EF8AF}
91+
{EF2591EB-8810-433B-BAD6-A41540801342} = {E36BF269-7F5D-4DE7-99B0-14567F9CD6B3}
8592
EndGlobalSection
8693
GlobalSection(ExtensibilityGlobals) = postSolution
8794
SolutionGuid = {E5E0FFF6-54C3-4BA1-91F3-EF3513A18D5D}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFramework>net9.0</TargetFramework>
5+
<ImplicitUsings>enable</ImplicitUsings>
6+
<Nullable>enable</Nullable>
7+
<IsPackable>false</IsPackable>
8+
</PropertyGroup>
9+
10+
<ItemGroup>
11+
<PackageReference Include="FluentAssertions.Json" Version="6.1.0" />
12+
<PackageReference Include="coverlet.collector" Version="6.0.2" />
13+
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
14+
<PackageReference Include="xunit" Version="2.9.2" />
15+
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.2" />
16+
</ItemGroup>
17+
18+
<ItemGroup>
19+
<ProjectReference Include="..\..\src\HttpClientToCurl\HttpClientToCurl.csproj" />
20+
</ItemGroup>
21+
22+
<ItemGroup>
23+
<Using Include="Xunit" />
24+
</ItemGroup>
25+
26+
</Project>
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
using HttpClientToCurl.Config;
2+
3+
namespace HttpMessageHandlerTest.UnitTest.Builders;
4+
5+
public class CompositConfigBuilder
6+
{
7+
private readonly CompositConfig _config;
8+
public CompositConfigBuilder()
9+
{
10+
_config = new CompositConfig();
11+
}
12+
13+
public CompositConfigBuilder SetTurnOnAll(bool turnOnAll)
14+
{
15+
_config.TurnOnAll = turnOnAll;
16+
return this;
17+
}
18+
19+
public CompositConfigBuilder SetShowOnConsole(ConsoleConfig? consoleConfig)
20+
{
21+
_config.ShowOnConsole = consoleConfig;
22+
return this;
23+
}
24+
25+
public CompositConfigBuilder SetSaveToFile(FileConfig? fileConfig)
26+
{
27+
_config.SaveToFile = fileConfig;
28+
return this;
29+
}
30+
31+
public CompositConfig Build()
32+
{
33+
return _config;
34+
}
35+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
using Microsoft.Extensions.DependencyInjection;
2+
3+
namespace HttpMessageHandlerTest.UnitTest.Builders;
4+
5+
public class HttpMessageHandlerBuilder : Microsoft.Extensions.Http.HttpMessageHandlerBuilder
6+
{
7+
private readonly IList<DelegatingHandler> _additional = [];
8+
public override string Name { get; set; }
9+
public override HttpMessageHandler PrimaryHandler { get; set; }
10+
public override IList<DelegatingHandler> AdditionalHandlers => _additional;
11+
public override IServiceProvider Services => new ServiceCollection().BuildServiceProvider();
12+
public override HttpMessageHandler Build() => PrimaryHandler;
13+
14+
public HttpMessageHandlerBuilder()
15+
{
16+
Name = "test";
17+
PrimaryHandler = new HttpClientHandler();
18+
}
19+
}
Lines changed: 258 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,258 @@
1+
using System.Net;
2+
using HttpClientToCurl.Config;
3+
using HttpClientToCurl.HttpMessageHandlers;
4+
using HttpMessageHandlerTest.UnitTest.Fakes;
5+
using HttpMessageHandlerTest.UnitTest.Builders;
6+
using FluentAssertions;
7+
8+
namespace HttpMessageHandlerTest.UnitTest;
9+
10+
public class CurlGeneratorHttpMessageHandlerTests
11+
{
12+
[Fact]
13+
public async Task CurlGeneratorHttpMessageHandler_ReturnsResponse_When_TurnOffAll()
14+
{
15+
// Arrange
16+
var config = new CompositConfigBuilder()
17+
.SetTurnOnAll(false)
18+
.Build();
19+
var handler = new CurlGeneratorHttpMessageHandler(new FakeOptionsMonitor<CompositConfig>(config))
20+
{
21+
InnerHandler = new FakeHttpMessageHandler()
22+
};
23+
24+
var request = new HttpRequestMessage(HttpMethod.Get, "http://localhost/test");
25+
26+
// Act
27+
using var invoker = new HttpMessageInvoker(handler);
28+
var response = await invoker.SendAsync(request, CancellationToken.None);
29+
30+
// Assert
31+
response.Should().NotBeNull();
32+
response.StatusCode.Should().Be(HttpStatusCode.OK);
33+
}
34+
35+
[Fact]
36+
public async Task CurlGeneratorHttpMessageHandler_ReturnsResponse_When_TurnOnAll_But_ShowOnConsole_And_SaveToFile_AreNot_Configured()
37+
{
38+
// Arrange
39+
var config = new CompositConfigBuilder()
40+
.SetTurnOnAll(true)
41+
.SetShowOnConsole(null)
42+
.SetSaveToFile(null)
43+
.Build();
44+
var handler = new CurlGeneratorHttpMessageHandler(new FakeOptionsMonitor<CompositConfig>(config))
45+
{
46+
InnerHandler = new FakeHttpMessageHandler()
47+
};
48+
49+
var request = new HttpRequestMessage(HttpMethod.Get, "http://localhost/test");
50+
51+
// Act
52+
using var invoker = new HttpMessageInvoker(handler);
53+
var response = await invoker.SendAsync(request, CancellationToken.None);
54+
55+
// Assert
56+
response.Should().NotBeNull();
57+
response.StatusCode.Should().Be(HttpStatusCode.OK);
58+
}
59+
60+
[Fact]
61+
public async Task CurlGeneratorHttpMessageHandler_ReturnsResponse_When_TurnOnAll_But_ShowOnConsole_And_SaveToFile_TurnOff()
62+
{
63+
// Arrange
64+
var config = new CompositConfigBuilder()
65+
.SetTurnOnAll(true)
66+
.SetShowOnConsole(new ConsoleConfig
67+
{
68+
TurnOn = false,
69+
})
70+
.SetSaveToFile(new FileConfig()
71+
{
72+
TurnOn = false
73+
})
74+
.Build();
75+
var handler = new CurlGeneratorHttpMessageHandler(new FakeOptionsMonitor<CompositConfig>(config))
76+
{
77+
InnerHandler = new FakeHttpMessageHandler()
78+
};
79+
80+
var request = new HttpRequestMessage(HttpMethod.Get, "http://localhost/test");
81+
82+
// Act
83+
using var invoker = new HttpMessageInvoker(handler);
84+
var response = await invoker.SendAsync(request, CancellationToken.None);
85+
86+
// Assert
87+
response.Should().NotBeNull();
88+
response.StatusCode.Should().Be(HttpStatusCode.OK);
89+
}
90+
91+
[Fact]
92+
public async Task CurlGeneratorHttpMessageHandler_WritesToConsole_When_ShowOnConsole_TurnOn()
93+
{
94+
// Arrange
95+
var config = new CompositConfigBuilder()
96+
.SetTurnOnAll(true)
97+
.SetShowOnConsole(new ConsoleConfig
98+
{
99+
TurnOn = true,
100+
EnableCodeBeautification = false
101+
})
102+
.Build();
103+
104+
var handler = new CurlGeneratorHttpMessageHandler(new FakeOptionsMonitor<CompositConfig>(config))
105+
{
106+
InnerHandler = new FakeHttpMessageHandler()
107+
};
108+
109+
var request = new HttpRequestMessage(HttpMethod.Get, "http://localhost/api/test");
110+
111+
var sw = new StringWriter();
112+
var originalOut = Console.Out;
113+
try
114+
{
115+
Console.SetOut(sw);
116+
117+
// Act
118+
using var invoker = new HttpMessageInvoker(handler);
119+
var response = await invoker.SendAsync(request, CancellationToken.None);
120+
121+
// Assert
122+
response.Should().NotBeNull();
123+
response.StatusCode.Should().Be(HttpStatusCode.OK);
124+
125+
var output = sw.ToString();
126+
output.Should().Contain("curl");
127+
}
128+
finally
129+
{
130+
Console.SetOut(originalOut);
131+
}
132+
}
133+
134+
[Fact]
135+
public async Task CurlGeneratorHttpMessageHandler_WritesToFile_When_SaveToFile_TurnOn()
136+
{
137+
// Arrange
138+
var tempPath = Path.GetTempPath();
139+
var filename = Guid.NewGuid().ToString("N");
140+
141+
var config = new CompositConfigBuilder()
142+
.SetTurnOnAll(true)
143+
.SetSaveToFile(new FileConfig
144+
{
145+
TurnOn = true,
146+
Path = tempPath,
147+
Filename = filename
148+
})
149+
.Build();
150+
151+
var monitor = new FakeOptionsMonitor<CompositConfig>(config);
152+
var handler = new CurlGeneratorHttpMessageHandler(monitor)
153+
{
154+
InnerHandler = new FakeHttpMessageHandler()
155+
};
156+
157+
var request = new HttpRequestMessage(HttpMethod.Post, "http://localhost/api/test") { Content = new StringContent("hello") };
158+
159+
var filePath = Path.Combine(tempPath.TrimEnd(Path.DirectorySeparatorChar), filename + ".curl");
160+
161+
try
162+
{
163+
if (File.Exists(filePath))
164+
{
165+
File.Delete(filePath);
166+
}
167+
168+
// Act
169+
using var invoker = new HttpMessageInvoker(handler);
170+
var response = await invoker.SendAsync(request, CancellationToken.None);
171+
172+
// Assert
173+
response.Should().NotBeNull();
174+
response.StatusCode.Should().Be(HttpStatusCode.OK);
175+
176+
File.Exists(filePath).Should().BeTrue();
177+
var content = File.ReadAllText(filePath);
178+
content.Should().Contain("curl");
179+
}
180+
finally
181+
{
182+
if (File.Exists(filePath))
183+
{
184+
File.Delete(filePath);
185+
}
186+
}
187+
}
188+
189+
[Fact]
190+
public async Task CurlGeneratorHttpMessageHandler_WritesToConsole_And_WritesToFile_When_ShowOnConsole_And_SaveToFile_TurnOn()
191+
{
192+
// Arrange
193+
var tempPath = Path.GetTempPath();
194+
var filename = Guid.NewGuid().ToString("N");
195+
196+
var config = new CompositConfigBuilder()
197+
.SetTurnOnAll(true)
198+
.SetShowOnConsole(new ConsoleConfig
199+
{
200+
TurnOn = true,
201+
EnableCodeBeautification = false
202+
})
203+
.SetSaveToFile(new FileConfig
204+
{
205+
TurnOn = true,
206+
Path = tempPath,
207+
Filename = filename
208+
})
209+
.Build();
210+
211+
var handler = new CurlGeneratorHttpMessageHandler(new FakeOptionsMonitor<CompositConfig>(config))
212+
{
213+
InnerHandler = new FakeHttpMessageHandler()
214+
};
215+
216+
var request = new HttpRequestMessage(HttpMethod.Get, "http://localhost/api/test");
217+
218+
var sw = new StringWriter();
219+
var originalOut = Console.Out;
220+
var filePath = Path.Combine(tempPath.TrimEnd(Path.DirectorySeparatorChar), filename + ".curl");
221+
222+
try
223+
{
224+
if (File.Exists(filePath))
225+
{
226+
File.Delete(filePath);
227+
}
228+
229+
Console.SetOut(sw);
230+
231+
// Act
232+
using var invoker = new HttpMessageInvoker(handler);
233+
var response = await invoker.SendAsync(request, CancellationToken.None);
234+
235+
// Assert
236+
response.Should().NotBeNull();
237+
response.StatusCode.Should().Be(HttpStatusCode.OK);
238+
239+
var output = sw.ToString();
240+
output.Should().Contain("curl");
241+
242+
response.Should().NotBeNull();
243+
response.StatusCode.Should().Be(HttpStatusCode.OK);
244+
245+
File.Exists(filePath).Should().BeTrue();
246+
var content = File.ReadAllText(filePath);
247+
content.Should().Contain("curl");
248+
}
249+
finally
250+
{
251+
Console.SetOut(originalOut);
252+
if (File.Exists(filePath))
253+
{
254+
File.Delete(filePath);
255+
}
256+
}
257+
}
258+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
using System.Net;
2+
3+
namespace HttpMessageHandlerTest.UnitTest.Fakes;
4+
5+
public class FakeHttpMessageHandler : HttpMessageHandler
6+
{
7+
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
8+
{
9+
return Task.FromResult(new HttpResponseMessage(HttpStatusCode.OK));
10+
}
11+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
using Microsoft.Extensions.Options;
2+
3+
namespace HttpMessageHandlerTest.UnitTest.Fakes;
4+
5+
public class FakeOptionsMonitor<T>(T value) : IOptionsMonitor<T> where T : class
6+
{
7+
public T CurrentValue { get; } = value;
8+
public T Get(string? name) => CurrentValue;
9+
public IDisposable OnChange(Action<T, string> listener) => new Disposable();
10+
private sealed class Disposable : IDisposable { public void Dispose() { } }
11+
}

0 commit comments

Comments
 (0)