FamilyNido — a self-hosted PWA for a single household: shared calendar, chores, meals, school agenda, health records and a family wall. One instance per family, deployable with `docker compose` on any home server. Stack: .NET 10 (ASP.NET Core Minimal APIs) + EF Core 10 + PostgreSQL 16 on the backend, Angular 21 (standalone, signals, zoneless) + Tailwind CSS v4 on the frontend, SignalR for realtime, optional OIDC alongside local credentials, integration via a versioned `/api/v1/**` public API. See README.md for the module overview and how to deploy.
82 lines
3.1 KiB
C#
82 lines
3.1 KiB
C#
using System.Net.Http.Json;
|
|
using System.Text.Json;
|
|
using System.Text.Json.Serialization;
|
|
using FamilyNido.Persistence;
|
|
using Microsoft.Extensions.DependencyInjection;
|
|
|
|
namespace FamilyNido.Tests.Integration;
|
|
|
|
/// <summary>
|
|
/// Common base for integration tests. Resets the DB before each test, exposes
|
|
/// helpers for getting an <see cref="ApplicationDbContext"/> and an HTTP client,
|
|
/// and is opted into <see cref="IntegrationCollection"/> automatically so
|
|
/// concrete tests only need to inherit.
|
|
/// </summary>
|
|
[Collection(IntegrationCollection.Name)]
|
|
public abstract class IntegrationTestBase : IAsyncLifetime
|
|
{
|
|
/// <summary>Shared fixture — Postgres + WebApplicationFactory.</summary>
|
|
protected IntegrationFixture Fixture { get; }
|
|
|
|
/// <summary>Test-scoped HTTP client. Cookies persist within the same test.</summary>
|
|
protected HttpClient Client { get; private set; } = null!;
|
|
|
|
/// <summary>Constructor — xUnit injects the fixture via the collection.</summary>
|
|
protected IntegrationTestBase(IntegrationFixture fixture)
|
|
{
|
|
Fixture = fixture;
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public async Task InitializeAsync()
|
|
{
|
|
await Fixture.ResetAsync();
|
|
Client = Fixture.Factory.CreateClient(new Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactoryClientOptions
|
|
{
|
|
// Cookies are essential — local-login signs the session into one.
|
|
HandleCookies = true,
|
|
AllowAutoRedirect = false,
|
|
});
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public Task DisposeAsync()
|
|
{
|
|
Client?.Dispose();
|
|
return Task.CompletedTask;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Run the supplied delegate inside a fresh DI scope with an
|
|
/// <see cref="ApplicationDbContext"/> available — used by tests to seed
|
|
/// data or assert persisted state.
|
|
/// </summary>
|
|
protected async Task<T> WithDbAsync<T>(Func<ApplicationDbContext, Task<T>> action)
|
|
{
|
|
await using var scope = Fixture.Factory.Services.CreateAsyncScope();
|
|
var db = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>();
|
|
return await action(db);
|
|
}
|
|
|
|
/// <summary>Void overload of <see cref="WithDbAsync{T}"/> for seeding helpers.</summary>
|
|
protected async Task WithDbAsync(Func<ApplicationDbContext, Task> action)
|
|
{
|
|
await using var scope = Fixture.Factory.Services.CreateAsyncScope();
|
|
var db = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>();
|
|
await action(db);
|
|
}
|
|
|
|
/// <summary>
|
|
/// JSON options that mirror the API: enums serialised as strings (the
|
|
/// API registers <see cref="JsonStringEnumConverter"/> globally on
|
|
/// <c>HttpJsonOptions</c>). Tests use this when reading bodies.
|
|
/// </summary>
|
|
protected static readonly JsonSerializerOptions JsonOptions = new(JsonSerializerDefaults.Web)
|
|
{
|
|
Converters = { new JsonStringEnumConverter() },
|
|
};
|
|
|
|
/// <summary>Read the response as <typeparamref name="T"/> using the API's JSON conventions.</summary>
|
|
protected static Task<T?> ReadAsync<T>(HttpResponseMessage response)
|
|
=> response.Content.ReadFromJsonAsync<T>(JsonOptions);
|
|
}
|