159 lines
6.3 KiB
C#
159 lines
6.3 KiB
C#
using System.Net;
|
|
using System.Net.Http.Json;
|
|
using FluentAssertions;
|
|
using Microsoft.Extensions.DependencyInjection;
|
|
using PageManager.Api.Api.Dtos;
|
|
using PageManager.Api.Data;
|
|
using PageManager.Api.Data.Models;
|
|
using PageManager.Api.Tests.Integration.Fixtures;
|
|
|
|
namespace PageManager.Api.Tests.Integration;
|
|
|
|
[Collection("Postgres")]
|
|
public class ImportControllerTests : IAsyncLifetime
|
|
{
|
|
private readonly TestWebAppFactory _factory;
|
|
private readonly HttpClient _client;
|
|
|
|
public ImportControllerTests(PostgresFixture postgres)
|
|
{
|
|
_factory = new TestWebAppFactory(postgres);
|
|
_client = _factory.CreateClient();
|
|
}
|
|
|
|
public async Task InitializeAsync()
|
|
{
|
|
using var scope = _factory.Services.CreateScope();
|
|
var db = scope.ServiceProvider.GetRequiredService<AppDbContext>();
|
|
await Microsoft.EntityFrameworkCore.RelationalDatabaseFacadeExtensions
|
|
.ExecuteSqlRawAsync(db.Database,
|
|
"TRUNCATE import_queue_items, import_sources RESTART IDENTITY CASCADE");
|
|
}
|
|
|
|
public Task DisposeAsync()
|
|
{
|
|
_client.Dispose();
|
|
_factory.Dispose();
|
|
return Task.CompletedTask;
|
|
}
|
|
|
|
// ── GET /api/sources ──────────────────────────────────────────────────────
|
|
|
|
[Fact]
|
|
public async Task GetSources_EmptyDb_Returns200WithEmptyArray()
|
|
{
|
|
var response = await _client.GetAsync("/api/sources");
|
|
response.StatusCode.Should().Be(HttpStatusCode.OK);
|
|
var sources = await response.Content.ReadFromJsonAsync<ImportSourceDto[]>();
|
|
sources.Should().BeEmpty();
|
|
}
|
|
|
|
[Fact]
|
|
public async Task GetSources_SeededSource_ReturnsMappedDto()
|
|
{
|
|
await SeedSourceAsync(name: "My Library", type: ImportSourceType.Folder, path: "/books");
|
|
|
|
var response = await _client.GetAsync("/api/sources");
|
|
var sources = await response.Content.ReadFromJsonAsync<ImportSourceDto[]>();
|
|
|
|
sources.Should().HaveCount(1);
|
|
sources![0].Name.Should().Be("My Library");
|
|
sources[0].Type.Should().Be("folder");
|
|
sources[0].Enabled.Should().BeTrue();
|
|
}
|
|
|
|
// ── PATCH /api/sources/{id} ───────────────────────────────────────────────
|
|
|
|
[Fact]
|
|
public async Task UpdateSource_Found_Returns200WithUpdatedEnabled()
|
|
{
|
|
var id = await SeedSourceAsync();
|
|
|
|
var response = await _client.PatchAsJsonAsync($"/api/sources/{id}", new { enabled = false });
|
|
|
|
response.StatusCode.Should().Be(HttpStatusCode.OK);
|
|
var dto = await response.Content.ReadFromJsonAsync<ImportSourceDto>();
|
|
dto!.Enabled.Should().BeFalse();
|
|
}
|
|
|
|
[Fact]
|
|
public async Task UpdateSource_NotFound_Returns404()
|
|
{
|
|
var response = await _client.PatchAsJsonAsync("/api/sources/missing-id", new { enabled = false });
|
|
response.StatusCode.Should().Be(HttpStatusCode.NotFound);
|
|
}
|
|
|
|
// ── GET /api/queue ────────────────────────────────────────────────────────
|
|
|
|
[Fact]
|
|
public async Task GetQueue_EmptyDb_Returns200WithEmptyArray()
|
|
{
|
|
var response = await _client.GetAsync("/api/queue");
|
|
response.StatusCode.Should().Be(HttpStatusCode.OK);
|
|
var items = await response.Content.ReadFromJsonAsync<QueueItemDto[]>();
|
|
items.Should().BeEmpty();
|
|
}
|
|
|
|
// ── DELETE /api/queue/{id} ────────────────────────────────────────────────
|
|
|
|
[Fact]
|
|
public async Task RemoveQueueItem_Found_Returns204()
|
|
{
|
|
var id = await SeedQueueItemAsync();
|
|
var response = await _client.DeleteAsync($"/api/queue/{id}");
|
|
response.StatusCode.Should().Be(HttpStatusCode.NoContent);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task RemoveQueueItem_NotFound_Returns404()
|
|
{
|
|
var response = await _client.DeleteAsync("/api/queue/missing-id");
|
|
response.StatusCode.Should().Be(HttpStatusCode.NotFound);
|
|
}
|
|
|
|
// ── POST /api/queue/{id}/retry ────────────────────────────────────────────
|
|
|
|
[Fact]
|
|
public async Task RetryQueueItem_FailedItem_Returns204()
|
|
{
|
|
var id = await SeedQueueItemAsync(status: QueueItemStatus.Failed);
|
|
var response = await _client.PostAsync($"/api/queue/{id}/retry", null);
|
|
response.StatusCode.Should().Be(HttpStatusCode.NoContent);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task RetryQueueItem_NonFailedItem_Returns404()
|
|
{
|
|
var id = await SeedQueueItemAsync(status: QueueItemStatus.Completed);
|
|
var response = await _client.PostAsync($"/api/queue/{id}/retry", null);
|
|
response.StatusCode.Should().Be(HttpStatusCode.NotFound);
|
|
}
|
|
|
|
// ── Helpers ───────────────────────────────────────────────────────────────
|
|
|
|
private async Task<string> SeedSourceAsync(
|
|
string name = "Test Source",
|
|
ImportSourceType type = ImportSourceType.Folder,
|
|
string path = "/test")
|
|
{
|
|
using var scope = _factory.Services.CreateScope();
|
|
var db = scope.ServiceProvider.GetRequiredService<AppDbContext>();
|
|
var source = new ImportSource { Name = name, Type = type, Path = path };
|
|
db.ImportSources.Add(source);
|
|
await db.SaveChangesAsync();
|
|
return source.Id;
|
|
}
|
|
|
|
private async Task<string> SeedQueueItemAsync(
|
|
string filename = "book.epub",
|
|
QueueItemStatus status = QueueItemStatus.Queued)
|
|
{
|
|
using var scope = _factory.Services.CreateScope();
|
|
var db = scope.ServiceProvider.GetRequiredService<AppDbContext>();
|
|
var item = new ImportQueueItem { Filename = filename, Status = status, Source = "https://example.com" };
|
|
db.ImportQueueItems.Add(item);
|
|
await db.SaveChangesAsync();
|
|
return item.Id;
|
|
}
|
|
}
|