using backend.Conversations; using backend.Data; using FluentAssertions; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using Xunit; namespace backend.tests; public class ConversationLogTests(ApiFactory factory) : IClassFixture { private readonly ApiFactory _factory = factory; private async Task<(AppDbContext db, ConversationLog log, Guid deviceId)> ArrangeAsync() { var scope = _factory.Services.CreateScope(); var db = scope.ServiceProvider.GetRequiredService(); var log = scope.ServiceProvider.GetRequiredService(); var deviceId = Guid.NewGuid(); return (db, log, deviceId); } [Fact] public async Task Start_creates_conversation_row() { var (db, log, deviceId) = await ArrangeAsync(); var convo = await log.StartAsync(deviceId, default); var row = await db.Conversations.FirstAsync(c => c.Id == convo.Id); row.DeviceId.Should().Be(deviceId); row.EndedAt.Should().BeNull(); row.EndReason.Should().Be(EndReason.Unknown); } [Fact] public async Task Turns_are_persisted_with_monotonic_index_per_conversation() { var (db, log, deviceId) = await ArrangeAsync(); var convo = await log.StartAsync(deviceId, default); await log.AppendUserAsync(convo.Id, "hi", default); await log.AppendAssistantAsync(convo.Id, "hello there", default); await log.AppendToolAsync(convo.Id, "get_current_time", "{}", "{\"now\":\"2026-06-11T00:00:00Z\"}", default); var turns = await db.Turns .Where(t => t.ConversationId == convo.Id) .OrderBy(t => t.Index) .ToListAsync(); turns.Should().HaveCount(3); turns.Select(t => t.Index).Should().Equal(0, 1, 2); turns.Select(t => t.Role).Should().Equal(TurnRole.User, TurnRole.Assistant, TurnRole.Tool); turns[0].Text.Should().Be("hi"); turns[1].Text.Should().Be("hello there"); turns[2].ToolName.Should().Be("get_current_time"); } [Fact] public async Task End_sets_ended_at_and_reason() { var (db, log, deviceId) = await ArrangeAsync(); var convo = await log.StartAsync(deviceId, default); await log.EndAsync(convo.Id, EndReason.Idle, default); var row = await db.Conversations.FirstAsync(c => c.Id == convo.Id); row.EndedAt.Should().NotBeNull(); row.EndReason.Should().Be(EndReason.Idle); } }