refactor: strip WP-/RB- ticket refs from backend (RD-19)
The backend half of the sweep RD-18 did for the front end. git blame holds the provenance and stays correct when the code moves; the comment names a closed ticket and tells the reader nothing the sentence around it does not. public/letter.css and LetterHtml.golden.html change together, because the renderer inlines the CSS and the golden file snapshots the result. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -14,7 +14,7 @@ public class AanvraagTests(TestWebApplicationFactory factory) : IClassFixture<Te
|
||||
|
||||
private async Task<AanvraagDetailDto> Create(string type = "registratie")
|
||||
{
|
||||
// WP-35: one Concept per type is now server-enforced, and these tests share one DB
|
||||
// One Concept per type is now server-enforced, and these tests share one DB
|
||||
// (IClassFixture). Clear any leftover Concept so each test starts from a clean slate.
|
||||
var existing = await List();
|
||||
Assert.NotNull(existing);
|
||||
@@ -103,7 +103,7 @@ public class AanvraagTests(TestWebApplicationFactory factory) : IClassFixture<Te
|
||||
Assert.Equal(HttpStatusCode.Conflict, again.StatusCode);
|
||||
}
|
||||
|
||||
// --- WP-35: one Concept per case type (server-enforced) ---
|
||||
// --- One Concept per case type (server-enforced) ---
|
||||
|
||||
[Fact]
|
||||
public async Task Creating_a_second_concept_of_the_same_type_conflicts()
|
||||
@@ -146,7 +146,7 @@ public class AanvraagTests(TestWebApplicationFactory factory) : IClassFixture<Te
|
||||
Assert.Equal(HttpStatusCode.Conflict, (await _client.DeleteAsync($"/api/v1/aanvragen/{a.Id}")).StatusCode);
|
||||
}
|
||||
|
||||
// --- WP-53: citizen-scoping — GET /aanvragen must never leak across identities. ---
|
||||
// --- Citizen-scoping — GET /aanvragen must never leak across identities. ---
|
||||
|
||||
[Fact]
|
||||
public async Task Applications_are_scoped_to_the_caller_bsn()
|
||||
@@ -181,7 +181,7 @@ public class AanvraagTests(TestWebApplicationFactory factory) : IClassFixture<Te
|
||||
}
|
||||
}
|
||||
|
||||
// --- WP-68 (F1): a citizen may only reference their own uploads — submit/draft-sync must
|
||||
// --- A citizen may only reference their own uploads — submit/draft-sync must
|
||||
// reject a foreign documentId rather than silently attaching it. ---
|
||||
|
||||
private static async Task<UploadResponse> UploadAs(HttpClient client, string owner, string localId)
|
||||
|
||||
@@ -9,8 +9,8 @@ using BigRegister.Tests.Builders;
|
||||
namespace BigRegister.Tests.Acceptance;
|
||||
|
||||
/// <summary>
|
||||
/// Behaviour-level tests for the besluit lifecycle (WP-65b/66/68), built through the
|
||||
/// <see cref="Given"/> type-state builder (WP-70) rather than the full wizard/upload dance
|
||||
/// Behaviour-level tests for the besluit lifecycle, built through the
|
||||
/// <see cref="Given"/> type-state builder rather than the full wizard/upload dance
|
||||
/// <see cref="BeoordelingTests"/> uses — a fixture that's already Submitted (or already
|
||||
/// Decided) is a two-line Given, not fifteen. Each test persists its own Given-built
|
||||
/// <see cref="Aanvraag"/> straight into the isolated per-class SQLite file (no HTTP round trip
|
||||
|
||||
@@ -8,8 +8,8 @@ using BigRegister.Tests.Builders;
|
||||
namespace BigRegister.Tests.Acceptance;
|
||||
|
||||
/// <summary>
|
||||
/// Behaviour-level tests for the scholing-threshold enforcement (WP-69) over
|
||||
/// <c>POST /aanvragen/{id}/submit</c> (the wizard's real path — WP-72 deleted the legacy
|
||||
/// Behaviour-level tests for the scholing-threshold enforcement over
|
||||
/// <c>POST /aanvragen/{id}/submit</c> (the wizard's real path — a later change deleted the legacy
|
||||
/// <c>POST /intakes</c> endpoint this once also covered). Built through the <see
|
||||
/// cref="Given"/> type-state builder, mirroring <see cref="BesluitLifecycleTests"/> rather
|
||||
/// than the full wizard/upload dance — the builder's default owner IS <see
|
||||
|
||||
@@ -5,7 +5,7 @@ using BigRegister.Api.Contracts;
|
||||
namespace BigRegister.Tests.Acceptance;
|
||||
|
||||
/// <summary>
|
||||
/// Contract test for the FE/BE seam on phone-number stripping (WP-75). Both sides share
|
||||
/// Contract test for the FE/BE seam on phone-number stripping. Both sides share
|
||||
/// the same format regex (<c>^0\d{9}$</c>) but, until this test, diverged on what they
|
||||
/// strip before checking it: the FE's <c>parseTelefoonnummer</c>
|
||||
/// (registratie/domain/value-objects/telefoonnummer.ts) also drops parentheses and maps a
|
||||
|
||||
@@ -6,7 +6,7 @@ using Microsoft.AspNetCore.Mvc.Testing;
|
||||
|
||||
namespace BigRegister.Tests;
|
||||
|
||||
/// WP-36: admin cross-owner case list + admin delete, gated by `cases:manage`.
|
||||
/// Admin cross-owner case list + admin delete, gated by `cases:manage`.
|
||||
public class AdminCasesTests(TestWebApplicationFactory factory) : IClassFixture<TestWebApplicationFactory>
|
||||
{
|
||||
private readonly HttpClient _client = factory.CreateClient();
|
||||
@@ -35,7 +35,7 @@ public class AdminCasesTests(TestWebApplicationFactory factory) : IClassFixture<
|
||||
list.EnsureSuccessStatusCode();
|
||||
var cases = (await list.Content.ReadFromJsonAsync<List<AanvraagSummaryDto>>())!;
|
||||
var mine = cases.Single(x => x.Id == a.Id);
|
||||
// RB-03/BIO-003: the owner is carried, but masked — it is a BSN, and this list is
|
||||
// BIO-003: the owner is carried, but masked — it is a BSN, and this list is
|
||||
// read by someone who is not the subject.
|
||||
Assert.Equal("******782", mine.Owner);
|
||||
Assert.DoesNotContain(DocumentStore.DemoOwner, mine.Owner);
|
||||
|
||||
@@ -9,7 +9,7 @@ using Microsoft.AspNetCore.Mvc.Testing;
|
||||
|
||||
namespace BigRegister.Tests;
|
||||
|
||||
/// WP-41: the persisted authz/PII-reveal audit trail is queryable, data-minimised (no PII).
|
||||
/// The persisted authz/PII-reveal audit trail is queryable, data-minimised (no PII).
|
||||
public class AuthzAuditTests(TestWebApplicationFactory factory) : IClassFixture<TestWebApplicationFactory>
|
||||
{
|
||||
private readonly HttpClient _client = factory.CreateClient();
|
||||
@@ -59,7 +59,7 @@ public class AuthzAuditTests(TestWebApplicationFactory factory) : IClassFixture<
|
||||
Assert.Contains(await AuditLog(), e => e.Action == "brief:reveal-bignummer");
|
||||
}
|
||||
|
||||
/// RB-07/BIO-007: the trail used to record only denials, so `/beheer/audit` could answer
|
||||
/// BIO-007: the trail used to record only denials, so `/beheer/audit` could answer
|
||||
/// "who was turned away" but not "who changed this" — for a register whose integrity is the
|
||||
/// product, the wrong half. Every gate now audits the real decision.
|
||||
[Fact]
|
||||
@@ -69,10 +69,10 @@ public class AuthzAuditTests(TestWebApplicationFactory factory) : IClassFixture<
|
||||
Assert.Contains(await AuditLog(), e => e.Action == "cases:manage" && e.Decision == "allow" && e.Role == "Admin");
|
||||
}
|
||||
|
||||
/// RB-08/BIO-003: the admin upload delete used to be gated by a standalone X-Admin
|
||||
/// BIO-003: the admin upload delete used to be gated by a standalone X-Admin
|
||||
/// header, outside Authz and writing no AuthzAuditStore row at all. Routing it through
|
||||
/// CasesAdmin (cases:manage) gives it the same allow-path row every other admin-cases
|
||||
/// endpoint gets, for free, per RB-07. `CasesAdmin` audits under a fixed "cases"
|
||||
/// endpoint gets, for free. `CasesAdmin` audits under a fixed "cases"
|
||||
/// resource shared with the other admin-cases endpoints, so this asserts a **count**
|
||||
/// increase — reading the store directly (not via `GET /admin/audit`, itself a
|
||||
/// `CasesAdmin` endpoint that would write its own row and confound the count) —
|
||||
@@ -120,7 +120,7 @@ public class AuthzAuditTests(TestWebApplicationFactory factory) : IClassFixture<
|
||||
Assert.Contains(await AuditLog(), e => e.Action == "brief:submit" && e.Decision == "deny");
|
||||
}
|
||||
|
||||
/// RB-02/BIO-008: the schema test below asserts on **column names**, so a BSN inside a
|
||||
/// BIO-008: the schema test below asserts on **column names**, so a BSN inside a
|
||||
/// column called `Resource` was invisible to it — and one was there, concatenated as
|
||||
/// `"brief/" + Bsn`. This asserts on the stored **values** instead. Four documents
|
||||
/// promise this trail holds no PII; this is the test that makes the promise checkable.
|
||||
|
||||
@@ -82,7 +82,7 @@ public class AuthzTests
|
||||
Assert.False(Authz.Decisions(Approver, "draft", DrafterId).CanRevealBigNummer);
|
||||
}
|
||||
|
||||
// --- CanBeoordelen (WP-62) --------------------------------------------------------------
|
||||
// --- CanBeoordelen --------------------------------------------------------------
|
||||
|
||||
[Fact]
|
||||
public void CanBeoordelen_true_for_a_medewerker_with_the_behandelaar_rol()
|
||||
|
||||
@@ -7,8 +7,8 @@ using Microsoft.AspNetCore.Mvc.Testing;
|
||||
|
||||
namespace BigRegister.Tests;
|
||||
|
||||
/// WP-65 (read side): one aanvraag's case-treatment detail, gated by the same medewerker
|
||||
/// capability (`CanBeoordelen`, WP-62) as the werkvoorraad list (WP-64).
|
||||
/// One aanvraag's case-treatment detail (read side), gated by the same medewerker
|
||||
/// capability (`CanBeoordelen`) as the werkvoorraad list.
|
||||
public class BeoordelingTests(TestWebApplicationFactory factory) : IClassFixture<TestWebApplicationFactory>
|
||||
{
|
||||
private readonly HttpClient _client = factory.CreateClient();
|
||||
@@ -152,7 +152,7 @@ public class BeoordelingTests(TestWebApplicationFactory factory) : IClassFixture
|
||||
Assert.Equal("Goedgekeurd", view.Aanvraag.Status.Tag);
|
||||
Assert.False(view.Decisions.CanBesluiten); // terminal — no further decision allowed
|
||||
|
||||
// RB-07/BIO-007: the gate records that a behandelaar was allowed to act; this records
|
||||
// BIO-007: the gate records that a behandelaar was allowed to act; this records
|
||||
// what they decided, which is the question /beheer/audit exists to answer.
|
||||
Assert.Contains(AuthzAuditStore.List(), e =>
|
||||
e.Action == "aanvraag:besluit" && e.Decision == "allow" &&
|
||||
@@ -222,7 +222,7 @@ public class BeoordelingTests(TestWebApplicationFactory factory) : IClassFixture
|
||||
}
|
||||
}
|
||||
|
||||
// WP-68 (F2): the transition-legality check now runs inside RecordBesluit's write lock, so
|
||||
// The transition-legality check now runs inside RecordBesluit's write lock, so
|
||||
// two besluiten racing on the same still-open aanvraag can't both pass the check before
|
||||
// either writes — exactly one commits, the other sees the now-terminal status.
|
||||
[Fact]
|
||||
|
||||
@@ -28,7 +28,7 @@ public class BriefEndpointTests(TestWebApplicationFactory factory) : IClassFixtu
|
||||
return new SaveBriefRequest(sections);
|
||||
}
|
||||
|
||||
/// RB-23: `GET /brief` no longer seeds a brief on first call, so every test that
|
||||
/// `GET /brief` no longer seeds a brief on first call, so every test that
|
||||
/// needs one present creates it explicitly through `POST /brief/reset`
|
||||
/// (`BriefStore.ResetAndCreate`) — the same command the "start over" affordance uses.
|
||||
private async Task<BriefDto> SeedBrief()
|
||||
@@ -48,7 +48,7 @@ public class BriefEndpointTests(TestWebApplicationFactory factory) : IClassFixtu
|
||||
return req;
|
||||
}
|
||||
|
||||
// --- RB-23/CQ-007: GET /brief is a pure query — it must not create a row. ---
|
||||
// --- CQ-007: GET /brief is a pure query — it must not create a row. ---
|
||||
|
||||
[Fact]
|
||||
public async Task Get_returns_404_and_writes_no_row_when_no_brief_exists_for_the_owner()
|
||||
@@ -181,8 +181,8 @@ public class BriefEndpointTests(TestWebApplicationFactory factory) : IClassFixtu
|
||||
Assert.NotNull(submitted);
|
||||
Assert.Equal("submitted", submitted.Brief.Status.Tag);
|
||||
|
||||
// RB-07/BIO-007: the allow side of the transition leaves a row, not just a log line.
|
||||
// Resource is the bare "brief" — never the owner's BSN (RB-02).
|
||||
// BIO-007: the allow side of the transition leaves a row, not just a log line.
|
||||
// Resource is the bare "brief" — never the owner's BSN.
|
||||
Assert.Contains(AuthzAuditStore.List(),
|
||||
e => e.Action == "brief:submit" && e.Decision == "allow" && e.Resource == "brief");
|
||||
}
|
||||
|
||||
@@ -14,9 +14,9 @@ public static class TestIdentities
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Type-state test-data builder for <see cref="Aanvraag"/> (WP-70; simplified at WP-73). "Build
|
||||
/// Type-state test-data builder for <see cref="Aanvraag"/>. "Build
|
||||
/// test data through the same door production code uses" — <see cref="Aanvraag"/> itself is now
|
||||
/// the closed Concept/Submitted/Decided union WP-73 introduced, so this builder no longer needs
|
||||
/// the closed Concept/Submitted/Decided union, so this builder no longer needs
|
||||
/// to mirror production's guards (step-index bounds, "Afwijzen needs a toelichting") by hand —
|
||||
/// it just calls the real nested constructors/required members, which enforce them. A call that
|
||||
/// would build an illegal Aanvraag (e.g. deciding a still-Concept aanvraag, or an Afwijzen with
|
||||
@@ -61,7 +61,7 @@ public sealed class ConceptAanvraag
|
||||
|
||||
/// Submits the draft — always assigns a Referentie AND SubmittedAt together (mirrors
|
||||
/// <c>ApplicationStore.Submit</c>), so a fixture built this way can never hit the
|
||||
/// null-forgiving derefs the pre-WP-73 flat Aanvraag needed (there's nothing to force any
|
||||
/// null-forgiving derefs the earlier flat Aanvraag needed (there's nothing to force any
|
||||
/// more: both are required, non-null members of <see cref="Aanvraag.Submitted"/>).
|
||||
public SubmittedAanvraag Submitted(bool autoApprovable = false) => new(_type, _owner, autoApprovable);
|
||||
|
||||
@@ -110,7 +110,7 @@ public sealed class SubmittedAanvraag
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>Records a behandelaar's decision. Unlike the pre-WP-73 builder, there is no
|
||||
/// <summary>Records a behandelaar's decision. Unlike the earlier builder, there is no
|
||||
/// hand-written toelichting guard mirroring <c>BeoordelingRules.RequiresToelichting</c> any
|
||||
/// more — <see cref="Aanvraag.Decided.Afgewezen"/>/<see cref="Aanvraag.Decided.MeerInfoGevraagd"/>
|
||||
/// simply have a `required string Toelichting` member; the null-coalescing throw below is the
|
||||
@@ -186,7 +186,7 @@ public sealed class SubmittedAanvraag
|
||||
/// (<see cref="BigRegister.Domain.Beoordeling.BeoordelingRules.CanDecide"/>); a fixture that
|
||||
/// needs a SECOND besluit (the MeerInfoGevraagd "still decidable" case) builds fresh from
|
||||
/// <see cref="Given.Concept"/> again, exactly as a real second request would. Just a one-line
|
||||
/// wrapper around the already-fully-built <see cref="Aanvraag.Decided"/> value — WP-73 moved
|
||||
/// wrapper around the already-fully-built <see cref="Aanvraag.Decided"/> value — construction moved
|
||||
/// all the actual construction (and its invariant enforcement) into
|
||||
/// <see cref="SubmittedAanvraag.Decided"/> itself, so there's nothing left for this type to do
|
||||
/// except keep <c>.Decided(...).Build()</c> a valid two-call chain for the existing test
|
||||
|
||||
@@ -4,9 +4,9 @@ namespace BigRegister.Tests.Domain;
|
||||
|
||||
public class ApplicationRuleTests
|
||||
{
|
||||
// WP-63: the published lifecycle (ADR-0002) must name exactly these five tags, in this
|
||||
// The published lifecycle (ADR-0002) must name exactly these five tags, in this
|
||||
// order — ToStatusDto's string literals must keep matching Enum.ToString(), and Ingediend/
|
||||
// MeerInfoGevraagd (unreachable until WP-65 adds the behandelaar transition) stay defined.
|
||||
// MeerInfoGevraagd (unreachable until a later change adds the behandelaar transition) stay defined.
|
||||
[Fact]
|
||||
public void AanvraagStatusTag_covers_the_published_lifecycle()
|
||||
{
|
||||
|
||||
@@ -16,7 +16,7 @@ public class BeoordelingRuleTests
|
||||
public void Only_open_statuses_are_decidable(AanvraagStatusTag tag, bool expected) =>
|
||||
Assert.Equal(expected, BeoordelingRules.CanDecide(tag));
|
||||
|
||||
// WP-68 (F6): the toelichting rule, moved here from an inline endpoint check.
|
||||
// The toelichting rule, moved here from an inline endpoint check.
|
||||
[Theory]
|
||||
[InlineData(Besluit.Goedkeuren, false)]
|
||||
[InlineData(Besluit.Afwijzen, true)]
|
||||
@@ -24,12 +24,12 @@ public class BeoordelingRuleTests
|
||||
public void Only_a_non_approval_requires_a_toelichting(Besluit besluit, bool expected) =>
|
||||
Assert.Equal(expected, BeoordelingRules.RequiresToelichting(besluit));
|
||||
|
||||
// WP-68 (T3): the transition table at the AGGREGATE level, not just against a bare tag —
|
||||
// The transition table at the AGGREGATE level, not just against a bare tag —
|
||||
// an Aanvraag whose BesluitStatus already records a terminal decision computes a terminal
|
||||
// StatusAt, and CanDecide refuses a further besluit regardless of which one. Pins the
|
||||
// domain statement "Afgewezen/Goedgekeurd → no further besluit" independent of the
|
||||
// endpoint's own (integration-level) Already_decided_case_rejects_a_further_besluit.
|
||||
// WP-70: built via Given, not a hand-rolled Aanvraag literal — Decided(Besluit.Afwijzen) with
|
||||
// Built via Given, not a hand-rolled Aanvraag literal — Decided(Besluit.Afwijzen) with
|
||||
// no toelichting simply couldn't compile as a fixture here.
|
||||
[Theory]
|
||||
[InlineData(Besluit.Goedkeuren)]
|
||||
|
||||
@@ -4,7 +4,7 @@ namespace BigRegister.Tests.Domain;
|
||||
|
||||
public class IntakeRuleTests
|
||||
{
|
||||
// The arguments ARE the Given (WP-69/bdd.mdx) — these degenerate to When/Then.
|
||||
// The arguments ARE the Given (bdd.mdx) — these degenerate to When/Then.
|
||||
|
||||
[Fact]
|
||||
public void Below_threshold_with_no_answer_is_incomplete() =>
|
||||
|
||||
@@ -87,7 +87,7 @@ public class EndpointTests(TestWebApplicationFactory factory) : IClassFixture<Te
|
||||
new { telefoon = "nope" });
|
||||
Assert.Equal(HttpStatusCode.UnprocessableEntity, res.StatusCode);
|
||||
// The Submit helper's rejection shape — was asserted through POST /registrations until
|
||||
// RB-06 deleted it; /change-requests is the other endpoint on the same helper.
|
||||
// that endpoint was deleted; /change-requests is the other endpoint on the same helper.
|
||||
var contentType = res.Content.Headers.ContentType;
|
||||
Assert.NotNull(contentType);
|
||||
Assert.Contains("application/problem+json", contentType.ToString());
|
||||
@@ -200,8 +200,8 @@ public class EndpointTests(TestWebApplicationFactory factory) : IClassFixture<Te
|
||||
public async Task User_delete_blocked_with_409_once_linked_to_submission()
|
||||
{
|
||||
var doc = await Upload(Guid.NewGuid().ToString());
|
||||
// Through the real submit path (RB-06 deleted POST /registrations, which was the only
|
||||
// other caller of DocumentStore.Link and had no ownership guard on it).
|
||||
// Through the real submit path (POST /registrations was the only other caller of
|
||||
// DocumentStore.Link and had no ownership guard on it; it has since been deleted).
|
||||
var created = await _client.PostAsJsonAsync("/api/v1/aanvragen", new { type = "registratie" });
|
||||
var aanvraag = (await created.Content.ReadFromJsonAsync<AanvraagDetailDto>())!;
|
||||
var submit = await _client.PostAsJsonAsync($"/api/v1/aanvragen/{aanvraag.Id}/submit",
|
||||
@@ -213,7 +213,7 @@ public class EndpointTests(TestWebApplicationFactory factory) : IClassFixture<Te
|
||||
[Fact]
|
||||
public async Task Admin_delete_requires_admin_role()
|
||||
{
|
||||
// RB-08: routed through CasesAdmin (cases:manage), like the other admin-cases
|
||||
// Routed through CasesAdmin (cases:manage), like the other admin-cases
|
||||
// endpoints, not the standalone X-Admin header this used to accept.
|
||||
var doc = await Upload(Guid.NewGuid().ToString());
|
||||
Assert.Equal(HttpStatusCode.Forbidden, (await _client.DeleteAsync($"/api/v1/admin/uploads/{doc.DocumentId}")).StatusCode);
|
||||
|
||||
@@ -6,7 +6,7 @@ using Microsoft.AspNetCore.Mvc.Testing;
|
||||
|
||||
namespace BigRegister.Tests;
|
||||
|
||||
/// WP-47: runtime feature flags — catalog in code, admin-toggled, server-enforced.
|
||||
/// Runtime feature flags — catalog in code, admin-toggled, server-enforced.
|
||||
public class FeatureFlagTests(TestWebApplicationFactory factory) : IClassFixture<TestWebApplicationFactory>
|
||||
{
|
||||
private readonly HttpClient _client = factory.CreateClient();
|
||||
|
||||
@@ -45,7 +45,7 @@ public class IdempotencyTests(TestWebApplicationFactory factory) : IClassFixture
|
||||
Assert.NotEqual(firstBody!.Referentie, secondBody!.Referentie);
|
||||
}
|
||||
|
||||
// RB-18/BIO-018: IdempotencyStore used to key on the raw client-supplied header alone, so
|
||||
// BIO-018: IdempotencyStore used to key on the raw client-supplied header alone, so
|
||||
// caller B replaying caller A's Idempotency-Key got caller A's cached reference back —
|
||||
// a cross-caller leak of a value caller B never submitted. The store now keys on
|
||||
// "{SubjectId}:{idemKey}", so the same header value from two different callers is two
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<!doctype html><html lang="nl"><head><meta charset="utf-8"><title>golden-brief-1</title><style>/* letter.css — the FE⇄BE letter-rendering CONTRACT (WP-24/WP-25).
|
||||
<!doctype html><html lang="nl"><head><meta charset="utf-8"><title>golden-brief-1</title><style>/* letter.css — the FE⇄BE letter-rendering CONTRACT.
|
||||
*
|
||||
* One stylesheet, two consumers: the FE letter canvas loads it via <link>
|
||||
* (index.html + Storybook preview-head), the backend HTML renderer (WP-25)
|
||||
* (index.html + Storybook preview-head), the backend HTML renderer
|
||||
* inlines this same file. Its class-parity test is the fence against drift.
|
||||
*
|
||||
* Class vocabulary: .letter, .letter__letterhead, .letter__body,
|
||||
|
||||
@@ -6,7 +6,7 @@ using BigRegister.Domain.Letters;
|
||||
namespace BigRegister.Tests;
|
||||
|
||||
/// <summary>
|
||||
/// WP-25's fence against drift between the backend renderer and the FE letter
|
||||
/// The fence against drift between the backend renderer and the FE letter
|
||||
/// canvas: a golden-file snapshot of a fixed brief + template, and a class-parity
|
||||
/// check that every `letter`-prefixed class the renderer emits exists in the
|
||||
/// shared `public/letter.css` contract. Neither test launches a browser.
|
||||
|
||||
@@ -6,7 +6,7 @@ using Microsoft.AspNetCore.Mvc.Testing;
|
||||
|
||||
namespace BigRegister.Tests;
|
||||
|
||||
/// WP-52: the inbound Notificaties (NRC) webhook — auth accept/reject + the audit trail it
|
||||
/// The inbound Notificaties (NRC) webhook — auth accept/reject + the audit trail it
|
||||
/// writes via AuthzAuditStore (no Principal exists for an NRC caller, so this doesn't go
|
||||
/// through the Principal-shaped AuditAuthz helper the user-facing endpoints use).
|
||||
public class NotificatieTests(TestWebApplicationFactory factory) : IClassFixture<TestWebApplicationFactory>
|
||||
|
||||
@@ -6,7 +6,7 @@ using BigRegister.Domain.Authorization;
|
||||
namespace BigRegister.Tests;
|
||||
|
||||
/// <summary>
|
||||
/// Exercises the OpenZaak document source against a stub HttpMessageHandler (WP-51): an
|
||||
/// Exercises the OpenZaak document source against a stub HttpMessageHandler: an
|
||||
/// upload registers a DRC enkelvoudiginformatieobject, and linking to a zaak POSTs a
|
||||
/// zaakinformatieobject per document once a zaak URL is known.
|
||||
/// </summary>
|
||||
@@ -48,7 +48,7 @@ public class OpenZaakDocumentSourceTests
|
||||
Assert.Equal("local-1", response.LocalId);
|
||||
Assert.NotEmpty(response.DocumentId);
|
||||
|
||||
// Registered locally too (dual-write, same reasoning as CreateZaak/WP-50) — content
|
||||
// Registered locally too (dual-write, same reasoning as CreateZaak) — content
|
||||
// preview/download keeps working regardless of Zgw:Enabled.
|
||||
var stored = DocumentStore.Get(response.DocumentId);
|
||||
Assert.NotNull(stored);
|
||||
@@ -59,7 +59,7 @@ public class OpenZaakDocumentSourceTests
|
||||
Assert.Contains("123443210", body); // bronorganisatie
|
||||
Assert.Contains("paspoort.pdf", body);
|
||||
Assert.Contains(Convert.ToBase64String("%PDF-1.4 fake"u8.ToArray()), body); // inhoud
|
||||
// WP-59: "identiteit" is mapped to "vertrouwelijk" in the confidentialiteit stamdata.
|
||||
// "identiteit" is mapped to "vertrouwelijk" in the confidentialiteit stamdata.
|
||||
Assert.Contains("\"vertrouwelijkheidaanduiding\":\"vertrouwelijk\"", body);
|
||||
}
|
||||
|
||||
@@ -78,7 +78,7 @@ public class OpenZaakDocumentSourceTests
|
||||
Assert.Contains("\"vertrouwelijkheidaanduiding\":\"openbaar\"", body);
|
||||
}
|
||||
|
||||
// WP-60: once DocumentStore.Add has committed, a ZGW-side failure (config gap or transport)
|
||||
// Once DocumentStore.Add has committed, a ZGW-side failure (config gap or transport)
|
||||
// no longer throws — the local document is authoritative and DrcUrl stays null (the same
|
||||
// detector LinkToZaak already skips on for pre-Zgw documents).
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ using Microsoft.AspNetCore.Mvc.Testing;
|
||||
namespace BigRegister.Tests;
|
||||
|
||||
/// <summary>
|
||||
/// WP-54: the one test that proves the BFF actually talks to a REAL OpenZaak — auth accepted,
|
||||
/// The one test that proves the BFF actually talks to a REAL OpenZaak — auth accepted,
|
||||
/// real response shapes, real pagination/zaaktype→aanvraag-type mapping — rather than the stub
|
||||
/// HttpMessageHandler every other Zgw test (<see cref="ZgwZaakMapperTests"/>,
|
||||
/// <see cref="OpenZaakZaakSourceTests"/>) uses. Requires the harness in <c>backend/openzaak/</c>
|
||||
@@ -36,7 +36,7 @@ public class OpenZaakIntegrationTests
|
||||
.UseSetting("Zgw:ClientId", "bigregister-test")
|
||||
.UseSetting("Zgw:Secret", "bigregister-test-secret")
|
||||
.UseSetting("Zgw:UserId", "bigregister-test")
|
||||
.UseSetting("Zgw:UserRepresentation", "WP-54 integration test")
|
||||
.UseSetting("Zgw:UserRepresentation", "OpenZaak integration test")
|
||||
.UseSetting("Zgw:ZaaktypeUrls:herregistratie", zaaktypeUrl));
|
||||
}
|
||||
|
||||
|
||||
@@ -182,7 +182,7 @@ public class OpenZaakZaakSourceTests
|
||||
Assert.Throws<InvalidOperationException>(() => source.CreateZaak(aanvraag, DateTimeOffset.UtcNow, caller));
|
||||
}
|
||||
|
||||
// --- WP-66: besluit write (a status transition on an existing zaak) --------------------
|
||||
// --- Besluit write (a status transition on an existing zaak) --------------------
|
||||
|
||||
[Fact]
|
||||
public void RecordBesluit_posts_the_last_statustype_with_besluit_and_toelichting()
|
||||
@@ -308,7 +308,7 @@ public class OpenZaakZaakSourceTests
|
||||
Assert.Throws<InvalidOperationException>(() => source.RecordBesluit(aanvraag, Besluit.Goedkeuren, null, DateTimeOffset.UtcNow, caller));
|
||||
}
|
||||
|
||||
// --- WP-60: bounded retry in ZgwHttpClient, exercised through the create-zaak write path ---
|
||||
// --- Bounded retry in ZgwHttpClient, exercised through the create-zaak write path ---
|
||||
|
||||
private static (ZgwOptions options, Aanvraag.Submitted aanvraag, CallerIdentity caller) CreateZaakFixture()
|
||||
{
|
||||
|
||||
@@ -7,7 +7,7 @@ using Microsoft.AspNetCore.Mvc.Testing;
|
||||
namespace BigRegister.Tests;
|
||||
|
||||
/// <summary>
|
||||
/// Org templates (WP-23): admin-only endpoints, draft→publish versioning, and the
|
||||
/// Org templates: admin-only endpoints, draft→publish versioning, and the
|
||||
/// sent-brief immutability invariant (pin at send, republish touches unsent only).
|
||||
/// Same reset discipline as BriefEndpointTests — the stores are process-global.
|
||||
/// </summary>
|
||||
@@ -58,7 +58,7 @@ public class OrgTemplateEndpointTests(TestWebApplicationFactory factory) : IClas
|
||||
public async Task Publish_increments_the_version()
|
||||
{
|
||||
ResetStores();
|
||||
// One unsent brief for this sub-org (RB-23: GET no longer seeds — create explicitly).
|
||||
// One unsent brief for this sub-org (GET no longer seeds — create explicitly).
|
||||
await _client.PostAsync("/api/v1/brief/reset", null);
|
||||
|
||||
var res = await _client.SendAsync(Req(HttpMethod.Post, $"/api/v1/admin/org-template/{Registers}/publish", role: "admin"));
|
||||
@@ -74,7 +74,7 @@ public class OrgTemplateEndpointTests(TestWebApplicationFactory factory) : IClas
|
||||
public async Task Publish_appends_to_the_version_history()
|
||||
{
|
||||
ResetStores();
|
||||
await _client.PostAsync("/api/v1/brief/reset", null); // RB-23: GET no longer seeds — create explicitly
|
||||
await _client.PostAsync("/api/v1/brief/reset", null); // GET no longer seeds — create explicitly
|
||||
|
||||
var res = await _client.SendAsync(Req(HttpMethod.Post, $"/api/v1/admin/org-template/{Registers}/publish", role: "admin"));
|
||||
res.EnsureSuccessStatusCode();
|
||||
@@ -87,7 +87,7 @@ public class OrgTemplateEndpointTests(TestWebApplicationFactory factory) : IClas
|
||||
public async Task Publish_counts_the_unsent_briefs_it_affects()
|
||||
{
|
||||
ResetStores();
|
||||
// One unsent brief for this sub-org (RB-23: GET no longer seeds — create explicitly).
|
||||
// One unsent brief for this sub-org (GET no longer seeds — create explicitly).
|
||||
await _client.PostAsync("/api/v1/brief/reset", null);
|
||||
|
||||
var res = await _client.SendAsync(Req(HttpMethod.Post, $"/api/v1/admin/org-template/{Registers}/publish", role: "admin"));
|
||||
@@ -154,7 +154,7 @@ public class OrgTemplateEndpointTests(TestWebApplicationFactory factory) : IClas
|
||||
private async Task WalkBriefToSentThenRepublish()
|
||||
{
|
||||
ResetStores();
|
||||
var resetRes = await _client.PostAsync("/api/v1/brief/reset", null); // RB-23: create explicitly
|
||||
var resetRes = await _client.PostAsync("/api/v1/brief/reset", null); // create explicitly
|
||||
var brief = (await resetRes.Content.ReadFromJsonAsync<BriefViewDto>())!.Brief;
|
||||
var filled = brief.Sections
|
||||
.Select(s => new LetterSectionDto(s.SectionKey, s.Title, s.Required,
|
||||
@@ -211,7 +211,7 @@ public class OrgTemplateEndpointTests(TestWebApplicationFactory factory) : IClas
|
||||
public async Task Admin_cannot_slip_into_the_brief_review_flow()
|
||||
{
|
||||
ResetStores();
|
||||
var resetRes = await _client.PostAsync("/api/v1/brief/reset", null); // RB-23: create explicitly
|
||||
var resetRes = await _client.PostAsync("/api/v1/brief/reset", null); // create explicitly
|
||||
var brief = (await resetRes.Content.ReadFromJsonAsync<BriefViewDto>())!.Brief;
|
||||
var filled = brief.Sections
|
||||
.Select(s => new LetterSectionDto(s.SectionKey, s.Title, s.Required,
|
||||
|
||||
@@ -7,7 +7,7 @@ using Microsoft.AspNetCore.Mvc.Testing;
|
||||
namespace BigRegister.Tests;
|
||||
|
||||
/// <summary>
|
||||
/// WP-25: the two HTML preview endpoints. Both are excluded from the OpenAPI doc
|
||||
/// The two HTML preview endpoints. Both are excluded from the OpenAPI doc
|
||||
/// (see the drift check in the API-client generation step) — these tests hit them
|
||||
/// as plain HTTP, the same way the hand-written FE fetch does.
|
||||
/// </summary>
|
||||
@@ -41,7 +41,7 @@ public class PreviewEndpointTests(TestWebApplicationFactory factory) : IClassFix
|
||||
public async Task Preview_of_an_unsent_brief_renders_live_with_a_watermark()
|
||||
{
|
||||
ResetStores();
|
||||
await _client.PostAsync("/api/v1/brief/reset", null); // RB-23: GET no longer seeds — create explicitly
|
||||
await _client.PostAsync("/api/v1/brief/reset", null); // GET no longer seeds — create explicitly
|
||||
|
||||
var res = await _client.GetAsync("/api/v1/brief/preview");
|
||||
res.EnsureSuccessStatusCode();
|
||||
@@ -54,7 +54,7 @@ public class PreviewEndpointTests(TestWebApplicationFactory factory) : IClassFix
|
||||
public async Task Preview_of_a_sent_brief_serves_the_archive_unchanged_after_a_republish()
|
||||
{
|
||||
ResetStores();
|
||||
var resetRes = await _client.PostAsync("/api/v1/brief/reset", null); // RB-23: create explicitly
|
||||
var resetRes = await _client.PostAsync("/api/v1/brief/reset", null); // create explicitly
|
||||
var brief = (await resetRes.Content.ReadFromJsonAsync<BriefViewDto>())!.Brief;
|
||||
await _client.PutAsJsonAsync("/api/v1/brief", FilledFrom(brief));
|
||||
await _client.SendAsync(Req(HttpMethod.Post, "/api/v1/brief/submit"));
|
||||
|
||||
@@ -4,11 +4,11 @@ using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace BigRegister.Tests;
|
||||
|
||||
/// RB-12/BIO-016 (BL-006 — "the backend has zero automated architecture enforcement"): the
|
||||
/// BIO-016 (BL-006 — "the backend has zero automated architecture enforcement"): the
|
||||
/// only thing that used to keep an admin-shaped endpoint behind `Authz` was a human noticing
|
||||
/// in review. BIO-003 (`X-Admin`, a second gate outside `Authz`) and BIO-004 (two endpoints
|
||||
/// with no gate at all) are exactly the failure mode this test is a safety net for — and it is
|
||||
/// the safety net RB-19 (a 900-line `Program.cs` reorder) leans on, so its value is entirely in
|
||||
/// the safety net a 900-line `Program.cs` reorder leans on, so its value is entirely in
|
||||
/// being hard to fool.
|
||||
///
|
||||
/// Every mapped route must be accounted for exactly one of two ways:
|
||||
@@ -48,14 +48,14 @@ public class RouteInventoryTests(TestWebApplicationFactory factory) : IClassFixt
|
||||
new("GET", "/api/v1/duo/diplomas", "Static DUO reference fixture + manual-diploma policy — same for every caller."),
|
||||
new("GET", "/api/v1/intake/policy", "Config VALUE shipped for instant FE feedback (ADR-0001); the server re-validates as authority."),
|
||||
new("GET", "/api/v1/uploads/categories", "Static per-wizard category config, no PII, no per-caller distinction."),
|
||||
new("GET", "/api/v1/flags", "Feature-flag catalog + state, readable by any principal by design (WP-47) — only the PUT toggle is admin-gated."),
|
||||
new("GET", "/api/v1/flags", "Feature-flag catalog + state, readable by any principal by design — only the PUT toggle is admin-gated."),
|
||||
new("GET", "/api/v1/me", "Reflects only the ACTING caller's own role-derived capabilities — no other caller's data to leak."),
|
||||
|
||||
// --- Citizen-submitted writes / ownership-scoped inline (ctx.Zorgverlener()/ctx.Caller()),
|
||||
// not a role-only admin wrapper because the boundary is resource ownership, not a role. ---
|
||||
new("POST", "/api/v1/change-requests", "Citizen submission; Submit() records outcome + idempotency, attributed to the acting caller."),
|
||||
new("POST", "/api/v1/uploads", "Upload is attributed to ctx.Zorgverlener() as owner — there is no pre-existing resource to own yet."),
|
||||
new("GET", "/api/v1/uploads/{documentId}/content", "Ownership-scoped inline (RB-01/BIO-004): owning citizen, or a behandelaar via Authz.CanBeoordelen."),
|
||||
new("GET", "/api/v1/uploads/{documentId}/content", "Ownership-scoped inline (BIO-004): owning citizen, or a behandelaar via Authz.CanBeoordelen."),
|
||||
new("GET", "/api/v1/uploads/status", "Ownership-scoped inline: DocumentStore.ByLocalIds filtered to ctx.Zorgverlener().Bsn."),
|
||||
new("DELETE", "/api/v1/uploads/{documentId}", "Ownership-scoped inline: DocumentStore.DeleteOwned keyed by ctx.Zorgverlener().Bsn."),
|
||||
new("GET", "/api/v1/aanvragen", "Ownership-scoped inline: IZaakSource.ListMyCases(ctx.Zorgverlener(), ...)."),
|
||||
@@ -72,14 +72,14 @@ public class RouteInventoryTests(TestWebApplicationFactory factory) : IClassFixt
|
||||
// enforce/emit twin for this whole surface (Authz.CanActOn via BriefStore, ToView's
|
||||
// Decisions dto) — a different single-source-of-truth than the five Program.cs wrappers,
|
||||
// not a missing one. ---
|
||||
new("GET", "/api/v1/brief", "Ownership-scoped inline: BriefStore.Get(ctx.Zorgverlener().Bsn), 404 when absent (RB-23)."),
|
||||
new("GET", "/api/v1/brief", "Ownership-scoped inline: BriefStore.Get(ctx.Zorgverlener().Bsn), 404 when absent."),
|
||||
new("PUT", "/api/v1/brief", "Brief status-machine enforcement: BriefStore.Save + Authz.CanActOn (drafter-only)."),
|
||||
new("POST", "/api/v1/brief/submit", "Brief status-machine enforcement: BriefStore.Submit + Authz.CanActOn."),
|
||||
new("POST", "/api/v1/brief/approve", "Brief status-machine enforcement: BriefStore.Approve + Authz.CanActOn (approver != drafter)."),
|
||||
new("POST", "/api/v1/brief/reject", "Brief status-machine enforcement: BriefStore.Reject + Authz.CanActOn."),
|
||||
new("POST", "/api/v1/brief/send", "Brief status-machine enforcement: BriefStore.Send; not role-gated today, per the endpoint's own comment."),
|
||||
new("POST", "/api/v1/brief/reveal-bignummer", "Own inline capability + step-up check (Authz.CanRevealBigNummer + X-Step-Up), audited directly."),
|
||||
new("GET", "/api/v1/brief/preview", "Ownership-scoped inline: BriefStore.Get(ctx.Zorgverlener().Bsn), 404 when absent (RB-23); hand-written FE fetch."),
|
||||
new("GET", "/api/v1/brief/preview", "Ownership-scoped inline: BriefStore.Get(ctx.Zorgverlener().Bsn), 404 when absent; hand-written FE fetch."),
|
||||
new("POST", "/api/v1/brief/reset", "Deliberately unguarded demo affordance — the endpoint's own comment says so: 'showcase affordance only'."),
|
||||
];
|
||||
|
||||
|
||||
@@ -59,7 +59,7 @@ public class StamdataEndpointTests(TestWebApplicationFactory factory) : IClassFi
|
||||
Assert.Empty(table.Rows);
|
||||
}
|
||||
|
||||
/// RB-16/BIO-019: DateOnly.Parse used to throw FormatException on unparseable input,
|
||||
/// BIO-019: DateOnly.Parse used to throw FormatException on unparseable input,
|
||||
/// surfacing as an unhandled 500 instead of the 400-with-problem-details every other
|
||||
/// bad-input check in this endpoint file returns.
|
||||
[Fact]
|
||||
|
||||
@@ -12,8 +12,8 @@ namespace BigRegister.Tests;
|
||||
/// </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
|
||||
/// Declared references INTO stamdata keys — the FK-like invariants the build gate enforces.
|
||||
/// 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.
|
||||
@@ -23,7 +23,7 @@ public class StamdataValidationTests
|
||||
private static readonly IReadOnlySet<string> BeroepCodes =
|
||||
StamdataFile.Load<Beroep>("beroepen").Select(b => b.Code).ToHashSet(StringComparer.Ordinal);
|
||||
|
||||
// Every document category id that exists across any wizard (WP-59's confidentialiteit
|
||||
// Every document category id that exists across any wizard (the confidentialiteit
|
||||
// table points at these) — "org-logo" resolves too, even though it's deliberately absent
|
||||
// from the confidentialiteit table itself (falls back to "openbaar").
|
||||
private static readonly IReadOnlySet<string> DocumentCategoryIds = new[] { "registratie", "herregistratie", "org-template" }
|
||||
@@ -38,7 +38,7 @@ public class StamdataValidationTests
|
||||
SeedData.Diplomas.Select(d => d.Opleiding),
|
||||
key => Professions.ByProgram.ContainsKey(key)),
|
||||
// Stamdata → stamdata references: two tables point at beroepen.code, so deleting or
|
||||
// renaming a beroep that either still uses fails the build (WP-48 gate, generalized).
|
||||
// renaming a beroep that either still uses fails the build (a stamdata gate, generalized).
|
||||
new StamdataRef(
|
||||
"Opleiding.beroep → beroepen.code",
|
||||
StamdataFile.Load<Opleiding>("opleidingen").Select(o => o.Beroep),
|
||||
@@ -47,7 +47,7 @@ public class StamdataValidationTests
|
||||
"Specialisme.beroep → beroepen.code",
|
||||
StamdataFile.Load<Specialisme>("specialismen").Select(s => s.Beroep),
|
||||
key => BeroepCodes.Contains(key)),
|
||||
// WP-59: a confidentialiteit row for a category that no wizard ever asks for is dead
|
||||
// A confidentialiteit row for a category that no wizard ever asks for is dead
|
||||
// config — fail the build rather than let it silently rot.
|
||||
new StamdataRef(
|
||||
"DocumentConfidentialiteit.CategoryId → a real document category",
|
||||
|
||||
@@ -6,10 +6,10 @@ using Microsoft.AspNetCore.Mvc.Testing;
|
||||
|
||||
namespace BigRegister.Tests;
|
||||
|
||||
/// WP-53 (extended WP-62): the dev stub identity provider — role from X-Role (unchanged
|
||||
/// The dev stub identity provider — role from X-Role (unchanged
|
||||
/// behaviour, applies to either actor kind), subject BSN from X-Subject defaulting to the
|
||||
/// single seeded citizen so every existing request (none of which send X-Subject) resolves
|
||||
/// exactly as before this WP. X-Medewerker (+ X-Rollen) selects the medewerker actor kind.
|
||||
/// exactly as before. X-Medewerker (+ X-Rollen) selects the medewerker actor kind.
|
||||
public class StubIdentityProviderTests
|
||||
{
|
||||
private static CallerIdentity Resolve(
|
||||
@@ -96,7 +96,7 @@ public class StubIdentityProviderTests
|
||||
Assert.Equal(PrincipalRole.Admin, caller.Role);
|
||||
}
|
||||
|
||||
/// RB-09/BIO-002: IIdentityProvider.Resolve can now return null ("no identity"), but this
|
||||
/// BIO-002: IIdentityProvider.Resolve can now return null ("no identity"), but this
|
||||
/// stub's own contract stays non-nullable — it is a developer convenience that always invents
|
||||
/// a caller, never a source of "no identity" itself. A request with genuinely no headers at
|
||||
/// all still resolves to the seeded citizen, unchanged.
|
||||
@@ -107,7 +107,7 @@ public class StubIdentityProviderTests
|
||||
}
|
||||
}
|
||||
|
||||
/// RB-09/BIO-002: in Production, StubIdentityProvider is not registered at all (it is
|
||||
/// BIO-002: in Production, StubIdentityProvider is not registered at all (it is
|
||||
/// Development-only) and there is no real DigiD/employee-SSO IIdentityProvider in this POC yet —
|
||||
/// so a Production build must fail at startup rather than silently resolving every request to
|
||||
/// the seeded citizen (the failure mode BIO-002 documents).
|
||||
|
||||
@@ -6,7 +6,7 @@ using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace BigRegister.Tests;
|
||||
|
||||
/// RB-15/BIO-015: `app.UseSwagger()`/`app.UseSwaggerUI()` used to run unconditionally — the
|
||||
/// BIO-015: `app.UseSwagger()`/`app.UseSwaggerUI()` used to run unconditionally — the
|
||||
/// OpenAPI document (every route + request/response shape) and SwaggerUI's "Try it out" were
|
||||
/// reachable in every environment, including a real deployment. Both are now gated behind
|
||||
/// `app.Environment.IsDevelopment()`.
|
||||
@@ -16,26 +16,26 @@ public class SwaggerGateTests(TestWebApplicationFactory factory) : IClassFixture
|
||||
public async Task Swagger_document_is_served_in_development()
|
||||
{
|
||||
// The default test environment (WebApplicationFactory<T> defaults to "Development" when
|
||||
// nothing overrides it — same fact RB-09's implementation note relies on) — this is the
|
||||
// nothing overrides it — same fact a related implementation note relies on) — this is the
|
||||
// regression guard that the gate didn't also break the documented `npm run gen:api` /
|
||||
// local-dev-Swagger-UI experience.
|
||||
var res = await factory.CreateClient().GetAsync("/swagger/v1/swagger.json");
|
||||
Assert.Equal(HttpStatusCode.OK, res.StatusCode);
|
||||
}
|
||||
|
||||
/// Production cannot boot at all today (RB-09: no real IIdentityProvider exists yet), which
|
||||
/// Production cannot boot at all today (no real IIdentityProvider exists yet), which
|
||||
/// is a *stronger* guarantee than "no Swagger in Production" — but it also means a plain
|
||||
/// `UseEnvironment("Production")` host never reaches this middleware to prove the gate
|
||||
/// itself works, only that the whole app refuses to start. This uses a third environment
|
||||
/// name (neither "Development" nor "Production") with a test-supplied `IIdentityProvider` —
|
||||
/// the one thing Program.cs doesn't register outside those two branches — so the host
|
||||
/// actually boots and this test exercises the real gate, not RB-09's unrelated startup throw.
|
||||
/// actually boots and this test exercises the real gate, not that unrelated startup throw.
|
||||
[Fact]
|
||||
public async Task Swagger_document_is_not_served_outside_development()
|
||||
{
|
||||
// Built on top of the shared `factory` fixture (via WithWebHostBuilder), not a bare `new
|
||||
// WebApplicationFactory<Program>()` — that keeps this host on the fixture's own per-class
|
||||
// isolated AppDb temp path (see TestWebApplicationFactory's doc comment; RB-12's
|
||||
// isolated AppDb temp path (see TestWebApplicationFactory's doc comment; an earlier
|
||||
// implementation note records the "table already exists" collision a bare factory hits
|
||||
// by sharing the mutable static Db.ConnectionString instead).
|
||||
using var staging = factory.WithWebHostBuilder(builder => builder
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Mvc.Testing;
|
||||
|
||||
// WP-22's stores read a single static Db.ConnectionString (there's no DI, matching
|
||||
// their pre-WP-22 static-Dictionary shape — see Data/Db.cs). That's correct for a
|
||||
// These stores read a single static Db.ConnectionString (there's no DI, matching
|
||||
// their earlier static-Dictionary shape — see Data/Db.cs). That's correct for a
|
||||
// real single-instance process, but xUnit's default parallel-across-classes
|
||||
// execution would run multiple WebApplicationFactory hosts concurrently in this
|
||||
// ONE test process, each overwriting that same static field with its own temp-file
|
||||
@@ -14,7 +14,7 @@ using Microsoft.AspNetCore.Mvc.Testing;
|
||||
namespace BigRegister.Tests;
|
||||
|
||||
/// <summary>
|
||||
/// WP-22 moved Applications/Documents/Briefs off in-memory dictionaries onto a real
|
||||
/// A migration moved Applications/Documents/Briefs off in-memory dictionaries onto a real
|
||||
/// SQLite file (see Data/Db.cs). Unlike static dictionaries, a shared file path
|
||||
/// would let concurrent test classes' WebApplicationFactory instances hit the same
|
||||
/// file at once — xUnit runs different test classes in parallel by default, and
|
||||
@@ -29,7 +29,7 @@ public sealed class TestWebApplicationFactory : WebApplicationFactory<Program>
|
||||
|
||||
protected override void ConfigureWebHost(IWebHostBuilder builder) => builder
|
||||
.UseSetting("ConnectionStrings:AppDb", $"Data Source={_dbPath}")
|
||||
// WP-52: a fixed shared secret so NotificatieTests can exercise the accept path —
|
||||
// A fixed shared secret so NotificatieTests can exercise the accept path —
|
||||
// the appsettings.json default is "" (reject everything), which no test should rely on.
|
||||
.UseSetting("Zgw:NotificatieAuthorization", "test-nrc-secret");
|
||||
|
||||
|
||||
@@ -7,10 +7,10 @@ using Microsoft.AspNetCore.Mvc.Testing;
|
||||
|
||||
namespace BigRegister.Tests;
|
||||
|
||||
/// Who may see what about an upload. RB-01/BIO-004: GET /uploads/{id}/content and
|
||||
/// Who may see what about an upload. BIO-004: GET /uploads/{id}/content and
|
||||
/// /uploads/status used to take no HttpContext at all — a diploma or identity scan was
|
||||
/// protected by GUID unguessability alone, while DELETE on the same resource was
|
||||
/// owner-scoped. RB-04/BIO-005: the document audit trail recorded the raw owner BSN as
|
||||
/// owner-scoped. BIO-005: the document audit trail recorded the raw owner BSN as
|
||||
/// its Actor, on a store whose own doc comment says it holds no PII.
|
||||
public class UploadAccessTests(TestWebApplicationFactory factory) : IClassFixture<TestWebApplicationFactory>
|
||||
{
|
||||
|
||||
@@ -5,8 +5,8 @@ using Microsoft.AspNetCore.Mvc.Testing;
|
||||
|
||||
namespace BigRegister.Tests;
|
||||
|
||||
/// WP-64: the behandelportal's queue of aanvragen needing treatment, gated by the
|
||||
/// medewerker capability `CanBeoordelen` (WP-62) — not the admin role.
|
||||
/// The behandelportal's queue of aanvragen needing treatment, gated by the
|
||||
/// medewerker capability `CanBeoordelen` — not the admin role.
|
||||
public class WerkvoorraadTests(TestWebApplicationFactory factory) : IClassFixture<TestWebApplicationFactory>
|
||||
{
|
||||
private readonly HttpClient _client = factory.CreateClient();
|
||||
@@ -38,7 +38,7 @@ public class WerkvoorraadTests(TestWebApplicationFactory factory) : IClassFixtur
|
||||
var queue = (await res.Content.ReadFromJsonAsync<List<AanvraagSummaryDto>>())!;
|
||||
var mine = queue.Single(x => x.Id == a.Id);
|
||||
Assert.Equal("InBehandeling", mine.Status.Tag);
|
||||
// RB-03/BIO-003: masked, like /admin/cases — both inherit ToAdminSummaryDto.
|
||||
// BIO-003: masked, like /admin/cases — both inherit ToAdminSummaryDto.
|
||||
Assert.Equal("******782", mine.Owner);
|
||||
}
|
||||
finally
|
||||
|
||||
@@ -9,7 +9,7 @@ using Microsoft.Extensions.DependencyInjection;
|
||||
namespace BigRegister.Tests;
|
||||
|
||||
/// <summary>
|
||||
/// WP-60's required verification: a ZGW failure mid-submit must not leave the two write sides
|
||||
/// The required verification: a ZGW failure mid-submit must not leave the two write sides
|
||||
/// silently diverged — it's flagged (<see cref="Aanvraag.ZgwError"/>, an audit row) instead.
|
||||
/// Not an <see cref="IClassFixture{TFixture}"/> off <see cref="TestWebApplicationFactory"/>: that
|
||||
/// fixture hardcodes <c>Zgw:Enabled=false</c>, so this builds its own factory the same way
|
||||
@@ -111,7 +111,7 @@ public class ZgwDivergenceTests
|
||||
Assert.Null(stored.ZgwError);
|
||||
}
|
||||
|
||||
/// RB-05/BIO-009: `ZgwError` is persisted to SQLite and written to the application log, so
|
||||
/// BIO-009: `ZgwError` is persisted to SQLite and written to the application log, so
|
||||
/// the message it carries may not include the response body (OpenZaak echoes the request in
|
||||
/// its errors) or the request's query string (ZGW filters travel there, and one of them is
|
||||
/// `rol__betrokkeneIdentificatie__natuurlijkPersoon__inpBsn`).
|
||||
|
||||
@@ -10,7 +10,7 @@ namespace BigRegister.Tests;
|
||||
/// Factored out of OpenZaakZaakSourceTests once OpenZaakDocumentSourceTests needed the
|
||||
/// identical stub.
|
||||
///
|
||||
/// WP-60: an optional <paramref name="status"/> callback lets a test inject a failing status
|
||||
/// An optional <paramref name="status"/> callback lets a test inject a failing status
|
||||
/// for a given url on a given (0-based) attempt — e.g. "503 on the first call to /zaken, then
|
||||
/// let it through" — to exercise ZgwHttpClient's retry without a live server. When it returns
|
||||
/// a non-2xx code, <paramref name="respond"/> is not called for that attempt (so a test that
|
||||
|
||||
@@ -59,7 +59,7 @@ public class ZgwTokenProviderTests
|
||||
[Fact]
|
||||
public void Mint_with_a_medewerker_caller_uses_the_medewerkerId_as_user_id()
|
||||
{
|
||||
// WP-62: SubjectId is what ZgwTokenProvider.Mint reads — a medewerker's is its
|
||||
// SubjectId is what ZgwTokenProvider.Mint reads — a medewerker's is its
|
||||
// medewerkerId, not a BSN, and this is the only place that's directly observable.
|
||||
var caller = new MedewerkerCaller("m.jansen", [MedewerkerRol.Behandelaar], "M. Jansen", PrincipalRole.Drafter);
|
||||
var token = new ZgwTokenProvider(Options).Mint(caller);
|
||||
|
||||
Reference in New Issue
Block a user