Files
text-mosaic/src/mosaic.test.ts
T
2026-07-25 23:11:25 +08:00

148 lines
4.2 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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)
})
})