using FluentAssertions; using InboxIntel.Application.Abstractions; using InboxIntel.Application.Search; using InboxIntel.Domain.Entities; using InboxIntel.Infrastructure.Persistence; using InboxIntel.Infrastructure.Search; using Microsoft.EntityFrameworkCore; using Xunit; namespace InboxIntel.IntegrationTests; /// /// Regression guard for the V-10 pagination clamp: the clamp must live at the /// user-facing controller boundary, NOT in SearchService — otherwise internal /// callers (CleanupService resolves targets with pageSize 10000) would be silently /// capped, breaking bulk cleanup-by-query. This proves the service itself honours a /// large page. /// public class SearchPaginationTests { private sealed class FakeCurrentUser : ICurrentUser { public Guid UserId { get; set; } public bool IsAuthenticated => UserId != Guid.Empty; } [Fact] public async Task SearchService_does_not_clamp_large_internal_page() { var user = Guid.NewGuid(); var opts = new DbContextOptionsBuilder() .UseInMemoryDatabase(nameof(SearchService_does_not_clamp_large_internal_page)).Options; using (var seed = new AppDbContext(opts, new FakeCurrentUser())) { var sender = new Sender { UserId = user, Address = "sender@example.com", DisplayName = "Sender" }; seed.Senders.Add(sender); for (var i = 0; i < 250; i++) seed.Emails.Add(new Email { UserId = user, GmailMessageId = $"m{i}", Subject = $"s{i}", SenderId = sender.Id, Sender = sender }); await seed.SaveChangesAsync(); } using var ctx = new AppDbContext(opts, new FakeCurrentUser { UserId = user }); // Mirrors how CleanupService resolves targets: a large page via the parser. var request = GmailQueryParser.Parse(null, page: 1, pageSize: 10_000); var result = await new SearchService(ctx).SearchAsync(user, request); result.Items.Should().HaveCount(250); // not capped at 200 result.TotalCount.Should().Be(250); } }