namespace TaxBaik.Application.Tests; using TaxBaik.Application.Services; using TaxBaik.Domain.Entities; using TaxBaik.Domain.Interfaces; using Microsoft.Extensions.Caching.Memory; using Xunit; public class InquiryServiceTests { [Fact] public async Task UpdateStatusAsync_WhenStatusIsInvalid_ThrowsValidationException() { var service = new InquiryService(new FakeInquiryRepository(), new FakeInquiryNotificationService(), new MemoryCache(new MemoryCacheOptions())); await Assert.ThrowsAsync(() => service.UpdateStatusAsync(1, "invalid")); } [Fact] public async Task SubmitAsync_StoresEmailAndNewStatus() { var repository = new FakeInquiryRepository(); var service = new InquiryService(repository, new FakeInquiryNotificationService(), new MemoryCache(new MemoryCacheOptions())); await service.SubmitAsync("홍길동", "010-1234-5678", "기장", "문의합니다.", "user@example.com"); Assert.Equal("user@example.com", repository.Inquiries.Single().Email); Assert.Equal("new", repository.Inquiries.Single().Status); } private sealed class FakeInquiryRepository : IInquiryRepository { public List Inquiries { get; } = []; public Task CreateAsync(Inquiry inquiry, CancellationToken cancellationToken = default) { inquiry.Id = Inquiries.Count + 1; Inquiries.Add(inquiry); return Task.FromResult(inquiry.Id); } public Task GetByIdAsync(int id, CancellationToken cancellationToken = default) => Task.FromResult(Inquiries.FirstOrDefault(x => x.Id == id)); public Task<(IEnumerable Items, int Total)> GetPagedAsync( int page, int pageSize, string? status = null, CancellationToken cancellationToken = default) { var items = status == null ? Inquiries : Inquiries.Where(x => x.Status == status).ToList(); return Task.FromResult<(IEnumerable, int)>((items, items.Count())); } public Task CountAsync(CancellationToken cancellationToken = default) => Task.FromResult(Inquiries.Count); public Task CountThisMonthAsync(CancellationToken cancellationToken = default) => Task.FromResult(Inquiries.Count); public Task CountByStatusAsync(string status, CancellationToken cancellationToken = default) => Task.FromResult(Inquiries.Count(x => x.Status == status)); public Task CountByDateRangeAsync(DateTime startDate, DateTime endDate, CancellationToken cancellationToken = default) => Task.FromResult(Inquiries.Count(x => x.CreatedAt >= startDate && x.CreatedAt <= endDate)); public Task CountByStatusAndDateAsync(string status, DateTime startDate, DateTime endDate, CancellationToken cancellationToken = default) => Task.FromResult(Inquiries.Count(x => x.Status == status && x.CreatedAt >= startDate && x.CreatedAt <= endDate)); public Task UpdateStatusAsync(int id, string status, CancellationToken cancellationToken = default) { var inquiry = Inquiries.FirstOrDefault(x => x.Id == id); if (inquiry != null) inquiry.Status = status; return Task.CompletedTask; } public Task UpdateAdminMemoAsync(int id, string? adminMemo, CancellationToken cancellationToken = default) { var inquiry = Inquiries.FirstOrDefault(x => x.Id == id); if (inquiry != null) inquiry.AdminMemo = adminMemo; return Task.CompletedTask; } public Task LinkClientAsync(int inquiryId, int clientId, CancellationToken cancellationToken = default) { var inquiry = Inquiries.FirstOrDefault(x => x.Id == inquiryId); if (inquiry != null) inquiry.ClientId = clientId; return Task.CompletedTask; } } private sealed class FakeInquiryNotificationService : IInquiryNotificationService { public Task NotifyCreatedAsync(int inquiryId, string name, string phone, string serviceType, string message, string? ipAddress, DateTime createdAtUtc, CancellationToken ct = default) => Task.CompletedTask; public Task NotifyStatusChangedAsync(int inquiryId, string name, string phone, string serviceType, string previousStatus, string newStatus, string? changedBy = null, CancellationToken ct = default) => Task.CompletedTask; } }