test(phase6b): add ROOT curriculum acceptance coverage + real-Lab rehearsal

e2e-phase6b.js (24 checks): hierarchy for the 13 new modules across
ROOT 05-10/12/Capstone, prerequisite chains through the newly-inserted
front-door modules, lesson/knowledge-check completion in brand-new
modules, a newly-wired scenario (job-task) completing correctly, the
optional-vs-required (is_required) completion rule, persistence/
isolation/RBAC/draft-hiding proven against the new content, and the
capstone server-side unlock fix in both directions (a direct bypass
attempt rejected with blocking_stage_ids, an ordinary exam unaffected,
a legitimately unlocked attempt still accepted and its completion
reflected in the course content model).

test-phase6b-root-course-rehearsal.js + wrapper (11 checks, real k3s):
one continuous student narrative through a brand-new module (ROOT 07
Services & Service Discovery, not the Phase 6A gold module) — real Lab
launch, the Lab's own setup_commands, a real 'kubectl expose' against
the live cluster, real validate, logout/login/Resume Learning, Lab
destroy/recreate with academic progress intact — plus an instructor
narrative proving Curriculum view, Prepare Labs, and classroom/learning
tooling all still work against the expanded curriculum.

e2e-phase6a.js updated for two of this phase's own intentional,
documented behavior changes (not defects): resume now points at
root-05's new mental-model module instead of the old bare-lab bank, and
root-10's gold module now unlocks the new troubleshooting-framework
module rather than the bare-lab bank directly.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
This commit is contained in:
2026-08-26 09:09:20 -05:00
co-authored by Claude Sonnet 5
parent e0f954e714
commit 505783e5f7
4 changed files with 571 additions and 5 deletions
+17 -5
View File
@@ -128,8 +128,10 @@ async function main() {
await test('resume points at the first activity of the first unlocked course', async () => {
const { body } = await casey.request('GET', `/api/student/programs/${enrollmentId}/courses`);
assert.equal(body.resume.course_id, 'root-05-kubernetes-foundations');
assert.equal(body.resume.module_id, 'root-05-m01-pods-and-kubectl-basics');
assert.equal(body.resume.activity_id, 'root-05-m01-pods-and-kubectl-basics-a01-pod-basics-mcq');
// Phase 6B added root-05-m00 (the mental-model module) as the new front door, sequenced
// before the pre-existing bare-lab bank — see docs/PHASE6B_ROOT_CURRICULUM_EXPANSION.md.
assert.equal(body.resume.module_id, 'root-05-m00-kubernetes-mental-model');
assert.equal(body.resume.activity_id, 'root-05-m00-a01-lesson-mental-model');
});
await test('a student cannot mark a lab activity complete manually — only real scenario completion counts', async () => {
@@ -138,8 +140,15 @@ async function main() {
assert.equal(body.error, 'not_manually_completable');
});
await test('completing root-05\'s 20 real scenarios marks the course complete and unlocks root-06', async () => {
await test('completing root-05\'s 20 real scenarios + the Phase 6B mental-model module marks the course complete and unlocks root-06', async () => {
for (const sid of FOUNDATION) await completeScenario(casey, sid);
// Phase 6B's root-05-m00 has its own manual-marker activities (lesson/demonstration/
// knowledge_check/guided_exercise/reflection) that scenario completion alone never
// satisfies — root-05 the COURSE isn't complete until these are marked too.
for (const suffix of ['a01-lesson-mental-model', 'a02-demonstration-desired-state', 'a03-knowledge-check', 'a04-guided-exercise-namespaces', 'a06-reflection']) {
const { status } = await casey.request('POST', `/api/student/activities/root-05-m00-${suffix}/complete`);
assert.equal(status, 200);
}
const { body } = await casey.request('GET', `/api/student/programs/${enrollmentId}/courses`);
const root05 = courseById(body.courses, 'root-05-kubernetes-foundations');
assert.equal(root05.complete, true);
@@ -180,8 +189,11 @@ async function main() {
const goldModule = moduleById(body.courses, 'root-10-m01-health-probes-and-troubleshooting');
assert.equal(goldModule.complete, true);
assert.equal(goldModule.percent_complete, 100);
const nextModule = moduleById(body.courses, 'root-10-m02-general-troubleshooting');
assert.equal(nextModule.locked, false, 'root-10-m02 must unlock once the gold module is complete');
// Phase 6B inserted root-10-m03 (the troubleshooting framework module) between the gold
// module and the pre-existing bare-lab bank — m02's prerequisite is now m03, not m01
// directly. See docs/PHASE6B_ROOT_CURRICULUM_EXPANSION.md.
const frameworkModule = moduleById(body.courses, 'root-10-m03-troubleshooting-framework');
assert.equal(frameworkModule.locked, false, 'root-10-m03 must unlock once the gold module is complete');
});
// ── Assessment/capstone-derived completion (auto_assessment) ────────────────────────────