76ce35458c
Add comprehensive unit tests for SearchApiClient covering: - Route verification tests for all 6 methods (GetUserSearches, GetQueuedSearches, GetSearch, CopySearch, CreateSearch, GetResults) - Success tests verifying deserialization for all methods - Representative error tests (404 for GetSearch, 401 for GetUserSearches) Uses MockHttpMessageHandler to verify correct API routes are called and ApiResult<T> pattern maps responses correctly.
254 lines
7.8 KiB
C#
254 lines
7.8 KiB
C#
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<SearchViewModel> { 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<SearchViewModel> { 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
|
|
};
|
|
}
|