112 lines
5.3 KiB
TypeScript
112 lines
5.3 KiB
TypeScript
import { describe, expect, test } from "vitest";
|
|
import { getAbsoluteApiUrl, getAbsoluteUrl, getBaseURL, getWebhooksUrl } from "./url.server";
|
|
|
|
const productionEnv = {
|
|
NODE_ENV: "production",
|
|
APP_NAME: "Production App",
|
|
DATABASE_URL: "postgres://example.invalid/app",
|
|
APP_BASE_DOMAIN: "example.com",
|
|
WEB_APP_HOST: "app.example.com",
|
|
ADMIN_APP_HOST: "admin.example.com",
|
|
API_HOST: "api.example.com",
|
|
// Production secret-strength hardening (packages/config/src/web.ts) requires
|
|
// >= 32 chars and SESSION_SECRET != CRYPTO_SECRET in production. These are
|
|
// length-valid, distinct test placeholders so the URL helpers can load config.
|
|
SESSION_SECRET: "session-secret-value-0123456789abcdef",
|
|
CRYPTO_SECRET: "crypto-secret-value-fedcba9876543210",
|
|
API_ACCESS_TOKEN: "api-access-token-value-0123456789abcdef",
|
|
JWT_SECRET: "jwt-secret-value-0123456789abcdef0123",
|
|
};
|
|
|
|
const testEnv = {
|
|
APP_NAME: "Test App",
|
|
DATABASE_URL: "postgres://example.invalid/app",
|
|
SESSION_SECRET: "session-secret-value",
|
|
CRYPTO_SECRET: "crypto-secret-value",
|
|
API_ACCESS_TOKEN: "api-access-token-value",
|
|
JWT_SECRET: "jwt-secret-value",
|
|
};
|
|
|
|
describe("url.server", () => {
|
|
test("keeps production base URLs on https even when forwarded protocol is http", () => {
|
|
const request = new Request("http://internal.local/dashboard", {
|
|
headers: {
|
|
host: "internal.local",
|
|
"x-forwarded-host": "app.example.com",
|
|
"x-forwarded-proto": "http",
|
|
},
|
|
});
|
|
|
|
expect(getBaseURL(request, { env: productionEnv })).toBe("https://app.example.com");
|
|
});
|
|
|
|
test("uses forwarded protocol outside production when injected env is not development", () => {
|
|
const request = new Request("http://internal.local/dashboard", {
|
|
headers: {
|
|
host: "internal.local",
|
|
"x-forwarded-host": "preview.example.com",
|
|
"x-forwarded-proto": "https",
|
|
},
|
|
});
|
|
|
|
expect(getBaseURL(request, { env: { ...testEnv, NODE_ENV: "test" } })).toBe("https://preview.example.com");
|
|
});
|
|
|
|
test("forces http in development mode through injected env", () => {
|
|
const request = new Request("https://localhost:3000/dashboard", {
|
|
headers: {
|
|
host: "localhost:3000",
|
|
"x-forwarded-host": "localhost:3000",
|
|
"x-forwarded-proto": "https",
|
|
},
|
|
});
|
|
|
|
expect(getBaseURL(request, { env: { ...testEnv, NODE_ENV: "development" } })).toBe("http://localhost:3000");
|
|
});
|
|
|
|
test("uses webhook override from injected env", () => {
|
|
const request = new Request("https://app.example.com/dashboard");
|
|
|
|
expect(getWebhooksUrl(request, { env: { ...productionEnv, WEBHOOKS_URL: "https://hooks.example.com/webhooks" } })).toBe("https://hooks.example.com/webhooks");
|
|
});
|
|
|
|
test("generates absolute versioned API URLs from routing config", () => {
|
|
const request = new Request("https://app.example.com/dashboard");
|
|
|
|
expect(getAbsoluteApiUrl(request, "billing/events", { env: productionEnv })).toBe("https://api.example.com/v1/billing/events");
|
|
expect(getAbsoluteApiUrl(request, "/v1/health", { env: productionEnv })).toBe("https://api.example.com/v1/health");
|
|
expect(getAbsoluteApiUrl(request, "/api/v1/health", { env: productionEnv })).toBe("https://api.example.com/v1/health");
|
|
expect(getAbsoluteApiUrl(request, "/api/v1?active=true", { env: productionEnv })).toBe("https://api.example.com/v1?active=true");
|
|
expect(getAbsoluteApiUrl(request, "/v1#docs", { env: productionEnv })).toBe("https://api.example.com/v1#docs");
|
|
});
|
|
|
|
test("builds host-aware absolute URLs from the current canonical request host", () => {
|
|
expect(getAbsoluteUrl(new Request("https://admin.example.com/settings"), "/settings/profile", { env: productionEnv })).toBe(
|
|
"https://admin.example.com/settings/profile"
|
|
);
|
|
expect(getAbsoluteUrl(new Request("https://example.com/acme/settings"), "/acme/dashboard", { env: productionEnv })).toBe(
|
|
"https://app.example.com/acme/dashboard"
|
|
);
|
|
});
|
|
|
|
test("builds public app URLs on the web host even from admin requests", () => {
|
|
const adminRequest = new Request("https://admin.example.com/settings");
|
|
|
|
expect(getAbsoluteUrl(adminRequest, "/invitation/invite_token", { env: productionEnv })).toBe("https://app.example.com/invitation/invite_token");
|
|
expect(getAbsoluteUrl(adminRequest, "/docs/en", { env: productionEnv })).toBe("https://app.example.com/docs/en");
|
|
expect(getAbsoluteUrl(adminRequest, "/healthcheck", { env: productionEnv })).toBe("https://app.example.com/healthcheck");
|
|
expect(getAbsoluteUrl(adminRequest, "/readyz", { env: productionEnv })).toBe("https://app.example.com/readyz");
|
|
expect(getAbsoluteUrl(adminRequest, "/swagger", { env: productionEnv })).toBe("https://app.example.com/swagger");
|
|
expect(getAbsoluteUrl(adminRequest, "/v1/health", { env: productionEnv })).toBe("https://api.example.com/v1/health");
|
|
});
|
|
|
|
test("normalizes absolute URL inputs to same-origin canonical paths", () => {
|
|
const adminRequest = new Request("https://admin.example.com/settings");
|
|
|
|
expect(getAbsoluteUrl(adminRequest, "https://external.example/login?redirectTo=%2Fdashboard#top", { env: productionEnv })).toBe(
|
|
"https://app.example.com/login?redirectTo=%2Fdashboard#top"
|
|
);
|
|
expect(getAbsoluteUrl(adminRequest, "//external.example/v1/health", { env: productionEnv })).toBe("https://api.example.com/v1/health");
|
|
});
|
|
});
|