mirror of
https://github.com/NomaDamas/k-skill.git
synced 2026-09-14 16:37:18 +08:00
720964cf49
This snapshots the current repository updates as a coherent release-prep baseline: workspace/package scaffolding, release automation docs and workflows, refreshed skill/setup documentation, roadmap expansion, and the README thumbnail polish. Constraint: Node packages in this repo must use npm workspaces and Changesets for releases Constraint: Python release automation stays scaffold-only until a real package exists Rejected: Split the current work into multiple commits | user asked to commit the current changes together Confidence: medium Scope-risk: moderate Reversibility: clean Directive: Keep release docs, workflows, and package metadata aligned when adding future packages Tested: npm run ci Not-tested: GitHub Actions execution on remote after push
96 lines
2.8 KiB
JavaScript
96 lines
2.8 KiB
JavaScript
const test = require("node:test");
|
|
const assert = require("node:assert/strict");
|
|
const fs = require("node:fs");
|
|
const path = require("node:path");
|
|
|
|
const {
|
|
evaluateTicket,
|
|
getDetailResult,
|
|
getLatestRound,
|
|
getResult
|
|
} = require("../src/index");
|
|
const {
|
|
extractLatestRoundFromHtml,
|
|
normalizeRoundItem,
|
|
selectRoundItem
|
|
} = require("../src/parse");
|
|
|
|
const fixturesDir = path.join(__dirname, "fixtures");
|
|
const latestResultHtml = fs.readFileSync(path.join(fixturesDir, "latest-result.html"), "utf8");
|
|
const round1216Payload = JSON.parse(
|
|
fs.readFileSync(path.join(fixturesDir, "round-1216.json"), "utf8")
|
|
);
|
|
|
|
test("extractLatestRoundFromHtml parses the latest round from the official result page", () => {
|
|
assert.equal(extractLatestRoundFromHtml(latestResultHtml), 1216);
|
|
});
|
|
|
|
test("normalizeRoundItem maps official JSON into the public detail shape", () => {
|
|
const detail = normalizeRoundItem(selectRoundItem(round1216Payload, 1216));
|
|
|
|
assert.equal(detail.round, 1216);
|
|
assert.equal(detail.drawDate, "2026-03-21");
|
|
assert.deepEqual(detail.numbers, [3, 10, 14, 15, 23, 24]);
|
|
assert.equal(detail.bonus, 25);
|
|
assert.equal(detail.payouts[0].rank, 1);
|
|
assert.equal(detail.payouts[0].winners, 14);
|
|
assert.equal(detail.winnersByPurchaseType?.auto, 8);
|
|
});
|
|
|
|
test("evaluateTicket returns the right rank for an exact match", () => {
|
|
const detail = normalizeRoundItem(selectRoundItem(round1216Payload, 1216));
|
|
const checked = evaluateTicket(detail, [3, 10, 14, 15, 23, 24]);
|
|
|
|
assert.equal(checked.rank, 1);
|
|
assert.equal(checked.outcome, "1등");
|
|
assert.deepEqual(checked.matchedNumbers, [3, 10, 14, 15, 23, 24]);
|
|
});
|
|
|
|
test("evaluateTicket rejects duplicate numbers", () => {
|
|
const detail = normalizeRoundItem(selectRoundItem(round1216Payload, 1216));
|
|
|
|
assert.throws(() => {
|
|
evaluateTicket(detail, [3, 3, 14, 15, 23, 24]);
|
|
}, /duplicates/);
|
|
});
|
|
|
|
test("public fetchers can consume injected fixtures via mocked fetch", async () => {
|
|
const originalFetch = global.fetch;
|
|
|
|
global.fetch = async (url) => {
|
|
if (String(url).includes("/lt645/result")) {
|
|
return makeResponse(true, latestResultHtml);
|
|
}
|
|
|
|
return makeResponse(true, round1216Payload);
|
|
};
|
|
|
|
try {
|
|
assert.equal(await getLatestRound(), 1216);
|
|
assert.deepEqual(await getResult(1216), {
|
|
round: 1216,
|
|
drawDate: "2026-03-21",
|
|
numbers: [3, 10, 14, 15, 23, 24],
|
|
bonus: 25
|
|
});
|
|
|
|
const detail = await getDetailResult(1216);
|
|
assert.equal(detail.payouts[1].rank, 2);
|
|
} finally {
|
|
global.fetch = originalFetch;
|
|
}
|
|
});
|
|
|
|
/**
|
|
* @param {boolean} ok
|
|
* @param {string|object} body
|
|
*/
|
|
function makeResponse(ok, body) {
|
|
return new Response(typeof body === "string" ? body : JSON.stringify(body), {
|
|
status: ok ? 200 : 500,
|
|
headers: {
|
|
"content-type": typeof body === "string" ? "text/html" : "application/json"
|
|
}
|
|
});
|
|
}
|