TermRemoteCtl/apps/windows_agent/tests/TermRemoteCtl.Agent.IntegrationTests/Api/PairingEndpointsTests.cs

135 lines
4.9 KiB
C#

using System.Net;
using System.Net.Http.Json;
using System.Text;
using System.Text.Json;
using Microsoft.AspNetCore.Mvc.Testing;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using TermRemoteCtl.Agent.Configuration;
namespace TermRemoteCtl.Agent.IntegrationTests.Api;
public sealed class PairingEndpointsTests
{
[Fact]
public async Task Redeem_Returns_BadRequest_When_Request_Is_Incomplete()
{
await using var fixture = new PairingApiFixture();
using var client = fixture.CreateClient();
using var response = await client.PostAsJsonAsync(
"/api/pairing/redeem",
new
{
code = "123456"
});
var payload = await response.Content.ReadFromJsonAsync<RedeemResponse>();
Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
Assert.NotNull(payload);
Assert.False(payload.Success);
Assert.Equal("invalid_request", payload.ErrorCode);
Assert.Equal("rejected", payload.Status);
}
[Fact]
public async Task Redeem_Returns_BadRequest_When_Json_Is_Malformed()
{
await using var fixture = new PairingApiFixture();
using var client = fixture.CreateClient();
using var content = new StringContent("{\"code\":", Encoding.UTF8, "application/json");
using var response = await client.PostAsync("/api/pairing/redeem", content);
var payload = await response.Content.ReadFromJsonAsync<RedeemResponse>();
Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
Assert.NotNull(payload);
Assert.False(payload.Success);
Assert.Equal("invalid_request", payload.ErrorCode);
Assert.Equal("rejected", payload.Status);
}
[Fact]
public async Task Redeem_Returns_BadRequest_When_Request_Body_Is_Empty()
{
await using var fixture = new PairingApiFixture();
using var client = fixture.CreateClient();
using var content = new StringContent(string.Empty, Encoding.UTF8, "application/json");
using var response = await client.PostAsync("/api/pairing/redeem", content);
var payload = await response.Content.ReadFromJsonAsync<RedeemResponse>();
Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
Assert.NotNull(payload);
Assert.False(payload.Success);
Assert.Equal("invalid_request", payload.ErrorCode);
Assert.Equal("rejected", payload.Status);
}
[Fact]
public async Task CreateCode_Does_Not_Write_Secret_Code_To_Audit_Log()
{
await using var fixture = new PairingApiFixture();
using var client = fixture.CreateClient();
var code = await client.PostFromJsonAsync<PairingCodeResponse>("/api/pairing/code");
var auditLog = await File.ReadAllTextAsync(fixture.GetAuditLogPath(), Encoding.UTF8);
Assert.NotNull(code);
Assert.DoesNotContain(code!.Value, auditLog, StringComparison.Ordinal);
}
private sealed class PairingApiFixture : WebApplicationFactory<Program>
{
private readonly string _dataRoot = Path.Combine(Path.GetTempPath(), "TermRemoteCtl.Tests", Guid.NewGuid().ToString("N"));
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
builder.UseEnvironment("Development");
builder.ConfigureAppConfiguration((_, configBuilder) =>
{
configBuilder.AddInMemoryCollection(new Dictionary<string, string?>
{
["Agent:DataRoot"] = _dataRoot,
["Agent:BindAddress"] = "127.0.0.1",
["Agent:HttpsPort"] = "9443",
["Agent:WebSocketFrameFlushMilliseconds"] = "33",
["Agent:RingBufferLineLimit"] = "4000"
});
});
}
public string GetAuditLogPath()
{
var options = Services.GetRequiredService<IOptions<AgentOptions>>().Value;
return Path.Combine(options.DataRoot, "audit.log.jsonl");
}
public new async ValueTask DisposeAsync()
{
await base.DisposeAsync();
if (Directory.Exists(_dataRoot))
{
Directory.Delete(_dataRoot, true);
}
}
}
private sealed record RedeemResponse(bool Success, string ErrorCode, string Status);
private sealed record PairingCodeResponse(string Value, DateTimeOffset ExpiresAtUtc);
}
internal static class HttpClientJsonExtensions
{
public static async Task<T?> PostFromJsonAsync<T>(this HttpClient client, string requestUri)
{
using var response = await client.PostAsync(requestUri, JsonContent.Create(new { }));
response.EnsureSuccessStatusCode();
return await response.Content.ReadFromJsonAsync<T>();
}
}