UNPKG

alepha

Version:

Easy-to-use modern TypeScript framework for building many kind of applications.

529 lines (405 loc) 16.9 kB
import { Alepha } from "alepha"; import { describe, it } from "vitest"; import { CryptoProvider } from "../../../crypto/index.ts"; import type { ServerRequestData } from "../interfaces/ServerRequest.ts"; import { ServerRequestParser } from "../services/ServerRequestParser.ts"; const createMockRequestData = ( headers: Record<string, string> = {}, ): ServerRequestData => ({ method: "GET", url: new URL("https://example.com/test"), headers, query: {}, params: {}, raw: { node: undefined, web: undefined }, }) as ServerRequestData; describe("ServerRequestParser", () => { describe("getRequestIp", () => { it("should use X-Forwarded-For when TRUST_PROXY is true", ({ expect }) => { const alepha = Alepha.create({ env: { TRUST_PROXY: "true" } }); const parser = alepha.inject(ServerRequestParser); const request = createMockRequestData({ "x-forwarded-for": "203.0.113.195, 70.41.3.18, 150.172.238.178", }); expect(parser.getRequestIp(request)).toBe("203.0.113.195"); }); it("should use first IP from X-Forwarded-For chain", ({ expect }) => { const alepha = Alepha.create({ env: { TRUST_PROXY: "true" } }); const parser = alepha.inject(ServerRequestParser); const request = createMockRequestData({ "x-forwarded-for": "192.168.1.1, 10.0.0.1", }); expect(parser.getRequestIp(request)).toBe("192.168.1.1"); }); it("should use X-Real-IP as fallback", ({ expect }) => { const alepha = Alepha.create({ env: { TRUST_PROXY: "true" } }); const parser = alepha.inject(ServerRequestParser); const request = createMockRequestData({ "x-real-ip": "192.168.1.100", }); expect(parser.getRequestIp(request)).toBe("192.168.1.100"); }); it("should ignore proxy headers when TRUST_PROXY is false", ({ expect, }) => { const alepha = Alepha.create({ env: { TRUST_PROXY: "false" } }); const parser = alepha.inject(ServerRequestParser); const request = createMockRequestData({ "x-forwarded-for": "203.0.113.195", "x-real-ip": "192.168.1.100", }); // Should return undefined since no raw connection IP expect(parser.getRequestIp(request)).toBe(undefined); }); }); describe("getRequestGeo", () => { it("should parse Cloudflare geo headers", ({ expect }) => { const alepha = Alepha.create(); const parser = alepha.inject(ServerRequestParser); const request = createMockRequestData({ "cf-ipcountry": "US", "cf-ipcity": "San Francisco", "cf-region": "California", "cf-iplatitude": "37.7749", "cf-iplongitude": "-122.4194", }); const geo = parser.getRequestGeo(request); expect(geo.country).toBe("US"); expect(geo.city).toBe("San Francisco"); expect(geo.region).toBe("California"); expect(geo.latitude).toBe("37.7749"); expect(geo.longitude).toBe("-122.4194"); }); it("should parse Vercel geo headers", ({ expect }) => { const alepha = Alepha.create(); const parser = alepha.inject(ServerRequestParser); const request = createMockRequestData({ "x-vercel-ip-country": "FR", "x-vercel-ip-city": "Paris", "x-vercel-ip-country-region": "IDF", "x-vercel-ip-latitude": "48.8566", "x-vercel-ip-longitude": "2.3522", }); const geo = parser.getRequestGeo(request); expect(geo.country).toBe("FR"); expect(geo.city).toBe("Paris"); expect(geo.region).toBe("IDF"); expect(geo.latitude).toBe("48.8566"); expect(geo.longitude).toBe("2.3522"); }); it("should parse AWS CloudFront geo headers", ({ expect }) => { const alepha = Alepha.create(); const parser = alepha.inject(ServerRequestParser); const request = createMockRequestData({ "cloudfront-viewer-country": "JP", }); const geo = parser.getRequestGeo(request); expect(geo.country).toBe("JP"); }); it("should return empty geo for missing headers", ({ expect }) => { const alepha = Alepha.create(); const parser = alepha.inject(ServerRequestParser); const request = createMockRequestData({}); const geo = parser.getRequestGeo(request); expect(geo.country).toBe(undefined); expect(geo.city).toBe(undefined); expect(geo.region).toBe(undefined); expect(geo.latitude).toBe(undefined); expect(geo.longitude).toBe(undefined); }); }); describe("getIsBot", () => { it("should detect Googlebot", ({ expect }) => { const alepha = Alepha.create(); const parser = alepha.inject(ServerRequestParser); const request = createMockRequestData({ "user-agent": "Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)", }); expect(parser.getIsBot(request)).toBe(true); }); it("should detect Bingbot", ({ expect }) => { const alepha = Alepha.create(); const parser = alepha.inject(ServerRequestParser); const request = createMockRequestData({ "user-agent": "Mozilla/5.0 (compatible; bingbot/2.0; +http://www.bing.com/bingbot.htm)", }); expect(parser.getIsBot(request)).toBe(true); }); it("should detect curl", ({ expect }) => { const alepha = Alepha.create(); const parser = alepha.inject(ServerRequestParser); const request = createMockRequestData({ "user-agent": "curl/7.68.0", }); expect(parser.getIsBot(request)).toBe(true); }); it("should detect Python requests", ({ expect }) => { const alepha = Alepha.create(); const parser = alepha.inject(ServerRequestParser); const request = createMockRequestData({ "user-agent": "python-requests/2.25.1", }); expect(parser.getIsBot(request)).toBe(true); }); it("should detect HeadlessChrome", ({ expect }) => { const alepha = Alepha.create(); const parser = alepha.inject(ServerRequestParser); const request = createMockRequestData({ "user-agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/91.0.4472.114 Safari/537.36", }); expect(parser.getIsBot(request)).toBe(true); }); it("should detect GPTBot", ({ expect }) => { const alepha = Alepha.create(); const parser = alepha.inject(ServerRequestParser); const request = createMockRequestData({ "user-agent": "Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko; compatible; GPTBot/1.0; +https://openai.com/gptbot)", }); expect(parser.getIsBot(request)).toBe(true); }); it("should detect ClaudeBot", ({ expect }) => { const alepha = Alepha.create(); const parser = alepha.inject(ServerRequestParser); const request = createMockRequestData({ "user-agent": "ClaudeBot/1.0", }); expect(parser.getIsBot(request)).toBe(true); }); it("should not detect regular browser as bot", ({ expect }) => { const alepha = Alepha.create(); const parser = alepha.inject(ServerRequestParser); const request = createMockRequestData({ "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36", }); expect(parser.getIsBot(request)).toBe(false); }); it("should return false for missing user-agent", ({ expect }) => { const alepha = Alepha.create(); const parser = alepha.inject(ServerRequestParser); const request = createMockRequestData({}); expect(parser.getIsBot(request)).toBe(false); }); }); describe("getIsMobile", () => { it("should detect Android device", ({ expect }) => { const alepha = Alepha.create(); const parser = alepha.inject(ServerRequestParser); const request = createMockRequestData({ "user-agent": "Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.120 Mobile Safari/537.36", }); expect(parser.getIsMobile(request)).toBe(true); }); it("should detect iPhone", ({ expect }) => { const alepha = Alepha.create(); const parser = alepha.inject(ServerRequestParser); const request = createMockRequestData({ "user-agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 14_6 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.1.1 Mobile/15E148 Safari/604.1", }); expect(parser.getIsMobile(request)).toBe(true); }); it("should detect iPad", ({ expect }) => { const alepha = Alepha.create(); const parser = alepha.inject(ServerRequestParser); const request = createMockRequestData({ "user-agent": "Mozilla/5.0 (iPad; CPU OS 14_6 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.1.1 Mobile/15E148 Safari/604.1", }); expect(parser.getIsMobile(request)).toBe(true); }); it("should detect Windows Phone", ({ expect }) => { const alepha = Alepha.create(); const parser = alepha.inject(ServerRequestParser); const request = createMockRequestData({ "user-agent": "Mozilla/5.0 (Windows Phone 10.0; Android 6.0.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Mobile Safari/537.36 Edge/15.15254", }); expect(parser.getIsMobile(request)).toBe(true); }); it("should not detect desktop as mobile", ({ expect }) => { const alepha = Alepha.create(); const parser = alepha.inject(ServerRequestParser); const request = createMockRequestData({ "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36", }); expect(parser.getIsMobile(request)).toBe(false); }); it("should return false for missing user-agent", ({ expect }) => { const alepha = Alepha.create(); const parser = alepha.inject(ServerRequestParser); const request = createMockRequestData({}); expect(parser.getIsMobile(request)).toBe(false); }); }); describe("getProtocol", () => { it("should use X-Forwarded-Proto header", ({ expect }) => { const alepha = Alepha.create(); const parser = alepha.inject(ServerRequestParser); const request = createMockRequestData({ "x-forwarded-proto": "https", }); expect(parser.getProtocol(request)).toBe("https"); }); it("should handle HTTP from X-Forwarded-Proto", ({ expect }) => { const alepha = Alepha.create(); const parser = alepha.inject(ServerRequestParser); const request = createMockRequestData({ "x-forwarded-proto": "http", }); expect(parser.getProtocol(request)).toBe("http"); }); it("should parse Cloudflare CF-Visitor header", ({ expect }) => { const alepha = Alepha.create(); const parser = alepha.inject(ServerRequestParser); const request = createMockRequestData({ "cf-visitor": '{"scheme":"https"}', }); expect(parser.getProtocol(request)).toBe("https"); }); it("should handle invalid CF-Visitor JSON gracefully", ({ expect }) => { const alepha = Alepha.create(); const parser = alepha.inject(ServerRequestParser); const request = createMockRequestData({ "cf-visitor": "invalid-json", }); // Should fall back to URL scheme (http since mock URL is https) expect(parser.getProtocol(request)).toBe("https"); }); it("should use URL scheme as fallback", ({ expect }) => { const alepha = Alepha.create(); const parser = alepha.inject(ServerRequestParser); const request = createMockRequestData({}); // Our mock URL is https://example.com/test expect(parser.getProtocol(request)).toBe("https"); }); it("should default to http when URL is http", ({ expect }) => { const alepha = Alepha.create(); const parser = alepha.inject(ServerRequestParser); const request = { ...createMockRequestData({}), url: new URL("http://example.com/test"), }; expect(parser.getProtocol(request)).toBe("http"); }); }); describe("getLanguage", () => { it("should parse simple Accept-Language", ({ expect }) => { const alepha = Alepha.create(); const parser = alepha.inject(ServerRequestParser); const request = createMockRequestData({ "accept-language": "en-US", }); expect(parser.getLanguage(request)).toBe("en-US"); }); it("should return first language from list", ({ expect }) => { const alepha = Alepha.create(); const parser = alepha.inject(ServerRequestParser); const request = createMockRequestData({ "accept-language": "en-US,en;q=0.9,fr;q=0.8", }); expect(parser.getLanguage(request)).toBe("en-US"); }); it("should handle quality values", ({ expect }) => { const alepha = Alepha.create(); const parser = alepha.inject(ServerRequestParser); const request = createMockRequestData({ "accept-language": "fr;q=0.9,en;q=0.8", }); expect(parser.getLanguage(request)).toBe("fr"); }); it("should return undefined for missing header", ({ expect }) => { const alepha = Alepha.create(); const parser = alepha.inject(ServerRequestParser); const request = createMockRequestData({}); expect(parser.getLanguage(request)).toBe(undefined); }); it("should handle complex Accept-Language", ({ expect }) => { const alepha = Alepha.create(); const parser = alepha.inject(ServerRequestParser); const request = createMockRequestData({ "accept-language": "zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7", }); expect(parser.getLanguage(request)).toBe("zh-CN"); }); }); describe("getReferer", () => { it("should parse valid referer URL", ({ expect }) => { const alepha = Alepha.create(); const parser = alepha.inject(ServerRequestParser); const request = createMockRequestData({ referer: "https://google.com/search?q=test", }); const referer = parser.getReferer(request); expect(referer?.url).toBe("https://google.com/search?q=test"); expect(referer?.hostname).toBe("google.com"); expect(referer?.pathname).toBe("/search"); }); it("should handle referrer header (alternative spelling)", ({ expect }) => { const alepha = Alepha.create(); const parser = alepha.inject(ServerRequestParser); const request = createMockRequestData({ referrer: "https://example.org/page", }); const referer = parser.getReferer(request); expect(referer?.url).toBe("https://example.org/page"); expect(referer?.hostname).toBe("example.org"); expect(referer?.pathname).toBe("/page"); }); it("should prefer referer over referrer", ({ expect }) => { const alepha = Alepha.create(); const parser = alepha.inject(ServerRequestParser); const request = createMockRequestData({ referer: "https://first.com/", referrer: "https://second.com/", }); const referer = parser.getReferer(request); expect(referer?.hostname).toBe("first.com"); }); it("should return undefined for missing header", ({ expect }) => { const alepha = Alepha.create(); const parser = alepha.inject(ServerRequestParser); const request = createMockRequestData({}); expect(parser.getReferer(request)).toBe(undefined); }); it("should return undefined for invalid URL", ({ expect }) => { const alepha = Alepha.create(); const parser = alepha.inject(ServerRequestParser); const request = createMockRequestData({ referer: "not-a-valid-url", }); expect(parser.getReferer(request)).toBe(undefined); }); }); describe("getRequestId", () => { it("should extract X-Request-ID header", ({ expect }) => { const alepha = Alepha.create(); const parser = alepha.inject(ServerRequestParser); const request = createMockRequestData({ "x-request-id": "abc-123-def", }); expect(parser.getRequestId(request)).toBe("abc-123-def"); }); it("should return UUID for missing header", ({ expect }) => { const reqId = "00000000-0000-0000-0000-000000000000"; const alepha = Alepha.create().with({ provide: CryptoProvider, use: class extends CryptoProvider { randomUUID() { return reqId; } }, }); const parser = alepha.inject(ServerRequestParser); const request = createMockRequestData({}); expect(parser.getRequestId(request)).toBe(reqId); }); }); });