import { parseJsonBounded, BodyTooLargeError, BodyParseError } from '../parse-json';

function makeReq(body: string, headers: Record<string, string> = {}): Request {
    return new Request('http://localhost/x', {
        method: 'POST',
        body,
        headers: { 'content-type': 'application/json', ...headers },
    });
}

describe('parseJsonBounded', () => {
    it('parses small valid JSON', async () => {
        const req = makeReq(JSON.stringify({ a: 1 }));
        const body = await parseJsonBounded<{ a: number }>(req);
        expect(body.a).toBe(1);
    });

    it('throws BodyTooLargeError on oversize via content-length', async () => {
        const req = new Request('http://localhost/x', {
            method: 'POST',
            body: 'x'.repeat(200),
            headers: { 'content-length': '999999999' },
        });
        await expect(parseJsonBounded(req, { maxBytes: 100 })).rejects.toBeInstanceOf(
            BodyTooLargeError
        );
    });

    it('throws BodyTooLargeError on oversize via streamed bytes', async () => {
        // Send a real >max body without lying about content-length.
        const huge = 'x'.repeat(2_000);
        const req = makeReq(huge);
        await expect(parseJsonBounded(req, { maxBytes: 1_000 })).rejects.toBeInstanceOf(
            BodyTooLargeError
        );
    });

    it('throws BodyParseError on malformed JSON', async () => {
        const req = makeReq('{not-json}');
        await expect(parseJsonBounded(req)).rejects.toBeInstanceOf(BodyParseError);
    });

    it('returns {} on empty body', async () => {
        const req = new Request('http://localhost/x', { method: 'POST' });
        const body = await parseJsonBounded(req);
        expect(body).toEqual({});
    });

    it('respects custom maxBytes override', async () => {
        const ok = JSON.stringify({ payload: 'a'.repeat(900) });
        const req = makeReq(ok);
        const body = await parseJsonBounded<{ payload: string }>(req, { maxBytes: 5_000 });
        expect(body.payload.length).toBe(900);
    });
});
