CI gate (authoritative): generalize the dangling-reference test in StamdataValidationTests
into a declared, extensible reference list ("every declared reference into a stamdata key
resolves against the currently-valid stamdata"), starting with Diploma.Opleiding →
professions.program. Removing/renaming a referenced program, or expiring it while current
data still references it, fails the PR build (ADR-0004). Editor (fast feedback): confirm
before delete (warns a referenced row fails CI) and, for temporal tables, a "Sluiten per
vandaag" action that closes validity (geldigTot) — steering to expire over hard delete.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
88 lines
3.5 KiB
C#
88 lines
3.5 KiB
C#
using BigRegister.Api.Data;
|
|
using BigRegister.Domain.Diplomas;
|
|
using BigRegister.Stamdata;
|
|
|
|
namespace BigRegister.Tests;
|
|
|
|
/// <summary>
|
|
/// The compile-time gate for business-editable stamdata (ADR-0004). The C# compiler
|
|
/// already catches shape/type mistakes; these tests catch the referential integrity it
|
|
/// can't, so a bad config edit fails the build instead of reaching production.
|
|
/// </summary>
|
|
public class StamdataValidationTests
|
|
{
|
|
/// Declared references INTO stamdata keys — the FK-like invariants the build gate enforces
|
|
/// (WP-48). Add an entry when a consumer starts depending on a stamdata key; the gate then
|
|
/// fails a delete/rename/expire that orphans it. Resolvers use the "valid today" views, so
|
|
/// expiring a row (geldigTot in the past) that current data still references also fails —
|
|
/// which steers the editor toward closing validity only once nothing current relies on it.
|
|
private sealed record StamdataRef(string Description, IEnumerable<string> Keys, Func<string, bool> Resolves);
|
|
|
|
private static readonly IReadOnlyList<StamdataRef> References = new[]
|
|
{
|
|
new StamdataRef(
|
|
"Diploma.Opleiding → professions.program (valid today)",
|
|
SeedData.Diplomas.Select(d => d.Opleiding),
|
|
key => Professions.ByProgram.ContainsKey(key)),
|
|
};
|
|
|
|
[Fact]
|
|
public void Every_declared_reference_into_stamdata_resolves()
|
|
{
|
|
// The dangling-reference guard (generalized): a referenced key with no (currently valid)
|
|
// stamdata row would silently break its consumer. Fail the build instead of prod.
|
|
foreach (var r in References)
|
|
foreach (var key in r.Keys)
|
|
Assert.True(r.Resolves(key),
|
|
$"Dangling stamdata reference [{r.Description}]: '{key}' no longer resolves — " +
|
|
"deleting or expiring the referenced row would break it.");
|
|
}
|
|
|
|
[Fact]
|
|
public void Profession_map_has_no_blank_programs_or_professions()
|
|
{
|
|
Assert.All(Professions.ByProgram, kv =>
|
|
{
|
|
Assert.False(string.IsNullOrWhiteSpace(kv.Key), "A profession-map program key is blank.");
|
|
Assert.False(string.IsNullOrWhiteSpace(kv.Value), $"Program '{kv.Key}' maps to a blank profession.");
|
|
});
|
|
}
|
|
|
|
[Fact]
|
|
public void Manual_professions_are_non_empty_and_distinct()
|
|
{
|
|
var professions = DiplomaRules.ManualProfessions();
|
|
Assert.NotEmpty(professions);
|
|
Assert.Equal(professions.Count, professions.Distinct().Count());
|
|
}
|
|
|
|
[Fact]
|
|
public void Every_policy_question_has_an_id_and_wording()
|
|
{
|
|
Assert.All(PolicyQuestions.All, q =>
|
|
{
|
|
Assert.False(string.IsNullOrWhiteSpace(q.Id), "A policy question has a blank id.");
|
|
Assert.False(string.IsNullOrWhiteSpace(q.Vraag), $"Policy question '{q.Id}' has blank wording.");
|
|
});
|
|
}
|
|
|
|
[Fact]
|
|
public void Manual_question_set_has_distinct_ids() // no field is asked twice
|
|
{
|
|
var ids = PolicyQuestions.ManualSet.Select(q => q.Id).ToList();
|
|
Assert.Equal(ids.Count, ids.Distinct().Count());
|
|
}
|
|
|
|
// The GENERIC gate (ADR-0004): every table registered in the catalog is validated the
|
|
// same way — its JSON deserializes into its typed record, keys are non-blank and don't
|
|
// overlap in time, and valid-time windows are well-formed. A new stamdata type is covered
|
|
// the moment it's added to StamdataCatalog; no new test needed. A bad edit fails the build.
|
|
[Fact]
|
|
public void Every_catalog_table_is_valid()
|
|
{
|
|
foreach (var table in StamdataCatalog.All)
|
|
Assert.True(table.Validate().Count == 0,
|
|
$"Stamdata table '{table.Id}' has problems: {string.Join("; ", table.Validate())}");
|
|
}
|
|
}
|