mirror of
https://github.com/pupperpowell/bibdle.git
synced 2026-08-22 22:32:28 -04:00
Added verse-submission infrastructure
This commit is contained in:
@@ -0,0 +1,141 @@
|
||||
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);
|
||||
}
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user