Archived
Project to GitHub
This commit is contained in:
@@ -0,0 +1,198 @@
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user