85 lines
2.6 KiB
C#
85 lines
2.6 KiB
C#
using ClaudeMailbox;
|
|
using ClaudeMailbox.Config;
|
|
using Microsoft.AspNetCore.Builder;
|
|
using Microsoft.AspNetCore.Hosting;
|
|
using Microsoft.Extensions.DependencyInjection;
|
|
using Microsoft.Extensions.Hosting;
|
|
|
|
namespace ClaudeMailbox.Tests;
|
|
|
|
/// <summary>
|
|
/// Spins up a full ClaudeMailbox WebApplication on an ephemeral port against a temp SQLite file.
|
|
/// Disposable — removes the DB and stops the host on dispose.
|
|
/// </summary>
|
|
public sealed class TestHost : IAsyncDisposable
|
|
{
|
|
private readonly WebApplication _app;
|
|
private readonly string _dbPath;
|
|
|
|
public HttpClient Client { get; }
|
|
public string BaseUrl { get; }
|
|
public string DbPath => _dbPath;
|
|
|
|
private TestHost(WebApplication app, string dbPath, string baseUrl)
|
|
{
|
|
_app = app;
|
|
_dbPath = dbPath;
|
|
BaseUrl = baseUrl;
|
|
Client = new HttpClient { BaseAddress = new Uri(baseUrl) };
|
|
}
|
|
|
|
public static async Task<TestHost> StartAsync()
|
|
{
|
|
var dbPath = Path.Combine(Path.GetTempPath(), $"claude-mailbox-test-{Guid.NewGuid():N}.db");
|
|
var cfg = new DaemonConfig
|
|
{
|
|
Port = 0,
|
|
BindAddress = "127.0.0.1",
|
|
DbPath = dbPath,
|
|
};
|
|
|
|
var builder = ServerHost.CreateBuilder(cfg);
|
|
builder.WebHost.UseUrls("http://127.0.0.1:0");
|
|
|
|
var app = builder.Build();
|
|
ServerHost.ConfigurePipeline(app);
|
|
|
|
await app.StartAsync();
|
|
|
|
// Discover the port Kestrel picked.
|
|
var server = app.Services.GetRequiredService<Microsoft.AspNetCore.Hosting.Server.IServer>();
|
|
var feature = server.Features.Get<Microsoft.AspNetCore.Hosting.Server.Features.IServerAddressesFeature>();
|
|
var url = feature?.Addresses.FirstOrDefault()
|
|
?? throw new InvalidOperationException("No bound URL after start.");
|
|
|
|
return new TestHost(app, dbPath, url);
|
|
}
|
|
|
|
public HttpClient NewClientFor(string mailboxName)
|
|
{
|
|
var c = new HttpClient { BaseAddress = new Uri(BaseUrl) };
|
|
c.DefaultRequestHeaders.Add("X-Mailbox", mailboxName);
|
|
return c;
|
|
}
|
|
|
|
public async ValueTask DisposeAsync()
|
|
{
|
|
Client.Dispose();
|
|
await _app.StopAsync();
|
|
await _app.DisposeAsync();
|
|
|
|
// Allow SQLite handle to release before deleting.
|
|
GC.Collect();
|
|
GC.WaitForPendingFinalizers();
|
|
|
|
foreach (var ext in new[] { "", "-wal", "-shm" })
|
|
{
|
|
var path = _dbPath + ext;
|
|
if (File.Exists(path))
|
|
{
|
|
try { File.Delete(path); } catch { /* best-effort cleanup */ }
|
|
}
|
|
}
|
|
}
|
|
}
|