import { test } from 'node:test'
import assert from 'node:assert/strict'
import {
htmlToText,
parseModelJson,
callModel,
DASHSCOPE_URL,
MODELS_URL,
MODEL_ID
} from './extract.mjs'
const HTML = `
<h3><strong>英雄</strong></h3>
<h4 style="x"><img src="x" /><em><strong>沙漠皇帝 阿兹尔</strong></em></h4>
<p> W - 沙兵现身<br /> 【征服者】现在会在每次命中时叠加2层,而不是1层</p>
<h4><img src="w" /><em>德玛西亚之力 盖伦</em></h4>
<p>真实伤害:150 / 250 / 350 → 125 / 200 / 275</p>
`
test('htmlToText 去标签、转实体、保留行结构', () => {
const text = htmlToText(HTML)
assert.match(text, /沙漠皇帝 阿兹尔/)
assert.match(text, /真实伤害:150 \/ 250 \/ 350 → 125 \/ 200 \/ 275/)
assert.ok(!text.includes('<'))
assert.ok(!text.includes('→'))
})
test('htmlToText 解码大小写混合的箭头实体与中文标点实体', () => {
const text = htmlToText(
'<p>移动速度:330 ⇒ 335</p><p>“引号”…©—</p>'
)
assert.match(text, /移动速度:330 → 335/)
assert.equal(text.includes('&'), false)
assert.match(text, /“引号”…©—/)
})
test('parseModelJson 容忍代码栅栏包装', () => {
assert.deepEqual(parseModelJson('```json\n{"a":1}\n```'), { a: 1 })
assert.deepEqual(parseModelJson('{"a":1}'), { a: 1 })
})
test('callModel 发 POST 到 DashScope 并解析回包', async () => {
let captured = null
const fetchFn = async (url, init) => {
captured = { url, init }
return {
ok: true,
json: async () => ({
choices: [{ message: { content: '{"isPatchNotes":true,"champions":[]}' } }]
})
}
}
const out = await callModel('正文', 'tok', fetchFn)
assert.equal(captured.url, DASHSCOPE_URL)
assert.equal(MODELS_URL, DASHSCOPE_URL)
assert.equal(captured.init.method, 'POST')
assert.equal(captured.init.headers['Content-Type'], 'application/json')
assert.equal(JSON.parse(captured.init.body).model, MODEL_ID)
assert.equal(JSON.parse(captured.init.body).temperature, 0)
assert.equal(captured.init.headers.Authorization, 'Bearer tok')
assert.deepEqual(out, { isPatchNotes: true, champions: [] })
})
test('callModel 非 200 抛错', async () => {
const fetchFn = async () => ({ ok: false, status: 429, text: async () => 'rate limited' })
await assert.rejects(() => callModel('x', 'tok', fetchFn), /DashScope HTTP 429/)
})