Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 6 additions & 5 deletions src/lib/lesson-ref.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@
* Grammar: `m<N>l<M>` — lowercase `m`, one-or-more digits, lowercase `l`,
* one-or-more digits. 10xdevs3 ships with modules 0..5 (module 0 is the
* prework), so the module number is range-checked here; lesson numbers
* must be positive (≥ 1) but are not capped (the API is the source of
* truth on per-module lesson counts).
* must be non-negative (≥ 0) but are not capped (the API is the source of
* truth on per-module lesson counts). Lesson 0 is reserved for general,
* non-lesson-bound artifacts (e.g. m0l0 — the General Toolkit).
*
* Accepted: m0l1, m1l1, m1l2, m5l3
* Rejected: M1L1, m1, 1-1, m1l0, m6l1, "", whitespace, trailing garbage
* Accepted: m0l0, m0l1, m1l1, m1l2, m5l3
* Rejected: M1L1, m1, 1-1, m6l1, "", whitespace, trailing garbage
*
* The CLI exposes a single entry — `parseLessonRef` — used by `10x get`
* to validate user input before hitting the API.
Expand Down Expand Up @@ -37,7 +38,7 @@ export function parseLessonRef(raw: string): ParsedLessonRef | null {
const lesson = Number.parseInt(match[2]!, 10);
if (!Number.isFinite(module) || !Number.isFinite(lesson)) return null;
if (module < MIN_MODULE || module > MAX_MODULE) return null;
if (lesson < 1) return null;
if (lesson < 0) return null;
return { lessonId: `m${module}l${lesson}`, module, lesson };
}

Expand Down
12 changes: 8 additions & 4 deletions tests/lesson-ref.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,14 @@ describe("parseLessonRef — valid input", () => {
});
});

it("parses general toolkit m0l0 (lesson 0)", () => {
expect(parseLessonRef("m0l0")).toEqual({
lessonId: "m0l0",
module: 0,
lesson: 0,
});
});

it("exports the module-range constants used by the parser", () => {
expect(MIN_MODULE).toBe(0);
expect(MAX_MODULE).toBe(5);
Expand Down Expand Up @@ -83,10 +91,6 @@ describe("parseLessonRef — rejected input", () => {
expect(parseLessonRef("m-1l1")).toBeNull();
});

it("rejects lesson zero (m1l0)", () => {
expect(parseLessonRef("m1l0")).toBeNull();
});

it("rejects out-of-range module m6l1 (course has modules 1..5)", () => {
expect(parseLessonRef("m6l1")).toBeNull();
});
Expand Down