Lessons integration coverage: Phase 4 (tutorial completion, idempotent no-op, anti-spoof attempt-token enforcement) shipped #451

Closed
opened 2026-07-07 15:50:28 +00:00 by spikerj · 0 comments
Owner

Tracking ticket for the Lessons + code-execution/grading integration test expansion (see lessons_integration_tests plan; spikersoft-issues#448 for Phase 0-2, spikersoft-issues#449 for Phase 3).

Shipped in spikersoft-backend#176 (merged):

  • Phase 4: POST /{n}/complete-tutorial covered end-to-end for Tutorial-kind lessons (records progress directly, no worker needed) vs Challenge-kind lessons (400 - must go through /CodeRunner/lesson); a re-click on an already-completed tutorial with a stale/forged token is an idempotent 200 no-op rather than 410; a forged token on a never-completed lesson is hard-rejected with 410 and writes zero progress state. Anti-spoof: an otherwise-correct C# solution submitted against an expired (Redis-deleted) or foreign (issued for a different lesson) attempt token is accepted with 202 but the worker's own attempt-cache lookup rejects it before grading runs, so CompletedAt never advances - proving the token check is enforced by the worker itself, not just the controller.

Side finding: discovered and filed spikersoft-issues#450 for a pre-existing (not introduced by this work) test-host teardown flake where dotnet test reports every test as Passed but exits non-zero once 3+ Lessons integration test classes share the TestContainers collection in one run.

Still to come (follow-up PR, same plan):

  • Phase 5: capability-gated Python/Node/C/C++ grading loop with controller-level 202+publish fallback.

Definition of green: full integration suite passes locally (Docker up), no regressions to the pre-existing tests.

Tracking ticket for the Lessons + code-execution/grading integration test expansion (see `lessons_integration_tests` plan; spikersoft-issues#448 for Phase 0-2, spikersoft-issues#449 for Phase 3). **Shipped** in spikersoft-backend#176 (merged): - Phase 4: `POST /{n}/complete-tutorial` covered end-to-end for Tutorial-kind lessons (records progress directly, no worker needed) vs Challenge-kind lessons (400 - must go through `/CodeRunner/lesson`); a re-click on an already-completed tutorial with a stale/forged token is an idempotent 200 no-op rather than 410; a forged token on a never-completed lesson is hard-rejected with 410 and writes zero progress state. Anti-spoof: an otherwise-correct C# solution submitted against an expired (Redis-deleted) or foreign (issued for a different lesson) attempt token is accepted with 202 but the worker's own attempt-cache lookup rejects it before grading runs, so `CompletedAt` never advances - proving the token check is enforced by the worker itself, not just the controller. **Side finding**: discovered and filed spikersoft-issues#450 for a pre-existing (not introduced by this work) test-host teardown flake where `dotnet test` reports every test as `Passed` but exits non-zero once 3+ Lessons integration test classes share the `TestContainers` collection in one run. **Still to come** (follow-up PR, same plan): - Phase 5: capability-gated Python/Node/C/C++ grading loop with controller-level 202+publish fallback. Definition of green: full integration suite passes locally (Docker up), no regressions to the pre-existing tests.
Sign in to join this conversation.