clipboard-manager.test.tsā¢10.4 kB
import { ClipboardManager } from '../clipboard-manager.js';
import { DatabaseManager } from '../database.js';
import { existsSync, rmSync } from 'fs';
import { join } from 'path';
import { tmpdir } from 'os';
describe('ClipboardManager', () => {
let dbPath: string;
let dbManager: DatabaseManager;
let clipboardManager: ClipboardManager;
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);
clipboardManager = new ClipboardManager(dbManager);
// Create a test session
const db = dbManager.getConnection();
const timestamp = Date.now();
db.prepare('INSERT INTO sessions (session_id, created_at, last_activity) VALUES (?, ?, ?)').run(
'test-session-1',
timestamp,
timestamp
);
});
afterEach(() => {
if (dbManager) {
dbManager.close();
const keyPath = dbManager.getEncryptionKeyPath();
if (keyPath && existsSync(keyPath)) {
rmSync(keyPath);
}
}
if (existsSync(dbPath)) {
rmSync(dbPath);
}
});
describe('Store Clipboard Content - RED', () => {
it('should store encrypted content for session', () => {
clipboardManager.setClipboard('test-session-1', {
content: 'line 1\nline 2\nline 3',
sourceFile: '/path/to/file.ts',
startLine: 5,
endLine: 7,
operationType: 'copy',
});
const db = dbManager.getConnection();
const clipboard = db
.prepare('SELECT * FROM clipboard_buffer WHERE session_id = ?')
.get('test-session-1') as any;
expect(clipboard).toBeDefined();
expect(clipboard.content).toBe('[encrypted]');
expect(clipboard.encrypted_payload).toBeDefined();
expect(clipboard.encryption_iv).toBeDefined();
expect(clipboard.encryption_tag).toBeDefined();
});
it('should include source metadata', () => {
clipboardManager.setClipboard('test-session-1', {
content: 'test content',
sourceFile: '/path/to/source.ts',
startLine: 10,
endLine: 15,
operationType: 'copy',
});
const clipboard = clipboardManager.getClipboard('test-session-1');
expect(clipboard?.startLine).toBe(10);
expect(clipboard?.endLine).toBe(15);
expect(clipboard?.sourceFile).toBe('/path/to/source.ts');
});
it('should overwrite previous clipboard', () => {
clipboardManager.setClipboard('test-session-1', {
content: 'first content',
sourceFile: '/first.ts',
startLine: 1,
endLine: 1,
operationType: 'copy',
});
clipboardManager.setClipboard('test-session-1', {
content: 'second content',
sourceFile: '/second.ts',
startLine: 2,
endLine: 2,
operationType: 'cut',
});
const clipboard = clipboardManager.getClipboard('test-session-1');
expect(clipboard?.content).toBe('second content');
expect(clipboard?.sourceFile).toBe('/second.ts');
expect(clipboard?.operationType).toBe('cut');
});
it('should distinguish copy vs cut', () => {
clipboardManager.setClipboard('test-session-1', {
content: 'test',
sourceFile: '/test.ts',
startLine: 1,
endLine: 1,
operationType: 'cut',
});
const clipboard = clipboardManager.getClipboard('test-session-1');
expect(clipboard?.operationType).toBe('cut');
});
it('should record timestamp', () => {
const beforeSet = Date.now();
clipboardManager.setClipboard('test-session-1', {
content: 'test',
sourceFile: '/test.ts',
startLine: 1,
endLine: 1,
operationType: 'copy',
});
const afterSet = Date.now();
const db = dbManager.getConnection();
const clipboard = db
.prepare('SELECT * FROM clipboard_buffer WHERE session_id = ?')
.get('test-session-1') as any;
expect(clipboard.copied_at).toBeGreaterThanOrEqual(beforeSet);
expect(clipboard.copied_at).toBeLessThanOrEqual(afterSet);
});
});
describe('Retrieve Clipboard Content - RED', () => {
it('should retrieve clipboard for session', () => {
const db = dbManager.getConnection();
db.prepare(
`INSERT INTO clipboard_buffer
(session_id, content, source_file, start_line, end_line, copied_at, operation_type)
VALUES (?, ?, ?, ?, ?, ?, ?)`
).run('test-session-1', 'retrieved content', '/source.ts', 5, 10, Date.now(), 'copy');
const clipboard = clipboardManager.getClipboard('test-session-1');
expect(clipboard).toBeDefined();
expect(clipboard?.content).toBe('retrieved content');
expect(clipboard?.sourceFile).toBe('/source.ts');
expect(clipboard?.startLine).toBe(5);
expect(clipboard?.endLine).toBe(10);
});
it('should retrieve encrypted clipboard for session', () => {
clipboardManager.setClipboard('test-session-1', {
content: 'secret',
sourceFile: '/secret.ts',
startLine: 4,
endLine: 8,
operationType: 'copy',
});
const clipboard = clipboardManager.getClipboard('test-session-1');
expect(clipboard?.content).toBe('secret');
expect(clipboard?.sourceFile).toBe('/secret.ts');
expect(clipboard?.startLine).toBe(4);
expect(clipboard?.endLine).toBe(8);
});
it('should return null if clipboard empty', () => {
const clipboard = clipboardManager.getClipboard('test-session-1');
expect(clipboard).toBeNull();
});
it('should include all metadata', () => {
const db = dbManager.getConnection();
const copiedAt = Date.now();
db.prepare(
`INSERT INTO clipboard_buffer
(session_id, content, source_file, start_line, end_line, copied_at, operation_type)
VALUES (?, ?, ?, ?, ?, ?, ?)`
).run('test-session-1', 'content', '/file.ts', 1, 5, copiedAt, 'cut');
const clipboard = clipboardManager.getClipboard('test-session-1');
expect(clipboard?.operationType).toBe('cut');
expect(clipboard?.copiedAt).toBe(copiedAt);
});
it('should not return clipboard from other sessions', () => {
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);
db.prepare(
`INSERT INTO clipboard_buffer
(session_id, content, source_file, start_line, end_line, copied_at, operation_type)
VALUES (?, ?, ?, ?, ?, ?, ?)`
).run('other-session', 'other content', '/other.ts', 1, 1, Date.now(), 'copy');
const clipboard = clipboardManager.getClipboard('test-session-1');
expect(clipboard).toBeNull();
});
});
describe('Clear Clipboard - RED', () => {
it('should clear clipboard for session', () => {
const db = dbManager.getConnection();
db.prepare(
`INSERT INTO clipboard_buffer
(session_id, content, source_file, start_line, end_line, copied_at, operation_type)
VALUES (?, ?, ?, ?, ?, ?, ?)`
).run('test-session-1', 'content', '/file.ts', 1, 1, Date.now(), 'copy');
clipboardManager.clearClipboard('test-session-1');
const clipboard = db
.prepare('SELECT * FROM clipboard_buffer WHERE session_id = ?')
.get('test-session-1');
expect(clipboard).toBeUndefined();
});
it('should not affect other sessions', () => {
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);
db.prepare(
`INSERT INTO clipboard_buffer
(session_id, content, source_file, start_line, end_line, copied_at, operation_type)
VALUES (?, ?, ?, ?, ?, ?, ?)`
).run('test-session-1', 'content1', '/file1.ts', 1, 1, Date.now(), 'copy');
db.prepare(
`INSERT INTO clipboard_buffer
(session_id, content, source_file, start_line, end_line, copied_at, operation_type)
VALUES (?, ?, ?, ?, ?, ?, ?)`
).run('other-session', 'content2', '/file2.ts', 1, 1, Date.now(), 'copy');
clipboardManager.clearClipboard('test-session-1');
const otherClipboard = db
.prepare('SELECT * FROM clipboard_buffer WHERE session_id = ?')
.get('other-session');
expect(otherClipboard).toBeDefined();
});
it('should not error if clipboard already empty', () => {
expect(() => {
clipboardManager.clearClipboard('test-session-1');
}).not.toThrow();
});
});
describe('Has Content - RED', () => {
it('should return true if clipboard has content', () => {
const db = dbManager.getConnection();
db.prepare(
`INSERT INTO clipboard_buffer
(session_id, content, source_file, start_line, end_line, copied_at, operation_type)
VALUES (?, ?, ?, ?, ?, ?, ?)`
).run('test-session-1', 'content', '/file.ts', 1, 1, Date.now(), 'copy');
expect(clipboardManager.hasContent('test-session-1')).toBe(true);
});
it('should return false if clipboard is empty', () => {
expect(clipboardManager.hasContent('test-session-1')).toBe(false);
});
});
describe('Content Size Limit - RED', () => {
it('should enforce maximum content size', () => {
const largeContent = 'x'.repeat(11 * 1024 * 1024); // 11MB
expect(() => {
clipboardManager.setClipboard('test-session-1', {
content: largeContent,
sourceFile: '/file.ts',
startLine: 1,
endLine: 1000,
operationType: 'copy',
});
}).toThrow('exceeds maximum size');
});
it('should accept content under size limit', () => {
const okContent = 'x'.repeat(5 * 1024 * 1024); // 5MB
expect(() => {
clipboardManager.setClipboard('test-session-1', {
content: okContent,
sourceFile: '/file.ts',
startLine: 1,
endLine: 1000,
operationType: 'copy',
});
}).not.toThrow();
});
});
});