import { describe, it, expect, afterEach, beforeEach } from 'vitest'; import { createServer, type Server } from 'node:http'; import type { AddressInfo } from 'node:net'; import { fetchText, fetchBuffer } from '../../src/lib/server/http'; let server: Server; let baseUrl: string; beforeEach(async () => { server = createServer(); await new Promise((resolve) => server.listen(0, '127.0.0.1', resolve)); const addr = server.address() as AddressInfo; baseUrl = `http://127.0.0.1:${addr.port}`; }); afterEach(async () => { await new Promise((resolve) => server.close(() => resolve())); }); describe('fetchText', () => { it('returns body text', async () => { server.on('request', (_req, res) => { res.writeHead(200, { 'content-type': 'text/html' }); res.end('hi'); }); expect(await fetchText(`${baseUrl}/`)).toBe('hi'); }); it('rejects non-http schemes', async () => { await expect(fetchText('file:///etc/passwd')).rejects.toThrow(/scheme/i); }); it('enforces max bytes', async () => { const big = 'x'.repeat(200); server.on('request', (_req, res) => { res.writeHead(200, { 'content-type': 'text/plain' }); res.end(big); }); await expect(fetchText(`${baseUrl}/`, { maxBytes: 100 })).rejects.toThrow(/exceeds/i); }); it('times out slow responses', async () => { server.on('request', () => { // never respond }); await expect(fetchText(`${baseUrl}/`, { timeoutMs: 150 })).rejects.toThrow(); }); it('allowTruncate returns first maxBytes instead of throwing', async () => { const head = 'hi'; const filler = 'x'.repeat(2000); server.on('request', (_req, res) => { res.writeHead(200, { 'content-type': 'text/html' }); res.end(head + filler); }); const text = await fetchText(`${baseUrl}/`, { maxBytes: 100, allowTruncate: true }); // First 100 bytes of body — should contain the opening at least expect(text.length).toBeLessThanOrEqual(2048); // chunk boundary may overshoot exact bytes slightly expect(text).toContain(''); expect(text).toContain(''); }); }); describe('fetchBuffer', () => { it('returns bytes + content-type', async () => { server.on('request', (_req, res) => { res.writeHead(200, { 'content-type': 'image/png' }); res.end(Buffer.from([1, 2, 3, 4])); }); const { data, contentType } = await fetchBuffer(`${baseUrl}/`); expect(Array.from(data)).toEqual([1, 2, 3, 4]); expect(contentType).toBe('image/png'); }); });