Skip to main content
Glama
ClaudeCodePostToolUseAdapter.test.ts9.25 kB
/** * Tests for ClaudeCodeAdapter (PostToolUse mode) */ import { describe, test, expect, beforeEach } from 'vitest'; import { ClaudeCodeAdapter } from '../../src/adapters/ClaudeCodeAdapter'; describe('ClaudeCodeAdapter (PostToolUse)', () => { let adapter: ClaudeCodeAdapter; beforeEach(() => { adapter = new ClaudeCodeAdapter(); }); describe('parseInput', () => { test('parses complete PostToolUse input', () => { const input = JSON.stringify({ session_id: 'session-123', transcript_path: '/path/to/transcript.txt', cwd: '/workspace', permission_mode: 'ask', hook_event_name: 'PostToolUse', tool_name: 'Read', tool_input: { file_path: '/test/file.ts', limit: 100 }, tool_response: { content: 'file content' }, tool_use_id: 'tool-use-456', llm_tool: 'claude-code', }); const context = adapter.parseInput(input); expect(context.tool_name).toBe('Read'); expect(context.tool_input).toEqual({ file_path: '/test/file.ts', limit: 100 }); expect(context.tool_response).toEqual({ content: 'file content' }); expect(context.cwd).toBe('/workspace'); expect(context.session_id).toBe('session-123'); expect(context.llm_tool).toBe('claude-code'); expect(context.hook_event_name).toBe('PostToolUse'); }); test.each([ ['Write', { file_path: '/test/file.ts', content: 'test' }], ['Edit', { file_path: '/test/file.ts', old_string: 'a', new_string: 'b' }], ['Read', { file_path: '/test/file.ts' }], ])('parses %s tool input correctly', (toolName, toolInput) => { const input = JSON.stringify({ session_id: 'session-test', transcript_path: '/path/to/transcript.txt', cwd: '/workspace', permission_mode: 'ask', hook_event_name: 'PostToolUse', tool_name: toolName, tool_input: toolInput, tool_response: {}, tool_use_id: 'tool-use-123', }); const context = adapter.parseInput(input); expect(context.tool_name).toBe(toolName); expect(context.tool_input).toEqual(toolInput); }); test('parses tool_input with file_path', () => { const input = JSON.stringify({ session_id: 'session-789', transcript_path: '/path/to/transcript.txt', cwd: '/workspace', permission_mode: 'ask', hook_event_name: 'PostToolUse', tool_name: 'Read', tool_input: { file_path: '/test/input-file.ts' }, tool_response: {}, tool_use_id: 'tool-use-789', }); const context = adapter.parseInput(input); expect(context.tool_input.file_path).toBe('/test/input-file.ts'); }); test('parses tool_response correctly', () => { const input = JSON.stringify({ session_id: 'session-789', transcript_path: '/path/to/transcript.txt', cwd: '/workspace', permission_mode: 'ask', hook_event_name: 'PostToolUse', tool_name: 'Read', tool_input: {}, tool_response: { filePath: '/test/response-file.ts', content: 'file content' }, tool_use_id: 'tool-use-789', }); const context = adapter.parseInput(input); expect(context.tool_response).toEqual({ filePath: '/test/response-file.ts', content: 'file content', }); }); test('parses non-file tool operations', () => { const input = JSON.stringify({ session_id: 'session-999', transcript_path: '/path/to/transcript.txt', cwd: '/workspace', permission_mode: 'ask', hook_event_name: 'PostToolUse', tool_name: 'Bash', tool_input: { command: 'ls -la' }, tool_response: { output: 'file1.txt' }, tool_use_id: 'tool-use-999', }); const context = adapter.parseInput(input); expect(context.tool_name).toBe('Bash'); expect(context.tool_input).toEqual({ command: 'ls -la' }); expect(context.tool_response).toEqual({ output: 'file1.txt' }); }); test('handles missing llm_tool field', () => { const input = JSON.stringify({ session_id: 'session-111', transcript_path: '/path/to/transcript.txt', cwd: '/workspace', permission_mode: 'ask', hook_event_name: 'PostToolUse', tool_name: 'Read', tool_input: { file_path: '/test/file.ts' }, tool_response: {}, tool_use_id: 'tool-use-111', }); const context = adapter.parseInput(input); expect(context.llmTool).toBeUndefined(); }); test('throws error on invalid JSON', () => { expect(() => adapter.parseInput('invalid json')).toThrow(); }); test('throws error on empty input', () => { expect(() => adapter.parseInput('')).toThrow(); }); }); describe('formatOutput', () => { test('formats deny decision as block', () => { // First parse input to set hookEventName const input = JSON.stringify({ session_id: 'session-123', transcript_path: '/path/to/transcript.txt', cwd: '/workspace', permission_mode: 'ask', hook_event_name: 'PostToolUse', tool_name: 'Write', tool_input: { file_path: '/test/file.ts' }, tool_response: {}, tool_use_id: 'tool-use-456', }); adapter.parseInput(input); const response = { decision: 'deny' as const, message: 'Operation blocked', }; const output = adapter.formatOutput(response); const parsed = JSON.parse(output); expect(parsed.decision).toBe('block'); expect(parsed.reason).toBe('Operation blocked'); expect(parsed.hookSpecificOutput.hookEventName).toBe('PostToolUse'); }); test('formats allow decision with message as additionalContext', () => { // Set PostToolUse mode const input = JSON.stringify({ session_id: 'session-123', hook_event_name: 'PostToolUse', tool_name: 'Write', tool_input: {}, tool_response: {}, tool_use_id: 'tool-use-456', cwd: '/workspace', transcript_path: '/path', permission_mode: 'ask', }); adapter.parseInput(input); const response = { decision: 'allow' as const, message: 'Code review feedback', }; const output = adapter.formatOutput(response); const parsed = JSON.parse(output); expect(parsed.decision).toBeUndefined(); expect(parsed.reason).toBeUndefined(); expect(parsed.hookSpecificOutput.additionalContext).toBe('Code review feedback'); expect(parsed.hookSpecificOutput.hookEventName).toBe('PostToolUse'); }); test('formats allow decision without message', () => { // Set PostToolUse mode const input = JSON.stringify({ session_id: 'session-123', hook_event_name: 'PostToolUse', tool_name: 'Write', tool_input: {}, tool_response: {}, tool_use_id: 'tool-use-456', cwd: '/workspace', transcript_path: '/path', permission_mode: 'ask', }); adapter.parseInput(input); const response = { decision: 'allow' as const, message: '', }; const output = adapter.formatOutput(response); const parsed = JSON.parse(output); expect(parsed.decision).toBeUndefined(); expect(parsed.reason).toBeUndefined(); expect(parsed.hookSpecificOutput.additionalContext).toBeUndefined(); }); test('formats skip decision without blocking', () => { // Set PostToolUse mode const input = JSON.stringify({ session_id: 'session-123', hook_event_name: 'PostToolUse', tool_name: 'Write', tool_input: {}, tool_response: {}, tool_use_id: 'tool-use-456', cwd: '/workspace', transcript_path: '/path', permission_mode: 'ask', }); adapter.parseInput(input); const response = { decision: 'skip' as const, message: '', }; const output = adapter.formatOutput(response); const parsed = JSON.parse(output); // Skip returns empty object expect(Object.keys(parsed).length).toBe(0); }); test('formats ask decision without blocking', () => { // Set PostToolUse mode const input = JSON.stringify({ session_id: 'session-123', hook_event_name: 'PostToolUse', tool_name: 'Write', tool_input: {}, tool_response: {}, tool_use_id: 'tool-use-456', cwd: '/workspace', transcript_path: '/path', permission_mode: 'ask', }); adapter.parseInput(input); const response = { decision: 'ask' as const, message: 'Review needed', }; const output = adapter.formatOutput(response); const parsed = JSON.parse(output); // 'ask' is not 'deny' so no block, and not 'allow' so no additionalContext expect(parsed.decision).toBeUndefined(); expect(parsed.reason).toBeUndefined(); expect(parsed.hookSpecificOutput.additionalContext).toBeUndefined(); expect(parsed.hookSpecificOutput.hookEventName).toBe('PostToolUse'); }); }); });

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/AgiFlow/aicode-toolkit'

If you have feedback or need assistance with the MCP directory API, please join our Discord server