test(phase6c): acceptance suite, real-k3s rehearsal, and 6A/6B staleness fixes
Adds platform/test/e2e-phase6c.js (17 checks): upgraded-module activity
chains, documented-exception promotions, optional-lab non-blocking behavior,
no-fake-completion-from-viewing, idempotent completion, instructor-notes
exclusion and RBAC on new content, security-assessment pool/draw/cross-student
isolation/completion, the capstone gate's documented indifference to ROOT 09,
ROOT 11's honest partial/lesson-only rendering, and version-bump content
stability — plus running validate-course-content.js as part of the suite.
All 17 pass against both a warm and a freshly-reset dev stack.
Adds infra/scripts/drills/{run,test}-phase6c-security-config-rehearsal —
Section 22's real-k3s dress rehearsal: login -> program -> course -> lesson ->
demonstration -> knowledge-check -> guided-exercise -> real Lab -> real
kubectl (create Secret, apply Pod, wait for Ready) -> validation -> progress
persistence across logout/login -> instructor visibility of the resulting
learning state, walking root-07-m02-configuration-and-secrets (upgraded from
PARTIAL this phase). 10/10 pass against a real k3s Lab, no mocking.
Fixes two stale hardcoded assumptions in e2e-phase6a.js and e2e-phase6b.js
that Phase 6C's own legitimate content changes broke: the gold module
(root-10-m01) now has 7 activities (6 required + 1 new optional lab, not 6),
and root-05-m01 now has 2 required manual-marker activities the course-
completion path must also complete (its new bookending lesson/reflection).
Both suites are back to green (21/21 and 24/24) against a fresh stack.
Co-Authored-By: Claude Sonnet 5 <[email protected]>
This commit is contained in:
@@ -83,12 +83,15 @@ async function main() {
|
||||
assert.equal(gold.status, 'partial'); // real content (gold module) alongside un-authored modules
|
||||
});
|
||||
|
||||
await test('the gold-standard module is fully READY with 6 ordered activities and real content', async () => {
|
||||
await test('the gold-standard module is fully READY with 7 ordered activities and real content', async () => {
|
||||
// Phase 6C added one OPTIONAL practice lab (readiness-probe-task) after the original
|
||||
// required lab — see docs/PHASE6C_CURRICULUM_COMPLETION.md. The required teaching chain
|
||||
// (lesson/demo/kc/guided/lab/reflection) is otherwise byte-for-byte unchanged.
|
||||
const goldModule = moduleById(structure, 'root-10-m01-health-probes-and-troubleshooting');
|
||||
assert.equal(goldModule.status, 'ready');
|
||||
assert.equal(goldModule.activities.length, 6);
|
||||
assert.equal(goldModule.activities.length, 7);
|
||||
const types = goldModule.activities.map((a) => a.activity_type);
|
||||
assert.deepEqual(types, ['lesson', 'demonstration', 'knowledge_check', 'guided_exercise', 'lab', 'reflection']);
|
||||
assert.deepEqual(types, ['lesson', 'demonstration', 'knowledge_check', 'guided_exercise', 'lab', 'lab', 'reflection']);
|
||||
for (let i = 1; i < goldModule.activities.length; i++) assert.ok(goldModule.activities[i].sequence > goldModule.activities[i - 1].sequence);
|
||||
const lesson = goldModule.activities[0];
|
||||
assert.ok(lesson.body && lesson.body.length > 500, 'lesson content must actually be loaded from Markdown, not empty');
|
||||
@@ -149,6 +152,13 @@ async function main() {
|
||||
const { status } = await casey.request('POST', `/api/student/activities/root-05-m00-${suffix}/complete`);
|
||||
assert.equal(status, 200);
|
||||
}
|
||||
// Phase 6C added a bookending lesson + reflection around root-05-m01's 20-task kubectl-
|
||||
// fluency bank (see docs/PHASE6C_CURRICULUM_COMPLETION.md) — both are required manual
|
||||
// markers, so root-05 isn't complete until they're marked too.
|
||||
for (const suffix of ['a00-lesson-kubectl-fluency', 'a21-reflection']) {
|
||||
const { status } = await casey.request('POST', `/api/student/activities/root-05-m01-${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);
|
||||
@@ -187,8 +197,8 @@ async function main() {
|
||||
assert.equal(status, 200);
|
||||
const { body } = await casey.request('GET', `/api/student/programs/${enrollmentId}/courses`);
|
||||
const goldModule = moduleById(body.courses, 'root-10-m01-health-probes-and-troubleshooting');
|
||||
assert.equal(goldModule.complete, true);
|
||||
assert.equal(goldModule.percent_complete, 100);
|
||||
assert.equal(goldModule.complete, true, 'a module is complete once every REQUIRED activity is done, regardless of the one optional lab left untouched');
|
||||
assert.equal(goldModule.percent_complete, Math.round((6 / 7) * 100), 'percent_complete reflects ALL activities, optional included — Phase 6C added one optional lab');
|
||||
// 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.
|
||||
@@ -301,7 +311,7 @@ async function main() {
|
||||
psql(`UPDATE course_modules SET version = version + 1 WHERE module_id = 'root-10-m01-health-probes-and-troubleshooting'`);
|
||||
const { body } = await owner.request('GET', `/api/admin/programs/${PROGRAM}/courses`);
|
||||
const goldModule = moduleById(body, 'root-10-m01-health-probes-and-troubleshooting');
|
||||
assert.equal(goldModule.activities.length, 6, 'a version bump must never drop activities');
|
||||
assert.equal(goldModule.activities.length, 7, 'a version bump must never drop activities');
|
||||
assert.ok(goldModule.activities[0].body && goldModule.activities[0].body.length > 500, 'content must still load after a version bump');
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user