familynido/tests/FamilyNido.Tests/Integration/TestSeed.cs
Pablo Fernández a308041d59 Initial commit
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.
2026-05-13 00:23:14 +02:00

99 lines
3.5 KiB
C#

using System.Net.Http.Json;
using FamilyNido.Domain.Families;
using FamilyNido.Domain.Identity;
using FamilyNido.Persistence;
using FluentAssertions;
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.DependencyInjection;
namespace FamilyNido.Tests.Integration;
/// <summary>
/// Helpers for assembling the minimum domain graph an integration test needs:
/// a family, a user with a known local password, and one or more linked
/// members. Returned by reference so tests can chain seeding calls and assert
/// the persisted state.
/// </summary>
public static class TestSeed
{
/// <summary>Default password used by every seeded user. Hashed with PBKDF2 v3 by the password hasher.</summary>
public const string DefaultPassword = "Test1234!";
/// <summary>Compact bundle returned by <see cref="SeedFamilyAsync"/>.</summary>
public sealed record FamilyHandle(Family Family, FamilyMember Member, User User);
/// <summary>
/// Persist a family + an authenticable user with a linked member. Defaults
/// produce an admin adult; pass <paramref name="role"/> to vary.
/// </summary>
public static async Task<FamilyHandle> SeedFamilyAsync(
ApplicationDbContext db,
IServiceProvider services,
string email = "dan@example.com",
string displayName = "Dan",
FamilyRole role = FamilyRole.Admin,
string colorHex = "#C96442")
{
var hasher = services.GetRequiredService<IPasswordHasher<User>>();
var family = new Family { Name = "Familia Test", TimeZone = "Europe/Madrid" };
db.Families.Add(family);
var user = new User { Email = email, DisplayName = displayName, Role = role };
db.Users.Add(user);
var member = new FamilyMember
{
FamilyId = family.Id,
DisplayName = displayName,
MemberType = MemberType.Adult,
ColorHex = colorHex,
UserId = user.Id,
};
db.FamilyMembers.Add(member);
var credential = new UserCredential
{
UserId = user.Id,
Provider = IdentityProvider.Local,
PasswordHash = hasher.HashPassword(user, DefaultPassword),
};
db.UserCredentials.Add(credential);
await db.SaveChangesAsync();
return new FamilyHandle(family, member, user);
}
/// <summary>Append one extra member (not authenticable) to an existing family.</summary>
public static async Task<FamilyMember> SeedMemberAsync(
ApplicationDbContext db,
Guid familyId,
string displayName,
MemberType type = MemberType.Child,
string colorHex = "#7BA4A8",
DateOnly? birthDate = null)
{
var member = new FamilyMember
{
FamilyId = familyId,
DisplayName = displayName,
MemberType = type,
ColorHex = colorHex,
BirthDate = birthDate,
};
db.FamilyMembers.Add(member);
await db.SaveChangesAsync();
return member;
}
/// <summary>
/// Sign the supplied client in via <c>POST /api/auth/local/login</c>. The
/// session cookie is preserved on the client because <c>HandleCookies</c>
/// is enabled on the test factory.
/// </summary>
public static async Task LoginAsync(HttpClient client, string email, string password = DefaultPassword)
{
var response = await client.PostAsJsonAsync("/api/auth/local/login", new { email, password });
response.IsSuccessStatusCode.Should().BeTrue($"login should succeed (got {response.StatusCode})");
}
}