49 lines · 2.0 KB
import { describe, expect, test } from "bun:test";
import { sanitizePath, sanitizeUrl, scrubProperties, setRepoVisibility } from "./analytics";

const ORIGIN = "https://gitfor.dev";

describe("analytics URL scrubbing", () => {
  test("app pages keep their path; queries and fragments are dropped", () => {
    expect(sanitizeUrl(`${ORIGIN}/settings/billing?period=2026-10#x`, ORIGIN)).toBe(`${ORIGIN}/settings/billing`);
    expect(sanitizePath("/explore")).toBe("/explore");
    expect(sanitizePath("/alice")).toBe("/alice");
  });

  test("repository paths are redacted unless the repo is known public", () => {
    expect(sanitizePath("/acme/secret/blob/main/keys.txt")).toBe("/[private]/[private]/blob");
    setRepoVisibility("acme", "secret", true);
    expect(sanitizePath("/acme/secret/blob/main/keys.txt")).toBe("/[private]/[private]/blob");
    setRepoVisibility("acme", "web", false);
    expect(sanitizePath("/acme/web/pull/3")).toBe("/acme/web/pull/3");
    setRepoVisibility("acme", "web", true);
    expect(sanitizePath("/acme/web/pull/3")).toBe("/[private]/[private]/pull");
  });

  test("other origins keep only their origin", () => {
    expect(sanitizeUrl("https://checkout.stripe.com/c/pay/cs_test_123?x=1", ORIGIN)).toBe(
      "https://checkout.stripe.com",
    );
    expect(sanitizeUrl("$direct", ORIGIN)).toBe("$direct");
  });

  test("nested payloads, URL keys and titles are scrubbed", () => {
    const clean = scrubProperties(
      {
        title: "acme/secret · gitfor",
        $current_url: `${ORIGIN}/acme/secret/issues?q=x`,
        $web_vitals_FCP_event: { navigationURL: `${ORIGIN}/acme/secret/issues`, value: 1 },
        $heatmap_data: { [`${ORIGIN}/acme/secret/tree/main`]: [{ x: 1 }] },
        $search: "?q=x",
        $elements_chain: 'a.nav:href="/acme/secret/blob/main/keys.txt"nth-child="1"',
        $el_href: "/acme/secret/tree/main?x=1",
      },