// Tests for backpressure guards — paste truncation and stdout buffer limits. // Uses bun:test. Tests the logic from Terminal.svelte and sidecar-manager.ts. import { describe, it, expect } from 'bun:test'; // ── Constants (replicated from source) ────────────────────────────────────── const MAX_PASTE_CHUNK = 64 * 1024; // 64 KB (Terminal.svelte) const MAX_LINE_SIZE = 10 * 1024 * 1024; // 10 MB (sidecar-manager.ts) const MAX_PENDING_BUFFER = 50 * 1024 * 1024; // 50 MB (sidecar-manager.ts) // ── Replicated truncation logic ────────────────────────────────────────────── function truncatePaste(payload: string): { text: string; wasTruncated: boolean } { if (payload.length > MAX_PASTE_CHUNK) { return { text: payload.slice(0, MAX_PASTE_CHUNK), wasTruncated: true }; } return { text: payload, wasTruncated: false }; } function applyBufferBackpressure(buffer: string): string { // If buffer exceeds MAX_PENDING_BUFFER, keep only last MAX_LINE_SIZE bytes if (buffer.length > MAX_PENDING_BUFFER) { return buffer.slice(-MAX_LINE_SIZE); } return buffer; } function shouldTruncateLine(buffer: string): boolean { // If buffer exceeds MAX_LINE_SIZE without a newline, truncate return buffer.length > MAX_LINE_SIZE && !buffer.includes('\n'); } // ── Tests ─────────────────────────────────────────────────────────────────── describe('paste truncation', () => { it('passes through text under 64KB', () => { const text = 'hello world'; const result = truncatePaste(text); expect(result.wasTruncated).toBe(false); expect(result.text).toBe(text); }); it('passes through text exactly at 64KB', () => { const text = 'x'.repeat(MAX_PASTE_CHUNK); const result = truncatePaste(text); expect(result.wasTruncated).toBe(false); expect(result.text.length).toBe(MAX_PASTE_CHUNK); }); it('truncates text over 64KB', () => { const text = 'x'.repeat(MAX_PASTE_CHUNK + 1000); const result = truncatePaste(text); expect(result.wasTruncated).toBe(true); expect(result.text.length).toBe(MAX_PASTE_CHUNK); }); it('preserves first 64KB of content on truncation', () => { const prefix = 'START-'; const text = prefix + 'x'.repeat(MAX_PASTE_CHUNK + 1000); const result = truncatePaste(text); expect(result.text.startsWith('START-')).toBe(true); }); }); describe('stdout buffer backpressure', () => { it('leaves buffer unchanged under 50MB', () => { const buffer = 'x'.repeat(1000); expect(applyBufferBackpressure(buffer)).toBe(buffer); }); it('truncates buffer over 50MB to last 10MB', () => { const buffer = 'x'.repeat(MAX_PENDING_BUFFER + 1000); const result = applyBufferBackpressure(buffer); expect(result.length).toBe(MAX_LINE_SIZE); }); it('keeps tail of buffer (most recent data)', () => { const head = 'H'.repeat(MAX_PENDING_BUFFER); const tail = 'T'.repeat(MAX_LINE_SIZE); const buffer = head + tail; const result = applyBufferBackpressure(buffer); // Result should be the last MAX_LINE_SIZE chars, which is all T's expect(result).toBe(tail); }); }); describe('line size guard', () => { it('no truncation for buffer with newlines', () => { const buffer = 'x'.repeat(MAX_LINE_SIZE + 100) + '\nmore data'; expect(shouldTruncateLine(buffer)).toBe(false); }); it('truncates when buffer exceeds MAX_LINE_SIZE without newline', () => { const buffer = 'x'.repeat(MAX_LINE_SIZE + 1); expect(shouldTruncateLine(buffer)).toBe(true); }); it('no truncation at exactly MAX_LINE_SIZE', () => { const buffer = 'x'.repeat(MAX_LINE_SIZE); expect(shouldTruncateLine(buffer)).toBe(false); }); });