From 6588c2bc39caf7383d896531640f79a3bc506413 Mon Sep 17 00:00:00 2001 From: MythEclipse Date: Thu, 7 May 2026 22:41:08 +0700 Subject: [PATCH] test: add unit tests for relay-utils functions --- bun.lock | 3 ++ package.json | 1 + src/lib/relay-utils.test.ts | 85 +++++++++++++++++++++++++++++++++++++ tsconfig.json | 1 + 4 files changed, 90 insertions(+) create mode 100644 src/lib/relay-utils.test.ts diff --git a/bun.lock b/bun.lock index d6d0730..4da0955 100644 --- a/bun.lock +++ b/bun.lock @@ -23,6 +23,7 @@ "@types/node": "^20", "@types/react": "^19", "@types/react-dom": "^19", + "bun-types": "^1.3.13", "eslint": "^9", "eslint-config-next": "16.2.5", "tailwindcss": "^4", @@ -437,6 +438,8 @@ "browserslist": ["browserslist@4.28.2", "", { "dependencies": { "baseline-browser-mapping": "^2.10.12", "caniuse-lite": "^1.0.30001782", "electron-to-chromium": "^1.5.328", "node-releases": "^2.0.36", "update-browserslist-db": "^1.2.3" }, "bin": { "browserslist": "cli.js" } }, "sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg=="], + "bun-types": ["bun-types@1.3.13", "", { "dependencies": { "@types/node": "*" } }, "sha512-QXKeHLlOLqQX9LgYaHJfzdBaV21T63HhFJnvuRCcjZiaUDpbs5ED1MgxbMra71CsryN/1dAoXuJJJwIv/2drVA=="], + "bundle-name": ["bundle-name@4.1.0", "", { "dependencies": { "run-applescript": "^7.0.0" } }, "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q=="], "bytes": ["bytes@3.1.2", "", {}, "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg=="], diff --git a/package.json b/package.json index 4fb9f1c..84a53da 100644 --- a/package.json +++ b/package.json @@ -27,6 +27,7 @@ "@types/node": "^20", "@types/react": "^19", "@types/react-dom": "^19", + "bun-types": "^1.3.13", "eslint": "^9", "eslint-config-next": "16.2.5", "tailwindcss": "^4", diff --git a/src/lib/relay-utils.test.ts b/src/lib/relay-utils.test.ts new file mode 100644 index 0000000..f07aa3c --- /dev/null +++ b/src/lib/relay-utils.test.ts @@ -0,0 +1,85 @@ +import { test, expect, describe } from "bun:test"; +import { + normalizeTargetUrl, + filterHeaders, + shouldSendBody, + isAllowedTarget, +} from "./relay-utils"; + +describe("relay-utils", () => { + describe("normalizeTargetUrl", () => { + test("should combine target and path", () => { + expect(normalizeTargetUrl("https://example.com", "/api/v1")).toBe("https://example.com/api/v1"); + }); + + test("should handle trailing slash in target", () => { + expect(normalizeTargetUrl("https://example.com/", "/api/v1")).toBe("https://example.com/api/v1"); + }); + + test("should return null if target is missing", () => { + expect(normalizeTargetUrl(null, "/api/v1")).toBe(null); + }); + }); + + describe("filterHeaders", () => { + test("should remove blocked headers", () => { + const headers = new Headers({ + "content-type": "application/json", + "cookie": "secret=123", + "x-vercel-id": "123", + "cf-ray": "123", + "host": "localhost", + "x-relay-target": "test", + }); + const filtered = filterHeaders(headers); + expect(filtered.has("content-type")).toBe(true); + expect(filtered.has("cookie")).toBe(false); + expect(filtered.has("x-vercel-id")).toBe(false); + expect(filtered.has("cf-ray")).toBe(false); + expect(filtered.has("host")).toBe(false); + expect(filtered.has("x-relay-target")).toBe(false); + }); + + test("should remove headers starting with blocked prefixes", () => { + const headers = new Headers({ + "x-vercel-custom": "val", + "cf-custom": "val", + "x-forwarded-for": "1.1.1.1", + }); + const filtered = filterHeaders(headers); + expect(filtered.has("x-vercel-custom")).toBe(false); + expect(filtered.has("cf-custom")).toBe(false); + expect(filtered.has("x-forwarded-for")).toBe(false); + }); + }); + + describe("shouldSendBody", () => { + test("should return false for GET and HEAD", () => { + expect(shouldSendBody("GET")).toBe(false); + expect(shouldSendBody("HEAD")).toBe(false); + }); + + test("should return true for POST, PUT, DELETE, PATCH", () => { + expect(shouldSendBody("POST")).toBe(true); + expect(shouldSendBody("PUT")).toBe(true); + expect(shouldSendBody("DELETE")).toBe(true); + expect(shouldSendBody("PATCH")).toBe(true); + }); + }); + + describe("isAllowedTarget", () => { + test("should allow http and https", () => { + expect(isAllowedTarget("https://example.com")).toBe(true); + expect(isAllowedTarget("http://example.com")).toBe(true); + }); + + test("should reject other protocols", () => { + expect(isAllowedTarget("ftp://example.com")).toBe(false); + expect(isAllowedTarget("javascript:alert(1)")).toBe(false); + }); + + test("should reject invalid URLs", () => { + expect(isAllowedTarget("not-a-url")).toBe(false); + }); + }); +}); diff --git a/tsconfig.json b/tsconfig.json index cf9c65d..f2e69de 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -12,6 +12,7 @@ "resolveJsonModule": true, "isolatedModules": true, "jsx": "react-jsx", + "types": ["bun-types"], "incremental": true, "plugins": [ {