// biome-ignore-all lint/suspicious/noExplicitAny: Any in tests
import assert from "node:assert";
import { afterEach, beforeEach, describe, it, mock } from "node:test";
import { cleanupTestDir, createTempTestDir, createTestFastifyInstance, mockLogger, } from "../test-utils/setup.js";
import { createTestRoutes } from "./test.js";
describe("Test Routes", () => {
    let app;
    let testDir;
    let originalFetch;
    let fetchMock;
    beforeEach(async () => {
        testDir = await createTempTestDir("test-route-test-");
        app = await createTestFastifyInstance();
        await app.register(createTestRoutes, {
            prefix: "/test",
            sourceDirectory: testDir,
            logger: mockLogger,
            deploymentUrl: "http://localhost:9000",
        });
        await app.ready();
        // Mock global fetch
        originalFetch = global.fetch;
        fetchMock = mock.fn(global.fetch);
        global.fetch = fetchMock;
    });
    afterEach(async () => {
        await app.close();
        await cleanupTestDir(testDir);
        // Restore original fetch
        global.fetch = originalFetch;
        fetchMock.mock.resetCalls();
    });
    describe("POST /test/", () => {
        it("should proxy GET request to local gateway", async () => {
            // Mock successful response
            fetchMock.mock.mockImplementation(async () => {
                return new Response(JSON.stringify({ message: "success" }), {
                    status: 200,
                    statusText: "OK",
                    headers: {
                        "content-type": "application/json",
                    },
                });
            });
            const response = await app.inject({
                method: "POST",
                url: "/test/",
                payload: {
                    method: "GET",
                    path: "/v1/hello",
                    headers: {
                        "x-custom-header": "test-value",
                    },
                },
            });
            assert.strictEqual(response.statusCode, 200);
            const body = JSON.parse(response.body);
            assert.strictEqual(body.status, 200);
            assert.strictEqual(body.statusText, "OK");
            assert.strictEqual(body.headers["content-type"], "application/json");
            assert.strictEqual(body.body, JSON.stringify({ message: "success" }));
            // Verify fetch was called with correct URL
            assert.strictEqual(fetchMock.mock.calls.length, 1);
            assert.strictEqual(fetchMock.mock.calls[0].arguments[0], "http://localhost:9000/v1/hello");
        });
        it("should proxy POST request with body", async () => {
            fetchMock.mock.mockImplementation(async () => {
                return new Response(JSON.stringify({ created: true }), {
                    status: 201,
                    statusText: "Created",
                    headers: {
                        "content-type": "application/json",
                    },
                });
            });
            const testPayload = JSON.stringify({ name: "test" });
            const response = await app.inject({
                method: "POST",
                url: "/test/",
                payload: {
                    method: "POST",
                    path: "/v1/items",
                    headers: {
                        "content-type": "application/json",
                    },
                    body: testPayload,
                },
            });
            assert.strictEqual(response.statusCode, 200);
            const body = JSON.parse(response.body);
            assert.strictEqual(body.status, 201);
            assert.strictEqual(body.body, JSON.stringify({ created: true }));
            // Verify fetch was called with body
            const fetchCall = fetchMock.mock.calls[0].arguments;
            assert.strictEqual(fetchCall[0], "http://localhost:9000/v1/items");
            assert.strictEqual(fetchCall[1]?.method, "POST");
            assert.strictEqual(fetchCall[1]?.body, testPayload);
        });
        it("should filter out problematic headers", async () => {
            fetchMock.mock.mockImplementation(async () => {
                return new Response("ok", { status: 200 });
            });
            const response = await app.inject({
                method: "POST",
                url: "/test/",
                payload: {
                    method: "GET",
                    path: "/v1/hello",
                    headers: {
                        host: "example.com",
                        connection: "keep-alive",
                        "x-custom": "should-be-kept",
                    },
                },
            });
            assert.strictEqual(response.statusCode, 200);
            // Verify headers were cleaned
            const fetchCall = fetchMock.mock.calls[0].arguments;
            const headers = fetchCall[1]?.headers;
            assert.ok(headers);
            assert.strictEqual(headers["x-custom"], "should-be-kept");
            assert.strictEqual(headers.host, undefined);
            assert.strictEqual(headers.connection, undefined);
        });
        it("should use overrideUrl when provided", async () => {
            fetchMock.mock.mockImplementation(async () => {
                return new Response("ok", { status: 200 });
            });
            const response = await app.inject({
                method: "POST",
                url: "/test/",
                payload: {
                    method: "GET",
                    path: "/v1/hello",
                    overrideUrl: "http://custom-host:8080",
                },
            });
            assert.strictEqual(response.statusCode, 200);
            // Verify custom URL was used
            assert.strictEqual(fetchMock.mock.calls[0].arguments[0], "http://custom-host:8080/v1/hello");
        });
        it("should handle gateway connection errors", async () => {
            // Mock connection error
            fetchMock.mock.mockImplementation(async () => {
                throw new TypeError("fetch failed");
            });
            const response = await app.inject({
                method: "POST",
                url: "/test/",
                payload: {
                    method: "GET",
                    path: "/v1/hello",
                },
            });
            assert.strictEqual(response.statusCode, 200);
            const body = JSON.parse(response.body);
            assert.strictEqual(body.status, 503);
            assert.strictEqual(body.statusText, "Service Unavailable");
            assert.strictEqual(body.headers["content-type"], "application/problem+json");
            const problemDetails = JSON.parse(body.body);
            assert.strictEqual(problemDetails.status, 503);
            assert.strictEqual(problemDetails.title, "Local Gateway Unavailable");
            assert.ok(problemDetails.detail.includes("local Zuplo gateway"));
        });
        it("should not include body for GET requests", async () => {
            fetchMock.mock.mockImplementation(async () => {
                return new Response("ok", { status: 200 });
            });
            const response = await app.inject({
                method: "POST",
                url: "/test/",
                payload: {
                    method: "GET",
                    path: "/v1/hello",
                    body: "this should be ignored",
                },
            });
            assert.strictEqual(response.statusCode, 200);
            // Verify no body was sent for GET
            const fetchCall = fetchMock.mock.calls[0].arguments;
            assert.strictEqual(fetchCall[1]?.body, undefined);
        });
        it("should not include body for HEAD requests", async () => {
            fetchMock.mock.mockImplementation(async () => {
                return new Response(null, { status: 200 });
            });
            const response = await app.inject({
                method: "POST",
                url: "/test/",
                payload: {
                    method: "HEAD",
                    path: "/v1/hello",
                    body: "this should be ignored",
                },
            });
            assert.strictEqual(response.statusCode, 200);
            // Verify no body was sent for HEAD
            const fetchCall = fetchMock.mock.calls[0].arguments;
            assert.strictEqual(fetchCall[1]?.body, undefined);
        });
        it("should handle different HTTP status codes", async () => {
            fetchMock.mock.mockImplementation(async () => {
                return new Response(JSON.stringify({ error: "Not Found" }), {
                    status: 404,
                    statusText: "Not Found",
                    headers: {
                        "content-type": "application/json",
                    },
                });
            });
            const response = await app.inject({
                method: "POST",
                url: "/test/",
                payload: {
                    method: "GET",
                    path: "/v1/nonexistent",
                },
            });
            assert.strictEqual(response.statusCode, 200);
            const body = JSON.parse(response.body);
            assert.strictEqual(body.status, 404);
            assert.strictEqual(body.statusText, "Not Found");
        });
        it("should preserve response headers", async () => {
            fetchMock.mock.mockImplementation(async () => {
                return new Response("ok", {
                    status: 200,
                    headers: {
                        "content-type": "text/plain",
                        "x-custom-response": "test-value",
                        "cache-control": "no-cache",
                    },
                });
            });
            const response = await app.inject({
                method: "POST",
                url: "/test/",
                payload: {
                    method: "GET",
                    path: "/v1/hello",
                },
            });
            assert.strictEqual(response.statusCode, 200);
            const body = JSON.parse(response.body);
            assert.strictEqual(body.headers["content-type"], "text/plain");
            assert.strictEqual(body.headers["x-custom-response"], "test-value");
            assert.strictEqual(body.headers["cache-control"], "no-cache");
        });
    });
});
//# sourceMappingURL=test.spec.js.map