using ManagerService.Controllers; using ManagerService.Data; using ManagerService.DTOs; using ManagerService.Services; using ManagerService.Tests.Infrastructure; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging.Abstractions; using Moq; using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Xunit; namespace ManagerService.Tests.Controllers { public class AiControllerTests { private static readonly AiChatResponse FakeResponse = new AiChatResponse { Reply = "OK", TokensUsed = 42 }; private AiController BuildController(MyInfoMateDbContext db, Mock? mockService = null) { mockService ??= new Mock(); mockService .Setup(s => s.ChatAsync(It.IsAny())) .ReturnsAsync(FakeResponse); return new AiController(mockService.Object, db, NullLogger.Instance); } private static AiChatRequest MakeRequest(string instanceId, AppType appType = AppType.Tablet) => new AiChatRequest { InstanceId = instanceId, AppType = appType, Message = "Bonjour" }; // ── FORBID CASES ───────────────────────────────────────────────────── [Fact] public async Task Chat_InstanceNotFound_ReturnsForbid() { using var db = DbContextFactory.Create(); var result = await BuildController(db).Chat(MakeRequest("unknown")); Assert.IsType(result); } [Fact] public async Task Chat_InstanceAssistantDisabled_ReturnsForbid() { using var db = DbContextFactory.Create(); db.Instances.Add(new Instance { Id = "i1", Name = "Musée", IsAssistant = false, DateCreation = DateTime.UtcNow }); db.ApplicationInstances.Add(new ApplicationInstance { Id = "ai1", InstanceId = "i1", AppType = AppType.Tablet, IsAssistant = true, Languages = new List() }); db.SaveChanges(); var result = await BuildController(db).Chat(MakeRequest("i1")); Assert.IsType(result); } [Fact] public async Task Chat_AppInstanceAssistantDisabled_ReturnsForbid() { using var db = DbContextFactory.Create(); db.Instances.Add(new Instance { Id = "i1", Name = "Musée", IsAssistant = true, DateCreation = DateTime.UtcNow }); db.ApplicationInstances.Add(new ApplicationInstance { Id = "ai1", InstanceId = "i1", AppType = AppType.Tablet, IsAssistant = false, Languages = new List() }); db.SaveChanges(); var result = await BuildController(db).Chat(MakeRequest("i1")); Assert.IsType(result); } [Fact] public async Task Chat_NoAppInstance_ReturnsForbid() { using var db = DbContextFactory.Create(); db.Instances.Add(new Instance { Id = "i1", Name = "Musée", IsAssistant = true, DateCreation = DateTime.UtcNow }); db.SaveChanges(); var result = await BuildController(db).Chat(MakeRequest("i1")); Assert.IsType(result); } // ── QUOTA COUNTER ──────────────────────────────────────────────────── [Fact] public async Task Chat_FirstRequestOfMonth_ResetsCounterAndAddsTokensUsed() { using var db = DbContextFactory.Create(); db.Instances.Add(new Instance { Id = "i1", Name = "Musée", IsAssistant = true, DateCreation = DateTime.UtcNow, AiTokensThisMonth = 99, AiUsageMonthKey = "2020-01" }); db.ApplicationInstances.Add(new ApplicationInstance { Id = "ai1", InstanceId = "i1", AppType = AppType.Tablet, IsAssistant = true, Languages = new List() }); db.SaveChanges(); await BuildController(db).Chat(MakeRequest("i1")); var inst = db.Instances.First(); Assert.Equal(42, inst.AiTokensThisMonth); Assert.Equal(DateTime.UtcNow.ToString("yyyy-MM"), inst.AiUsageMonthKey); } [Fact] public async Task Chat_SameMonth_AddsTokensUsedToCounter() { using var db = DbContextFactory.Create(); var monthKey = DateTime.UtcNow.ToString("yyyy-MM"); db.Instances.Add(new Instance { Id = "i1", Name = "Musée", IsAssistant = true, DateCreation = DateTime.UtcNow, AiTokensThisMonth = 3, AiUsageMonthKey = monthKey }); db.ApplicationInstances.Add(new ApplicationInstance { Id = "ai1", InstanceId = "i1", AppType = AppType.Tablet, IsAssistant = true, Languages = new List() }); db.SaveChanges(); await BuildController(db).Chat(MakeRequest("i1")); Assert.Equal(45, db.Instances.First().AiTokensThisMonth); } [Fact] public async Task Chat_QuotaAlreadyReached_ReturnsTooManyRequestsWithoutCallingAssistantService() { using var db = DbContextFactory.Create(); var monthKey = DateTime.UtcNow.ToString("yyyy-MM"); db.Instances.Add(new Instance { Id = "i1", Name = "Musée", IsAssistant = true, DateCreation = DateTime.UtcNow, AiTokensThisMonth = 100, AiTokensPerMonth = 100, AiUsageMonthKey = monthKey }); db.ApplicationInstances.Add(new ApplicationInstance { Id = "ai1", InstanceId = "i1", AppType = AppType.Tablet, IsAssistant = true, Languages = new List() }); db.SaveChanges(); var mockService = new Mock(); mockService.Setup(s => s.ChatAsync(It.IsAny())).ReturnsAsync(FakeResponse); var result = await BuildController(db, mockService).Chat(MakeRequest("i1")); var status = Assert.IsType(result); Assert.Equal(429, status.StatusCode); mockService.Verify(s => s.ChatAsync(It.IsAny()), Times.Never); } // ── NOMINAL ────────────────────────────────────────────────────────── [Fact] public async Task Chat_Success_CallsAssistantServiceAndReturns200() { using var db = DbContextFactory.Create(); var mockService = new Mock(); mockService.Setup(s => s.ChatAsync(It.IsAny())).ReturnsAsync(FakeResponse); db.Instances.Add(new Instance { Id = "i1", Name = "Musée", IsAssistant = true, DateCreation = DateTime.UtcNow, AiUsageMonthKey = DateTime.UtcNow.ToString("yyyy-MM") }); db.ApplicationInstances.Add(new ApplicationInstance { Id = "ai1", InstanceId = "i1", AppType = AppType.Tablet, IsAssistant = true, Languages = new List() }); db.SaveChanges(); var result = await BuildController(db, mockService).Chat(MakeRequest("i1")); var ok = Assert.IsType(result); Assert.Equal(FakeResponse, ok.Value); mockService.Verify(s => s.ChatAsync(It.IsAny()), Times.Once); } } }