test: add SearchApiClientTests with route and success/error tests
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.
This commit is contained in:
@@ -0,0 +1,253 @@
|
||||
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
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user