Skip to main content
Glama

Cut-Copy-Paste Clipboard Server

operation-logger.test.ts•14.4 kB
import { OperationLogger } from '../operation-logger.js'; import { DatabaseManager } from '../database.js'; import { existsSync, rmSync } from 'fs'; import { join } from 'path'; import { tmpdir } from 'os'; describe('OperationLogger', () => { let dbPath: string; let dbManager: DatabaseManager; let opLogger: OperationLogger; let testSessionId: string; beforeEach(() => { const uniqueId = `${Date.now()}-${process.pid}-${Math.random().toString(36).slice(2, 9)}`; dbPath = join(tmpdir(), `test-clipboard-${uniqueId}.db`); dbManager = new DatabaseManager(dbPath); opLogger = new OperationLogger(dbManager); // Create a test session const db = dbManager.getConnection(); testSessionId = 'test-session-1'; const timestamp = Date.now(); db.prepare('INSERT INTO sessions (session_id, created_at, last_activity) VALUES (?, ?, ?)').run( testSessionId, timestamp, timestamp ); }); afterEach(() => { if (dbManager) { dbManager.close(); const keyPath = dbManager.getEncryptionKeyPath(); if (keyPath && existsSync(keyPath)) { rmSync(keyPath); } } if (existsSync(dbPath)) { rmSync(dbPath); } }); describe('Log Copy Operation - RED', () => { it('should log copy with full metadata', () => { const opId = opLogger.logCopy(testSessionId, { sourceFile: '/path/to/file.ts', startLine: 10, endLine: 20, content: 'copied content', }); expect(opId).toBeGreaterThan(0); const db = dbManager.getConnection(); const op = db.prepare('SELECT * FROM operations_log WHERE id = ?').get(opId) as any; expect(op.operation_type).toBe('copy'); expect(op.source_file).toBe('/path/to/file.ts'); expect(op.source_start_line).toBe(10); expect(op.source_end_line).toBe(20); }); it('should include timestamp', () => { const beforeLog = Date.now(); const opId = opLogger.logCopy(testSessionId, { sourceFile: '/file.ts', startLine: 1, endLine: 5, content: 'content', }); const afterLog = Date.now(); const db = dbManager.getConnection(); const op = db.prepare('SELECT * FROM operations_log WHERE id = ?').get(opId) as any; expect(op.timestamp).toBeGreaterThanOrEqual(beforeLog); expect(op.timestamp).toBeLessThanOrEqual(afterLog); }); it('should link to session', () => { const opId = opLogger.logCopy(testSessionId, { sourceFile: '/file.ts', startLine: 1, endLine: 1, content: 'c', }); const db = dbManager.getConnection(); const op = db.prepare('SELECT * FROM operations_log WHERE id = ?').get(opId) as any; expect(op.session_id).toBe(testSessionId); }); }); describe('Log Cut Operation - RED', () => { it('should log cut with full metadata', () => { const opId = opLogger.logCut(testSessionId, { sourceFile: '/path/to/file.ts', startLine: 5, endLine: 15, content: 'cut content', }); const db = dbManager.getConnection(); const op = db.prepare('SELECT * FROM operations_log WHERE id = ?').get(opId) as any; expect(op.operation_type).toBe('cut'); expect(op.source_file).toBe('/path/to/file.ts'); }); it('should mark as undoable', () => { const opId = opLogger.logCut(testSessionId, { sourceFile: '/file.ts', startLine: 1, endLine: 5, content: 'content', }); const db = dbManager.getConnection(); const op = db.prepare('SELECT * FROM operations_log WHERE id = ?').get(opId) as any; expect(op.undoable).toBe(1); }); }); describe('Log Paste Operation - RED', () => { it('should log paste with all targets', () => { const targets = [ { filePath: '/file1.ts', targetLine: 10, originalContent: 'orig1' }, { filePath: '/file2.ts', targetLine: 20, originalContent: 'orig2' }, ]; const opId = opLogger.logPaste(testSessionId, { targets, content: 'pasted content', }); const db = dbManager.getConnection(); const op = db.prepare('SELECT * FROM operations_log WHERE id = ?').get(opId) as any; expect(op.operation_type).toBe('paste'); // Verify content_snapshot contains metadata, not actual content const snapshot = JSON.parse(op.content_snapshot); expect(snapshot.targets).toBe(2); expect(snapshot.bytes).toBe(Buffer.byteLength('pasted content', 'utf-8')); expect(snapshot.hasCutSource).toBe(false); }); it('should store content snapshots as metadata', () => { const targets = [{ filePath: '/file1.ts', targetLine: 5, originalContent: 'orig' }]; const opId = opLogger.logPaste(testSessionId, { targets, content: 'paste content', }); const db = dbManager.getConnection(); const op = db.prepare('SELECT * FROM operations_log WHERE id = ?').get(opId) as any; // Verify content_snapshot contains metadata summary, not plaintext const snapshot = JSON.parse(op.content_snapshot); expect(snapshot.targets).toBe(1); expect(snapshot.bytes).toBeGreaterThan(0); }); it('should create paste_history entries', () => { const targets = [{ filePath: '/file1.ts', targetLine: 10, originalContent: 'original1' }]; const opId = opLogger.logPaste(testSessionId, { targets, content: 'new content', }); const db = dbManager.getConnection(); const history = db .prepare('SELECT * FROM paste_history WHERE operation_log_id = ?') .all(opId) as any[]; expect(history.length).toBe(1); expect(history[0].file_path).toBe('/file1.ts'); expect(history[0].paste_line).toBe(10); // Verify content is encrypted in database expect(history[0].original_content).toBe('[encrypted]'); expect(history[0].modified_content).toBe('[encrypted]'); // Verify encryption metadata exists expect(history[0].encrypted_payload).toBeTruthy(); expect(history[0].encryption_iv).toBeTruthy(); expect(history[0].encryption_tag).toBeTruthy(); expect(history[0].encryption_version).toBe(1); }); it('should handle multiple targets', () => { const targets = [ { filePath: '/file1.ts', targetLine: 5, originalContent: 'orig1' }, { filePath: '/file2.ts', targetLine: 10, originalContent: 'orig2' }, { filePath: '/file3.ts', targetLine: 15, originalContent: 'orig3' }, ]; const opId = opLogger.logPaste(testSessionId, { targets, content: 'content', }); const db = dbManager.getConnection(); const history = db .prepare('SELECT * FROM paste_history WHERE operation_log_id = ?') .all(opId) as any[]; expect(history.length).toBe(3); expect(history.map((h) => h.file_path)).toEqual(['/file1.ts', '/file2.ts', '/file3.ts']); }); it('should encrypt sensitive content in paste history', () => { const sensitiveOriginal = 'API_KEY=sk-1234567890abcdef'; const sensitiveModified = 'PASSWORD=MySecretPass123!'; const sensitiveCut = 'TOKEN=bearer_xyz789'; const opId = opLogger.logPaste(testSessionId, { targets: [ { filePath: '/config.ts', targetLine: 5, originalContent: sensitiveOriginal, }, ], content: sensitiveModified, cutSourceFile: '/old-config.ts', cutSourceContent: sensitiveCut, }); const db = dbManager.getConnection(); const history = db .prepare('SELECT * FROM paste_history WHERE operation_log_id = ?') .get(opId) as any; // Verify sensitive content is NOT in plaintext in database expect(history.original_content).toBe('[encrypted]'); expect(history.modified_content).toBe('[encrypted]'); expect(history.cut_source_content).toBeNull(); // Verify encrypted payload exists expect(history.encrypted_payload).toBeTruthy(); expect(history.encryption_iv).toBeTruthy(); expect(history.encryption_tag).toBeTruthy(); // Verify we can retrieve and decrypt the content correctly const retrieved = opLogger.getLastPaste(testSessionId); expect(retrieved).not.toBeNull(); expect(retrieved!.targets[0].originalContent).toBe(sensitiveOriginal); expect(retrieved!.targets[0].modifiedContent).toBe(sensitiveModified); expect(retrieved!.cutSourceContent).toBe(sensitiveCut); }); }); describe('Log Undo Operation - RED', () => { it('should log undo', () => { // First create a paste operation const pasteOpId = opLogger.logPaste(testSessionId, { targets: [{ filePath: '/file.ts', targetLine: 5, originalContent: 'original' }], content: 'new', }); // Then undo it const undoOpId = opLogger.logUndo(testSessionId, pasteOpId); const db = dbManager.getConnection(); const op = db.prepare('SELECT * FROM operations_log WHERE id = ?').get(undoOpId) as any; expect(op.operation_type).toBe('undo'); }); it('should reference original operation', () => { const pasteOpId = opLogger.logPaste(testSessionId, { targets: [{ filePath: '/file.ts', targetLine: 5, originalContent: 'original' }], content: 'new', }); const undoOpId = opLogger.logUndo(testSessionId, pasteOpId); const db = dbManager.getConnection(); const op = db.prepare('SELECT * FROM operations_log WHERE id = ?').get(undoOpId) as any; // The reference is stored in content_snapshot as JSON expect(op.content_snapshot).toContain(String(pasteOpId)); }); it('should mark paste history as undone', () => { const pasteOpId = opLogger.logPaste(testSessionId, { targets: [{ filePath: '/file.ts', targetLine: 5, originalContent: 'original' }], content: 'new', }); opLogger.logUndo(testSessionId, pasteOpId); const db = dbManager.getConnection(); const history = db .prepare('SELECT * FROM paste_history WHERE operation_log_id = ?') .get(pasteOpId) as any; expect(history.undone).toBe(1); }); }); describe('Get Operation History - RED', () => { it('should retrieve operations for session', () => { opLogger.logCopy(testSessionId, { sourceFile: '/file1.ts', startLine: 1, endLine: 5, content: 'c1', }); opLogger.logCut(testSessionId, { sourceFile: '/file2.ts', startLine: 10, endLine: 15, content: 'c2', }); const history = opLogger.getHistory(testSessionId); expect(history.length).toBe(2); }); it('should order by timestamp DESC', () => { const wait = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)); return Promise.resolve() .then(() => opLogger.logCopy(testSessionId, { sourceFile: '/file1.ts', startLine: 1, endLine: 1, content: 'first', }) ) .then(() => wait(10)) .then(() => opLogger.logCopy(testSessionId, { sourceFile: '/file2.ts', startLine: 2, endLine: 2, content: 'second', }) ) .then(() => wait(10)) .then(() => opLogger.logCopy(testSessionId, { sourceFile: '/file3.ts', startLine: 3, endLine: 3, content: 'third', }) ) .then(() => { const history = opLogger.getHistory(testSessionId); expect(history[0].sourceFile).toBe('/file3.ts'); // Most recent first expect(history[2].sourceFile).toBe('/file1.ts'); // Oldest last }); }); it('should respect limit parameter', () => { for (let i = 0; i < 10; i++) { opLogger.logCopy(testSessionId, { sourceFile: `/file${i}.ts`, startLine: i, endLine: i, content: `c${i}`, }); } const history = opLogger.getHistory(testSessionId, 5); expect(history.length).toBe(5); }); it('should not return operations from other sessions', () => { // Create another session const db = dbManager.getConnection(); const timestamp = Date.now(); db.prepare( 'INSERT INTO sessions (session_id, created_at, last_activity) VALUES (?, ?, ?)' ).run('other-session', timestamp, timestamp); opLogger.logCopy(testSessionId, { sourceFile: '/file1.ts', startLine: 1, endLine: 1, content: 'c1', }); opLogger.logCopy('other-session', { sourceFile: '/file2.ts', startLine: 2, endLine: 2, content: 'c2', }); const history = opLogger.getHistory(testSessionId); expect(history.length).toBe(1); expect(history[0].sourceFile).toBe('/file1.ts'); }); }); describe('Get Last Paste - RED', () => { it('should retrieve last paste operation', () => { opLogger.logCopy(testSessionId, { sourceFile: '/file.ts', startLine: 1, endLine: 1, content: 'c', }); const pasteOpId = opLogger.logPaste(testSessionId, { targets: [{ filePath: '/target.ts', targetLine: 5, originalContent: 'orig' }], content: 'pasted', }); const lastPaste = opLogger.getLastPaste(testSessionId); expect(lastPaste).toBeDefined(); expect(lastPaste?.operationId).toBe(pasteOpId); }); it('should return null if no paste operations', () => { opLogger.logCopy(testSessionId, { sourceFile: '/file.ts', startLine: 1, endLine: 1, content: 'c', }); const lastPaste = opLogger.getLastPaste(testSessionId); expect(lastPaste).toBeNull(); }); it('should not return already undone pastes', () => { const pasteOpId = opLogger.logPaste(testSessionId, { targets: [{ filePath: '/file.ts', targetLine: 5, originalContent: 'orig' }], content: 'pasted', }); opLogger.logUndo(testSessionId, pasteOpId); const lastPaste = opLogger.getLastPaste(testSessionId); expect(lastPaste).toBeNull(); }); }); });

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/Pr0j3c7t0dd-Ltd/cut-copy-paste-mcp'

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