chore: initialize qiming workspace repository
This commit is contained in:
@@ -0,0 +1,90 @@
|
||||
/**
|
||||
* Unit tests for safety/auditLog.ts — ring buffer, record, getEntries, clear.
|
||||
*/
|
||||
|
||||
import { describe, it, expect, beforeEach } from 'vitest';
|
||||
import { AuditLog } from '../../src/safety/auditLog.js';
|
||||
|
||||
describe('AuditLog', () => {
|
||||
let log: AuditLog;
|
||||
|
||||
beforeEach(() => {
|
||||
log = new AuditLog();
|
||||
});
|
||||
|
||||
it('starts empty', () => {
|
||||
expect(log.length).toBe(0);
|
||||
expect(log.getEntries()).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('records entries with auto-generated timestamp', () => {
|
||||
log.record({ tool: 'gui_click', args: { x: 100, y: 200 }, success: true });
|
||||
expect(log.length).toBe(1);
|
||||
const entries = log.getEntries();
|
||||
expect(entries).toHaveLength(1);
|
||||
expect(entries[0].tool).toBe('gui_click');
|
||||
expect(entries[0].args).toEqual({ x: 100, y: 200 });
|
||||
expect(entries[0].success).toBe(true);
|
||||
expect(entries[0].timestamp).toBeDefined();
|
||||
});
|
||||
|
||||
it('returns entries most recent first', () => {
|
||||
log.record({ tool: 'tool_1', args: {}, success: true });
|
||||
log.record({ tool: 'tool_2', args: {}, success: true });
|
||||
log.record({ tool: 'tool_3', args: {}, success: true });
|
||||
const entries = log.getEntries();
|
||||
expect(entries[0].tool).toBe('tool_3');
|
||||
expect(entries[1].tool).toBe('tool_2');
|
||||
expect(entries[2].tool).toBe('tool_1');
|
||||
});
|
||||
|
||||
it('limits entries by count parameter', () => {
|
||||
for (let i = 0; i < 10; i++) {
|
||||
log.record({ tool: `tool_${i}`, args: {}, success: true });
|
||||
}
|
||||
const entries = log.getEntries(3);
|
||||
expect(entries).toHaveLength(3);
|
||||
expect(entries[0].tool).toBe('tool_9');
|
||||
expect(entries[1].tool).toBe('tool_8');
|
||||
expect(entries[2].tool).toBe('tool_7');
|
||||
});
|
||||
|
||||
it('wraps around at 1000 entries (ring buffer)', () => {
|
||||
for (let i = 0; i < 1050; i++) {
|
||||
log.record({ tool: `tool_${i}`, args: {}, success: true });
|
||||
}
|
||||
// Size should cap at 1000
|
||||
expect(log.length).toBe(1000);
|
||||
const entries = log.getEntries(1);
|
||||
expect(entries[0].tool).toBe('tool_1049');
|
||||
|
||||
// Oldest entry should be tool_50 (0-49 were overwritten)
|
||||
const allEntries = log.getEntries();
|
||||
expect(allEntries).toHaveLength(1000);
|
||||
expect(allEntries[allEntries.length - 1].tool).toBe('tool_50');
|
||||
});
|
||||
|
||||
it('clears all entries', () => {
|
||||
for (let i = 0; i < 5; i++) {
|
||||
log.record({ tool: `tool_${i}`, args: {}, success: true });
|
||||
}
|
||||
expect(log.length).toBe(5);
|
||||
log.clear();
|
||||
expect(log.length).toBe(0);
|
||||
expect(log.getEntries()).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('records durationMs when provided', () => {
|
||||
log.record({ tool: 'gui_screenshot', args: {}, success: true, durationMs: 150 });
|
||||
const entries = log.getEntries();
|
||||
expect(entries[0].durationMs).toBe(150);
|
||||
});
|
||||
|
||||
it('works correctly after clear and re-record', () => {
|
||||
log.record({ tool: 'before_clear', args: {}, success: true });
|
||||
log.clear();
|
||||
log.record({ tool: 'after_clear', args: {}, success: true });
|
||||
expect(log.length).toBe(1);
|
||||
expect(log.getEntries()[0].tool).toBe('after_clear');
|
||||
});
|
||||
});
|
||||
133
qimingclaw/crates/agent-gui-server/tests/safety/hotkeys.test.ts
Normal file
133
qimingclaw/crates/agent-gui-server/tests/safety/hotkeys.test.ts
Normal file
@@ -0,0 +1,133 @@
|
||||
/**
|
||||
* Unit tests for safety/hotkeys.ts — platform blacklists and key normalization.
|
||||
*/
|
||||
|
||||
import { describe, it, expect, vi } from 'vitest';
|
||||
|
||||
// Mock platform detection
|
||||
const mockGetPlatform = vi.fn();
|
||||
vi.mock('../../src/utils/platform.js', () => ({
|
||||
getPlatform: () => mockGetPlatform(),
|
||||
}));
|
||||
|
||||
import { validateHotkey } from '../../src/safety/hotkeys.js';
|
||||
|
||||
describe('validateHotkey', () => {
|
||||
describe('macOS blacklist', () => {
|
||||
it('blocks Cmd+Q', () => {
|
||||
mockGetPlatform.mockReturnValue('macos');
|
||||
const result = validateHotkey(['Meta', 'Q']);
|
||||
expect(result.blocked).toBe(true);
|
||||
expect(result.reason).toContain('Quit application');
|
||||
});
|
||||
|
||||
it('blocks Cmd+W', () => {
|
||||
mockGetPlatform.mockReturnValue('macos');
|
||||
const result = validateHotkey(['Meta', 'W']);
|
||||
expect(result.blocked).toBe(true);
|
||||
expect(result.reason).toContain('Close window');
|
||||
});
|
||||
|
||||
it('blocks Cmd+Opt+Escape', () => {
|
||||
mockGetPlatform.mockReturnValue('macos');
|
||||
const result = validateHotkey(['Meta', 'Alt', 'Escape']);
|
||||
expect(result.blocked).toBe(true);
|
||||
expect(result.reason).toContain('Force quit');
|
||||
});
|
||||
|
||||
it('blocks Cmd+Shift+Q (log out)', () => {
|
||||
mockGetPlatform.mockReturnValue('macos');
|
||||
const result = validateHotkey(['Meta', 'Shift', 'Q']);
|
||||
expect(result.blocked).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Windows blacklist', () => {
|
||||
it('blocks Alt+F4', () => {
|
||||
mockGetPlatform.mockReturnValue('windows');
|
||||
const result = validateHotkey(['Alt', 'F4']);
|
||||
expect(result.blocked).toBe(true);
|
||||
expect(result.reason).toContain('Close application');
|
||||
});
|
||||
|
||||
it('blocks Ctrl+Alt+Delete', () => {
|
||||
mockGetPlatform.mockReturnValue('windows');
|
||||
const result = validateHotkey(['Control', 'Alt', 'Delete']);
|
||||
expect(result.blocked).toBe(true);
|
||||
expect(result.reason).toContain('System menu');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Linux blacklist', () => {
|
||||
it('blocks Ctrl+Alt+Delete', () => {
|
||||
mockGetPlatform.mockReturnValue('linux');
|
||||
const result = validateHotkey(['Control', 'Alt', 'Delete']);
|
||||
expect(result.blocked).toBe(true);
|
||||
});
|
||||
|
||||
it('blocks Ctrl+Alt+Backspace', () => {
|
||||
mockGetPlatform.mockReturnValue('linux');
|
||||
const result = validateHotkey(['Control', 'Alt', 'Backspace']);
|
||||
expect(result.blocked).toBe(true);
|
||||
expect(result.reason).toContain('Kill X server');
|
||||
});
|
||||
});
|
||||
|
||||
describe('key alias normalization', () => {
|
||||
it('normalizes cmd to meta', () => {
|
||||
mockGetPlatform.mockReturnValue('macos');
|
||||
const result = validateHotkey(['cmd', 'q']);
|
||||
expect(result.blocked).toBe(true);
|
||||
});
|
||||
|
||||
it('normalizes command to meta', () => {
|
||||
mockGetPlatform.mockReturnValue('macos');
|
||||
const result = validateHotkey(['command', 'q']);
|
||||
expect(result.blocked).toBe(true);
|
||||
});
|
||||
|
||||
it('normalizes opt/option to alt', () => {
|
||||
mockGetPlatform.mockReturnValue('macos');
|
||||
const result = validateHotkey(['meta', 'opt', 'esc']);
|
||||
expect(result.blocked).toBe(true);
|
||||
});
|
||||
|
||||
it('normalizes ctrl to control', () => {
|
||||
mockGetPlatform.mockReturnValue('windows');
|
||||
const result = validateHotkey(['ctrl', 'alt', 'del']);
|
||||
expect(result.blocked).toBe(true);
|
||||
});
|
||||
|
||||
it('is case insensitive', () => {
|
||||
mockGetPlatform.mockReturnValue('macos');
|
||||
const result = validateHotkey(['META', 'Q']);
|
||||
expect(result.blocked).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('allowed combinations', () => {
|
||||
it('allows Cmd+C (copy)', () => {
|
||||
mockGetPlatform.mockReturnValue('macos');
|
||||
const result = validateHotkey(['Meta', 'C']);
|
||||
expect(result.blocked).toBe(false);
|
||||
});
|
||||
|
||||
it('allows Ctrl+S (save)', () => {
|
||||
mockGetPlatform.mockReturnValue('windows');
|
||||
const result = validateHotkey(['Control', 'S']);
|
||||
expect(result.blocked).toBe(false);
|
||||
});
|
||||
|
||||
it('allows single keys', () => {
|
||||
mockGetPlatform.mockReturnValue('macos');
|
||||
const result = validateHotkey(['Enter']);
|
||||
expect(result.blocked).toBe(false);
|
||||
});
|
||||
|
||||
it('key order does not matter', () => {
|
||||
mockGetPlatform.mockReturnValue('macos');
|
||||
const r1 = validateHotkey(['Q', 'Meta']);
|
||||
expect(r1.blocked).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user