Files
next-ai-draw-io/tests/unit/client-ip.test.ts
T

64 lines
2.0 KiB
TypeScript
Raw Normal View History

import { NextRequest } from "next/server"
import { afterEach, describe, expect, it } from "vitest"
import { getUserIdFromRequest } from "@/lib/user-id"
import { proxy } from "@/proxy"
const idFor = (ip: string) => `user-${Buffer.from(ip).toString("base64url")}`
afterEach(() => {
delete process.env.CLIENT_IP_HEADER
delete process.env.ORIGIN_SECRET
})
describe("getUserIdFromRequest", () => {
const req = new Request("http://localhost/api/chat", {
headers: {
"x-forwarded-for": "203.0.113.9, 198.51.100.7",
"cf-connecting-ip": "198.51.100.7",
},
})
it("uses the first X-Forwarded-For entry by default", () => {
expect(getUserIdFromRequest(req)).toBe(idFor("203.0.113.9"))
})
it("uses CLIENT_IP_HEADER when set", () => {
process.env.CLIENT_IP_HEADER = "cf-connecting-ip"
expect(getUserIdFromRequest(req)).toBe(idFor("198.51.100.7"))
})
it("is anonymous when the configured header is missing", () => {
process.env.CLIENT_IP_HEADER = "cf-connecting-ip"
expect(
getUserIdFromRequest(new Request("http://localhost/api/chat")),
).toBe("anonymous")
})
})
describe("ORIGIN_SECRET", () => {
const call = (path: string, secret?: string) =>
proxy(
new NextRequest(`http://localhost${path}`, {
headers: secret ? { "x-origin-secret": secret } : {},
}),
)
it("lets every API call through when unset", () => {
expect(call("/api/chat")).toBeUndefined()
})
it("refuses API calls without the right header", async () => {
process.env.ORIGIN_SECRET = "s3cret"
for (const res of [call("/api/chat"), call("/api/chat", "wrong")]) {
expect(res?.status).toBe(403)
}
expect(call("/api/chat", "s3cret")).toBeUndefined()
})
it("leaves pages alone, such as the health check on /", () => {
process.env.ORIGIN_SECRET = "s3cret"
expect(call("/")?.status).toBe(307)
expect(call("/en")).toBeUndefined()
})
})