import { describe, it, expect, beforeEach } from 'vitest' import { allowToolForSession, allowPatternForSession, isAllowedBySession, clearSessionPerms, sessionPermsSnapshot, suggestPattern, } from '../../src/tools/session-perms.js' beforeEach(() => { clearSessionPerms() }) describe('allowToolForSession / isAllowedBySession (tool-level)', () => { it('denies before any allowance', () => { expect(isAllowedBySession('Bash', { command: 'ls' })).toBe(false) }) it('allows after allowToolForSession', () => { allowToolForSession('Bash') expect(isAllowedBySession('Bash', { command: 'ls' })).toBe(true) }) it('allowance for one tool does not affect another', () => { allowToolForSession('Write') expect(isAllowedBySession('Bash', { command: 'ls' })).toBe(false) }) it('allows any input when tool is allowed', () => { allowToolForSession('Read') expect(isAllowedBySession('Read', { file_path: '/etc/passwd' })).toBe(true) expect(isAllowedBySession('Read', { file_path: '/anything' })).toBe(true) }) }) describe('allowPatternForSession / isAllowedBySession (pattern-level)', () => { it('denies when no pattern registered', () => { expect(isAllowedBySession('Bash', { command: 'git status' })).toBe(false) }) it('allows Bash command matching pattern', () => { allowPatternForSession('Bash', 'git *') expect(isAllowedBySession('Bash', { command: 'git status' })).toBe(true) expect(isAllowedBySession('Bash', { command: 'git commit -m "fix"' })).toBe(true) }) it('denies Bash command not matching pattern', () => { allowPatternForSession('Bash', 'git *') expect(isAllowedBySession('Bash', { command: 'npm install' })).toBe(false) }) it('allows Write matching path glob', () => { allowPatternForSession('Write', '/src/foo/**') expect(isAllowedBySession('Write', { file_path: '/src/foo/bar.ts' })).toBe(true) }) it('denies Write outside path glob', () => { allowPatternForSession('Write', '/src/foo/**') expect(isAllowedBySession('Write', { file_path: '/lib/bar.ts' })).toBe(false) }) it('allows multiple patterns for the same tool', () => { allowPatternForSession('Bash', 'git *') allowPatternForSession('Bash', 'npm *') expect(isAllowedBySession('Bash', { command: 'git status' })).toBe(true) expect(isAllowedBySession('Bash', { command: 'npm install' })).toBe(true) }) it('does not duplicate patterns on repeated add', () => { allowPatternForSession('Bash', 'git *') allowPatternForSession('Bash', 'git *') const snap = sessionPermsSnapshot() const patterns = snap.patterns.find(([tool]) => tool === 'Bash') expect(patterns?.[1]).toHaveLength(1) }) it('returns false when input has no relevant arg (unknown tool)', () => { allowPatternForSession('UnknownTool', 'some *') expect(isAllowedBySession('UnknownTool', { foo: 'some thing' })).toBe(false) }) }) describe('clearSessionPerms', () => { it('clears tool-level allowances', () => { allowToolForSession('Bash') clearSessionPerms() expect(isAllowedBySession('Bash', { command: 'ls' })).toBe(false) }) it('clears pattern-level allowances', () => { allowPatternForSession('Bash', 'git *') clearSessionPerms() expect(isAllowedBySession('Bash', { command: 'git status' })).toBe(false) }) }) describe('sessionPermsSnapshot', () => { it('returns empty state initially', () => { const snap = sessionPermsSnapshot() expect(snap.tools).toHaveLength(0) expect(snap.patterns).toHaveLength(0) }) it('reflects allowed tools', () => { allowToolForSession('Bash') allowToolForSession('Write') const snap = sessionPermsSnapshot() expect(snap.tools).toContain('Bash') expect(snap.tools).toContain('Write') }) it('reflects allowed patterns', () => { allowPatternForSession('Bash', 'git *') allowPatternForSession('Write', '/src/**') const snap = sessionPermsSnapshot() const bashEntry = snap.patterns.find(([tool]) => tool === 'Bash') expect(bashEntry?.[1]).toContain('git *') }) }) describe('suggestPattern', () => { describe('Bash', () => { it('suggests first-token + * for multi-word command', () => { expect(suggestPattern('Bash', { command: 'npm run build' })).toBe('npm *') }) it('suggests first-token + * for git', () => { expect(suggestPattern('Bash', { command: 'git status' })).toBe('git *') }) it('returns null for empty command', () => { expect(suggestPattern('Bash', { command: '' })).toBeNull() }) it('returns null when no command field', () => { expect(suggestPattern('Bash', {})).toBeNull() }) }) describe('Write / Edit / Read', () => { it('suggests parent dir + /** for absolute path', () => { expect(suggestPattern('Write', { file_path: '/src/foo/bar.ts' })).toBe('/src/foo/**') }) it('suggests parent dir + /** for Edit', () => { expect(suggestPattern('Edit', { file_path: '/home/user/project/src/main.ts' })).toBe('/home/user/project/src/**') }) it('returns null for bare filename (no slash)', () => { expect(suggestPattern('Write', { file_path: 'bar.ts' })).toBeNull() }) it('returns null when no file_path field', () => { expect(suggestPattern('Write', {})).toBeNull() }) }) it('returns null for tool with no known arg extractor', () => { expect(suggestPattern('AskUserQuestion', { question: 'What?' })).toBeNull() }) })