test(phase8): add enrollment operations coverage and Winter Break registration rehearsal
- e2e-phase8.js (16/16): offering CRUD/transitions/readiness, registration open/closed enforcement, capacity + concurrent final-seat race through the real registration endpoints, the public serializer's safety, website integration idempotency (sequential + concurrent), adult/minor paths, required-agreement conversion blocking with version snapshotting, offering->cohort membership, guardian recipient resolution, RBAC, and confirmation ROOT_ V1/self-paced/classroom flows are unaffected. - test-phase8-winter-camp-registration.js (12/12): the full commercial-path story with no payment code — website submits a real minor registration (real guardian created/matched), guardian agreement acceptance, a simulated admin-side payment confirmation, conversion to real enrollment/cohort, real account activation, an independent adult path, two students genuinely competing for the final seat, and a REAL Lab task completed by the minor student with the instructor then seeing the real progress. - e2e-phase7.js: fixed two now-stale test assumptions the new server-authoritative registration-window enforcement broke (an offering left in draft that Phase 7's own test never transitioned before registering against it) and widened a file-allowlist check for the new, legitimate public-offerings-routes.js. Regression: e2e-phase6f.js 17/17, e2e-phase5b.js 32/32, e2e-phase5c.js 29/29, all fresh-stack. Co-Authored-By: Claude Sonnet 5 <[email protected]>
This commit is contained in:
@@ -45,10 +45,12 @@ async function main() {
|
||||
const row = psql(`SELECT family FROM programs WHERE program_id = 'p7-bootcamp-check-${RUN_ID}'`);
|
||||
assert.equal(row, 'bootcamp');
|
||||
const src = execSync('grep -rln "recommended_min_age\\|recommended_max_age" src/*.js', { cwd: `${__dirname}/../api`, stdio: 'pipe' }).toString();
|
||||
// Only offerings.js/admin-offerings-routes.js should ever mention these fields, and neither
|
||||
// is an authorization/RBAC file — confirmed by name, not content-sniffing.
|
||||
// Only offerings.js/admin-offerings-routes.js (Phase 7) and public-offerings-routes.js
|
||||
// (Phase 8's public read-only serializer — displays the fields, never authorizes on them)
|
||||
// should ever mention these fields, and none of the three is an authorization/RBAC file —
|
||||
// confirmed by name, not content-sniffing.
|
||||
for (const file of src.trim().split('\n').filter(Boolean)) {
|
||||
assert.ok(/offerings\.js|admin-offerings-routes\.js/.test(file), `unexpected file referencing age-guidance fields: ${file}`);
|
||||
assert.ok(/offerings\.js|admin-offerings-routes\.js|public-offerings-routes\.js/.test(file), `unexpected file referencing age-guidance fields: ${file}`);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -95,6 +97,11 @@ async function main() {
|
||||
const b = await owner.request('POST', '/api/admin/offerings', { offeringId: offeringB, slug: `p7-camp-b-${RUN_ID}`, name: 'Camp B (self-paced)', programId: PROGRAM, deliveryMode: 'self_paced' });
|
||||
assert.equal(b.status, 201);
|
||||
assert.equal(b.body.capacity, null, 'a self-paced offering should be unlimited by default');
|
||||
// Phase 8 made registration-window enforcement real (server-side, not just client hiding a
|
||||
// button) — a still-draft offering now correctly rejects registration attempts. offeringB
|
||||
// needs to be open before anything in this file registers against it.
|
||||
await owner.request('POST', `/api/admin/offerings/${offeringB}/transition`, { status: 'published' });
|
||||
await owner.request('POST', `/api/admin/offerings/${offeringB}/transition`, { status: 'enrollment_open' });
|
||||
});
|
||||
|
||||
// ── 5: capacity ───────────────────────────────────────────────────────────────────────────
|
||||
@@ -208,6 +215,8 @@ async function main() {
|
||||
const agedOffering = `off-aged-${RUN_ID}`;
|
||||
const create = await owner.request('POST', '/api/admin/offerings', { offeringId: agedOffering, slug: `p7-aged-${RUN_ID}`, name: 'Aged Offering', programId: PROGRAM, deliveryMode: 'workshop', recommendedMinAge: 15, recommendedMaxAge: 18 });
|
||||
assert.equal(create.status, 201);
|
||||
await owner.request('POST', `/api/admin/offerings/${agedOffering}/transition`, { status: 'published' });
|
||||
await owner.request('POST', `/api/admin/offerings/${agedOffering}/transition`, { status: 'enrollment_open' });
|
||||
const reg = await owner.request('POST', `/api/admin/offerings/${agedOffering}/registrations`, { registrantDisplayName: 'Any Age Registrant', registrantEmail: `anyage-${RUN_ID}@example.com` });
|
||||
assert.equal(reg.status, 201, 'no age field on the registration itself, and none is checked');
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user