Skip to content

Fase 0: Fundament

20 user stories, 7 scenario’s + 1 edge case.

Een developer clonet de repo, installeert dependencies en checkt of de basis tooling werkt.

Dekt: 0.1.1, 0.1.2, 0.1.3, 0.1.4, 0.1.5, 0.1.6

Voorwaarden: Lege repo, Node 20, pnpm geinstalleerd

  1. Clone de repo en draai pnpm install
  2. Check mapstructuur: apps/web, apps/api, apps/docs, packages/shared
  3. Draai pnpm typecheck, geen fouten
  4. Draai pnpm lint, geen warnings
  5. Open packages/shared/src/types.ts, bevat gedeelde types
  6. Open packages/shared/src/validation.ts, bevat Zod schemas
  7. Check dat apps/web Tailwind CSS + cn() helper heeft
  8. Check dat shadcn/ui componenten beschikbaar zijn
  9. Check dat apps/api een Hono app is op Cloudflare Workers
  10. Check dat apps/docs een Starlight site is

Resultaat: Alle tools werken, strict mode actief, geen lint warnings, shared package is bruikbaar vanuit web en api.

Developer checkt of de Supabase database correct is opgezet met alle kerntabellen en RLS policies.

Dekt: 0.2.1, 0.2.2, 0.2.3

Voorwaarden: Supabase project aangemaakt

  1. Draai de eerste migratie
  2. Controleer dat tabellen bestaan: clients, dogs, vaccinations, users, services
  3. Controleer dat services tabel seed data bevat (alle diensten + prijzen)
  4. Log in als klant, probeer data van een andere klant op te vragen. Geblokkeerd door RLS.
  5. Check RLS policy: client_id = (SELECT auth.uid()) op klanttabellen
  6. Log in als admin, check dat alle data zichtbaar is via admin RLS policy (role IN ('admin', 'medewerker'))
  7. Probeer als anonieme gebruiker data op te vragen. Geblokkeerd.

Resultaat: Alle tabellen bestaan, seed data aanwezig, RLS blokkeert ongeautoriseerde toegang.

Een nieuwe klant maakt een account aan via /register en wordt correct gerouteerd.

Dekt: 0.3.1, 0.3.2, 0.3.5

Voorwaarden: App draait, database klaar

  1. Ga naar /register
  2. Controleer dat het formulier drie velden heeft: Naam, Email en Wachtwoord
  3. Vul een naam in (min. 2 karakters), email en wachtwoord (min. 8 tekens)
  4. Klik op “Account aanmaken”
  5. Check dat PKCE flow wordt gebruikt (geen implicit grant)
  6. Check dat een rij in de clients tabel wordt aangemaakt met onboarding_completed = false
  7. Na registratie, redirect naar /onboarding
  8. Log uit
  9. Ga naar /login, klik op “Wachtwoord vergeten?” (linkt naar /forgot-password)
  10. Vul email in, klik op “Verstuur resetlink”
  11. Check dat de melding altijd hetzelfde is: “Als dit emailadres bij ons bekend is, ontvang je een resetlink.”
  12. Open de resetlink uit de email. Komt op /reset-password met token in de URL.
  13. Vul “Nieuw wachtwoord” en “Bevestig wachtwoord” in
  14. Klik “Wachtwoord opslaan”
  15. Check redirect naar /login met succesmelding “Wachtwoord gewijzigd. Je kunt nu inloggen.”
  16. Log in met nieuw wachtwoord, redirect naar klant dashboard
  17. Vul twee wachtwoorden in die niet overeenkomen. Check foutmelding “Wachtwoorden komen niet overeen.”
  18. Vul een wachtwoord in korter dan 8 tekens. Check foutmelding “Wachtwoord moet minimaal 8 karakters zijn.”
  19. Gebruik een verlopen token (>1 uur). Check foutmelding “Deze resetlink is verlopen. Vraag een nieuwe aan.”
  20. Gebruik een ongeldig/verminkt token. Check foutmelding “Ongeldige link. Vraag een nieuwe resetlink aan.”

Resultaat: Account aangemaakt via PKCE, redirect naar onboarding, wachtwoord reset werkt, alle foutpaden bij reset geven de juiste melding.

Een admin logt in en wordt naar het admin panel gerouteerd. Klanten worden correct geweerd van admin routes.

Dekt: 0.3.3, 0.3.5

Voorwaarden: Admin account bestaat in database met role = 'admin'

  1. Ga naar /login
  2. Log in als admin met email + wachtwoord
  3. Check redirect naar /admin (niet /dashboard)
  4. Log uit
  5. Log in via magic link: klik “Inloggen met magic link”, vul email in, ontvang link
  6. Open de magic link, check redirect naar /admin
  7. Log in als klant (met voltooide onboarding). Check redirect naar /dashboard.
  8. Als klant, probeer handmatig naar /admin te navigeren. Check redirect naar /dashboard.
  9. Log in als admin, probeer handmatig naar /dashboard te navigeren. Check redirect naar /admin.
  10. Log uit, probeer /dashboard te bezoeken. Check redirect naar /login.
  11. Check dat de oorspronkelijke URL bewaard wordt: na redirect naar /login en opnieuw inloggen, kom je terug op de originele pagina.
  12. Log in als klant met onboarding_completed = false. Check redirect naar /onboarding, ongeacht welke route je probeerde.

Resultaat: Admin wordt correct gerouteerd via wachtwoord en magic link. Auth guards werken: klanten komen niet bij admin, admins niet bij klant dashboard, onboarding is verplicht.

Developer checkt of de API correct beveiligd is.

Dekt: 0.3.4, 0.4.1, 0.4.2, 0.4.3

Voorwaarden: API draait op Cloudflare Workers

  1. GET /health zonder token, moet 200 OK geven met body { status: "ok" }
  2. GET /api/bookings zonder token, moet 401 Unauthorized geven
  3. GET /api/bookings met geldige JWT, moet 200 geven
  4. POST /api/bookings met ongeldige data, moet 400 geven met Zod validatiefout
  5. Check CORS: request vanuit andere origin wordt geblokkeerd. Alleen APP_ORIGIN is toegestaan.
  6. Check dat CORS op localhost werkt als ENVIRONMENT !== 'production'
  7. Check dat foutmeldingen geen interne details bevatten (geen stack traces, geen error.message)
  8. Check dat /payments/webhook geen auth vereist (uitgezonderde route)
  9. Check dat alle queries een .limit() hebben (200-500 range)

Resultaat: Health endpoint open met correct format, alle andere routes beveiligd, CORS actief, input gevalideerd, geen interne details in foutmeldingen.

Klant en admin zien de juiste layout, navigatie werkt in meerdere talen.

Dekt: 0.5.1, 0.5.2, 0.5.3

Voorwaarden: App draait, gebruiker ingelogd

  1. Log in als klant. Check navigatie-items: Dashboard (/dashboard), Mijn honden (/dogs), Boekingen (/bookings), Facturen (/invoices), Account (/account).
  2. Check header: logo Dog Hotel Aruba, taalwissel (NL/EN), gebruikersnaam met dropdown (account, uitloggen).
  3. Check dat de actieve route visueel gemarkeerd is in de navigatie.
  4. Check sidebar op desktop, hamburger menu op mobiel (375px breed). Menu opent als overlay.
  5. Klik op de taalwissel, kies EN. Alle UI-teksten wisselen direct (geen pagina-reload, via react-i18next).
  6. Wissel terug naar NL.
  7. Check dat de taalvoorkeur wordt opgeslagen in localStorage voor niet-ingelogde gebruikers.
  8. Check dat voor ingelogde klanten de taalvoorkeur ook wordt opgeslagen in het profiel (preferred_language in clients tabel).
  9. Log uit en weer in. Check dat de taalvoorkeur behouden is (uit profiel geladen).
  10. Log in als admin. Check navigatie-items: Dashboard (/admin), Klanten (/admin/clients), Honden (/admin/dogs), Boekingen (/admin/bookings), Facturen (/admin/invoices), Instellingen (/admin/settings).
  11. Controleer dat admin layout een ander kleurenschema/accent heeft dan klant layout.
  12. Controleer dat de browser-taal wordt gedetecteerd voor niet-ingelogde gebruikers: als browser NL of EN is, wordt die gebruikt. Anders fallback naar EN.
  13. Zet browser-taal op FR (niet ondersteund). Check dat de app terugvalt op EN.

Resultaat: Beide layouts correct met juiste navigatie en routes, taalwissel werkt zonder reload, voorkeur persistent over sessies, browser-taal fallback werkt.

Push naar main triggert automatische deploys.

Dekt: 0.6.1

Voorwaarden: GitHub Actions geconfigureerd

  1. Push een kleine wijziging naar apps/web/. Check dat alleen web deploy start (naar Cloudflare Pages).
  2. Push een wijziging naar apps/api/. Check dat alleen API deploy start (naar Cloudflare Workers).
  3. Push een wijziging naar apps/docs/. Check dat alleen docs deploy start (naar Cloudflare Pages).
  4. Controleer dat path filtering werkt (geen onnodige deploys)
  5. Open een PR. Check dat typecheck en lint als quality gate draaien.
  6. Controleer dat alle drie deploys succesvol zijn op Cloudflare

Resultaat: Drie onafhankelijke workflows, path filtering actief, quality gate op PRs, deploys succesvol.

EC-1 Registratie en authenticatie edge cases

Section titled “EC-1 Registratie en authenticatie edge cases”

Alle foutpaden bij het aanmaken en gebruiken van accounts.

Versterkt: S-0.3, S-0.4, S-0.5

  1. Ga naar /register. Registreer met een emailadres dat al bestaat. Check foutmelding: “Dit emailadres is al geregistreerd. Probeer in te loggen.” (link naar /login).
  2. Registreer met ongeldig emailformat (bijv. “test@”). Check inline validatiefout: “Voer een geldig emailadres in.”
  3. Registreer met naam korter dan 2 karakters. Check inline validatiefout.
  4. Registreer met wachtwoord korter dan 8 tekens. Check foutmelding: “Wachtwoord moet minimaal 8 karakters zijn.”
  5. Registreer maar sluit browser voor bevestiging. Check dat registratie niet wordt afgerond.
  6. Gebruik verlopen magic link (>1 uur). Check foutmelding, niet een 500 error.
  7. Gebruik dezelfde magic link twee keer. Tweede keer geweigerd.
  8. Verstuur API request met verlopen JWT. Moet 401 geven, niet 500.
  9. Verstuur API request met JWT van klant A voor data van klant B. Moet 403 of lege response geven (IDOR check).
  10. Klant probeert admin-only API route te benaderen. Moet 403 geven.
  11. Medewerker met rol “trainer” probeert admin-only actie (bijv. dienst aanmaken). Geblokkeerd.
  12. Check dat /payments/webhook geen auth vereist (AC 0.3.4)
  13. Server error bij registratie (bijv. database down). Check generieke foutmelding: “Er ging iets mis. Probeer het later opnieuw.”
  14. Na login, laat de sessie verlopen (of verwijder de token). Check redirect naar /login.

Resultaat: Alle foutpaden geven duidelijke, generieke meldingen zonder interne details. Verlopen magic links en sessies leiden tot correcte redirects.