Commit Graph
5 Commits
Author SHA1 Message Date
ced4568 8956a95446 test(phase3b): add learning and assessment coverage
24 cases: attempt telemetry (start/fail/pass counters, duration), progressive hint sequencing
and exhaustion, hint persistence through a real Lab reset, curriculum stage lock/unlock,
capstone lock-before-prerequisites/unlock-after/random-draw-includes-required-Real-Ops/
cannot-double-start/completion-records-achievement, exam assignment/draw/timer/hint-policy,
cross-student session-read rejection (404, not 403 - consistent with the existing don't-confirm-
existence posture), two-student isolation, timer survives a Lab reset, an assessment session
survives a REAL docker compose restart api, and instructor analytics/needs-attention/RBAC.

Found and fixed two real product bugs in the process: result needed to become nullable (an
in-progress attempt has none yet) and hint_count needed a real DEFAULT 0 (Phase 2B left it
NULL/undefaulted since it wasn't measured yet - it is now, and NULL + 1 stays NULL in SQL).
2026-08-18 14:36:13 -05:00
ced4568 face0ae132 test(phase3a): add identity and RBAC end-to-end coverage
New e2e-phase3a.js (21 cases): no account enumeration, CSRF positive/negative, login rate
limiting, RBAC across all three roles, cohort-scoped instructor access (visibility + route
access), last-owner protection, live role/status re-evaluation (no separate revoke needed),
password change (old password rejected after, other sessions revoked), audit log content
(including a no-secrets-in-metadata assertion), student session RBAC/logout, and session
persistence across a REAL docker compose restart api mid-test.

New session-helper.js — a shared cookie-jar test helper (Node's fetch doesn't persist
cookies across calls) that also auto-attaches X-CSRF-Token from whichever rn_csrf_*
cookie it's holding, replacing the single-cookie-string trackers e2e-phase2a.js and
e2e-phase2b.js each had their own copy of.

e2e-phase2a.js and e2e-phase2b.js updated for the new auth model: admin login is now a real
bootstrapped OWNER_EMAIL/OWNER_PASSWORD account instead of the old shared ADMIN_PASSWORD.
All cases still passing: 10/10 and 12/12 respectively.
2026-08-18 13:36:45 -05:00
ced4568 6f1d202242 test(phase2b): add instructor classroom workflow coverage
Full acceptance test: admin auth (+ student-can't-call-admin check),
cohort create, 3-student add, curriculum assign, concurrent Prepare Labs
(3/3 ready, instant-reuse timing check), two students completing
different scenarios, dashboard reflecting completions, admin reset with
progress-intact assertion, admin destroy-labs with progress-intact
assertion for everyone, and a regression check that no Lab container
publishes a host port. 12/12 passing.
2026-08-18 12:23:17 -05:00
ced4568 ae75bb4cbe feat(auth): add server-controlled student and admin sessions
Replaces the Phase 2A pilot ?sid= query param with an HttpOnly,
SameSite=Lax session cookie. Gateway now derives student identity from
the session (via Platform API's /api/session/whoami) instead of trusting
a client-suppliable id, and checks ownership on every proxied HTTP and
WebSocket Lab request, not just the initial navigation.

Fixes a routing bug found during testing: the fallback route was using
'does this session own a running environment' to decide whether an
unprefixed /api/* call belonged to the Lab UI or the dashboard — true in
both places. Replaced with an explicit LAB_OWNED_PATHS allowlist of the
exact unprefixed paths the existing Lab UI actually calls.

e2e-phase2a.js rewritten to use a cookie jar instead of headers/query
params, with explicit impersonation-rejection tests added. 10/10 passing.
2026-08-18 12:22:58 -05:00
ced4568andClaude Sonnet 5 05421bcb0e test(phase2a): add end-to-end student environment coverage
platform/test/e2e-phase2a.js — 15 assert-based tests against a live
platform/docker-compose.dev.yml stack, exercising the full student flow
through the Gateway exactly as a browser would: roster -> login ->
curriculum -> launch (real k3s boot) -> Gateway HTTP routing -> invalid-token
rejection -> cross-student ownership rejection (?sid= mismatch) ->
same-student access -> progress relay (solve in one student's lab, confirm
it shows in their curriculum and NOT the other student's) -> reset (stable
launch_path, progress survives).

All 15 pass. This is slow (each real environment launch is a genuine k3s
cold start) and deliberately not meant for every commit — see
platform/README.md for when to run it.

Does not cover what needs a browser/WebSocket client — real Lab image
terminal (node-pty, kubectl execution, scenario validation) and idle-reaper
timing were verified manually; see
docs/PHASE2A_GATEWAY_AND_STUDENT_FLOW.md §8 for exactly what and how.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-08-18 11:16:44 -05:00