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

140 lines
8.0 KiB
C#

using MijnBesluit.Application.Models;
using MijnBesluit.Application.Services;
using MijnBesluit.Domain.Entities;
using MijnBesluit.Domain.Enums;
namespace MijnBesluit.SelfCheck;
/// <summary>
/// De versiehistorie uit de database: wie hem mag beheren en wat een gebruiker te zien krijgt.
///
/// De afspraak is eenvoudig: één bron voor site en app. Een gebruiker leest alleen de zichtbare
/// regels, nieuwste dag bovenaan; alleen een beheerder maakt, wijzigt, verbergt of verwijdert.
/// </summary>
public static class VersiehistorieChecks
{
private static async Task<User> BeheerderAsync(World w, string email = "beheer@example.nl")
{
var u = await w.MaakGebruikerAsync(email, "Beheer", "Er");
w.Store.Users.Single(x => x.Id == u.Id).IsBeheerder = true;
return u;
}
private static async Task<int> MaakAsync(World w, User beheerder, DateOnly datum, string kop, string? versie = null,
string tekst = "Uitleg.", bool zichtbaar = true)
{
var r = await w.Versiehistorie.MaakAsync(beheerder.Id, datum, versie, kop, tekst, zichtbaar);
if (!r.Succeeded) throw new AssertionException($"regel maken mislukte: {r.Error}");
return r.Value;
}
public static void Register(Runner runner)
{
runner.TestAsync("Versiehistorie: zonder regels is de lijst leeg", async () =>
{
var w = new World();
Assert.True((await w.Versiehistorie.ZichtbaarAsync()).Count == 0, "een lege tabel geeft een lege lijst");
});
runner.TestAsync("Versiehistorie: alleen een beheerder maakt, wijzigt of verwijdert", async () =>
{
var w = new World();
var jan = await w.MaakGebruikerAsync("jan@example.nl", "Jan");
var beheer = await BeheerderAsync(w);
var dag = new DateOnly(2026, 3, 1);
var id = await MaakAsync(w, beheer, dag, "Eerste");
Assert.True(!(await w.Versiehistorie.MaakAsync(jan.Id, dag, null, "Tweede", "Uitleg")).Succeeded,
"een gewone gebruiker maakt geen regel");
Assert.True(!(await w.Versiehistorie.OverzichtAsync(jan.Id)).Succeeded, "en ziet het beheeroverzicht niet");
Assert.True(!(await w.Versiehistorie.WijzigAsync(jan.Id, id, dag, null, "Anders", "Uitleg", true)).Succeeded,
"en wijzigt niets");
Assert.True(!(await w.Versiehistorie.VerwijderAsync(jan.Id, id)).Succeeded, "en verwijdert niets");
Assert.True((await w.Versiehistorie.ZichtbaarAsync()).Single().Kop == "Eerste", "de regel is onaangetast");
});
runner.TestAsync("Versiehistorie: nieuwste dag bovenaan, binnen een dag in invoervolgorde", async () =>
{
var w = new World();
var beheer = await BeheerderAsync(w);
await MaakAsync(w, beheer, new DateOnly(2026, 2, 1), "Oud, eerste");
await MaakAsync(w, beheer, new DateOnly(2026, 4, 1), "Nieuw, eerste", "1.1");
await MaakAsync(w, beheer, new DateOnly(2026, 2, 1), "Oud, tweede");
await MaakAsync(w, beheer, new DateOnly(2026, 4, 1), "Nieuw, tweede", "1.1");
var lijst = await w.Versiehistorie.ZichtbaarAsync();
Assert.True(lijst.Select(r => r.Kop).SequenceEqual(
new[] { "Nieuw, eerste", "Nieuw, tweede", "Oud, eerste", "Oud, tweede" }),
"de nieuwste dag staat bovenaan en binnen een dag de volgorde van invoer");
Assert.True(lijst[0].Datum == "2026-04-01" && lijst[0].Versie == "1.1", "datum als jjjj-mm-dd, met versie");
Assert.True(lijst[2].Versie is null, "een regel zonder versie heeft er geen");
});
runner.TestAsync("Versiehistorie: een verborgen regel ziet alleen de beheerder", async () =>
{
var w = new World();
var beheer = await BeheerderAsync(w);
var dag = new DateOnly(2026, 3, 1);
await MaakAsync(w, beheer, dag, "Zichtbaar");
var verborgen = await MaakAsync(w, beheer, dag, "Nog niet", zichtbaar: false);
Assert.True((await w.Versiehistorie.ZichtbaarAsync()).Select(r => r.Kop).SequenceEqual(new[] { "Zichtbaar" }),
"gebruikers zien de verborgen regel niet");
var overzicht = (await w.Versiehistorie.OverzichtAsync(beheer.Id)).Value!;
Assert.True(overzicht.Count == 2, "de beheerder ziet beide");
var w2 = await w.Versiehistorie.WijzigAsync(beheer.Id, verborgen, dag, null, "Nog niet", "Uitleg.", true);
Assert.True(w2.Succeeded, "tonen lukt");
Assert.True((await w.Versiehistorie.ZichtbaarAsync()).Count == 2, "daarna staat hij er ook voor gebruikers");
});
runner.TestAsync("Versiehistorie: wat niet klopt wordt geweigerd", async () =>
{
var w = new World();
var beheer = await BeheerderAsync(w);
var dag = new DateOnly(2026, 3, 1);
Assert.True(!(await w.Versiehistorie.MaakAsync(beheer.Id, dag, null, " ", "Uitleg")).Succeeded, "een lege kop mag niet");
Assert.True(!(await w.Versiehistorie.MaakAsync(beheer.Id, dag, null, "Kop", "")).Succeeded, "een lege uitleg mag niet");
Assert.True(!(await w.Versiehistorie.MaakAsync(beheer.Id, dag, null,
new string('k', VersiehistorieService.MaxKop + 1), "Uitleg")).Succeeded, "een te lange kop mag niet");
Assert.True(!(await w.Versiehistorie.MaakAsync(beheer.Id, dag, null, "Kop",
new string('t', VersiehistorieService.MaxTekst + 1))).Succeeded, "een te lange uitleg mag niet");
Assert.True(!(await w.Versiehistorie.MaakAsync(beheer.Id, dag, new string('1', VersiehistorieService.MaxVersie + 1),
"Kop", "Uitleg")).Succeeded, "een te lang versienummer mag niet");
Assert.True(!(await w.Versiehistorie.MaakAsync(beheer.Id, new DateOnly(2090, 1, 1), null, "Kop", "Uitleg")).Succeeded,
"een datum ver in de toekomst is een typefout");
Assert.True(!(await w.Versiehistorie.MaakAsync(beheer.Id, new DateOnly(1999, 1, 1), null, "Kop", "Uitleg")).Succeeded,
"een datum van lang geleden ook");
await MaakAsync(w, beheer, dag, "Dubbel");
Assert.True(!(await w.Versiehistorie.MaakAsync(beheer.Id, dag, null, "dubbel", "Uitleg")).Succeeded,
"dezelfde kop op dezelfde dag staat er al");
Assert.True((await w.Versiehistorie.MaakAsync(beheer.Id, dag.AddDays(1), null, "Dubbel", "Uitleg")).Succeeded,
"op een andere dag mag dezelfde kop wel");
});
runner.TestAsync("Versiehistorie: wijzigen en verwijderen, en het blijft vastgelegd", async () =>
{
var w = new World();
var beheer = await BeheerderAsync(w);
var dag = new DateOnly(2026, 3, 1);
var id = await MaakAsync(w, beheer, dag, "Eerste", tekst: "Oude uitleg");
Assert.True((await w.Versiehistorie.WijzigAsync(beheer.Id, id, dag, " 1.2 ", " Nieuwe kop ", " Nieuwe uitleg ", true)).Succeeded,
"wijzigen lukt");
var regel = (await w.Versiehistorie.ZichtbaarAsync()).Single();
Assert.True(regel.Kop == "Nieuwe kop" && regel.Tekst == "Nieuwe uitleg" && regel.Versie == "1.2",
"spaties eromheen verdwijnen");
Assert.True(!(await w.Versiehistorie.WijzigAsync(beheer.Id, 9999, dag, null, "Kop", "Uitleg", true)).Succeeded,
"een regel die er niet is, is niet te wijzigen");
Assert.True((await w.Versiehistorie.VerwijderAsync(beheer.Id, id)).Succeeded, "verwijderen lukt");
Assert.True((await w.Versiehistorie.ZichtbaarAsync()).Count == 0, "en dan is hij weg");
var acties = w.Store.Audit.Count(a => a.Action == AuditAction.VersiehistorieBeheerd);
Assert.True(acties == 3, "maken, wijzigen en verwijderen staan alle drie in het logboek");
});
}
}