Files
ai-terminal-assistant/packages/core/tests/unit/permission/web-checker.test.ts
T
kurihada 5e32375f0e feat: 重构为 Monorepo 架构并实现 HTTP Server
架构变更:
- 采用 pnpm workspaces 实现 Monorepo 结构
- 将现有代码迁移到 packages/core
- 新增 packages/server HTTP 服务层

Server 功能:
- REST API: 会话管理、工具管理、配置管理
- WebSocket: 实时双向通信支持
- SSE: 服务端事件推送
- Hono + Bun 作为运行时

API 端点:
- GET/POST /api/sessions - 会话 CRUD
- GET/POST /api/sessions/:id/messages - 消息管理
- GET /api/sessions/:id/events - SSE 事件流
- WS /api/ws/:sessionId - WebSocket 连接
- GET/POST /api/tools - 工具管理
- GET/PUT /api/config - 配置管理
2025-12-12 10:42:20 +08:00

186 lines
5.8 KiB
TypeScript

import { describe, it, expect, beforeEach, vi } from 'vitest';
import { WebPermissionChecker } from '../../../src/permission/checkers/web.js';
describe('WebPermissionChecker - Web 权限检查器', () => {
let checker: WebPermissionChecker;
beforeEach(() => {
checker = new WebPermissionChecker();
});
describe('构造和基本属性', () => {
it('有正确的名称', () => {
expect(checker.name).toBe('web');
});
it('默认配置为 ask', () => {
const config = checker.getConfig();
expect(config.default).toBe('ask');
});
it('默认允许高级搜索', () => {
const config = checker.getConfig();
expect(config.allowAdvancedSearch).toBe(true);
});
});
describe('checkWebPermission - 检查 Web 权限', () => {
it('默认策略为 allow 时允许', async () => {
checker.setConfig({ default: 'allow' });
const result = await checker.checkWebPermission({ query: 'test search' });
expect(result.allowed).toBe(true);
expect(result.action).toBe('allow');
});
it('默认策略为 deny 时拒绝', async () => {
checker.setConfig({ default: 'deny' });
const result = await checker.checkWebPermission({ query: 'test search' });
expect(result.allowed).toBe(false);
expect(result.action).toBe('deny');
});
it('默认策略为 ask 时需要确认', async () => {
checker.setConfig({ default: 'ask' });
const result = await checker.checkWebPermission({ query: 'test search' });
expect(result.allowed).toBe(false);
expect(result.action).toBe('ask');
expect(result.needsConfirmation).toBe(true);
});
it('不允许高级搜索时拒绝', async () => {
checker.setConfig({ allowAdvancedSearch: false });
const result = await checker.checkWebPermission({
query: 'test search',
searchDepth: 'advanced',
});
expect(result.allowed).toBe(false);
expect(result.reason).toContain('不允许深度搜索');
});
it('主题不在允许列表时拒绝', async () => {
checker.setConfig({ allowedTopics: ['general', 'news'] });
const result = await checker.checkWebPermission({
query: 'test search',
topic: 'finance',
});
expect(result.allowed).toBe(false);
expect(result.reason).toContain('不允许搜索主题');
});
it('主题在允许列表时通过', async () => {
checker.setConfig({ default: 'allow', allowedTopics: ['general', 'news'] });
const result = await checker.checkWebPermission({
query: 'test search',
topic: 'news',
});
expect(result.allowed).toBe(true);
});
it('空主题列表允许所有主题', async () => {
checker.setConfig({ default: 'allow', allowedTopics: [] });
const result = await checker.checkWebPermission({
query: 'test search',
topic: 'any-topic',
});
expect(result.allowed).toBe(true);
});
});
describe('会话权限管理', () => {
it('会话允许后不再询问', async () => {
const mockCallback = vi.fn().mockResolvedValue({ allow: true, remember: true });
checker.setAskCallback(mockCallback);
// 第一次调用,触发回调
const result1 = await checker.checkWebPermission({ query: 'test1' });
expect(result1.allowed).toBe(true);
expect(mockCallback).toHaveBeenCalledTimes(1);
// 第二次调用,使用会话权限
const result2 = await checker.checkWebPermission({ query: 'test2' });
expect(result2.allowed).toBe(true);
expect(mockCallback).toHaveBeenCalledTimes(1); // 不再调用
});
it('会话拒绝后不再询问', async () => {
const mockCallback = vi.fn().mockResolvedValue({ allow: false, remember: true });
checker.setAskCallback(mockCallback);
// 第一次调用
const result1 = await checker.checkWebPermission({ query: 'test1' });
expect(result1.allowed).toBe(false);
// 第二次调用
const result2 = await checker.checkWebPermission({ query: 'test2' });
expect(result2.allowed).toBe(false);
expect(mockCallback).toHaveBeenCalledTimes(1);
});
it('不记住权限时每次询问', async () => {
const mockCallback = vi.fn().mockResolvedValue({ allow: true, remember: false });
checker.setAskCallback(mockCallback);
await checker.checkWebPermission({ query: 'test1' });
await checker.checkWebPermission({ query: 'test2' });
expect(mockCallback).toHaveBeenCalledTimes(2);
});
it('清除会话权限后重新询问', async () => {
const mockCallback = vi.fn().mockResolvedValue({ allow: true, remember: true });
checker.setAskCallback(mockCallback);
await checker.checkWebPermission({ query: 'test1' });
checker.clearSessionPermissions();
await checker.checkWebPermission({ query: 'test2' });
expect(mockCallback).toHaveBeenCalledTimes(2);
});
});
describe('check - 通用接口', () => {
it('从 command 中提取查询', async () => {
checker.setConfig({ default: 'allow' });
const result = await checker.check({
command: 'web_search: test query',
workdir: '/test',
});
expect(result.allowed).toBe(true);
});
});
describe('配置管理', () => {
it('getConfig 返回配置副本', () => {
const config1 = checker.getConfig();
config1.default = 'deny';
const config2 = checker.getConfig();
expect(config2.default).toBe('ask'); // 原配置不变
});
it('setConfig 部分更新配置', () => {
checker.setConfig({ allowAdvancedSearch: false });
const config = checker.getConfig();
expect(config.allowAdvancedSearch).toBe(false);
expect(config.default).toBe('ask'); // 其他配置不变
});
});
});