qwen-code/packages/cli/src/utils/userPromptExpansionHook.test.ts
qqqys a47d26046c
feat(core): add user prompt expansion hooks (#4377)
* feat(core): add user prompt expansion hooks

* fix(cli): handle missing prompt expansion hooks

* fix(cli): preserve prompt expansion hook context

* fix(cli): gate user prompt expansion hooks

* fix(cli): bound prompt expansion context

* fix(cli): surface blocked prompt expansion to skills

* fix(core): fail closed on prompt expansion hook errors

* fix(core): fail open on prompt expansion setup errors

* fix(cli): block model-invocable prompt expansion

* fix(cli): surface prompt expansion block reason

* fix(core): fail closed prompt expansion setup errors

* fix(cli): keep blocked prompt expansion out of skill output

* fix(cli): surface blocked prompt expansion reason

* fix(core): sanitize prompt expansion context chaining

* fix(core): preserve prompt submit hook context

* fix(cli): address user prompt expansion review

* fix(core): harden prompt expansion hook output

* test(cli): cover prompt expansion ampersand escaping

* fix(core): preserve prompt submit hook context

* test(cli): cover MCP prompt expansion hook

* fix(core): route prompt submit hook output through wrapper

* fix(core): return model command hook blocks as errors

* fix(core): sanitize user prompt hook context

* fix(core): harden prompt expansion context sanitizing

* fix(core): forward skill command args

* refactor(core): remove redundant prompt submit hook output

* fix(core): fail open prompt expansion hook setup

* fix(core): include skill args in approval checks

* fix(cli): stop aborted prompt expansion hooks

* fix(core): sanitize skill arg descriptions

* fix(cli): report cancelled skill expansion accurately
2026-06-05 13:59:12 +08:00

116 lines
3.3 KiB
TypeScript

/**
* @license
* Copyright 2026 Qwen Team
* SPDX-License-Identifier: Apache-2.0
*/
import { describe, expect, it } from 'vitest';
import {
appendUserPromptExpansionAdditionalContext,
formatUserPromptExpansionBlockedMessage,
serializeUserPromptExpansionPrompt,
} from './userPromptExpansionHook.js';
describe('appendUserPromptExpansionAdditionalContext', () => {
it('returns content unchanged when additionalContext is undefined', () => {
expect(
appendUserPromptExpansionAdditionalContext('base prompt', undefined),
).toBe('base prompt');
});
it('appends additional context to string prompts', () => {
const result = appendUserPromptExpansionAdditionalContext(
'base prompt',
'hook context',
);
expect(result).toBe('base prompt\n\nhook context');
});
it('appends additional context to part arrays', () => {
const result = appendUserPromptExpansionAdditionalContext(
[{ text: 'base prompt' }],
'hook context',
);
expect(result).toEqual([
{ text: 'base prompt' },
{ text: '\n\nhook context' },
]);
});
it('appends additional context to a single part', () => {
const result = appendUserPromptExpansionAdditionalContext(
{ text: 'base prompt' },
'hook context',
);
expect(result).toEqual([
{ text: 'base prompt' },
{ text: '\n\nhook context' },
]);
});
});
describe('formatUserPromptExpansionBlockedMessage', () => {
it('escapes ampersands before angle brackets', () => {
const result = formatUserPromptExpansionBlockedMessage('a&b<c>');
expect(result).toBe('UserPromptExpansion blocked: a&amp;b&lt;c&gt;');
});
it('sanitizes and truncates block reasons', () => {
const longReason = `<policy>${'x'.repeat(10_000)}</policy>`;
const result = formatUserPromptExpansionBlockedMessage(longReason);
expect(result).toBe(
`UserPromptExpansion blocked: &lt;policy&gt;${'x'.repeat(9_986)}`,
);
expect(result.length).toBe('UserPromptExpansion blocked: '.length + 10_000);
});
it('does not leave a partial entity after truncation', () => {
const result = formatUserPromptExpansionBlockedMessage(
'x'.repeat(9_999) + '<',
);
expect(result).toBe(`UserPromptExpansion blocked: ${'x'.repeat(9_999)}`);
});
it('does not leave a partial ampersand entity after truncation', () => {
const result = formatUserPromptExpansionBlockedMessage(
'x'.repeat(9_998) + '&',
);
expect(result).toBe(`UserPromptExpansion blocked: ${'x'.repeat(9_998)}`);
});
});
describe('serializeUserPromptExpansionPrompt', () => {
it('returns string prompts unchanged', () => {
expect(serializeUserPromptExpansionPrompt('plain prompt')).toBe(
'plain prompt',
);
});
it('serializes part arrays with verbose formatting', () => {
expect(
serializeUserPromptExpansionPrompt([
{ text: 'first' },
{ inlineData: { mimeType: 'text/plain', data: 'ZGF0YQ==' } },
{ text: 'last' },
]),
).toBe('first<text/plain>last');
});
it('serializes a single part object', () => {
expect(serializeUserPromptExpansionPrompt({ text: 'single part' })).toBe(
'single part',
);
});
it('serializes empty part arrays to an empty string', () => {
expect(serializeUserPromptExpansionPrompt([])).toBe('');
});
});