Files
jfandClaude Sonnet 5.5 5446dd4170 Versiehistorie in een tabel, voor site en app
De versiehistorie stond als tekst in zowel de app als de website. Nu staat hij in
tabel version_history, met een beheerpagina (Beheer > Versiehistorie) op de site.
De site leest de tabel rechtstreeks, de app via de nieuwe actie app.versiehistorie.
Startrijen: db/handmatig/22b_versiehistorie_startrijen.sql (als root).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014MGLqt1uKfNARQHut9ifcx
2026-10-20 11:47:06 +00:00

190 lines
8.4 KiB
C#

using MijnBesluit.Application.Abstractions;
using MijnBesluit.Application.Services;
namespace MijnBesluit.SelfCheck;
/// <summary>Alles wat één testscenario nodig heeft, klaargezet in het geheugen.</summary>
public class World
{
public Store Store { get; } = new();
public TestClock Clock { get; } = new();
public TestEmail Email { get; } = new();
public TestPush Push { get; } = new();
/// <summary>Standaard uit; zet hem aan in een test die het verkleinen wil nakijken.</summary>
public TestVerkleiner Verkleiner { get; } = new();
public AccountService Accounts { get; }
public FamilyService Families { get; }
public VaultService Vault { get; }
public MessageService Messages { get; }
public LegacyService Legacy { get; }
public MeldingService Meldingen { get; }
public OverzichtService Overzicht { get; }
public SamenvoegService Samenvoegen { get; }
public BeheerService Beheer { get; }
public AdvertentieService Adverteren { get; }
public AccountOpheffenService Opheffen { get; }
public VaultCategoryService Soorten { get; }
public VersiehistorieService Versiehistorie { get; }
/// <summary>Maakt in één stap een account aan; scheelt in de scenario's het hele codegedoe.</summary>
public async Task<MijnBesluit.Domain.Entities.User> MaakGebruikerAsync(string email, string voornaam, string achternaam = "")
{
var resultaat = await Accounts.MaakAccountAsync(email, voornaam, achternaam);
if (!resultaat.Succeeded) throw new AssertionException($"account aanmaken mislukte: {resultaat.Error}");
return resultaat.Value!;
}
/// <summary>
/// Maakt iemand met een echt account die ook écht in de familie van <paramref name="eigenaar"/>
/// staat: toevoegen, uitnodigen, aannemen. Dat laatste is nodig omdat toevoegen alleen een
/// aantekening maakt; pas door de uitnodiging aan te nemen wordt het profiel van hemzelf en
/// staat hij in de kring van de eigenaar.
///
/// Die kring bepaalt wie er iets mag zeggen over een overlijden, dus tests die dat nabootsen
/// moeten hem ook echt opbouwen.
/// </summary>
public async Task<MijnBesluit.Domain.Entities.User> MaakFamilielidAsync(
MijnBesluit.Domain.Entities.User eigenaar, string voornaam, string achternaam, string email,
MijnBesluit.Domain.Enums.RelationType relatie = MijnBesluit.Domain.Enums.RelationType.Kind)
{
var familie = Store.Families.Single(f => f.OwnerPersonId == eigenaar.PersonId);
var profiel = await Families.AddMemberAsync(familie.Id, eigenaar.Id, voornaam, achternaam, email, relatie);
if (!profiel.Succeeded) throw new AssertionException($"toevoegen mislukte: {profiel.Error}");
var token = await Families.InviteAsync(familie.Id, profiel.Value!.Id, eigenaar.Id);
if (!token.Succeeded) throw new AssertionException($"uitnodigen mislukte: {token.Error}");
var account = await Families.AcceptInvitationAsync(token.Value!, Accounts);
if (!account.Succeeded) throw new AssertionException($"aannemen mislukte: {account.Error}");
return account.Value!;
}
/// <summary>
/// Wijst een of meer mensen aan als nalatenschapsbeheerder van <paramref name="betrokkene"/>.
///
/// Nodig in elke test die een overlijden meldt: alleen wie is aangewezen mag dat. Ze moeten
/// ook echt in zijn familie staan, dus gebruik hiervoor mensen uit MaakFamilielidAsync.
/// </summary>
public async Task BeheerderAsync(MijnBesluit.Domain.Entities.User betrokkene,
params MijnBesluit.Domain.Entities.User[] beheerders)
{
var volgorde = 1;
foreach (var beheerder in beheerders)
{
var r = await Legacy.AppointManagerAsync(betrokkene.Id, beheerder.PersonId, volgorde++);
if (!r.Succeeded) throw new AssertionException($"aanwijzen mislukte: {r.Error}");
}
}
/// <summary>Haalt de code uit de laatst verstuurde e-mail, zoals een gebruiker hem uit zijn postvak haalt.</summary>
public string LaatsteCodeUitMail(string email)
{
var mail = Email.Sent.Last(m => m.To == email);
var match = System.Text.RegularExpressions.Regex.Match(mail.Body, @"letter-spacing:5px;margin:20px 0"">(\d+)<");
if (!match.Success) throw new AssertionException("geen code gevonden in de e-mail");
return match.Groups[1].Value;
}
public World()
{
var uow = new Uow(Store);
var keys = new TestKeyProtector();
var files = new TestFiles();
var audit = new Audit(Store, Clock);
var users = new UserRepo(Store);
var people = new PersonRepo(Store);
var families = new FamilyRepo(Store);
var invitations = new InvitationRepo(Store);
var vault = new VaultRepo(Store);
var messages = new MessageRepo(Store);
var legacy = new LegacyRepo(Store);
var codes = new LoginCodeRepo(Store);
Accounts = new AccountService(users, people, families, codes, keys, Email, audit, uow, Clock, vault);
Families = new FamilyService(families, people, users, invitations, messages, legacy,
Email, audit, uow, Clock);
Soorten = new VaultCategoryService(new VaultCategoryRepo(Store), users, audit, uow, Clock);
Versiehistorie = new VersiehistorieService(new VersiehistorieRepo(Store), users, audit, uow, Clock);
Vault = new VaultService(vault, people, messages, users, keys, files, audit, uow, Clock, Verkleiner, families, legacy, Soorten);
Meldingen = new MeldingService(new ToestelRepo(Store), users, Push, uow, Clock);
Messages = new MessageService(messages, people, users, vault, keys, files, Email, audit, uow, Clock, Meldingen);
Legacy = new LegacyService(legacy, people, users, vault, messages, Email, audit, uow, Clock, Vault, families);
Overzicht = new OverzichtService(users, people, families, vault, messages, legacy, uow, Clock);
Samenvoegen = new SamenvoegService(people, families, invitations, messages, vault, legacy,
audit, uow, Clock, new SamenvoegRepo(Store));
Beheer = new BeheerService(users, people, legacy, audit, Clock, Legacy, Samenvoegen);
Adverteren = new AdvertentieService(new AdvertentieRepo(Store), users, Email, audit, uow,
Clock, Verkleiner);
Opheffen = new AccountOpheffenService(users, people, families, invitations, vault, messages,
legacy, codes, new ToestelRepo(Store), files, Email,
audit, uow, Clock);
}
}
public static class Assert
{
public static void True(bool condition, string what)
{
if (!condition) throw new AssertionException(what);
}
public static void Throws<TException>(Action action, string what) where TException : Exception
{
try { action(); }
catch (TException) { return; }
catch (Exception ex) { throw new AssertionException($"{what} (kreeg {ex.GetType().Name})"); }
throw new AssertionException($"{what} (er werd geen fout gegooid)");
}
}
public class AssertionException : Exception
{
public AssertionException(string message) : base(message) { }
}
/// <summary>Draait de scenario's en vat de uitkomst samen.</summary>
public class Runner
{
private int _passed, _failed;
public void Test(string name, Action body) => Run(name, () => { body(); return Task.CompletedTask; });
public void TestAsync(string name, Func<Task> body) => Run(name, body);
private void Run(string name, Func<Task> body)
{
try
{
body().GetAwaiter().GetResult();
_passed++;
Console.WriteLine($" OK {name}");
}
catch (AssertionException ex)
{
_failed++;
Console.WriteLine($" FOUT {name}");
Console.WriteLine($" verwacht: {ex.Message}");
}
catch (Exception ex)
{
_failed++;
Console.WriteLine($" FOUT {name}");
Console.WriteLine($" {ex.GetType().Name}: {ex.Message}");
}
}
public int Summary()
{
Console.WriteLine();
Console.WriteLine(_failed == 0
? $"Alle {_passed} controles geslaagd."
: $"{_passed} geslaagd, {_failed} mislukt.");
return _failed == 0 ? 0 : 1;
}
}