Coordinator ruling (brief author, 2026-09-26): Aindra -> Indra (因陀罗), Variyana -> Variyan (安逸), Dhanishtha -> Dhanishta (虚宿). Spelling aliases only; the brief's table and its typed test copy are unchanged. BLOCKED item struck through, PROGRESS / checklist / task index updated. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017eEAG8HD3mm8gsKXgk8uU8
125 lines
7.0 KiB
TypeScript
125 lines
7.0 KiB
TypeScript
import assert from "node:assert/strict";
|
|
import test from "node:test";
|
|
|
|
import {
|
|
KARANA_ZH,
|
|
NAKSHATRA_ZH,
|
|
TITHI_ZH,
|
|
YOGA_ZH,
|
|
normalizePanchangaName,
|
|
panchangaNameZh,
|
|
splitTithiName,
|
|
} from "../src/lib/panchanga-names-zh.ts";
|
|
|
|
/* The brief's table (TASK-people-ephemeris-ui-20260926 「中文名对照表」),
|
|
typed a second time here on purpose: the module and this list must agree
|
|
entry for entry, so an edit to either one alone fails. */
|
|
const BRIEF_TITHI: ReadonlyArray<readonly [string, string]> = [
|
|
["Pratipada", "初一"], ["Dwitiya", "初二"], ["Tritiya", "初三"], ["Chaturthi", "初四"],
|
|
["Panchami", "初五"], ["Shashthi", "初六"], ["Saptami", "初七"], ["Ashtami", "初八"],
|
|
["Navami", "初九"], ["Dashami", "初十"], ["Ekadashi", "十一"], ["Dwadashi", "十二"],
|
|
["Trayodashi", "十三"], ["Chaturdashi", "十四"],
|
|
];
|
|
const BRIEF_NAKSHATRA: ReadonlyArray<readonly [string, string]> = [
|
|
["Ashwini", "娄宿"], ["Bharani", "胃宿"], ["Krittika", "昴宿"], ["Rohini", "毕宿"],
|
|
["Mrigashira", "觜宿"], ["Ardra", "参宿"], ["Punarvasu", "井宿"], ["Pushya", "鬼宿"],
|
|
["Ashlesha", "柳宿"], ["Magha", "星宿"], ["Purva Phalguni", "张宿"], ["Uttara Phalguni", "翼宿"],
|
|
["Hasta", "轸宿"], ["Chitra", "角宿"], ["Swati", "亢宿"], ["Vishakha", "氐宿"],
|
|
["Anuradha", "房宿"], ["Jyeshtha", "心宿"], ["Mula", "尾宿"], ["Purva Ashadha", "箕宿"],
|
|
["Uttara Ashadha", "斗宿"], ["Shravana", "女宿"], ["Dhanishta", "虚宿"], ["Shatabhisha", "危宿"],
|
|
["Purva Bhadrapada", "室宿"], ["Uttara Bhadrapada", "壁宿"], ["Revati", "奎宿"],
|
|
];
|
|
const BRIEF_YOGA: ReadonlyArray<readonly [string, string]> = [
|
|
["Vishkambha", "支柱"], ["Priti", "喜爱"], ["Ayushman", "长寿"], ["Saubhagya", "好运"],
|
|
["Shobhana", "光彩"], ["Atiganda", "大险"], ["Sukarma", "善业"], ["Dhriti", "坚持"],
|
|
["Shula", "刺痛"], ["Ganda", "险结"], ["Vriddhi", "增长"], ["Dhruva", "恒定"],
|
|
["Vyaghata", "打击"], ["Harshana", "欢喜"], ["Vajra", "金刚"], ["Siddhi", "成就"],
|
|
["Vyatipata", "灾变"], ["Variyan", "安逸"], ["Parigha", "阻碍"], ["Shiva", "吉祥"],
|
|
["Siddha", "圆满"], ["Sadhya", "可成"], ["Shubha", "吉善"], ["Shukla", "清白"],
|
|
["Brahma", "梵天"], ["Indra", "因陀罗"], ["Vaidhriti", "分离"],
|
|
];
|
|
const BRIEF_KARANA: ReadonlyArray<readonly [string, string]> = [
|
|
["Bava", "巴瓦"], ["Balava", "巴拉瓦"], ["Kaulava", "考拉瓦"], ["Taitila", "泰提拉"],
|
|
["Garija", "加拉"], ["Vanija", "瓦尼贾"], ["Vishti", "毗湿提"], ["Shakuni", "沙昆尼"],
|
|
["Chatushpada", "四足"], ["Naga", "蛇"], ["Kimstughna", "金斯图格纳"],
|
|
];
|
|
|
|
test("the module tables are exactly the brief's tables, nothing added or dropped", () => {
|
|
assert.deepEqual(
|
|
Object.entries(TITHI_ZH).sort(),
|
|
[...BRIEF_TITHI, ["Purnima", "望日(满月)"], ["Amavasya", "朔日(新月)"]].sort(),
|
|
);
|
|
assert.deepEqual(Object.entries(NAKSHATRA_ZH).sort(), [...BRIEF_NAKSHATRA].sort());
|
|
assert.deepEqual(Object.entries(YOGA_ZH).sort(), [...BRIEF_YOGA].sort());
|
|
assert.deepEqual(Object.entries(KARANA_ZH).sort(), [...BRIEF_KARANA].sort());
|
|
assert.equal(Object.keys(NAKSHATRA_ZH).length, 27);
|
|
assert.equal(Object.keys(YOGA_ZH).length, 27);
|
|
assert.equal(Object.keys(KARANA_ZH).length, 11);
|
|
});
|
|
|
|
test("every tithi reads 白分 / 黑分 + day, full and new moon keep their own label", () => {
|
|
for (const [name, day] of BRIEF_TITHI) {
|
|
assert.equal(panchangaNameZh("tithi", `Shukla ${name}`), `白分${day}`, name);
|
|
assert.equal(panchangaNameZh("tithi", `Krishna ${name}`), `黑分${day}`, name);
|
|
}
|
|
assert.equal(panchangaNameZh("tithi", "Shukla Purnima"), "望日(满月)");
|
|
assert.equal(panchangaNameZh("tithi", "Krishna Amavasya"), "朔日(新月)");
|
|
assert.equal(panchangaNameZh("tithi", "Purnima"), "望日(满月)");
|
|
assert.equal(panchangaNameZh("tithi", "Amavasya"), "朔日(新月)");
|
|
// A bare day name has no fortnight; it is not guessed into one.
|
|
assert.equal(panchangaNameZh("tithi", "Dashami"), null);
|
|
});
|
|
|
|
test("every nakshatra, yoga and karana in the brief resolves", () => {
|
|
for (const [name, zh] of BRIEF_NAKSHATRA) assert.equal(panchangaNameZh("nakshatra", name), zh, name);
|
|
for (const [name, zh] of BRIEF_YOGA) assert.equal(panchangaNameZh("yoga", name), zh, name);
|
|
for (const [name, zh] of BRIEF_KARANA) assert.equal(panchangaNameZh("karana", name), zh, name);
|
|
});
|
|
|
|
test("the three listed aliases resolve to the same entry", () => {
|
|
assert.equal(panchangaNameZh("karana", "Gara"), "加拉");
|
|
assert.equal(panchangaNameZh("karana", "Garija"), "加拉");
|
|
assert.equal(panchangaNameZh("tithi", "Shukla Dvitiya"), "白分初二");
|
|
assert.equal(panchangaNameZh("tithi", "Krishna Dwitiya"), "黑分初二");
|
|
assert.equal(panchangaNameZh("tithi", "Shukla Shashti"), "白分初六");
|
|
assert.equal(panchangaNameZh("tithi", "Krishna Shashthi"), "黑分初六");
|
|
});
|
|
|
|
test("the engine's own spellings reach the existing entries (coordinator ruling 2026-09-26)", () => {
|
|
// `scripts/muhurta.py` spells these three differently from the brief's table.
|
|
// 原值:6de277a4 里断言这三个引擎拼法返回 null(照写梵文、待裁决)。
|
|
// 新值:映射到表内既有条目。
|
|
// 原因:协调方 2026-09-26 以任务书作者身份裁定补拼写别名,译名不变。
|
|
assert.equal(panchangaNameZh("yoga", "Aindra"), "因陀罗");
|
|
assert.equal(panchangaNameZh("yoga", "Indra"), "因陀罗");
|
|
assert.equal(panchangaNameZh("yoga", "Variyana"), "安逸");
|
|
assert.equal(panchangaNameZh("yoga", "Variyan"), "安逸");
|
|
assert.equal(panchangaNameZh("nakshatra", "Dhanishtha"), "虚宿");
|
|
assert.equal(panchangaNameZh("nakshatra", "Dhanishta"), "虚宿");
|
|
// An alias stays inside its own limb.
|
|
assert.equal(panchangaNameZh("nakshatra", "Aindra"), null);
|
|
});
|
|
|
|
test("case, spaces and hyphens are ignored", () => {
|
|
assert.equal(normalizePanchangaName("Uttara-Bhadra pada"), "uttarabhadrapada");
|
|
assert.equal(panchangaNameZh("nakshatra", "uttara bhadrapada"), "壁宿");
|
|
assert.equal(panchangaNameZh("nakshatra", "UTTARA-BHADRAPADA"), "壁宿");
|
|
assert.equal(panchangaNameZh("nakshatra", "PurvaPhalguni"), "张宿");
|
|
assert.equal(panchangaNameZh("tithi", "shukla-purnima"), "望日(满月)");
|
|
assert.equal(panchangaNameZh("karana", " vishti "), "毗湿提");
|
|
});
|
|
|
|
test("a name the table does not know returns null so the caller keeps the engine name", () => {
|
|
assert.equal(panchangaNameZh("nakshatra", "Abhijit"), null);
|
|
assert.equal(panchangaNameZh("yoga", ""), null);
|
|
assert.equal(panchangaNameZh("karana", "Unknown"), null);
|
|
// A yoga name is not looked up in another limb's table.
|
|
assert.equal(panchangaNameZh("nakshatra", "Vriddhi"), null);
|
|
});
|
|
|
|
test("tithi split separates the fortnight from the day", () => {
|
|
assert.deepEqual(splitTithiName("Shukla Purnima"), { paksha: "shukla", name: "purnima" });
|
|
assert.deepEqual(splitTithiName("Krishna Amavasya"), { paksha: "krishna", name: "amavasya" });
|
|
assert.deepEqual(splitTithiName("Navami"), { paksha: null, name: "navami" });
|
|
});
|