Files
bun.sh/test/regression/issue/test-21049.test.ts
2025-07-20 23:02:10 -07:00

183 lines
5.6 KiB
TypeScript

import { expect, test } from "bun:test";
import { bunEnv, bunExe } from "harness";
test("fetch with Request object respects redirect: 'manual' option", async () => {
// Test server that redirects
await using server = Bun.serve({
port: 0,
fetch(req) {
const url = new URL(req.url);
if (url.pathname === "/redirect") {
return new Response(null, {
status: 302,
headers: {
Location: "/target",
},
});
}
if (url.pathname === "/target") {
return new Response("Target reached", { status: 200 });
}
return new Response("Not found", { status: 404 });
},
});
// Test 1: Direct fetch with redirect: "manual" (currently works)
const directResponse = await fetch(`${server.url}/redirect`, {
redirect: "manual",
});
expect(directResponse.status).toBe(302);
expect(directResponse.url).toBe(`${server.url}/redirect`);
expect(directResponse.headers.get("location")).toBe("/target");
expect(directResponse.redirected).toBe(false);
// Test 2: Fetch with Request object and redirect: "manual" (currently broken)
const request = new Request(`${server.url}/redirect`, {
redirect: "manual",
});
const requestResponse = await fetch(request);
expect(requestResponse.status).toBe(302);
expect(requestResponse.url).toBe(`${server.url}/redirect`); // This should be the original URL, not the target
expect(requestResponse.headers.get("location")).toBe("/target");
expect(requestResponse.redirected).toBe(false);
// Test 3: Verify the behavior matches Node.js and Deno
const testScript = `
async function main() {
const request = new Request("${server.url}/redirect", {
redirect: "manual",
});
const response = await fetch(request);
console.log(JSON.stringify({
status: response.status,
url: response.url,
redirected: response.redirected,
location: response.headers.get("location")
}));
}
main();
`;
// Run with Bun
await using bunProc = Bun.spawn({
cmd: [bunExe(), "-e", testScript],
env: bunEnv,
stdout: "pipe",
stderr: "pipe",
});
const [bunStdout, bunExitCode] = await Promise.all([new Response(bunProc.stdout).text(), bunProc.exited]);
expect(bunExitCode).toBe(0);
const bunResult = JSON.parse(bunStdout.trim());
// The bug: Bun follows the redirect even though redirect: "manual" was specified
// Expected: status=302, url=original, redirected=false
// Actual (bug): status=200, url=target, redirected=true
expect(bunResult).toEqual({
status: 302,
url: `${server.url}/redirect`,
redirected: false,
location: "/target",
});
});
// Additional test to verify it works with external redirects
test("fetch with Request object respects redirect: 'manual' for external URLs", async () => {
// This test uses a real URL that redirects
using server = Bun.serve({
port: 0,
routes: {
"/redirect": new Response(null, {
status: 302,
headers: {
Location: "/target",
},
}),
"/target": new Response("Target reached", { status: 200 }),
},
});
const request = new Request(`${server.url}/redirect`, {
redirect: "manual",
});
const response = await fetch(request);
// When redirect: "manual" is set, we should get the redirect response
expect(response.status).toBe(302);
expect(response.url).toBe(`${server.url}/redirect`);
expect(response.redirected).toBe(false);
expect(response.headers.get("location")).toBe("/target");
});
// Test edge case: fetch with options but no redirect should use Request's redirect
test("fetch with Request respects redirect when fetch has other options but no redirect", async () => {
// Test server that redirects
await using server = Bun.serve({
port: 0,
fetch(req) {
const url = new URL(req.url);
if (url.pathname === "/redirect") {
return new Response(null, {
status: 302,
headers: {
Location: "/target",
},
});
}
if (url.pathname === "/target") {
return new Response("Target reached", {
status: 200,
headers: {
"X-Target": "true",
},
});
}
return new Response("Not found", { status: 404 });
},
});
// Create a Request with redirect: "manual"
const request = new Request(`${server.url}/redirect`, {
redirect: "manual",
headers: {
"X-Original": "request",
},
});
// Test 1: fetch with other options but NO redirect option
// Should use the Request's redirect: "manual"
const response1 = await fetch(request, {
headers: {
"X-Additional": "fetch-option",
},
// Note: no redirect option here
});
expect(response1.status).toBe(302);
expect(response1.url).toBe(`${server.url}/redirect`);
expect(response1.redirected).toBe(false);
expect(response1.headers.get("location")).toBe("/target");
// Test 2: fetch with explicit redirect option should override Request's redirect
const response2 = await fetch(request, {
headers: {
"X-Additional": "fetch-option",
},
redirect: "follow", // Explicitly override
});
expect(response2.status).toBe(200);
expect(response2.url).toBe(new URL("/target", server.url).href);
expect(response2.redirected).toBe(true);
expect(response2.headers.get("X-Target")).toBe("true");
// Test 3: fetch with empty options object should use Request's redirect
const response3 = await fetch(request, {});
expect(response3.status).toBe(302);
expect(response3.url).toBe(`${server.url}/redirect`);
expect(response3.redirected).toBe(false);
});