using backend.Realtime; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Mvc.Testing; using Microsoft.AspNetCore.TestHost; using Microsoft.Data.Sqlite; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; namespace backend.tests; public class ApiFactory : WebApplicationFactory { private readonly string _dbPath = Path.Combine(Path.GetTempPath(), $"assistant-test-{Guid.NewGuid():N}.db"); protected override void ConfigureWebHost(IWebHostBuilder builder) { builder.UseEnvironment("Test"); builder.ConfigureAppConfiguration((_, cfg) => { cfg.AddInMemoryCollection(new Dictionary { ["ConnectionStrings:Default"] = $"Data Source={_dbPath}", ["OPENAI_API_KEY"] = "test-key", }); }); builder.ConfigureTestServices(services => { services.AddScoped(); }); } protected override void Dispose(bool disposing) { base.Dispose(disposing); SqliteConnection.ClearAllPools(); foreach (var suffix in new[] { "", "-shm", "-wal", "-journal" }) { var p = _dbPath + suffix; try { if (File.Exists(p)) File.Delete(p); } catch { /* best effort */ } } } } public class FakeRealtimeSessionFactory( OpenAIKeyProvider keys, backend.Tools.ToolRegistry registry, backend.Conversations.ConversationLog log, ILoggerFactory loggerFactory) : RealtimeSessionFactory(keys, registry, log, loggerFactory) { public static ScriptedRealtimeUpstream Upstream { get; set; } = new(); public override Task CreateAsync( Guid deviceId, RealtimeSettings cfg, ISessionOutput output, backend.Tools.IDeviceChannel channel, CancellationToken ct) { Upstream.Push(new System.Text.Json.Nodes.JsonObject { ["type"] = "session.updated" }); var s = new RealtimeSession( deviceId, Upstream, output, log, registry, cfg, channel, loggerFactory.CreateLogger()); return Task.FromResult(s); } }