| |
|
|
| """ |
| Unit tests for VPN/Proxy configuration logic. |
| |
| Tests verify that proxy environment variables are set correctly |
| for different input formats and scenarios. |
| """ |
|
|
| import os |
| import pytest |
|
|
|
|
| @pytest.mark.parametrize( |
| "test_id, initial_no_proxy, vpn_url, expected_http_proxy, expected_https_proxy, expected_no_proxy", |
| [ |
| ( |
| "proxy_with_http_scheme", |
| None, |
| "http://192.168.1.103:2080", |
| "http://192.168.1.103:2080", |
| "http://192.168.1.103:2080", |
| "127.0.0.1,localhost" |
| ), |
| ( |
| "proxy_with_socks5_scheme", |
| None, |
| "socks5://192.168.1.103:1080", |
| "socks5://192.168.1.103:1080", |
| "socks5://192.168.1.103:1080", |
| "127.0.0.1,localhost" |
| ), |
| ( |
| "proxy_without_scheme", |
| None, |
| "192.168.1.103:2080", |
| "http://192.168.1.103:2080", |
| "http://192.168.1.103:2080", |
| "127.0.0.1,localhost" |
| ), |
| ( |
| "proxy_with_auth", |
| None, |
| "http://user:pass@192.168.1.103:2080", |
| "http://user:pass@192.168.1.103:2080", |
| "http://user:pass@192.168.1.103:2080", |
| "127.0.0.1,localhost" |
| ), |
| ( |
| "proxy_preserves_existing_no_proxy", |
| "internal.corp,*.example.com", |
| "http://192.168.1.103:2080", |
| "http://192.168.1.103:2080", |
| "http://192.168.1.103:2080", |
| "internal.corp,*.example.com,127.0.0.1,localhost" |
| ), |
| ( |
| "proxy_empty_url", |
| None, |
| "", |
| None, |
| None, |
| None |
| ), |
| ] |
| ) |
| def test_vpn_proxy_environment_setup( |
| test_id, |
| initial_no_proxy, |
| vpn_url, |
| expected_http_proxy, |
| expected_https_proxy, |
| expected_no_proxy, |
| monkeypatch |
| ): |
| """ |
| Parametrized test for VPN/Proxy setup via environment variables. |
| |
| Verifies that: |
| - HTTP_PROXY, HTTPS_PROXY, ALL_PROXY are set correctly |
| - URL normalization works (adds http:// if no scheme) |
| - NO_PROXY includes localhost and preserves existing values |
| - Empty URL doesn't set any proxy variables |
| """ |
| print(f"\n--- Running VPN/Proxy test: ID = {test_id} ---") |
| |
| |
| for key in ["HTTP_PROXY", "HTTPS_PROXY", "ALL_PROXY", "NO_PROXY"]: |
| monkeypatch.delenv(key, raising=False) |
| |
| |
| if initial_no_proxy: |
| monkeypatch.setenv("NO_PROXY", initial_no_proxy) |
| print(f"Initial NO_PROXY: '{initial_no_proxy}'") |
| |
| |
| print(f"VPN_PROXY_URL set to: '{vpn_url}'") |
| |
| |
| if vpn_url: |
| proxy_url_with_scheme = vpn_url if "://" in vpn_url else f"http://{vpn_url}" |
| os.environ['HTTP_PROXY'] = proxy_url_with_scheme |
| os.environ['HTTPS_PROXY'] = proxy_url_with_scheme |
| os.environ['ALL_PROXY'] = proxy_url_with_scheme |
| |
| no_proxy_hosts = os.environ.get("NO_PROXY", "") |
| local_hosts = "127.0.0.1,localhost" |
| if no_proxy_hosts: |
| os.environ["NO_PROXY"] = f"{no_proxy_hosts},{local_hosts}" |
| else: |
| os.environ["NO_PROXY"] = local_hosts |
| |
| |
| print("\n[Verification]") |
| |
| if expected_http_proxy: |
| actual_http_proxy = os.environ.get("HTTP_PROXY") |
| print(f"HTTP_PROXY: Expected '{expected_http_proxy}', Got '{actual_http_proxy}'") |
| assert actual_http_proxy == expected_http_proxy, "HTTP_PROXY mismatch!" |
| |
| actual_https_proxy = os.environ.get("HTTPS_PROXY") |
| print(f"HTTPS_PROXY: Expected '{expected_https_proxy}', Got '{actual_https_proxy}'") |
| assert actual_https_proxy == expected_https_proxy, "HTTPS_PROXY mismatch!" |
| |
| actual_all_proxy = os.environ.get("ALL_PROXY") |
| print(f"ALL_PROXY: Expected '{expected_http_proxy}', Got '{actual_all_proxy}'") |
| assert actual_all_proxy == expected_http_proxy, "ALL_PROXY mismatch!" |
| else: |
| |
| assert os.environ.get("HTTP_PROXY") is None, "HTTP_PROXY should be None!" |
| assert os.environ.get("HTTPS_PROXY") is None, "HTTPS_PROXY should be None!" |
| print("Proxy not set (as expected)") |
| |
| if expected_no_proxy: |
| actual_no_proxy = os.environ.get("NO_PROXY") |
| print(f"NO_PROXY: Expected '{expected_no_proxy}', Got '{actual_no_proxy}'") |
| assert actual_no_proxy == expected_no_proxy, "NO_PROXY mismatch!" |
| |
| print(f"--- Test '{test_id}' passed successfully ---") |
|
|
|
|
| def test_proxy_scheme_normalization(): |
| """ |
| Verifies that URLs without scheme are correctly normalized to http://. |
| |
| Tests various input formats: |
| - Plain host:port β http://host:port |
| - http:// β unchanged |
| - https:// β unchanged |
| - socks5:// β unchanged |
| """ |
| print("\n--- Test: Proxy scheme normalization ---") |
| |
| test_cases = [ |
| ("192.168.1.100:8080", "http://192.168.1.100:8080"), |
| ("http://192.168.1.100:8080", "http://192.168.1.100:8080"), |
| ("https://192.168.1.100:8080", "https://192.168.1.100:8080"), |
| ("socks5://192.168.1.100:8080", "socks5://192.168.1.100:8080"), |
| ("127.0.0.1:7890", "http://127.0.0.1:7890"), |
| ] |
| |
| for input_url, expected_url in test_cases: |
| print(f"\nInput: '{input_url}'") |
| |
| |
| proxy_url_with_scheme = input_url if "://" in input_url else f"http://{input_url}" |
| |
| print(f"Result: '{proxy_url_with_scheme}'") |
| print(f"Expected: '{expected_url}'") |
| assert proxy_url_with_scheme == expected_url, f"Normalization failed for '{input_url}'" |
| |
| print("\n--- Test passed: all schemes normalized correctly ---") |
|
|
|
|
| def test_no_proxy_list_merging(monkeypatch): |
| """ |
| Verifies correct merging of existing and new NO_PROXY values. |
| |
| Tests: |
| - Empty existing β only localhost |
| - Existing values β preserved and localhost added |
| - Duplicate localhost β acceptable (not a problem) |
| """ |
| print("\n--- Test: NO_PROXY list merging ---") |
| |
| test_cases = [ |
| |
| ("", "127.0.0.1,localhost"), |
| ("internal.local", "internal.local,127.0.0.1,localhost"), |
| ("192.168.0.0/16,10.0.0.0/8", "192.168.0.0/16,10.0.0.0/8,127.0.0.1,localhost"), |
| ("*.corp.com,localhost", "*.corp.com,localhost,127.0.0.1,localhost"), |
| ] |
| |
| for existing_value, expected_result in test_cases: |
| print(f"\nExisting NO_PROXY: '{existing_value}'") |
| |
| |
| if existing_value: |
| monkeypatch.setenv("NO_PROXY", existing_value) |
| else: |
| monkeypatch.delenv("NO_PROXY", raising=False) |
| |
| no_proxy_hosts = os.environ.get("NO_PROXY", "") |
| local_hosts = "127.0.0.1,localhost" |
| if no_proxy_hosts: |
| result = f"{no_proxy_hosts},{local_hosts}" |
| else: |
| result = local_hosts |
| |
| print(f"Result: '{result}'") |
| print(f"Expected: '{expected_result}'") |
| assert result == expected_result, f"Merging failed for '{existing_value}'" |
| |
| print("\n--- Test passed: lists merged correctly ---") |
|
|
|
|
| def test_proxy_does_not_affect_local_connections(monkeypatch): |
| """ |
| Verifies that local addresses (127.0.0.1, localhost) are always in NO_PROXY. |
| |
| This ensures that local tests don't go through VPN/proxy, |
| which would be slow and incorrect. |
| """ |
| print("\n--- Test: Local addresses excluded from proxy ---") |
| |
| |
| vpn_url = "http://vpn.example.com:8080" |
| os.environ['HTTP_PROXY'] = vpn_url |
| os.environ['HTTPS_PROXY'] = vpn_url |
| |
| no_proxy_hosts = os.environ.get("NO_PROXY", "") |
| local_hosts = "127.0.0.1,localhost" |
| if no_proxy_hosts: |
| os.environ["NO_PROXY"] = f"{no_proxy_hosts},{local_hosts}" |
| else: |
| os.environ["NO_PROXY"] = local_hosts |
| |
| no_proxy_value = os.environ.get("NO_PROXY") |
| print(f"NO_PROXY set to: '{no_proxy_value}'") |
| |
| |
| assert "127.0.0.1" in no_proxy_value, "127.0.0.1 must be in NO_PROXY!" |
| assert "localhost" in no_proxy_value, "localhost must be in NO_PROXY!" |
| |
| print("β
Local addresses correctly excluded from proxy") |
| print("--- Test passed ---") |
|
|
|
|
| def test_proxy_with_special_characters(): |
| """ |
| Verifies that proxy URLs with special characters in credentials work correctly. |
| |
| Tests authentication with: |
| - Special characters in password |
| - URL encoding (if needed) |
| """ |
| print("\n--- Test: Proxy with special characters in credentials ---") |
| |
| test_cases = [ |
| |
| ("http://user:p@ss@proxy.com:8080", "http://user:p@ss@proxy.com:8080"), |
| ("http://admin:P@ssw0rd!@192.168.1.1:3128", "http://admin:P@ssw0rd!@192.168.1.1:3128"), |
| ("socks5://user123:pass456@localhost:1080", "socks5://user123:pass456@localhost:1080"), |
| ] |
| |
| for input_url, expected_url in test_cases: |
| print(f"\nInput: '{input_url}'") |
| |
| |
| proxy_url_with_scheme = input_url if "://" in input_url else f"http://{input_url}" |
| |
| print(f"Result: '{proxy_url_with_scheme}'") |
| print(f"Expected: '{expected_url}'") |
| assert proxy_url_with_scheme == expected_url, f"Special chars handling failed for '{input_url}'" |
| |
| print("\n--- Test passed: special characters preserved correctly ---") |
|
|
|
|
| def test_empty_vpn_proxy_url_does_not_set_variables(monkeypatch): |
| """ |
| Verifies that empty VPN_PROXY_URL doesn't set any proxy variables. |
| |
| This is the default behavior - direct connection without proxy. |
| """ |
| print("\n--- Test: Empty VPN_PROXY_URL (direct connection) ---") |
| |
| |
| for key in ["HTTP_PROXY", "HTTPS_PROXY", "ALL_PROXY", "NO_PROXY"]: |
| monkeypatch.delenv(key, raising=False) |
| |
| |
| vpn_url = "" |
| |
| |
| if vpn_url: |
| proxy_url_with_scheme = vpn_url if "://" in vpn_url else f"http://{vpn_url}" |
| os.environ['HTTP_PROXY'] = proxy_url_with_scheme |
| os.environ['HTTPS_PROXY'] = proxy_url_with_scheme |
| os.environ['ALL_PROXY'] = proxy_url_with_scheme |
| |
| |
| assert os.environ.get("HTTP_PROXY") is None, "HTTP_PROXY should not be set!" |
| assert os.environ.get("HTTPS_PROXY") is None, "HTTPS_PROXY should not be set!" |
| assert os.environ.get("ALL_PROXY") is None, "ALL_PROXY should not be set!" |
| |
| print("β
No proxy variables set (direct connection)") |
| print("--- Test passed ---") |
|
|
|
|
| print("VPN/Proxy tests loaded. Will verify proxy setup logic!") |
|
|