Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions HttpClientToCurlGenerator.sln
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HttpRequestMessageToCurlTes
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HttpClientToCurl.Sample.InSpecific", "examples\HttpClientToCurl.Sample.InSpecific\HttpClientToCurl.Sample.InSpecific.csproj", "{9D56718F-C9E6-4C45-926D-97599072DA35}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HttpMessageHandlerTest", "tests\HttpMessageHandlerTest\HttpMessageHandlerTest.csproj", "{EF2591EB-8810-433B-BAD6-A41540801342}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -69,6 +71,10 @@ Global
{9D56718F-C9E6-4C45-926D-97599072DA35}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9D56718F-C9E6-4C45-926D-97599072DA35}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9D56718F-C9E6-4C45-926D-97599072DA35}.Release|Any CPU.Build.0 = Release|Any CPU
{EF2591EB-8810-433B-BAD6-A41540801342}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{EF2591EB-8810-433B-BAD6-A41540801342}.Debug|Any CPU.Build.0 = Debug|Any CPU
{EF2591EB-8810-433B-BAD6-A41540801342}.Release|Any CPU.ActiveCfg = Release|Any CPU
{EF2591EB-8810-433B-BAD6-A41540801342}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand All @@ -82,6 +88,7 @@ Global
{8CC76F1F-5845-D81E-5E9A-113F913A444B} = {E36BF269-7F5D-4DE7-99B0-14567F9CD6B3}
{69E31075-F14E-1DE2-1D6E-D934A5C0480F} = {E36BF269-7F5D-4DE7-99B0-14567F9CD6B3}
{9D56718F-C9E6-4C45-926D-97599072DA35} = {A8574DB9-8411-4F81-A82E-F97AD00EF8AF}
{EF2591EB-8810-433B-BAD6-A41540801342} = {E36BF269-7F5D-4DE7-99B0-14567F9CD6B3}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {E5E0FFF6-54C3-4BA1-91F3-EF3513A18D5D}
Expand Down
26 changes: 26 additions & 0 deletions tests/HttpMessageHandlerTest/HttpMessageHandlerTest.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<IsPackable>false</IsPackable>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="FluentAssertions.Json" Version="6.1.0" />
<PackageReference Include="coverlet.collector" Version="6.0.2" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
<PackageReference Include="xunit" Version="2.9.2" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.2" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\..\src\HttpClientToCurl\HttpClientToCurl.csproj" />
</ItemGroup>

<ItemGroup>
<Using Include="Xunit" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
using HttpClientToCurl.Config;

namespace HttpMessageHandlerTest.UnitTest.Builders;

public class CompositConfigBuilder
{
private readonly CompositConfig _config;
public CompositConfigBuilder()
{
_config = new CompositConfig();
}

public CompositConfigBuilder SetTurnOnAll(bool turnOnAll)
{
_config.TurnOnAll = turnOnAll;
return this;
}

public CompositConfigBuilder SetShowOnConsole(ConsoleConfig? consoleConfig)
{
_config.ShowOnConsole = consoleConfig;
return this;
}

public CompositConfigBuilder SetSaveToFile(FileConfig? fileConfig)
{
_config.SaveToFile = fileConfig;
return this;
}

public CompositConfig Build()
{
return _config;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using Microsoft.Extensions.DependencyInjection;

namespace HttpMessageHandlerTest.UnitTest.Builders;

public class HttpMessageHandlerBuilder : Microsoft.Extensions.Http.HttpMessageHandlerBuilder
{
private readonly IList<DelegatingHandler> _additional = [];
public override string Name { get; set; }
public override HttpMessageHandler PrimaryHandler { get; set; }
public override IList<DelegatingHandler> AdditionalHandlers => _additional;
public override IServiceProvider Services => new ServiceCollection().BuildServiceProvider();
public override HttpMessageHandler Build() => PrimaryHandler;

public HttpMessageHandlerBuilder()
{
Name = "test";
PrimaryHandler = new HttpClientHandler();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,258 @@
using System.Net;
using HttpClientToCurl.Config;
using HttpClientToCurl.HttpMessageHandlers;
using HttpMessageHandlerTest.UnitTest.Fakes;
using HttpMessageHandlerTest.UnitTest.Builders;
using FluentAssertions;

namespace HttpMessageHandlerTest.UnitTest;

public class CurlGeneratorHttpMessageHandlerTests
{
[Fact]
public async Task CurlGeneratorHttpMessageHandler_ReturnsResponse_When_TurnOffAll()
{
// Arrange
var config = new CompositConfigBuilder()
.SetTurnOnAll(false)
.Build();
var handler = new CurlGeneratorHttpMessageHandler(new FakeOptionsMonitor<CompositConfig>(config))
{
InnerHandler = new FakeHttpMessageHandler()
};

var request = new HttpRequestMessage(HttpMethod.Get, "http://localhost/test");

// Act
using var invoker = new HttpMessageInvoker(handler);
var response = await invoker.SendAsync(request, CancellationToken.None);

// Assert
response.Should().NotBeNull();
response.StatusCode.Should().Be(HttpStatusCode.OK);
}

[Fact]
public async Task CurlGeneratorHttpMessageHandler_ReturnsResponse_When_TurnOnAll_But_ShowOnConsole_And_SaveToFile_AreNot_Configured()
{
// Arrange
var config = new CompositConfigBuilder()
.SetTurnOnAll(true)
.SetShowOnConsole(null)
.SetSaveToFile(null)
.Build();
var handler = new CurlGeneratorHttpMessageHandler(new FakeOptionsMonitor<CompositConfig>(config))
{
InnerHandler = new FakeHttpMessageHandler()
};

var request = new HttpRequestMessage(HttpMethod.Get, "http://localhost/test");

// Act
using var invoker = new HttpMessageInvoker(handler);
var response = await invoker.SendAsync(request, CancellationToken.None);

// Assert
response.Should().NotBeNull();
response.StatusCode.Should().Be(HttpStatusCode.OK);
}

[Fact]
public async Task CurlGeneratorHttpMessageHandler_ReturnsResponse_When_TurnOnAll_But_ShowOnConsole_And_SaveToFile_TurnOff()
{
// Arrange
var config = new CompositConfigBuilder()
.SetTurnOnAll(true)
.SetShowOnConsole(new ConsoleConfig
{
TurnOn = false,
})
.SetSaveToFile(new FileConfig()
{
TurnOn = false
})
.Build();
var handler = new CurlGeneratorHttpMessageHandler(new FakeOptionsMonitor<CompositConfig>(config))
{
InnerHandler = new FakeHttpMessageHandler()
};

var request = new HttpRequestMessage(HttpMethod.Get, "http://localhost/test");

// Act
using var invoker = new HttpMessageInvoker(handler);
var response = await invoker.SendAsync(request, CancellationToken.None);

// Assert
response.Should().NotBeNull();
response.StatusCode.Should().Be(HttpStatusCode.OK);
}

[Fact]
public async Task CurlGeneratorHttpMessageHandler_WritesToConsole_When_ShowOnConsole_TurnOn()
{
// Arrange
var config = new CompositConfigBuilder()
.SetTurnOnAll(true)
.SetShowOnConsole(new ConsoleConfig
{
TurnOn = true,
EnableCodeBeautification = false
})
.Build();

var handler = new CurlGeneratorHttpMessageHandler(new FakeOptionsMonitor<CompositConfig>(config))
{
InnerHandler = new FakeHttpMessageHandler()
};

var request = new HttpRequestMessage(HttpMethod.Get, "http://localhost/api/test");

var sw = new StringWriter();
var originalOut = Console.Out;
try
{
Console.SetOut(sw);

// Act
using var invoker = new HttpMessageInvoker(handler);
var response = await invoker.SendAsync(request, CancellationToken.None);

// Assert
response.Should().NotBeNull();
response.StatusCode.Should().Be(HttpStatusCode.OK);

var output = sw.ToString();
output.Should().Contain("curl");
}
finally
{
Console.SetOut(originalOut);
}
}

[Fact]
public async Task CurlGeneratorHttpMessageHandler_WritesToFile_When_SaveToFile_TurnOn()
{
// Arrange
var tempPath = Path.GetTempPath();
var filename = Guid.NewGuid().ToString("N");

var config = new CompositConfigBuilder()
.SetTurnOnAll(true)
.SetSaveToFile(new FileConfig
{
TurnOn = true,
Path = tempPath,
Filename = filename
})
.Build();

var monitor = new FakeOptionsMonitor<CompositConfig>(config);
var handler = new CurlGeneratorHttpMessageHandler(monitor)
{
InnerHandler = new FakeHttpMessageHandler()
};

var request = new HttpRequestMessage(HttpMethod.Post, "http://localhost/api/test") { Content = new StringContent("hello") };

var filePath = Path.Combine(tempPath.TrimEnd(Path.DirectorySeparatorChar), filename + ".curl");

try
{
if (File.Exists(filePath))
{
File.Delete(filePath);
}

// Act
using var invoker = new HttpMessageInvoker(handler);
var response = await invoker.SendAsync(request, CancellationToken.None);

// Assert
response.Should().NotBeNull();
response.StatusCode.Should().Be(HttpStatusCode.OK);

File.Exists(filePath).Should().BeTrue();
var content = File.ReadAllText(filePath);
content.Should().Contain("curl");
}
finally
{
if (File.Exists(filePath))
{
File.Delete(filePath);
}
}
}

[Fact]
public async Task CurlGeneratorHttpMessageHandler_WritesToConsole_And_WritesToFile_When_ShowOnConsole_And_SaveToFile_TurnOn()
{
// Arrange
var tempPath = Path.GetTempPath();
var filename = Guid.NewGuid().ToString("N");

var config = new CompositConfigBuilder()
.SetTurnOnAll(true)
.SetShowOnConsole(new ConsoleConfig
{
TurnOn = true,
EnableCodeBeautification = false
})
.SetSaveToFile(new FileConfig
{
TurnOn = true,
Path = tempPath,
Filename = filename
})
.Build();

var handler = new CurlGeneratorHttpMessageHandler(new FakeOptionsMonitor<CompositConfig>(config))
{
InnerHandler = new FakeHttpMessageHandler()
};

var request = new HttpRequestMessage(HttpMethod.Get, "http://localhost/api/test");

var sw = new StringWriter();
var originalOut = Console.Out;
var filePath = Path.Combine(tempPath.TrimEnd(Path.DirectorySeparatorChar), filename + ".curl");

try
{
if (File.Exists(filePath))
{
File.Delete(filePath);
}

Console.SetOut(sw);

// Act
using var invoker = new HttpMessageInvoker(handler);
var response = await invoker.SendAsync(request, CancellationToken.None);

// Assert
response.Should().NotBeNull();
response.StatusCode.Should().Be(HttpStatusCode.OK);

var output = sw.ToString();
output.Should().Contain("curl");

response.Should().NotBeNull();
response.StatusCode.Should().Be(HttpStatusCode.OK);

File.Exists(filePath).Should().BeTrue();
var content = File.ReadAllText(filePath);
content.Should().Contain("curl");
}
finally
{
Console.SetOut(originalOut);
if (File.Exists(filePath))
{
File.Delete(filePath);
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
using System.Net;

namespace HttpMessageHandlerTest.UnitTest.Fakes;

public class FakeHttpMessageHandler : HttpMessageHandler
{
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
return Task.FromResult(new HttpResponseMessage(HttpStatusCode.OK));
}
}
11 changes: 11 additions & 0 deletions tests/HttpMessageHandlerTest/UnitTest/Fakes/FakeOptionsMonitor.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
using Microsoft.Extensions.Options;

namespace HttpMessageHandlerTest.UnitTest.Fakes;

public class FakeOptionsMonitor<T>(T value) : IOptionsMonitor<T> where T : class
{
public T CurrentValue { get; } = value;
public T Get(string? name) => CurrentValue;
public IDisposable OnChange(Action<T, string> listener) => new Disposable();
private sealed class Disposable : IDisposable { public void Dispose() { } }
}
Loading
Loading