This repository has been archived on 2026-07-22. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
Bsevita.Library/tests/Bsevita.Library.Api.Tests/ApiIntegrationTests.cs
T
2026-06-24 08:33:23 +02:00

199 lines
7.3 KiB
C#

using System.Net;
using System.Net.Http.Json;
using System.Text.Json.Nodes;
using Bsevita.Library.Api.Data.Generated;
using Bsevita.Library.Api.Data.Generated.Entities;
using Bsevita.Library.Models.Students;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc.Testing;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Logging;
namespace Bsevita.Library.Api.Tests;
public sealed class ApiIntegrationTests
{
[Fact]
public async Task SwaggerUi_AndSwaggerJson_AreAvailableInDevelopment()
{
await using var factory = new ApiFactory();
var client = factory.CreateClient();
var ui = await client.GetAsync("/swagger");
var document = await client.GetFromJsonAsync<JsonObject>("/swagger/v1/swagger.json");
Assert.Equal(HttpStatusCode.OK, ui.StatusCode);
Assert.Equal("Bsevita.Library.Api", document?["info"]?["title"]?.GetValue<string>());
Assert.NotNull(document?["paths"]?["/api/students"]);
Assert.NotNull(document?["paths"]?["/api/books"]);
Assert.NotNull(document?["paths"]?["/api/loans"]);
Assert.NotNull(document?["paths"]?["/api/returns/verify/{bookNumber}"]);
Assert.NotNull(document?["paths"]?["/api/reports/statistics"]);
}
[Fact]
public async Task PostStudent_ReturnsCreatedStudent()
{
await using var factory = new ApiFactory();
var client = factory.CreateClient();
var response = await client.PostAsJsonAsync("/api/students", new SaveStudentRequest
{
CardNumber = "S-1",
FirstName = "Ada",
LastName = "Lovelace",
ClassName = "4AHIT"
});
var body = await response.Content.ReadFromJsonAsync<StudentDto>();
Assert.Equal(HttpStatusCode.Created, response.StatusCode);
Assert.Equal("S-1", body?.CardNumber);
Assert.Equal("Ada Lovelace", body?.FullName);
}
[Fact]
public async Task InvalidBody_ReturnsValidationProblemDetails()
{
await using var factory = new ApiFactory();
var client = factory.CreateClient();
var response = await client.PostAsJsonAsync("/api/students", new { cardNumber = "" });
var problem = await response.Content.ReadFromJsonAsync<JsonObject>();
Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
Assert.Equal("application/problem+json", response.Content.Headers.ContentType?.MediaType);
Assert.Equal(400, problem?["status"]?.GetValue<int>());
Assert.NotNull(problem?["errors"]);
}
[Fact]
public async Task MissingResource_ReturnsProblemDetails()
{
await using var factory = new ApiFactory();
var client = factory.CreateClient();
var response = await client.GetAsync($"/api/students/{Guid.NewGuid()}");
var problem = await response.Content.ReadFromJsonAsync<JsonObject>();
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
Assert.Equal("application/problem+json", response.Content.Headers.ContentType?.MediaType);
Assert.Equal(404, problem?["status"]?.GetValue<int>());
Assert.Equal("Nicht gefunden", problem?["title"]?.GetValue<string>());
}
[Fact]
public async Task UnknownRoute_ReturnsNotFound()
{
await using var factory = new ApiFactory();
var client = factory.CreateClient();
var response = await client.GetAsync("/api/does-not-exist");
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
}
[Fact]
public async Task Health_ReturnsOk()
{
await using var factory = new ApiFactory();
var client = factory.CreateClient();
var response = await client.GetAsync("/health");
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
}
[Fact]
public async Task LoanConflict_ReturnsConflictProblemDetails()
{
await using var factory = new ApiFactory();
await factory.SeedBorrowedBookAsync();
var client = factory.CreateClient();
var response = await client.PostAsJsonAsync("/api/loans", new
{
cardNumber = "S-2",
bookNumber = "B-1"
});
var problem = await response.Content.ReadFromJsonAsync<JsonObject>();
Assert.Equal(HttpStatusCode.Conflict, response.StatusCode);
Assert.Equal("application/problem+json", response.Content.Headers.ContentType?.MediaType);
Assert.Equal(409, problem?["status"]?.GetValue<int>());
}
private sealed class ApiFactory : WebApplicationFactory<Program>
{
private readonly string _databaseName = $"Bsevita.Library.Api.Tests.{Guid.NewGuid():N}";
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
builder.UseEnvironment("Development");
builder.UseSetting("Database:ValidateOnStartup", "false");
builder.ConfigureLogging(logging => logging.ClearProviders());
builder.ConfigureServices(services =>
{
services.RemoveAll<LibraryDbContext>();
services.RemoveAll<DbContextOptions>();
services.RemoveAll<DbContextOptions<LibraryDbContext>>();
services.RemoveAll<IDbContextOptionsConfiguration<LibraryDbContext>>();
services.RemoveAll<TimeProvider>();
services.AddDbContext<LibraryDbContext>(options => options.UseInMemoryDatabase(_databaseName));
services.AddSingleton<TimeProvider>(new FixedTimeProvider(new DateTimeOffset(2026, 6, 22, 8, 0, 0, TimeSpan.Zero)));
});
}
public async Task SeedBorrowedBookAsync()
{
using var scope = Services.CreateScope();
var dbContext = scope.ServiceProvider.GetRequiredService<LibraryDbContext>();
var now = new DateTimeOffset(2026, 6, 22, 8, 0, 0, TimeSpan.Zero);
var firstStudent = new Student
{
StudentId = Guid.NewGuid(),
CardNumber = "S-1",
FirstName = "Ada",
LastName = "Lovelace",
ClassName = "4AHIT",
IsActive = true,
CreatedAt = now,
UpdatedAt = now
};
var secondStudent = new Student
{
StudentId = Guid.NewGuid(),
CardNumber = "S-2",
FirstName = "Grace",
LastName = "Hopper",
ClassName = "5BHIT",
IsActive = true,
CreatedAt = now,
UpdatedAt = now
};
var book = new Book
{
BookId = Guid.NewGuid(),
BookNumber = "B-1",
Title = "Borrowed",
Author = "A",
Subject = "S",
IsActive = true,
CreatedAt = now,
UpdatedAt = now
};
dbContext.AddRange(firstStudent, secondStudent, book, new Loan
{
LoanId = Guid.NewGuid(),
StudentId = firstStudent.StudentId,
BookId = book.BookId,
LoanedAt = now,
DueAt = now.AddDays(14)
});
await dbContext.SaveChangesAsync();
}
}
}