Files
hapi/cli/src/codex/utils/slashCommands.test.ts
T
ejj.ccandGitHub d160203bb2 fix(codex): support dynamic reasoning efforts (#1012)
* fix(codex): support model-reported reasoning efforts

* fix(web): prevent service worker edge caching

* ci: retrigger stuck Actions run

* fix(codex): accept dynamic reasoning effort values

* fix(web): restore reasoning effort on model switch failure
2026-07-12 18:38:56 +08:00

164 lines
5.9 KiB
TypeScript

import { describe, expect, it } from 'vitest';
import { resolveCodexSlashCommand } from './slashCommands';
const state = {
permissionMode: 'default' as const,
collaborationMode: 'default' as const,
model: 'gpt-5.5',
modelReasoningEffort: 'high' as const
};
describe('resolveCodexSlashCommand', () => {
it('enables plan mode without sending a turn', () => {
expect(resolveCodexSlashCommand('/plan', state)).toEqual({
kind: 'handled',
message: 'Codex plan mode enabled',
updates: { collaborationMode: 'plan' }
});
});
it('enables plan mode and sends prompt when /plan has text', () => {
expect(resolveCodexSlashCommand('/plan design the fix', state)).toEqual({
kind: 'replace',
text: 'design the fix',
message: 'Codex plan mode enabled',
updates: { collaborationMode: 'plan' }
});
});
it('returns to default collaboration mode', () => {
expect(resolveCodexSlashCommand('/plan off', { ...state, collaborationMode: 'plan' })).toEqual({
kind: 'handled',
message: 'Codex plan mode disabled',
updates: { collaborationMode: 'default' }
});
});
it('sets model, reasoning effort, and permission mode', () => {
expect(resolveCodexSlashCommand('/model gpt-5.4', state)).toMatchObject({
updates: { model: 'gpt-5.4' }
});
expect(resolveCodexSlashCommand('/reasoning low', state)).toMatchObject({
updates: { modelReasoningEffort: 'low' }
});
expect(resolveCodexSlashCommand('/reasoning max', state)).toMatchObject({
updates: { modelReasoningEffort: 'max' }
});
expect(resolveCodexSlashCommand('/reasoning EXTREME', state)).toMatchObject({
updates: { modelReasoningEffort: 'extreme' }
});
expect(resolveCodexSlashCommand('/permissions yolo', state)).toMatchObject({
updates: { permissionMode: 'yolo' }
});
});
it('enables Codex fast mode', () => {
expect(resolveCodexSlashCommand('/fast', state)).toEqual({
kind: 'handled',
message: 'Codex Fast mode enabled',
updates: { serviceTier: 'fast' }
});
expect(resolveCodexSlashCommand('/fast on', state)).toEqual({
kind: 'handled',
message: 'Codex Fast mode enabled',
updates: { serviceTier: 'fast' }
});
});
it('disables Codex fast mode with an explicit standard tier', () => {
expect(resolveCodexSlashCommand('/fast off', { ...state, serviceTier: 'fast' })).toEqual({
kind: 'handled',
message: 'Codex Fast mode disabled',
updates: { serviceTier: 'standard' }
});
});
it('shows Codex fast mode status', () => {
expect(resolveCodexSlashCommand('/fast status', { ...state, serviceTier: 'fast' })).toEqual({
kind: 'handled',
message: 'Codex Fast mode: on'
});
expect(resolveCodexSlashCommand('/fast status', state)).toEqual({
kind: 'handled',
message: 'Codex Fast mode: off'
});
});
it('rejects unknown Codex fast mode arguments', () => {
expect(resolveCodexSlashCommand('/fast turbo', state)).toEqual({
kind: 'handled',
message: 'Usage: /fast [on|off|status]'
});
});
it('resolves Codex goal commands for native handling', () => {
expect(resolveCodexSlashCommand('/goal', state)).toEqual({
kind: 'goal',
action: 'show'
});
expect(resolveCodexSlashCommand('/goal improve benchmark coverage', state)).toEqual({
kind: 'goal',
action: 'set',
objective: 'improve benchmark coverage'
});
expect(resolveCodexSlashCommand('/goal pause', state)).toEqual({
kind: 'goal',
action: 'pause'
});
expect(resolveCodexSlashCommand('/goal resume', state)).toEqual({
kind: 'goal',
action: 'resume'
});
expect(resolveCodexSlashCommand('/goal clear', state)).toEqual({
kind: 'goal',
action: 'clear'
});
});
it('rejects oversized Codex goal objectives', () => {
expect(resolveCodexSlashCommand(`/goal ${'x'.repeat(4001)}`, state)).toEqual({
kind: 'handled',
message: 'Goal objective must be at most 4000 characters.'
});
});
it('expands custom Codex prompt commands', () => {
expect(resolveCodexSlashCommand('/review src/index.ts', {
...state,
commands: [
{ name: 'review', source: 'project', content: 'Review this code.' }
]
})).toEqual({
kind: 'replace',
text: 'Review this code.\n\nUser arguments: src/index.ts',
message: 'Expanded /review'
});
});
it('handles unsupported Codex built-in commands instead of sending them to the model', () => {
for (const command of ['diff', 'undo', 'review', 'compat']) {
expect(resolveCodexSlashCommand(`/${command}`, state)).toEqual({
kind: 'handled',
message: `/${command} is a Codex CLI command that is not supported in HAPI sessions yet.`
});
}
});
it('expands custom prompts before checking unsupported built-in names', () => {
expect(resolveCodexSlashCommand('/review src/index.ts', {
...state,
commands: [
{ name: 'review', source: 'project', content: 'Review this code.' }
]
})).toEqual({
kind: 'replace',
text: 'Review this code.\n\nUser arguments: src/index.ts',
message: 'Expanded /review'
});
});
it('passes unknown slash commands through', () => {
expect(resolveCodexSlashCommand('/unknown', state)).toEqual({ kind: 'passthrough' });
});
});