mirror of
https://github.com/pupperpowell/bibdle.git
synced 2026-08-22 22:32:28 -04:00
142 lines
5.0 KiB
TypeScript
142 lines
5.0 KiB
TypeScript
import { describe, test, expect } from "bun:test";
|
|
import {
|
|
composeVerseWindow,
|
|
formatWindowReference,
|
|
getChapterCount,
|
|
getVerseCount,
|
|
extractVerses,
|
|
} from "$lib/server/xml-bible";
|
|
import { bookIdToNumber, getBookById } from "$lib/server/bible";
|
|
|
|
describe("getChapterCount / getVerseCount", () => {
|
|
test("Genesis has 50 chapters", () => {
|
|
expect(getChapterCount(bookIdToNumber["GEN"])).toBe(50);
|
|
});
|
|
|
|
test("Jude has 1 chapter", () => {
|
|
expect(getChapterCount(bookIdToNumber["JUD"])).toBe(1);
|
|
});
|
|
|
|
test("Genesis 1 has 31 verses", () => {
|
|
expect(getVerseCount(bookIdToNumber["GEN"], 1)).toBe(31);
|
|
});
|
|
});
|
|
|
|
describe("composeVerseWindow — default [v-2, v-1, v]", () => {
|
|
test("Genesis 1:3 → window is 1:1-3 (same chapter, hyphen)", () => {
|
|
const w = composeVerseWindow(bookIdToNumber["GEN"], 1, 3);
|
|
expect(w).not.toBeNull();
|
|
expect(w!.startChapter).toBe(1);
|
|
expect(w!.startVerse).toBe(1);
|
|
expect(w!.endChapter).toBe(1);
|
|
expect(w!.endVerse).toBe(3);
|
|
expect(w!.verses).toHaveLength(3);
|
|
expect(formatWindowReference(w!.bookName, w!.startChapter, w!.startVerse, w!.endChapter, w!.endVerse))
|
|
.toBe("Genesis 1:1-3");
|
|
});
|
|
|
|
test("selected verse is the last in the default window", () => {
|
|
const w = composeVerseWindow(bookIdToNumber["GEN"], 1, 10);
|
|
expect(w!.selectedVerse).toBe(10);
|
|
// verses[2] should equal Genesis 1:10's text
|
|
const direct = extractVerses(bookIdToNumber["GEN"], 1, 10, 1);
|
|
expect(w!.verses[2]).toBe(direct[0]);
|
|
});
|
|
});
|
|
|
|
describe("composeVerseWindow — fall-forward at book start", () => {
|
|
test("Genesis 1:1 → fall forward to [1,2,3]", () => {
|
|
const w = composeVerseWindow(bookIdToNumber["GEN"], 1, 1);
|
|
expect(w!.startChapter).toBe(1);
|
|
expect(w!.startVerse).toBe(1);
|
|
expect(w!.endChapter).toBe(1);
|
|
expect(w!.endVerse).toBe(3);
|
|
expect(w!.verses).toHaveLength(3);
|
|
expect(formatWindowReference(w!.bookName, w!.startChapter, w!.startVerse, w!.endChapter, w!.endVerse))
|
|
.toBe("Genesis 1:1-3");
|
|
});
|
|
|
|
test("Genesis 1:2 → fall forward to [2,3,4] (v-2 < 1)", () => {
|
|
const w = composeVerseWindow(bookIdToNumber["GEN"], 1, 2);
|
|
expect(w!.startVerse).toBe(2);
|
|
expect(w!.endVerse).toBe(4);
|
|
expect(w!.verses).toHaveLength(3);
|
|
});
|
|
|
|
test("John 1:1 → fall forward", () => {
|
|
const w = composeVerseWindow(bookIdToNumber["JHN"], 1, 1);
|
|
expect(w!.bookId).toBe("JHN");
|
|
expect(w!.startVerse).toBe(1);
|
|
expect(w!.endVerse).toBe(3);
|
|
});
|
|
});
|
|
|
|
describe("composeVerseWindow — cross-chapter within a book", () => {
|
|
test("Genesis 2:1 → window spans 1:30-2:1 (en-dash, two chapters)", () => {
|
|
const w = composeVerseWindow(bookIdToNumber["GEN"], 2, 1);
|
|
expect(w!.startChapter).toBe(1);
|
|
expect(w!.endChapter).toBe(2);
|
|
// Genesis 1 has 31 verses; selected index for 2:1 = 31 + 1 = 32; window = [30,31,32]
|
|
expect(w!.startVerse).toBe(30);
|
|
expect(w!.startChapter === 1 && w!.endChapter === 2).toBe(true);
|
|
expect(w!.verses).toHaveLength(3);
|
|
const ref = formatWindowReference(w!.bookName, w!.startChapter, w!.startVerse, w!.endChapter, w!.endVerse);
|
|
expect(ref).toBe("Genesis 1:30\u20132:1");
|
|
});
|
|
|
|
test("window never crosses a book boundary", () => {
|
|
// Every book has >= 3 verses, and windows are constrained in-book by construction.
|
|
// Spot-check the last verse of Malachi 4 (end of OT) still resolves in-book.
|
|
const mal = bookIdToNumber["MAL"];
|
|
const lastChapter = getChapterCount(mal);
|
|
const lastVerse = getVerseCount(mal, lastChapter);
|
|
const w = composeVerseWindow(mal, lastChapter, lastVerse);
|
|
expect(w).not.toBeNull();
|
|
expect(w!.bookId).toBe("MAL");
|
|
expect(w!.endChapter).toBe(lastChapter);
|
|
});
|
|
});
|
|
|
|
describe("composeVerseWindow — invalid input", () => {
|
|
test("unknown book number returns null", () => {
|
|
expect(composeVerseWindow(999, 1, 1)).toBeNull();
|
|
});
|
|
|
|
test("chapter out of range returns null", () => {
|
|
expect(composeVerseWindow(bookIdToNumber["GEN"], 999, 1)).toBeNull();
|
|
});
|
|
|
|
test("verse out of range returns null", () => {
|
|
expect(composeVerseWindow(bookIdToNumber["GEN"], 1, 9999)).toBeNull();
|
|
});
|
|
});
|
|
|
|
describe("formatWindowReference", () => {
|
|
test("same chapter, range", () => {
|
|
expect(formatWindowReference("Genesis", 1, 1, 1, 3)).toBe("Genesis 1:1-3");
|
|
});
|
|
|
|
test("same chapter, single verse", () => {
|
|
expect(formatWindowReference("Jude", 1, 5, 1, 5)).toBe("Jude 1:5");
|
|
});
|
|
|
|
test("cross chapter uses en-dash", () => {
|
|
expect(formatWindowReference("Genesis", 1, 31, 2, 1)).toBe("Genesis 1:31\u20132:1");
|
|
});
|
|
});
|
|
|
|
describe("composeVerseWindow — every book's opening verse is safe", () => {
|
|
// The spec guarantees every book has >=3 verses after its opening, so Gen 1:1-style
|
|
// fall-forward must succeed for the first verse of every book.
|
|
test("book 1:1 of all 66 books resolves to a 3-verse in-book window", () => {
|
|
const ids = Object.keys(bookIdToNumber);
|
|
for (const id of ids) {
|
|
const num = bookIdToNumber[id];
|
|
const w = composeVerseWindow(num, 1, 1);
|
|
expect(w, `${getBookById(id)!.name} 1:1 should resolve`).not.toBeNull();
|
|
expect(w!.verses).toHaveLength(3);
|
|
expect(w!.bookId).toBe(id);
|
|
}
|
|
});
|
|
});
|