recreate project

This commit is contained in:
2026-08-01 11:53:13 +08:00
commit 70976ff03a
17 changed files with 1970 additions and 0 deletions
+147
View File
@@ -0,0 +1,147 @@
import { describe, expect, it } from 'bun:test'
import {
generateMosaic,
selectKeywords,
type KeywordCandidate,
} from './mosaic'
const SOURCE =
'我一直以为记忆会忠实地保存一切,后来才发现,每一次回想都在重新编造过去。'
function retain(
candidates: KeywordCandidate[],
words: string[],
): KeywordCandidate[] {
return candidates.map((candidate) => ({
...candidate,
selected: words.includes(candidate.text),
}))
}
describe('selectKeywords', () => {
it('优先提供中文实词候选并排除常见停用词', () => {
const candidates = selectKeywords(SOURCE, 14, 1)
const words = candidates.map((candidate) => candidate.text)
expect(words).toContain('记忆')
expect(words).toContain('保存')
expect(words).toContain('回想')
expect(words).toContain('编造')
expect(words).not.toContain('一直')
expect(words).not.toContain('后来')
})
it('在有候选词时至少自动保留一个', () => {
const candidates = selectKeywords('风穿过废弃车站', 5, 1)
expect(candidates.some((candidate) => candidate.selected)).toBe(true)
})
it('显影 0% 不保留词语,100% 保留全部候选词', () => {
const hidden = selectKeywords(SOURCE, 0, 1)
const revealed = selectKeywords(SOURCE, 100, 1)
expect(hidden.some((candidate) => candidate.selected)).toBe(false)
expect(revealed.every((candidate) => candidate.selected)).toBe(true)
})
})
describe('generateMosaic', () => {
it('保留指定词语并彻底替换原标点', () => {
const candidates = retain(selectKeywords(SOURCE, 14, 1), [
'记忆',
'保存',
'回想',
'编造',
'过去',
])
const result = generateMosaic(SOURCE, candidates, {
volume: 100,
disorder: 58,
seed: 42,
})
expect(result.output).toContain('记忆')
expect(result.output).toContain('保存')
expect(result.output).toContain('回想')
expect(result.output).toContain('编造')
expect(result.output).toContain('过去')
expect(result.output).not.toMatch(/[]/u)
})
it('将总可见长度维持在设定体量附近', () => {
const candidates = selectKeywords(SOURCE, 14, 1)
const result = generateMosaic(SOURCE, candidates, {
volume: 100,
disorder: 58,
seed: 42,
})
const ratio = result.outputLength / result.inputLength
expect(ratio).toBeGreaterThanOrEqual(0.97)
expect(ratio).toBeLessThanOrEqual(1.03)
})
it('支持将体量压缩到 10% 或膨胀到 1000%', () => {
const compressed = generateMosaic(SOURCE, [], {
volume: 10,
disorder: 0,
seed: 42,
})
const expanded = generateMosaic(SOURCE, [], {
volume: 1000,
disorder: 0,
seed: 42,
})
expect(compressed.outputLength).toBe(Math.round(compressed.inputLength * 0.1))
expect(expanded.outputLength).toBe(expanded.inputLength * 10)
})
it('不生成可能跳出正常行框的 Unicode 组合符', () => {
const outputs = Array.from({ length: 20 }, (_, seed) =>
generateMosaic(SOURCE, [], {
volume: 1000,
disorder: 100,
seed,
}).output,
).join('')
expect(outputs).not.toMatch(/\p{Mark}/u)
})
it('相同种子可复现,不同种子会重新侵蚀', () => {
const candidates = selectKeywords(SOURCE, 14, 1)
const first = generateMosaic(SOURCE, candidates, {
volume: 100,
disorder: 58,
seed: 42,
})
const repeated = generateMosaic(SOURCE, candidates, {
volume: 100,
disorder: 58,
seed: 42,
})
const rerolled = generateMosaic(SOURCE, candidates, {
volume: 100,
disorder: 58,
seed: 43,
})
expect(repeated.output).toBe(first.output)
expect(rerolled.output).not.toBe(first.output)
})
it('保留换行但替换每一行中的隐藏内容', () => {
const source = '记忆,在这里。\n过去,在那里。'
const candidates = retain(selectKeywords(source, 20, 1), ['记忆', '过去'])
const result = generateMosaic(source, candidates, {
volume: 100,
disorder: 30,
seed: 7,
})
expect(result.output.split('\n')).toHaveLength(2)
expect(result.output).not.toMatch(/[]/u)
})
})