Eerste commit

This commit is contained in:
jf
2026-09-27 14:21:31 +00:00
commit de4ff128cb
295 changed files with 47374 additions and 0 deletions
@@ -0,0 +1,177 @@
using System.Net;
using System.Text.Json;
using MijnBesluit.Protocol;
using MijnBesluit.Server;
namespace MijnBesluit.SelfCheck;
/// <summary>
/// Loopt de hele keten door: app -> socket -> dispatcher -> Application-laag -> opslag en terug.
/// Alleen de database is hier vervangen door opslag in het geheugen.
/// </summary>
public static class EndToEndChecks
{
public static void Register(Runner runner)
{
runner.TestAsync("Van app tot kluis: registreren, familie, uitnodigen, bericht", async () =>
{
var wereld = new World();
var sessies = new SessionStore(wereld.Clock);
var dispatcher = new SocketDispatcher(sessies, work => work(new RequestScope
{
Accounts = wereld.Accounts,
Families = wereld.Families,
Vault = wereld.Vault,
Messages = wereld.Messages,
Legacy = wereld.Legacy,
Overzicht = wereld.Overzicht,
Samenvoegen = wereld.Samenvoegen
}));
await using var server = new SocketListener(
new SocketListenerOptions { Address = IPAddress.Loopback, Port = 0 },
dispatcher.HandleAsync);
server.Start();
await using var app = new MijnBesluitSocketClient("127.0.0.1", server.Port, useTls: false);
// Zonder token mag er niets.
var zonderToken = await app.SendAsync(SocketActions.VaultList);
Assert.True(zonderToken.Status == "Error" && zonderToken.Message!.Contains("Log opnieuw in"),
"zonder geldige sessie wordt een verzoek geweigerd");
// Registreren gaat in twee stappen: code aanvragen, code invullen.
var codeAanvraag = await app.SendAsync(SocketActions.RequestRegisterCode, new
{
email = "jan@example.nl",
firstName = "Jan",
lastName = "Jansen"
});
Assert.True(codeAanvraag.Status == "Ok", "de registratiecode wordt verstuurd");
Assert.True(!JsonSerializer.Serialize(codeAanvraag.Payload, SocketFraming.Json).Contains("jan@example.nl"),
"het antwoord toont het adres alleen afgeschermd");
var registratie = await app.SendAsync(SocketActions.Register, new
{
email = "jan@example.nl",
code = wereld.LaatsteCodeUitMail("jan@example.nl")
});
Assert.True(registratie.Status == "Ok", "registreren lukt met de code uit de e-mail");
app.Token = Field(registratie, "token");
Assert.True(!string.IsNullOrEmpty(app.Token), "er komt een token terug");
// Kluisitem opslaan en weer openen, helemaal via de socket.
var opslaan = await app.SendAsync(SocketActions.VaultSave, new
{
category = 3, // Wachtwoord
title = "Bank",
payload = new { website = "bank.nl", username = "jan", secret = "zeergeheim" },
releasePolicy = 1, // Na overlijden
releaseAt = (DateTime?)null
});
Assert.True(opslaan.Status == "Ok", "kluisitem opslaan lukt");
var lijst = await app.SendAsync(SocketActions.VaultList);
Assert.True(lijst.Status == "Ok", "de kluislijst komt terug");
Assert.True(!JsonSerializer.Serialize(lijst.Payload, SocketFraming.Json).Contains("zeergeheim"),
"de lijst bevat geen geheimen, alleen titels");
var itemId = long.Parse(Field(opslaan, "itemId")!);
var openen = await app.SendAsync(SocketActions.VaultOpen, new { id = itemId });
Assert.True(JsonSerializer.Serialize(openen.Payload, SocketFraming.Json).Contains("zeergeheim"),
"openen geeft de inhoud terug aan de eigenaar");
// Familie uitbreiden en uitnodigen.
var familie = wereld.Store.Families.Single();
var toevoegen = await app.SendAsync(SocketActions.AddMember, new
{
familyId = familie.Id,
firstName = "Eva",
lastName = "Jansen",
email = "eva@example.nl",
relation = 2, // Kind
relationLabel = (string?)null
});
Assert.True(toevoegen.Status == "Ok", "familielid toevoegen lukt");
var evaId = long.Parse(Field(toevoegen, "persoonId")!);
var uitnodigen = await app.SendAsync(SocketActions.Invite, new { familyId = familie.Id, personId = evaId });
Assert.True(uitnodigen.Status == "Ok", "uitnodigen lukt");
Assert.True(!JsonSerializer.Serialize(uitnodigen.Payload, SocketFraming.Json)
.Contains(wereld.Store.Invitations.Single().Email),
"het antwoord bevat geen uitnodigingstoken");
// Eva neemt de uitnodiging aan vanaf haar eigen toestel.
var mail = wereld.Email.Sent.Last(m => m.To == "eva@example.nl");
var token = mail.Body.Split("/uitnodiging/")[1].Split('"')[0];
await using var appVanEva = new MijnBesluitSocketClient("127.0.0.1", server.Port, useTls: false);
var accepteren = await appVanEva.SendAsync(SocketActions.AcceptInvitation, new { token });
Assert.True(accepteren.Status == "Ok", "Eva kan de uitnodiging aannemen via de app");
appVanEva.Token = Field(accepteren, "token");
// Jan ziet nu dat het profiel geclaimd is.
var leden = await app.SendAsync(SocketActions.ListMembers, new { familyId = familie.Id });
var ledenJson = JsonSerializer.Serialize(leden.Payload, SocketFraming.Json);
Assert.True(ledenJson.Contains("\"state\":2"), "Jan ziet het profiel van Eva als geclaimd");
// Jan zet een bericht klaar voor Eva, te bezorgen na zijn overlijden.
var bericht = await app.SendAsync(SocketActions.MessageCompose, new
{
toPersonId = evaId,
subject = "Voor later",
text = "Ik ben trots op je.",
deliverAt = (DateTime?)null,
afterDeath = true,
kind = 0,
vaultFileId = (long?)null
});
Assert.True(bericht.Status == "Ok", "bericht klaarzetten lukt");
var inboxVanEva = await appVanEva.SendAsync(SocketActions.Inbox);
Assert.True(JsonSerializer.Serialize(inboxVanEva.Payload, SocketFraming.Json) == "[]",
"Eva ziet het bericht nog niet");
// Uitloggen maakt het token onbruikbaar.
await app.SendAsync(SocketActions.SignOut);
var naUitloggen = await app.SendAsync(SocketActions.VaultList);
Assert.True(naUitloggen.Status == "Error", "na uitloggen werkt het token niet meer");
});
runner.TestAsync("Verlopen sessie wordt geweigerd", async () =>
{
var wereld = new World();
var sessies = new SessionStore(wereld.Clock, TimeSpan.FromHours(1));
var dispatcher = new SocketDispatcher(sessies, work => work(new RequestScope
{
Accounts = wereld.Accounts, Families = wereld.Families, Vault = wereld.Vault,
Messages = wereld.Messages, Legacy = wereld.Legacy, Overzicht = wereld.Overzicht
}));
await using var server = new SocketListener(
new SocketListenerOptions { Address = IPAddress.Loopback, Port = 0 }, dispatcher.HandleAsync);
server.Start();
await using var app = new MijnBesluitSocketClient("127.0.0.1", server.Port, useTls: false);
await app.SendAsync(SocketActions.RequestRegisterCode,
new { email = "a@example.nl", firstName = "A", lastName = "A" });
var reg = await app.SendAsync(SocketActions.Register,
new { email = "a@example.nl", code = wereld.LaatsteCodeUitMail("a@example.nl") });
app.Token = Field(reg, "token");
Assert.True((await app.SendAsync(SocketActions.VaultList)).Status == "Ok", "meteen na inloggen werkt het");
wereld.Clock.Advance(TimeSpan.FromHours(2));
Assert.True((await app.SendAsync(SocketActions.VaultList)).Status == "Error", "na twee uur is de sessie verlopen");
});
}
/// <summary>Haalt één veld uit de payload van een antwoord.</summary>
private static string? Field(SocketResponse response, string name)
{
var json = JsonSerializer.Serialize(response.Payload, SocketFraming.Json);
using var doc = JsonDocument.Parse(json);
return doc.RootElement.TryGetProperty(name, out var value)
? (value.ValueKind == JsonValueKind.String ? value.GetString() : value.ToString())
: null;
}
}