using Data.SeaHavenIndustries; using FluentAssertions; using Microsoft.EntityFrameworkCore; using SeaHaven.DataServices.Implementation; using SeaHaven.Services.DTOs; using SeaHaven.Services.Implementation; using SeaHaven.Services.Validation; using Xunit; namespace Api.SeaHavenIndustries.Tests; public class FollowUpServiceTests { private static ApplicationDbContext NewContext() { var options = new DbContextOptionsBuilder() .UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString()) .Options; return new ApplicationDbContext(options); } private static FollowUpService NewService(ApplicationDbContext ctx) => new(new FollowUpDataService(ctx), new CreateFollowUpValidation(), new UpdateFollowUpValidation()); private static FollowUps SeedFollowUp(ApplicationDbContext ctx, string reason, string status = "Open") { var f = new FollowUps { Reason = reason, ScheduleDate = new DateTime(2026, 8, 1), ScheduleStartTime = new TimeSpan(9, 0, 0), Status = status, createdby = "user-1", CreatedDate = DateTime.Now }; ctx.FollowUps.Add(f); ctx.SaveChanges(); return f; } [Fact] public async Task CreateOpenFollowUpAsync_ForcesOpenStatusAndPersistsFields() { using var ctx = NewContext(); var service = NewService(ctx); var id = await service.CreateOpenFollowUpAsync(new FollowUpCreateRequestDTO { Reason = "Call back", ScheduleDate = new DateTime(2026, 9, 1), ScheduleStartTime = new TimeSpan(10, 30, 0), EmployeeId = 5, WorkOrderId = 7, AccountId = 9, LocationId = 11 }, "user-9", CancellationToken.None); var entity = ctx.FollowUps.Single(); entity.Id.Should().Be(id); entity.Status.Should().Be("Open"); entity.Reason.Should().Be("Call back"); entity.createdby.Should().Be("user-9"); entity.EmployeeId.Should().Be(5); entity.WorkOrderId.Should().Be(7); entity.CreatedDate.Should().NotBeNull(); } [Fact] public async Task GetFollowUpListAsync_DefaultsToOpenAndMapsShape() { using var ctx = NewContext(); SeedFollowUp(ctx, "Open one", "Open"); SeedFollowUp(ctx, "Done one", "Completed"); var page = await NewService(ctx).GetFollowUpListAsync(1, 12, "", null, CancellationToken.None); page.Items.Should().HaveCount(1); page.Items.Single().Reason.Should().Be("Open one"); page.Items.Single().Status.Should().Be("Open"); page.PageSize.Should().Be(12); } [Fact] public async Task GetFollowUpDetailAsync_MapsRelatedNamesAndResolvesCreator() { using var ctx = NewContext(); var employee = new Employee { FirstName = "Sara", LastName = "Lee" }; ctx.Employees.Add(employee); ctx.SaveChanges(); var account = new Accounts { Name = "Acme" }; ctx.Accounts.Add(account); ctx.SaveChanges(); var user = new ApplicationUser { Id = "user-1", FirstName = "Creator", LastName = "One", UserName = "cone" }; var fu = new FollowUps { Reason = "Detail", ScheduleDate = new DateTime(2026, 8, 2), ScheduleStartTime = new TimeSpan(8, 0, 0), Status = "Open", EmployeeId = employee.Id, AccountId = account.Id, createdby = "user-1", CreatedDate = DateTime.Now }; ctx.Users.Add(user); ctx.FollowUps.Add(fu); ctx.SaveChanges(); var detail = await NewService(ctx).GetFollowUpDetailAsync(fu.Id, CancellationToken.None); detail.Should().NotBeNull(); detail!.AssignedTo.Should().Be("Sara Lee"); detail.AccountName.Should().Be("Acme"); detail.CreatedById.Should().Be("user-1"); detail.CreatedByName.Should().Be("Creator One"); } [Fact] public async Task UpdateFollowUpFieldsAsync_UpdatesAndThrowsWhenMissing() { using var ctx = NewContext(); var existing = SeedFollowUp(ctx, "Old reason"); await NewService(ctx).UpdateFollowUpFieldsAsync(existing.Id, new FollowUpUpdateRequestDTO { Id = existing.Id, Reason = "New reason", ScheduleDate = new DateTime(2026, 10, 1), ScheduleStartTime = new TimeSpan(11, 0, 0) }, CancellationToken.None); var row = ctx.FollowUps.Single(); row.Reason.Should().Be("New reason"); var act = () => NewService(ctx).UpdateFollowUpFieldsAsync(existing.Id + 999, new FollowUpUpdateRequestDTO { Id = existing.Id + 999 }, CancellationToken.None); await act.Should().ThrowAsync(); } [Fact] public async Task SetFollowUpStatusAsync_TogglesStatusAndReturnsFalseWhenMissing() { using var ctx = NewContext(); var existing = SeedFollowUp(ctx, "To complete"); (await NewService(ctx).SetFollowUpStatusAsync(existing.Id, "Completed", CancellationToken.None)).Should().BeTrue(); ctx.FollowUps.Single().Status.Should().Be("Completed"); (await NewService(ctx).SetFollowUpStatusAsync(existing.Id + 999, "Completed", CancellationToken.None)).Should().BeFalse(); } [Fact] public async Task RemoveFollowUpAsync_RemovesAndReturnsFalseWhenMissing() { using var ctx = NewContext(); var existing = SeedFollowUp(ctx, "Gone"); (await NewService(ctx).RemoveFollowUpAsync(existing.Id, CancellationToken.None)).Should().BeTrue(); (await NewService(ctx).RemoveFollowUpAsync(existing.Id, CancellationToken.None)).Should().BeFalse(); ctx.FollowUps.Should().BeEmpty(); } }