Skip to content

Commit c179034

Browse files
Python Client SDK gateway tests (#226)
* Gateway tests * nit: remove previous code * nit * nit:fix test * Address review * Address review * Increase wait for timeout * nit * Update default timeout
1 parent c5e8af2 commit c179034

4 files changed

Lines changed: 109 additions & 6 deletions

File tree

test/e2e/clients/python/framework/context.py

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
from test.e2e.clients.python.framework.predicates import (
2020
deployment_ready,
2121
warmpool_ready,
22+
gateway_address_ready,
2223
)
2324
import subprocess
2425
from urllib3.exceptions import ReadTimeoutError
@@ -179,7 +180,7 @@ def wait_for_warmpool_ready(
179180
namespace: Optional[str] = None,
180181
timeout=DEFAULT_TIMEOUT_SECONDS,
181182
):
182-
"""Waits for a SandboxWarmPool to have at least min_ready ready sandboxes"""
183+
"""Waits for a SandboxWarmPool to have all the required number ready sandboxes"""
183184
if namespace is None:
184185
namespace = self.namespace
185186
if not namespace:
@@ -200,6 +201,33 @@ def wait_for_warmpool_ready(
200201
timeout,
201202
)
202203

204+
def wait_for_gateway_address(
205+
self,
206+
name: str,
207+
namespace: Optional[str] = None,
208+
timeout=DEFAULT_TIMEOUT_SECONDS,
209+
):
210+
"""Waits for a Gateway to have an address in its status"""
211+
if namespace is None:
212+
namespace = self.namespace
213+
if not namespace:
214+
raise ValueError("Namespace must be provided.")
215+
216+
custom_objects_api = self.get_custom_objects_api()
217+
218+
return self.wait_for_object(
219+
functools.partial(
220+
custom_objects_api.list_namespaced_custom_object,
221+
group="gateway.networking.k8s.io",
222+
version="v1",
223+
plural="gateways",
224+
),
225+
name,
226+
namespace,
227+
gateway_address_ready(),
228+
timeout,
229+
)
230+
203231

204232
if __name__ == "__main__":
205233
# Example Usage
@@ -214,4 +242,4 @@ def wait_for_warmpool_ready(
214242
finally:
215243
if tc and tc.namespace:
216244
print(f"Cleaning up namespace: {tc.namespace}")
217-
# tc.delete_namespace()
245+
# tc.delete_namespace()

test/e2e/clients/python/framework/predicates.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ def check(obj: kubernetes.client.V1Pod) -> bool:
4444

4545
def warmpool_ready():
4646
"""
47-
Predicate to check if a SandboxWarmPool (CR) has at least min_ready ready sandboxes.
47+
Predicate to check if a SandboxWarmPool (CR) has all the required number of ready sandboxes.
4848
"""
4949

5050
def check(obj: Dict[str, Any]) -> bool:
@@ -59,3 +59,17 @@ def check(obj: Dict[str, Any]) -> bool:
5959
return ready_replicas == replicas
6060

6161
return check
62+
63+
64+
def gateway_address_ready():
65+
"""Predicate to check if a Gateway has an address."""
66+
67+
def check(obj: Dict[str, Any]) -> bool:
68+
if not isinstance(obj, dict):
69+
return False
70+
71+
status = obj.get("status") or {}
72+
addresses = status.get("addresses") or []
73+
return len(addresses) > 0
74+
75+
return check

test/e2e/clients/python/test_e2e_python_sdk.py

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
from test.e2e.clients.python.framework.context import TestContext
1717

1818
import pytest
19+
import yaml
1920
from agentic_sandbox import SandboxClient
2021

2122
TEST_MANIFESTS_DIR = "test/e2e/clients/python/test_manifests"
@@ -25,6 +26,10 @@
2526
ROUTER_YAML_PATH = (
2627
"clients/python/agentic-sandbox-client/sandbox-router/sandbox_router.yaml"
2728
)
29+
GATEWAY_YAML_PATH = (
30+
"clients/python/agentic-sandbox-client/gateway-kind/gateway-kind.yaml"
31+
)
32+
GATEWAY_NAME = "kind-gateway"
2833

2934

3035
@pytest.fixture(scope="module")
@@ -70,6 +75,18 @@ def deploy_router(tc, temp_namespace):
7075
tc.wait_for_deployment_ready("sandbox-router-deployment", namespace=temp_namespace)
7176

7277

78+
@pytest.fixture(scope="function")
79+
def deploy_gateway(tc, temp_namespace):
80+
"""Deploys the sandbox gateway into the test namespace"""
81+
with open(GATEWAY_YAML_PATH, "r") as f:
82+
manifest = f.read()
83+
84+
print(f"Applying gateway manifest to namespace: {temp_namespace}")
85+
tc.apply_manifest_text(manifest, namespace=temp_namespace)
86+
print("Waiting for gateway to get an address...")
87+
tc.wait_for_gateway_address(GATEWAY_NAME, namespace=temp_namespace)
88+
89+
7390
@pytest.fixture(scope="function")
7491
def sandbox_template(tc, temp_namespace):
7592
"""Deploys the sandbox template into the test namespace"""
@@ -144,3 +161,46 @@ def test_python_sdk_router_mode_warmpool(
144161

145162
except Exception as e:
146163
pytest.fail(f"SDK test with warmpool failed: {e}")
164+
165+
166+
def test_python_sdk_gateway_mode(
167+
tc, temp_namespace, sandbox_template, deploy_router, deploy_gateway
168+
):
169+
"""Tests the Python SDK in Production mode (with Gateway and Router) without warmpool."""
170+
try:
171+
with SandboxClient(
172+
template_name=sandbox_template,
173+
namespace=temp_namespace,
174+
gateway_name=GATEWAY_NAME,
175+
gateway_namespace=temp_namespace,
176+
) as sandbox:
177+
print("\n--- Running SDK tests without warmpool ---")
178+
run_sdk_tests(sandbox)
179+
print("SDK test without warmpool passed!")
180+
181+
except Exception as e:
182+
pytest.fail(f"SDK test without warmpool failed: {e}")
183+
184+
185+
def test_python_sdk_gateway_mode_warmpool(
186+
tc,
187+
temp_namespace,
188+
sandbox_template,
189+
deploy_router,
190+
sandbox_warmpool,
191+
deploy_gateway,
192+
):
193+
"""Tests the Python SDK in Production mode (with gateway and router) with warmpool."""
194+
try:
195+
with SandboxClient(
196+
template_name=sandbox_template,
197+
namespace=temp_namespace,
198+
gateway_name=GATEWAY_NAME,
199+
gateway_namespace=temp_namespace,
200+
) as sandbox:
201+
print("\n--- Running SDK tests with warmpool ---")
202+
run_sdk_tests(sandbox)
203+
print("SDK test with warmpool passed!")
204+
205+
except Exception as e:
206+
pytest.fail(f"SDK test with warmpool failed: {e}")

test/e2e/framework/client.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ import (
3737
)
3838

3939
const (
40-
// DefaultTimeout is the default timeout for WaitForObject.
40+
// DefaultTimeout is the default timeout for WaitForObject and WaitForObjectNotFound.
4141
DefaultTimeout = 60 * time.Second
4242
)
4343

@@ -197,8 +197,9 @@ func (cl *ClusterClient) MustWaitForObject(obj client.Object, p ...predicates.Ob
197197
// WaitForObjectNotFound waits for the specified object to not exist.
198198
func (cl *ClusterClient) WaitForObjectNotFound(ctx context.Context, obj client.Object) error {
199199
cl.Helper()
200-
// Static 30 second timeout, this can be adjusted if needed
201-
timeoutCtx, cancel := context.WithTimeout(ctx, 30*time.Second)
200+
// Static 1 minute timeout, this can be adjusted if needed
201+
timeoutCtx, cancel := context.WithTimeout(ctx, DefaultTimeout)
202+
202203
defer cancel()
203204
start := time.Now()
204205
nn := types.NamespacedName{Name: obj.GetName(), Namespace: obj.GetNamespace()}

0 commit comments

Comments
 (0)