5e32375f0e
架构变更: - 采用 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 - 配置管理
186 lines
5.8 KiB
TypeScript
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'); // 其他配置不变
|
|
});
|
|
});
|
|
});
|