{
  "name": "Citation URL normalization implementation fixtures",
  "version": "1.0.0",
  "published": "2026-08-12",
  "license": "CC0-1.0",
  "scope": "Synthetic test cases for a conservative local comparison-key policy. These are not observed AI-answer citations and do not assert publisher canonicals.",
  "policy": {
    "allowedProtocols": ["http:", "https:"],
    "removeFragments": true,
    "trackingParameters": ["utm_*", "gclid", "dclid", "fbclid", "msclkid", "mc_cid", "mc_eid", "_hs*"],
    "sortRetainedParameters": true,
    "forceHttps": false,
    "mergeWww": false,
    "stripTrailingSlash": "fixture-specific"
  },
  "fixtures": [
    {"id":"host-fragment-utm","input":"https://EXAMPLE.org/Guide?utm_source=newsletter#method","stripTrailingSlash":false,"expected":"https://example.org/Guide","reason":"Lowercase only the host; remove an analytics parameter and fragment."},
    {"id":"preserve-path-case","input":"https://Docs.Example.org/API/V1","stripTrailingSlash":false,"expected":"https://docs.example.org/API/V1","reason":"Path casing is preserved because a server may treat it as meaningful."},
    {"id":"retain-content-param","input":"https://example.org/report?edition=morning&utm_campaign=launch","stripTrailingSlash":false,"expected":"https://example.org/report?edition=morning","reason":"A content-bearing parameter remains while the analytics parameter is removed."},
    {"id":"remove-click-ids","input":"https://example.org/page?gclid=abc&fbclid=def&msclkid=ghi","stripTrailingSlash":false,"expected":"https://example.org/page","reason":"Documented click identifiers are removed."},
    {"id":"sort-retained-params","input":"https://example.org/search?z=last&a=first&utm_medium=email","stripTrailingSlash":false,"expected":"https://example.org/search?a=first&z=last","reason":"Retained parameters are sorted by key and value."},
    {"id":"preserve-repeated-keys","input":"https://example.org/filter?tag=beta&tag=alpha&utm_source=test","stripTrailingSlash":false,"expected":"https://example.org/filter?tag=alpha&tag=beta","reason":"Repeated retained keys stay repeated and are sorted deterministically."},
    {"id":"reject-relative","input":"/guide?utm_source=email","stripTrailingSlash":false,"error":"absolute_http_url_required","reason":"Relative inputs remain explicit errors rather than disappearing from the denominator."},
    {"id":"reject-non-http","input":"ftp://example.org/archive","stripTrailingSlash":false,"error":"absolute_http_url_required","reason":"Only absolute HTTP and HTTPS URLs are in scope."},
    {"id":"preserve-root-slash","input":"https://example.org/?utm_source=email","stripTrailingSlash":true,"expected":"https://example.org/","reason":"The origin root slash is retained."},
    {"id":"strip-nonroot-slash","input":"https://example.org/report/?utm_source=email","stripTrailingSlash":true,"expected":"https://example.org/report","reason":"A versioned policy may strip a non-root trailing slash."},
    {"id":"preserve-nonroot-slash","input":"https://example.org/report/?utm_source=email","stripTrailingSlash":false,"expected":"https://example.org/report/","reason":"A conservative policy may preserve a non-root trailing slash."},
    {"id":"preserve-encoded-path","input":"https://example.org/research/AI%20visibility?utm_term=test","stripTrailingSlash":false,"expected":"https://example.org/research/AI%20visibility","reason":"The percent-encoded path is not decoded or rewritten."},
    {"id":"preserve-www","input":"https://www.example.org/guide?utm_source=email","stripTrailingSlash":false,"expected":"https://www.example.org/guide","reason":"The www hostname is not merged with the apex domain."},
    {"id":"preserve-http-scheme","input":"http://example.org/guide?utm_source=email","stripTrailingSlash":false,"expected":"http://example.org/guide","reason":"The implementation does not infer an HTTPS redirect."},
    {"id":"fragment-only-change","input":"https://example.org/guide#evidence","stripTrailingSlash":false,"expected":"https://example.org/guide","reason":"The fragment is excluded from the comparison key but remains in the raw observation."}
  ]
}
