from __future__ import annotations import socket import pytest from app.url_policy import NetworkRules, UrlPolicyError, normalize_url, resolve_and_validate def test_normalize_url_adds_https_and_removes_fragment() -> None: result = normalize_url("example.com/docs#section", NetworkRules()) assert result == "https://example.com/docs" def test_normalize_url_preserves_encoded_path_delimiters() -> None: result = normalize_url("https://example.com/a%2Fb", NetworkRules()) assert result == "https://example.com/a%2Fb" def test_normalize_url_rejects_credentials_and_unsafe_ports() -> None: with pytest.raises(UrlPolicyError, match="credentials"): normalize_url("https://user:secret@example.com", NetworkRules()) with pytest.raises(UrlPolicyError, match="ports 80 and 443"): normalize_url("https://example.com:8443", NetworkRules()) def test_resolve_blocks_loopback_by_default(monkeypatch: pytest.MonkeyPatch) -> None: monkeypatch.setattr( socket, "getaddrinfo", lambda *args, **kwargs: [(socket.AF_INET, socket.SOCK_STREAM, 6, "", ("127.0.0.1", 0))], ) with pytest.raises(UrlPolicyError, match="Private, local, and reserved"): resolve_and_validate("https://example.com", NetworkRules())