|
1 | | -import json |
2 | | -import os |
3 | 1 | import unittest |
4 | | -import urllib.request |
5 | | -from typing import Optional |
6 | | - |
7 | | -from testcontainers.core.container import DockerContainer |
8 | | -from testcontainers.core.network import Network |
9 | | -from testcontainers.core.wait_strategies import PortWaitStrategy |
10 | | -from testcontainers.core.waiting_utils import wait_container_is_ready |
11 | 2 |
|
12 | 3 | from zitadel_client.transport_options import TransportOptions |
13 | | -from zitadel_client.zitadel import Zitadel |
14 | | - |
15 | | -FIXTURES_DIR = os.path.join(os.path.dirname(__file__), "fixtures") |
16 | | - |
17 | | - |
18 | | -@wait_container_is_ready() |
19 | | -def _wait_for_wiremock(host: str, port: str) -> None: |
20 | | - url = f"http://{host}:{port}/__admin/mappings" |
21 | | - with urllib.request.urlopen(url, timeout=5) as resp: # noqa: S310 |
22 | | - if resp.status != 200: |
23 | | - raise ConnectionError(f"WireMock not ready: {resp.status}") |
24 | 4 |
|
25 | 5 |
|
26 | 6 | class TransportOptionsTest(unittest.TestCase): |
27 | | - host: Optional[str] = None |
28 | | - http_port: Optional[str] = None |
29 | | - https_port: Optional[str] = None |
30 | | - proxy_port: Optional[str] = None |
31 | | - ca_cert_path: Optional[str] = None |
32 | | - wiremock: DockerContainer = None |
33 | | - proxy: DockerContainer = None |
34 | | - network: Network = None |
35 | | - |
36 | | - @classmethod |
37 | | - def setup_class(cls) -> None: |
38 | | - cls.ca_cert_path = os.path.join(FIXTURES_DIR, "ca.pem") |
39 | | - keystore_path = os.path.join(FIXTURES_DIR, "keystore.p12") |
40 | | - squid_conf = os.path.join(FIXTURES_DIR, "squid.conf") |
41 | | - |
42 | | - cls.network = Network().create() |
43 | | - |
44 | | - cls.wiremock = ( |
45 | | - DockerContainer("wiremock/wiremock:3.12.1") |
46 | | - .with_network(cls.network) |
47 | | - .with_network_aliases("wiremock") |
48 | | - .with_exposed_ports(8080, 8443) |
49 | | - .with_volume_mapping(keystore_path, "/home/wiremock/keystore.p12", mode="ro") |
50 | | - .with_volume_mapping( |
51 | | - os.path.join(FIXTURES_DIR, "mappings"), "/home/wiremock/mappings", mode="ro" |
52 | | - ) |
53 | | - .with_command( |
54 | | - "--https-port 8443" |
55 | | - " --https-keystore /home/wiremock/keystore.p12" |
56 | | - " --keystore-password password" |
57 | | - " --keystore-type PKCS12" |
58 | | - " --global-response-templating" |
59 | | - ) |
60 | | - ) |
61 | | - cls.wiremock.start() |
62 | | - |
63 | | - cls.proxy = ( |
64 | | - DockerContainer("ubuntu/squid:6.10-24.10_beta") |
65 | | - .with_network(cls.network) |
66 | | - .with_exposed_ports(3128) |
67 | | - .with_volume_mapping(squid_conf, "/etc/squid/squid.conf", mode="ro") |
68 | | - .waiting_for(PortWaitStrategy(3128)) |
69 | | - ) |
70 | | - cls.proxy.start() |
71 | 7 |
|
72 | | - cls.host = cls.wiremock.get_container_host_ip() |
73 | | - cls.http_port = cls.wiremock.get_exposed_port(8080) |
74 | | - cls.https_port = cls.wiremock.get_exposed_port(8443) |
75 | | - cls.proxy_port = cls.proxy.get_exposed_port(3128) |
| 8 | + def test_defaults_returns_empty(self) -> None: |
| 9 | + self.assertEqual({}, TransportOptions.defaults().to_session_kwargs()) |
76 | 10 |
|
77 | | - _wait_for_wiremock(cls.host, cls.http_port) |
| 11 | + def test_insecure_sets_verify_false(self) -> None: |
| 12 | + opts = TransportOptions(insecure=True) |
| 13 | + self.assertEqual({"verify": False}, opts.to_session_kwargs()) |
78 | 14 |
|
79 | | - @classmethod |
80 | | - def teardown_class(cls) -> None: |
81 | | - if cls.proxy is not None: |
82 | | - cls.proxy.stop() |
83 | | - if cls.wiremock is not None: |
84 | | - cls.wiremock.stop() |
85 | | - if cls.network is not None: |
86 | | - cls.network.remove() |
87 | | - |
88 | | - def test_custom_ca_cert(self) -> None: |
89 | | - zitadel = Zitadel.with_client_credentials( |
90 | | - f"https://{self.host}:{self.https_port}", |
91 | | - "dummy-client", |
92 | | - "dummy-secret", |
93 | | - transport_options=TransportOptions(ca_cert_path=self.ca_cert_path), |
94 | | - ) |
95 | | - self.assertIsNotNone(zitadel) |
96 | | - |
97 | | - def test_insecure_mode(self) -> None: |
98 | | - zitadel = Zitadel.with_client_credentials( |
99 | | - f"https://{self.host}:{self.https_port}", |
100 | | - "dummy-client", |
101 | | - "dummy-secret", |
102 | | - transport_options=TransportOptions(insecure=True), |
103 | | - ) |
104 | | - self.assertIsNotNone(zitadel) |
105 | | - |
106 | | - def test_default_headers(self) -> None: |
107 | | - zitadel = Zitadel.with_client_credentials( |
108 | | - f"http://{self.host}:{self.http_port}", |
109 | | - "dummy-client", |
110 | | - "dummy-secret", |
111 | | - transport_options=TransportOptions(default_headers={"X-Custom-Header": "test-value"}), |
112 | | - ) |
113 | | - self.assertIsNotNone(zitadel) |
114 | | - |
115 | | - zitadel.settings.get_general_settings({}) |
116 | | - |
117 | | - verify_body = json.dumps( |
118 | | - { |
119 | | - "url": "/zitadel.settings.v2.SettingsService/GetGeneralSettings", |
120 | | - "headers": {"X-Custom-Header": {"equalTo": "test-value"}}, |
121 | | - } |
122 | | - ).encode() |
123 | | - req = urllib.request.Request( |
124 | | - f"http://{self.host}:{self.http_port}/__admin/requests/count", |
125 | | - data=verify_body, |
126 | | - headers={"Content-Type": "application/json"}, |
127 | | - method="POST", |
128 | | - ) |
129 | | - with urllib.request.urlopen(req) as resp: # noqa: S310 |
130 | | - result = json.loads(resp.read().decode()) |
131 | | - self.assertGreaterEqual(result["count"], 1, "Custom header should be present on API call") |
| 15 | + def test_ca_cert_path_sets_verify(self) -> None: |
| 16 | + opts = TransportOptions(ca_cert_path="/path/to/ca.pem") |
| 17 | + self.assertEqual({"verify": "/path/to/ca.pem"}, opts.to_session_kwargs()) |
132 | 18 |
|
133 | | - def test_proxy_url(self) -> None: |
134 | | - zitadel = Zitadel.with_access_token( |
135 | | - "http://wiremock:8080", |
136 | | - "test-token", |
137 | | - transport_options=TransportOptions(proxy_url=f"http://{self.host}:{self.proxy_port}"), |
| 19 | + def test_proxy_url_sets_proxies(self) -> None: |
| 20 | + opts = TransportOptions(proxy_url="http://proxy:3128") |
| 21 | + self.assertEqual( |
| 22 | + {"proxies": {"http": "http://proxy:3128", "https": "http://proxy:3128"}}, |
| 23 | + opts.to_session_kwargs(), |
138 | 24 | ) |
139 | | - self.assertIsNotNone(zitadel) |
140 | | - zitadel.settings.get_general_settings({}) |
141 | 25 |
|
142 | | - def test_no_ca_cert_fails(self) -> None: |
143 | | - with self.assertRaises(Exception): # noqa: B017 |
144 | | - Zitadel.with_client_credentials( |
145 | | - f"https://{self.host}:{self.https_port}", |
146 | | - "dummy-client", |
147 | | - "dummy-secret", |
148 | | - ) |
| 26 | + def test_insecure_takes_precedence_over_ca_cert(self) -> None: |
| 27 | + opts = TransportOptions(insecure=True, ca_cert_path="/path/to/ca.pem") |
| 28 | + self.assertEqual({"verify": False}, opts.to_session_kwargs()) |
| 29 | + |
| 30 | + def test_immutability(self) -> None: |
| 31 | + opts = TransportOptions.defaults() |
| 32 | + with self.assertRaises(AttributeError): |
| 33 | + opts.insecure = True # type: ignore[misc] |
| 34 | + |
| 35 | + def test_defaults_factory(self) -> None: |
| 36 | + opts = TransportOptions.defaults() |
| 37 | + self.assertEqual({}, dict(opts.default_headers)) |
| 38 | + self.assertIsNone(opts.ca_cert_path) |
| 39 | + self.assertFalse(opts.insecure) |
| 40 | + self.assertIsNone(opts.proxy_url) |
0 commit comments