using System.Net; using System.Text.Json; using JdeScoping.Client.Services; using JdeScoping.Core.ApiContracts; using JdeScoping.Core.ViewModels; using RichardSzalay.MockHttp; using Shouldly; namespace JdeScoping.Client.Tests.Services; public class SearchApiClientTests { private readonly MockHttpMessageHandler _mockHttp; private readonly SearchApiClient _client; private static readonly JsonSerializerOptions JsonOptions = new() { PropertyNamingPolicy = JsonNamingPolicy.CamelCase }; public SearchApiClientTests() { _mockHttp = new MockHttpMessageHandler(); var httpClient = new HttpClient(_mockHttp) { BaseAddress = new Uri("http://localhost/") }; _client = new SearchApiClient(httpClient); } // Route verification tests [Fact] public async Task GetUserSearchesAsync_CallsCorrectRoute() { // Arrange var request = _mockHttp.Expect(HttpMethod.Get, $"http://localhost/{ApiRoutes.Search.Base}") .Respond("application/json", "[]"); // Act await _client.GetUserSearchesAsync(); // Assert _mockHttp.GetMatchCount(request).ShouldBe(1); } [Fact] public async Task GetQueuedSearchesAsync_CallsCorrectRoute() { // Arrange var request = _mockHttp.Expect(HttpMethod.Get, $"http://localhost/{ApiRoutes.Search.Queue}") .Respond("application/json", "[]"); // Act await _client.GetQueuedSearchesAsync(); // Assert _mockHttp.GetMatchCount(request).ShouldBe(1); } [Fact] public async Task GetSearchAsync_CallsCorrectRoute() { // Arrange var searchId = 42; var request = _mockHttp.Expect(HttpMethod.Get, $"http://localhost/{ApiRoutes.Search.GetById(searchId)}") .Respond("application/json", JsonSerializer.Serialize(CreateTestSearch(searchId), JsonOptions)); // Act await _client.GetSearchAsync(searchId); // Assert _mockHttp.GetMatchCount(request).ShouldBe(1); } [Fact] public async Task CreateSearchAsync_CallsCorrectRoute_WithPostMethod() { // Arrange var request = _mockHttp.Expect(HttpMethod.Post, $"http://localhost/{ApiRoutes.Search.Base}") .Respond("application/json", "123"); // Act await _client.CreateSearchAsync(CreateTestSearch(0)); // Assert _mockHttp.GetMatchCount(request).ShouldBe(1); } [Fact] public async Task GetResultsAsync_CallsCorrectRoute() { // Arrange var searchId = 42; var request = _mockHttp.Expect(HttpMethod.Get, $"http://localhost/{ApiRoutes.Search.GetResults(searchId)}") .Respond("application/octet-stream", new MemoryStream(new byte[] { 1, 2, 3 })); // Act await _client.GetResultsAsync(searchId); // Assert _mockHttp.GetMatchCount(request).ShouldBe(1); } [Fact] public async Task CopySearchAsync_CallsCorrectRoute() { // Arrange var searchId = 42; var request = _mockHttp.Expect(HttpMethod.Get, $"http://localhost/{ApiRoutes.Search.GetCopy(searchId)}") .Respond("application/json", JsonSerializer.Serialize(CreateTestSearch(100), JsonOptions)); // Act await _client.CopySearchAsync(searchId); // Assert _mockHttp.GetMatchCount(request).ShouldBe(1); } // Success tests [Fact] public async Task GetUserSearchesAsync_Success_ReturnsSearchList() { // Arrange var searches = new List { CreateTestSearch(1), CreateTestSearch(2) }; _mockHttp.When(HttpMethod.Get, $"http://localhost/{ApiRoutes.Search.Base}") .Respond("application/json", JsonSerializer.Serialize(searches, JsonOptions)); // Act var result = await _client.GetUserSearchesAsync(); // Assert result.IsSuccess.ShouldBeTrue(); result.Value.Count.ShouldBe(2); result.Value[0].Id.ShouldBe(1); } [Fact] public async Task GetSearchAsync_Success_ReturnsSearch() { // Arrange var search = CreateTestSearch(42); _mockHttp.When(HttpMethod.Get, $"http://localhost/{ApiRoutes.Search.GetById(42)}") .Respond("application/json", JsonSerializer.Serialize(search, JsonOptions)); // Act var result = await _client.GetSearchAsync(42); // Assert result.IsSuccess.ShouldBeTrue(); result.Value.Id.ShouldBe(42); } [Fact] public async Task CreateSearchAsync_Success_ReturnsId() { // Arrange _mockHttp.When(HttpMethod.Post, $"http://localhost/{ApiRoutes.Search.Base}") .Respond("application/json", "123"); // Act var result = await _client.CreateSearchAsync(CreateTestSearch(0)); // Assert result.IsSuccess.ShouldBeTrue(); result.Value.ShouldBe(123); } [Fact] public async Task GetQueuedSearchesAsync_Success_ReturnsSearchList() { // Arrange var searches = new List { CreateTestSearch(1) }; _mockHttp.When(HttpMethod.Get, $"http://localhost/{ApiRoutes.Search.Queue}") .Respond("application/json", JsonSerializer.Serialize(searches, JsonOptions)); // Act var result = await _client.GetQueuedSearchesAsync(); // Assert result.IsSuccess.ShouldBeTrue(); result.Value.Count.ShouldBe(1); } [Fact] public async Task GetResultsAsync_Success_ReturnsBytes() { // Arrange - GetResultsAsync returns byte[] for Excel file var expectedBytes = new byte[] { 0x50, 0x4B, 0x03, 0x04 }; // ZIP/XLSX header _mockHttp.When(HttpMethod.Get, $"http://localhost/{ApiRoutes.Search.GetResults(42)}") .Respond("application/octet-stream", new MemoryStream(expectedBytes)); // Act var result = await _client.GetResultsAsync(42); // Assert result.IsSuccess.ShouldBeTrue(); result.Value.ShouldBe(expectedBytes); } [Fact] public async Task CopySearchAsync_Success_ReturnsCopiedSearch() { // Arrange var copiedSearch = CreateTestSearch(100); copiedSearch.Name = "Copy of Search 42"; _mockHttp.When(HttpMethod.Get, $"http://localhost/{ApiRoutes.Search.GetCopy(42)}") .Respond("application/json", JsonSerializer.Serialize(copiedSearch, JsonOptions)); // Act var result = await _client.CopySearchAsync(42); // Assert result.IsSuccess.ShouldBeTrue(); result.Value.Id.ShouldBe(100); result.Value.Name.ShouldBe("Copy of Search 42"); } // Representative error tests [Fact] public async Task GetSearchAsync_404_ReturnsNotFound() { // Arrange _mockHttp.When(HttpMethod.Get, $"http://localhost/{ApiRoutes.Search.GetById(999)}") .Respond(HttpStatusCode.NotFound); // Act var result = await _client.GetSearchAsync(999); // Assert result.IsNotFound.ShouldBeTrue(); } [Fact] public async Task GetUserSearchesAsync_401_ReturnsUnauthorized() { // Arrange _mockHttp.When(HttpMethod.Get, $"http://localhost/{ApiRoutes.Search.Base}") .Respond(HttpStatusCode.Unauthorized); // Act var result = await _client.GetUserSearchesAsync(); // Assert result.IsUnauthorized.ShouldBeTrue(); } private static SearchViewModel CreateTestSearch(int id) => new() { Id = id, Name = $"Search {id}", UserName = "testuser", Status = Core.Models.Enums.SearchStatus.New }; }