Skip to content

🌱 Add re-runnable Northwind demo data seeding + DB reset for local dev#1570

Draft
jernejk wants to merge 5 commits into
mainfrom
540-seed-demo-data
Draft

🌱 Add re-runnable Northwind demo data seeding + DB reset for local dev#1570
jernejk wants to merge 5 commits into
mainfrom
540-seed-demo-data

Conversation

@jernejk

@jernejk jernejk commented Jul 3, 2026

Copy link
Copy Markdown
Member

What

Adds re-runnable demo-data seeding and DB reset for local dev — the seed/restore follow-up called out in #1562. A fresh clone with zero backups now gets a fully alive Rewards app in one command:

./rewards-dev db seed --dev-email you@ssw.com.au   # idempotent — re-run any time to top up
./rewards-dev db reset                             # drop DBs → migrate → seed (asks to confirm)

Sample of the generated fictional avatars
Figure: Sample of the pre-generated avatar style (60 in total, uploaded to the local blob emulator at seed time).

What you get

  • ~60 fictional, real-sounding users — Bob Northwind (top of the leaderboard, as the boss should be), the classic 9 Northwind employees as staff (profiles, skills, scannable QR achievements), and Northwind's customer contacts as the community. Fake emails, fictional socials.
  • 3 years of history (configurable --years): scans, quiz passes, reward claims, and event clusters — an annual Northwind DevCon, monthly user groups, quarterly hack days — so Today / Week / Month / Year / All-time leaderboards all look real.
  • Rewards catalog + claims + pending redemptions for the AdminUI screens. Claims are generated chronologically against each user's running balance: no user is ever seeded into negative points, including open pending redemptions.
  • Your own user: --dev-email (required) pre-creates it with history and its own QR achievement, keyed on the login email so it binds the first time you sign in.
  • Quizzes with questions/answers; playable ones are kept completion-free.

How it works

  • DemoDataSeeder + declarative DemoDataSet in Application (beside the legacy SampleDataSeeder, untouched). Seed-owned rows use demo:* natural keys.
  • Idempotent + gap-filling by construction: every decision is a stable hash of (person, calendar date) — no RNG. Re-running after months away fills exactly the missing days; a second run on the same day adds nothing (verified: +0/+0/+0/+0).
  • tools/DataSeeder console (minimal DI — no Hangfire/Firebase) auto-discovers the Aspire SQL/Azurite containers and the SA password from the AppHost user-secrets; rewards-dev db … wraps it.
  • Aspire dashboard buttons on rewards-sql: DB: Seed demo data and DB: Reset + reseed (confirmation dialog; connection strings passed as env vars, never argv).
  • reset force-drops both DBs even while the stack runs (SINGLE_USER WITH ROLLBACK IMMEDIATE) and refuses non-local servers.
  • Avatars/reward images are pre-generated assets uploaded to the local Azurite demo-assets container on first seed.
  • Docs: _docs/Aspire-Local-Dev.md + the manage-database agent skill.

Verified

First seed on an empty DB, then the same command again:

Seed complete: Users = 61, StaffMembers = 10, Rewards = 7, Quizzes = 4,
               AwardsAdded = 2439, ClaimsAdded = 143, CompletionsAdded = 27, PendingAdded = 6
Re-run:        AwardsAdded = 0,    ClaimsAdded = 0,   CompletionsAdded = 0,  PendingAdded = 0

Sanity queries after seeding: 26 users active in the last 7 days, DevCon cohorts grow 8 → 19 → 33 across the 3 years, minimum balance across all users is +100 (never negative), 60/60 users + 7/7 rewards have blob image URLs.

Tests

6 unit tests over the real ApplicationDbContext (EF InMemory): same-day idempotency, gap-fill top-up, no-negative-balance invariant, dev-user creation, flagship ranking, playable-quiz protection.

Note

Found while building: SubmitUserQuizCommandValidator blocks a quiz for everyone once any user passes it (CompletedQuizzes.AnyAsync(c => c.QuizId == quizId && c.Passed) — no user filter). Not fixed here (out of scope); the seeder works around it. Worth its own issue.

This is groundwork for multi-tenancy seeding (#1507 / #1511) — the Northwind dataset is a ready-made sample-tenant dataset.

Closes #540

Pair programming

✏️ Yes — JK pairing with Claude (Fable 5)

🤖 Generated with Claude Code

jernejk and others added 5 commits July 3, 2026 15:17
Closes #540

- DemoDataSeeder: idempotent, hash-deterministic Northwind dataset — 60 fictional
  users, staff with skills/QR achievements, rewards, quizzes, 3 years of activity
  history with event clusters; re-runs top up gaps without duplicating
- Claims generated chronologically; no user can end up with a negative balance
  (including open pending redemptions)
- tools/DataSeeder console: seed / reset (drop -> migrate -> seed) with docker
  connection auto-discovery; rewards-dev db seed|reset wrapper
- Aspire dashboard commands on rewards-sql: DB: Seed demo data / DB: Reset + reseed
- --dev-email pre-creates the developer's own user so the app binds on first login

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…reset guard, safer conn-string building

- New claims on a re-run now leave open pending-redemption costs covered
  (replayed claims keep their original bookkeeping, so runs stay deterministic)
- reset refuses non-local servers unless --allow-remote
- Auto-discovered conn string built via SqlConnectionStringBuilder; AppHost
  UserSecretsId parsed from the csproj with the known id as fallback
- Dev QR achievement keyed by email; --dev-email documented as seed identity

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@jernejk jernejk self-assigned this Jul 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

API - Add seed profile command line args

1 participant