test(domain): store finds inscriptions due for a herregistratie reminder (refs #18)

New IRegistrationStore.FindDueForHerregistratieReminderAsync — the sweep's
candidate set. The production store is stubbed to an empty list so the new test
fails; the green commit filters on the aggregate's own reminder-due rule.

refs #18
This commit is contained in:
not
2026-07-23 11:52:58 +02:00
parent 22215865b3
commit 3b3a44b177
4 changed files with 39 additions and 0 deletions
+7
View File
@@ -107,6 +107,13 @@ public interface IRegistrationStore
/// registration, or <c>null</c> if they have none in flight. Lets the self-service portal resume /// registration, or <c>null</c> if they have none in flight. Lets the self-service portal resume
/// an existing registration after a refresh (S-26); terminal registrations are not resumed.</summary> /// an existing registration after a refresh (S-26); terminal registrations are not resumed.</summary>
Task<Registration?> FindOpenByBsnAsync(string bsn, CancellationToken ct = default); Task<Registration?> FindOpenByBsnAsync(string bsn, CancellationToken ct = default);
/// <summary>The inscriptions whose herregistratie reminder is due as of <paramref name="asOf"/> and
/// not yet sent — the herregistratie reminder sweep's candidate set (S-17). The predicate is the
/// aggregate's own <see cref="Registration.HerregistratieReminderDue"/> rule, so the store never
/// duplicates the herregistratie policy.</summary>
Task<IReadOnlyList<Registration>> FindDueForHerregistratieReminderAsync(
DateTimeOffset asOf, CancellationToken ct = default);
} }
/// <summary> /// <summary>
@@ -26,4 +26,8 @@ public sealed class InMemoryRegistrationStore : IRegistrationStore
public Task<Registration?> FindOpenByBsnAsync(string bsn, CancellationToken ct = default) public Task<Registration?> FindOpenByBsnAsync(string bsn, CancellationToken ct = default)
=> Task.FromResult(_byId.Values.FirstOrDefault(r => => Task.FromResult(_byId.Values.FirstOrDefault(r =>
r.Bsn == bsn && r.Status is RegistrationStatus.Ingediend or RegistrationStatus.InBehandeling)); r.Bsn == bsn && r.Status is RegistrationStatus.Ingediend or RegistrationStatus.InBehandeling));
public Task<IReadOnlyList<Registration>> FindDueForHerregistratieReminderAsync(
DateTimeOffset asOf, CancellationToken ct = default)
=> Task.FromResult<IReadOnlyList<Registration>>([]); // RED stub
} }
+5
View File
@@ -26,6 +26,11 @@ internal sealed class FakeRegistrationStore : IRegistrationStore
=> Task.FromResult(_byId.Values.FirstOrDefault(r => => Task.FromResult(_byId.Values.FirstOrDefault(r =>
r.Bsn == bsn && r.Status is RegistrationStatus.Ingediend or RegistrationStatus.InBehandeling)); r.Bsn == bsn && r.Status is RegistrationStatus.Ingediend or RegistrationStatus.InBehandeling));
public Task<IReadOnlyList<Registration>> FindDueForHerregistratieReminderAsync(
DateTimeOffset asOf, CancellationToken ct = default)
=> Task.FromResult<IReadOnlyList<Registration>>(
_byId.Values.Where(r => r.HerregistratieReminderDue(asOf)).ToList());
public void Seed(Registration registration) => _byId[registration.Id] = registration; public void Seed(Registration registration) => _byId[registration.Id] = registration;
} }
@@ -96,4 +96,27 @@ public class InMemoryRegistrationStoreTests
Assert.Null(await store.FindOpenByBsnAsync("123456782")); Assert.Null(await store.FindOpenByBsnAsync("123456782"));
} }
[Fact]
public async Task Finds_only_the_inscriptions_due_for_a_herregistratie_reminder()
{
var now = new DateTimeOffset(2026, 7, 23, 0, 0, 0, TimeSpan.Zero);
var store = new InMemoryRegistrationStore();
var due = Registration.Submit("123456782");
due.AttachZaak(new Uri("http://openzaak/zaken/api/v1/zaken/abc"));
due.Approve(now - Registration.HerregistratieGeldigheid + Registration.Herinneringstermijn);
await store.SaveAsync(due);
var freshlyInscribed = Registration.Submit("111111110");
freshlyInscribed.AttachZaak(new Uri("http://openzaak/zaken/api/v1/zaken/def"));
freshlyInscribed.Approve(now);
await store.SaveAsync(freshlyInscribed);
await store.SaveAsync(Registration.Submit("222222222")); // still INGEDIEND — never inscribed
var result = await store.FindDueForHerregistratieReminderAsync(now);
Assert.Equal([due.Id], result.Select(r => r.Id).ToArray());
}
} }