79 lines
3.4 KiB
JavaScript
79 lines
3.4 KiB
JavaScript
/**
|
||||
|
|
* `src/web/locale-pref.ts` —— 语言偏好写进 `settings.yaml` 的**按行对账**逻辑。
|
|||
|
|
*
|
|||
|
|
* 为什么专门测它:这个函数**直接改用户的 `settings.yaml`** —— 写坏了 dsh 会拒绝加载整个文档
|
|||
|
|
* (用户实例直接起不来)。所以这里钉的是三件事:
|
|||
|
|
* · 只碰 `locale.preference` 那两行,**其它块 / 注释 / 顺序一律原样保留**;
|
|||
|
|
* · 幂等(值相同 ⇒ `changed: false`,调用方据此跳过写盘);
|
|||
|
|
* · 行尾风格跟着原文件走(CRLF 文件不会被写成混合行尾)。
|
|||
|
|
*/
|
|||
|
|
import { test } from 'node:test'
|
|||
|
|
import assert from 'node:assert/strict'
|
|||
|
|
import { LOCALE_IDS, isLocaleId, reconcileLocalePreference } from '../lib/web/locale-pref.js'
|
|||
|
|
|
|||
|
|
test('LOCALE_IDS 与官方 dsh-client-locale 一致(en 是官方 FALLBACK)', () => {
|
|||
|
|
assert.deepEqual([...LOCALE_IDS], ['en', 'zh'])
|
|||
|
|
assert.equal(isLocaleId('en'), true)
|
|||
|
|
assert.equal(isLocaleId('zh'), true)
|
|||
|
|
assert.equal(isLocaleId('ja'), false)
|
|||
|
|
assert.equal(isLocaleId(''), false)
|
|||
|
|
assert.equal(isLocaleId(undefined), false)
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
test('空文档 ⇒ 建出 locale 块', () => {
|
|||
|
|
const r = reconcileLocalePreference('', 'zh')
|
|||
|
|
assert.equal(r.changed, true)
|
|||
|
|
assert.equal(r.text, 'locale:\n preference: zh\n')
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
test('没有 locale 块 ⇒ 追加,且不动已有内容', () => {
|
|||
|
|
const src = 'llm-pi-ai:\n providers:\n my-gw:\n baseURL: https://x\n'
|
|||
|
|
const r = reconcileLocalePreference(src, 'zh')
|
|||
|
|
assert.equal(r.changed, true)
|
|||
|
|
assert.ok(r.text.startsWith(src), '原有内容必须原样在前')
|
|||
|
|
assert.ok(r.text.includes('locale:\n preference: zh'))
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
test('已有 locale 块但没有 preference ⇒ 紧跟块头插入一行', () => {
|
|||
|
|
const src = 'locale:\n other: keep\nfoo: bar\n'
|
|||
|
|
const r = reconcileLocalePreference(src, 'zh')
|
|||
|
|
assert.equal(r.text, 'locale:\n preference: zh\n other: keep\nfoo: bar\n')
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
test('值不同 ⇒ 原地替换(只改那一行)', () => {
|
|||
|
|
const src = 'a: 1\nlocale:\n preference: en\nb: 2\n'
|
|||
|
|
const r = reconcileLocalePreference(src, 'zh')
|
|||
|
|
assert.equal(r.changed, true)
|
|||
|
|
assert.equal(r.text, 'a: 1\nlocale:\n preference: zh\nb: 2\n')
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
test('值相同 ⇒ 幂等(changed: false,文本一字不动)', () => {
|
|||
|
|
const src = 'locale:\n preference: zh\n'
|
|||
|
|
const r = reconcileLocalePreference(src, 'zh')
|
|||
|
|
assert.equal(r.changed, false)
|
|||
|
|
assert.equal(r.text, src)
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
test('注释与其它块一律保留', () => {
|
|||
|
|
const src = '# 我自己的注释\nllm-pi-ai:\n # 内层注释\n providers: {}\nlocale:\n preference: en\n'
|
|||
|
|
const r = reconcileLocalePreference(src, 'zh')
|
|||
|
|
assert.ok(r.text.includes('# 我自己的注释'))
|
|||
|
|
assert.ok(r.text.includes(' # 内层注释'))
|
|||
|
|
assert.ok(r.text.includes(' preference: zh'))
|
|||
|
|
assert.equal(r.text.split('\n').length, src.split('\n').length, '行数不变(没多写也没少写)')
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
test('CRLF 文档 ⇒ 保持 CRLF(不产生混合行尾)', () => {
|
|||
|
|
const src = 'a: 1\r\nlocale:\r\n preference: en\r\n'
|
|||
|
|
const r = reconcileLocalePreference(src, 'zh')
|
|||
|
|
assert.ok(r.text.includes(' preference: zh\r\n'))
|
|||
|
|
assert.equal(r.text.includes('\n\n'), false)
|
|||
|
|
assert.equal(r.text.replace(/\r\n/g, '').includes('\n'), false, '不许出现裸 LF')
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
test('两次调用等价(第二次不再 changed)', () => {
|
|||
|
|
const first = reconcileLocalePreference('x: 1\n', 'zh')
|
|||
|
|
const second = reconcileLocalePreference(first.text, 'zh')
|
|||
|
|
assert.equal(second.changed, false)
|
|||
|
|
assert.equal(second.text, first.text)
|
|||
|
|
})
|