#!/usr/bin/env python3 """Test script to verify client_id validation compliance with W3C IndieAuth spec Section 3.2.""" import sys from urllib.parse import urlparse # Import the validation function sys.path.insert(0, '/home/phil/Projects/Gondulf/src') from gondulf.utils.validation import normalize_client_id def test_client_id(client_id: str, should_pass: bool, reason: str): """Test a single client_id against the spec.""" try: result = normalize_client_id(client_id) passed = True print(f"āœ“ Accepted: {client_id} -> {result}") except ValueError as e: passed = False print(f"āœ— Rejected: {client_id} ({str(e)})") if passed != should_pass: print(f" ERROR: Expected {'pass' if should_pass else 'fail'}: {reason}") return False return True def main(): """Run comprehensive client_id validation tests.""" print("Testing client_id validation against W3C IndieAuth spec Section 3.2") print("=" * 70) tests = [ # Valid client_ids per spec ("https://example.com", True, "Valid HTTPS URL with domain"), ("https://example.com/", True, "Valid HTTPS URL with path /"), ("https://example.com/app", True, "Valid HTTPS URL with path"), ("https://example.com/app/client", True, "Valid HTTPS URL with multiple path segments"), ("https://example.com?foo=bar", True, "Valid HTTPS URL with query string"), ("https://example.com:8080", True, "Valid HTTPS URL with port"), ("https://example.com:8080/app", True, "Valid HTTPS URL with port and path"), ("http://localhost", True, "Valid HTTP localhost (loopback allowed)"), ("http://localhost:3000", True, "Valid HTTP localhost with port"), ("http://127.0.0.1", True, "Valid HTTP IPv4 loopback"), ("http://127.0.0.1:3000", True, "Valid HTTP IPv4 loopback with port"), ("http://[::1]", True, "Valid HTTP IPv6 loopback"), ("http://[::1]:3000", True, "Valid HTTP IPv6 loopback with port"), # Invalid client_ids per spec ("http://example.com", False, "HTTP not allowed for non-loopback"), ("https://example.com#fragment", False, "Fragment not allowed"), ("https://user:pass@example.com", False, "Username/password not allowed"), ("https://192.168.1.1", False, "IPv4 address not allowed (except 127.0.0.1)"), ("https://[2001:db8::1]", False, "IPv6 address not allowed (except ::1)"), ("https://10.0.0.1", False, "Private IPv4 not allowed"), ("https://172.16.0.1", False, "Private IPv4 not allowed"), ("example.com", False, "Missing scheme"), ("ftp://example.com", False, "Invalid scheme (not http/https)"), ("https://", False, "Missing hostname"), ("https://example.com/../secret", False, "Path with .. segments not allowed"), ("https://example.com/./secret", False, "Path with . segments not allowed"), # Edge cases that spec might not explicitly cover ("HTTPS://EXAMPLE.COM", True, "Uppercase scheme/host should be normalized"), ("https://example.com:443", True, "Default HTTPS port should be normalized"), ("http://localhost:80", True, "Default HTTP port on localhost"), ("https://xn--e1afmkfd.xn--p1ai", True, "Internationalized domain names (IDN)"), ] all_passed = True pass_count = 0 fail_count = 0 for client_id, should_pass, reason in tests: if test_client_id(client_id, should_pass, reason): pass_count += 1 else: fail_count += 1 all_passed = False print("\n" + "=" * 70) print(f"Results: {pass_count} passed, {fail_count} failed") if all_passed: print("\nāœ… All tests passed! client_id validation appears spec-compliant.") else: print("\nāŒ Some tests failed. Review the validation logic against the spec.") return 0 if all_passed else 1 if __name__ == "__main__": sys.exit(main())