From e2fcf6be2b4319f1059dc90cf3a0b98d00f5b5fe Mon Sep 17 00:00:00 2001 From: admin Date: Thu, 15 Oct 2026 09:06:15 +0000 Subject: [PATCH] =?UTF-8?q?test:=20=E8=A3=9C=E6=B8=AC=E5=95=9F=E5=8B=95?= =?UTF-8?q?=E6=8C=81=E4=B9=85=E8=88=87=E8=A7=92=E8=89=B2=E5=BE=AE=E8=AA=BF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Sonnet 5.5 --- hooks/persist.test.ts | 76 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 hooks/persist.test.ts diff --git a/hooks/persist.test.ts b/hooks/persist.test.ts new file mode 100644 index 0000000..901dc81 --- /dev/null +++ b/hooks/persist.test.ts @@ -0,0 +1,76 @@ +import { expect, mock, test } from 'claude-code/testing' + +import { CHARACTERS } from './characters' + +const setup = (on: any, active?: string) => { + mock.store(on, active ? { active } : undefined) + on('command.register', () => ({ value: { name: 'x' } }) as never) + on('session.start', () => ({ cwd: '/tmp' }) as never) + on('command.run', () => ({ text: '' })) + on('prompt.compose', () => ({ sections: [] })) +} +const compose = ($: any) => $.prompt.compose({ model: 'm', promptModel: 'm', surfaces: [], tools: [], outputStyle: null, traits: [] } as never) +const persona = async ($: any): Promise => (await compose($)).sections.find((s: any) => s.id === 'jsc-anima:persona')?.text +const start = ($: any) => $.session.start({ source: 'startup', cwd: '/tmp' } as never) + +test('啟動狀態跨 session 保留:下次開啟自動恢復', async ($, on) => { + setup(on, 'sao-suguha') + await start($) + expect(await persona($)).toContain('直葉') +}) + +test('啟動角色後寫入儲存,新 session 恢復;離開後不再恢復', async ($, on) => { + setup(on) + await start($) + await $.command.run({ command: 'sao-asuna', args: '' } as never) + await $.command.run({ command: 'anima-off', args: '' } as never) + await $.command.run({ command: 'sao-suguha', args: '' } as never) + await start($) + expect(await persona($)).toContain('直葉') + await $.command.run({ command: 'anima-off', args: '' } as never) + await start($) + expect(await persona($)).toBeUndefined() +}) + +test('在 /config 關閉 persist 後不恢復', { options: { persist: false } }, async ($, on) => { + setup(on, 'sao-asuna') + await start($) + expect(await persona($)).toBeUndefined() +}) + +test('儲存的角色不存在時不恢復', async ($, on) => { + setup(on, 'no-such-character') + await start($) + expect(await persona($)).toBeUndefined() +}) + +test('微調數值軸:反映在 persona,其餘維持角色包預設,且不改動角色包', { options: { axisGentle: 2, axisHumor: 9 } }, async ($, on) => { + setup(on, 'sao-asuna') + const before = { ...CHARACTERS.find(c => c.slug === 'sao-asuna')!.axes } + await start($) + const text = (await persona($))! + expect(text).toContain('溫柔 2') + expect(text).toContain('幽默 9') + expect(text).toContain(`傲嬌 ${before.tsundere}`) + expect(CHARACTERS.find(c => c.slug === 'sao-asuna')!.axes).toEqual(before) +}) + +test('未微調(-1)沿用角色包預設', { options: { axisGentle: -1 } }, async ($, on) => { + setup(on, 'sao-asuna') + await start($) + expect(await persona($)).toContain(`溫柔 ${CHARACTERS.find(c => c.slug === 'sao-asuna')!.axes.gentle}`) +}) + +test('8 個數值軸都可微調', { + options: { axisGentle: 1, axisTsundere: 2, axisLively: 3, axisFormal: 4, axisTalkative: 5, axisHumor: 6, axisProactive: 7, axisExpressive: 8 }, +}, async ($, on) => { + setup(on, 'sao-asuna') + await start($) + expect(await persona($)).toContain('溫柔 1、傲嬌 2、活潑 3、正式 4、話量 5、幽默 6、主動 7、情緒外顯 8') +}) + +test('超出 0-10 的覆蓋值被忽略', { options: { axisGentle: 11 } }, async ($, on) => { + setup(on, 'sao-asuna') + await start($) + expect(await persona($)).toContain(`溫柔 ${CHARACTERS.find(c => c.slug === 'sao-asuna')!.axes.gentle}`) +}) -- 2.53.0