Files
tracer-cloud--opensre/tests/integrations/argocd/test_client.py
T
wehub-resource-sync 4b6817381b
CI (OpenClaw E2E) / openclaw test (push) Has been cancelled
CI / coverage-report (push) Has been cancelled
CI / test-kubernetes (push) Has been cancelled
CI / should-run-thorough (push) Has been cancelled
CI / test-thorough (cloudwatch-demo) (push) Has been cancelled
CI / test-thorough (flink-ecs) (push) Has been cancelled
CI / test-thorough (upstream-lambda) (push) Has been cancelled
CI / test-thorough (prefect-ecs-fargate) (push) Has been cancelled
Release / build-binaries (zip, opensre.exe, onefile, windows-latest, windows-x64) (push) Has been cancelled
Benchmark image — build + push to ECR (any adapter) / build + push (push) Has been cancelled
CI / quality (ubuntu-latest) (push) Has been cancelled
CI / test (tools-runtime) (push) Has been cancelled
CI / test (e2e-general) (push) Has been cancelled
CI / test (cli-runtime) (push) Has been cancelled
CI / test (e2e-provider-and-openclaw) (push) Has been cancelled
CI / test (integrations-and-misc) (push) Has been cancelled
Release / verify (push) Has been cancelled
Release / build-python-dist (push) Has been cancelled
Release / build-binaries (tar.gz, opensre, onedir, macos-15-intel, darwin-x64) (push) Has been cancelled
Release / build-binaries (tar.gz, opensre, onedir, macos-latest, darwin-arm64) (push) Has been cancelled
Release / build-binaries (tar.gz, opensre, onedir, ubuntu-22.04, linux-x64) (push) Has been cancelled
Release / publish-release (push) Has been cancelled
Release / publish-main-release (push) Has been cancelled
Interactive Shell Live (PR + post-merge) / turn-checks (no-LLM) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
Interactive Shell Live (PR + post-merge) / turn-live shard ${{ matrix.shard_index }} (push) Has been cancelled
Release / prepare (push) Has been cancelled
Release / build-binaries (tar.gz, opensre, onedir, ubuntu-22.04-arm, linux-arm64) (push) Has been cancelled
Synthetic Deterministic Tests / Synthetic offline (deterministic) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:10:45 +08:00

675 lines
24 KiB
Python

"""Tests for the Argo CD API client."""
from __future__ import annotations
import json
from typing import Any
import httpx
import pytest
from pydantic import ValidationError
from integrations.argocd.client import ArgoCDClient, ArgoCDConfig, make_argocd_client
def _application_payload(name: str = "payments-api") -> dict[str, Any]:
return {
"metadata": {"name": name, "namespace": "argocd"},
"spec": {
"project": "default",
"source": {"repoURL": "https://github.com/example/payments", "targetRevision": "main"},
"destination": {"server": "https://kubernetes.default.svc", "namespace": "payments"},
},
"status": {
"sync": {"status": "OutOfSync", "revision": "abc123"},
"health": {"status": "Degraded", "message": "Deployment unavailable"},
"history": [
{"revision": "old111", "deployedAt": "2026-04-01T00:00:00Z", "id": 1},
{"revision": "abc123", "deployedAt": "2026-04-02T00:00:00Z", "id": 2},
],
"operationState": {"phase": "Succeeded", "syncResult": {"revision": "abc123"}},
"summary": {"images": ["registry.example.com/payments:abc123"]},
},
}
def test_config_normalizes_base_url_bearer_prefix_and_verify_ssl() -> None:
config = ArgoCDConfig(base_url="https://argocd.example.com/", bearer_token="Bearer tok_test")
insecure_config = ArgoCDConfig(
base_url="https://argocd.example.com/", bearer_token="tok_test", verify_ssl="false"
)
assert config.base_url == "https://argocd.example.com"
assert config.bearer_token == "tok_test"
assert config.verify_ssl is True
assert insecure_config.verify_ssl is False
def test_make_argocd_client_requires_base_url_and_auth() -> None:
assert make_argocd_client("", bearer_token="tok") is None
assert make_argocd_client("https://argocd.example.com") is None
assert make_argocd_client("https://argocd.example.com", bearer_token="tok") is not None
assert (
make_argocd_client("https://argocd.example.com", username="admin", password="pw")
is not None
)
def test_probe_access_success(monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.setattr(
ArgoCDClient,
"list_applications",
lambda _self, **_kwargs: {
"success": True,
"applications": [{"name": "payments"}],
"total": 1,
},
)
client = ArgoCDClient(
ArgoCDConfig(base_url="https://argocd.example.com", bearer_token="tok_test")
)
result = client.probe_access()
assert result.status == "passed"
assert "1 application" in result.detail
def test_config_rejects_plain_http_except_loopback() -> None:
with pytest.raises(ValidationError):
ArgoCDConfig(base_url="http://argocd.example.com", bearer_token="tok")
local = ArgoCDConfig(base_url="http://127.0.0.1:8080", bearer_token="tok")
assert local.base_url == "http://127.0.0.1:8080"
ipv6_local = ArgoCDConfig(base_url="http://[::1]:8080", bearer_token="tok")
assert ipv6_local.base_url == "http://[::1]:8080"
assert make_argocd_client("http://argocd.example.com", bearer_token="tok") is None
def test_config_rejects_ambiguous_auth_methods() -> None:
with pytest.raises(ValidationError, match="one auth method"):
ArgoCDConfig(
base_url="https://argocd.example.com",
bearer_token="tok_test",
username="admin",
password="pw",
)
assert (
make_argocd_client(
"https://argocd.example.com",
bearer_token="tok_test",
username="admin",
password="pw",
)
is None
)
def test_config_only_strips_bearer_prefix_from_bearer_token() -> None:
token_config = ArgoCDConfig(
base_url="https://argocd.example.com",
bearer_token="Bearer tok_prefixed",
project="bearer platform",
app_namespace="bearer namespace",
integration_id="bearer integration",
)
assert token_config.bearer_token == "tok_prefixed"
assert token_config.project == "bearer platform"
assert token_config.app_namespace == "bearer namespace"
assert token_config.integration_id == "bearer integration"
login_config = ArgoCDConfig(
base_url="https://argocd.example.com",
username="bearer admin",
password="bearer password",
)
assert login_config.username == "bearer admin"
assert login_config.password == "bearer password"
def test_list_applications_uses_bearer_auth_and_normalizes_status() -> None:
seen_requests: list[httpx.Request] = []
def handler(request: httpx.Request) -> httpx.Response:
seen_requests.append(request)
assert request.headers["authorization"] == "Bearer tok_test"
assert request.url.path == "/api/v1/applications"
return httpx.Response(200, json={"items": [_application_payload()]}, request=request)
client = ArgoCDClient(
ArgoCDConfig(base_url="https://argocd.example.com", bearer_token="tok_test"),
transport=httpx.MockTransport(handler),
)
result = client.list_applications(projects=["default"], selector="app=payments")
assert result["success"] is True
assert result["total"] == 1
app = result["applications"][0]
assert app["name"] == "payments-api"
assert app["sync_status"] == "OutOfSync"
assert app["health_status"] == "Degraded"
assert app["revision"] == "abc123"
assert app["history_count"] == 2
assert "projects=default" in str(seen_requests[0].url)
assert "selector=app%3Dpayments" in str(seen_requests[0].url)
def test_username_password_login_fetches_session_token_lazily() -> None:
seen: list[tuple[str, str, str]] = []
def handler(request: httpx.Request) -> httpx.Response:
auth = request.headers.get("authorization", "")
seen.append((request.method, request.url.path, auth))
if request.method == "POST" and request.url.path == "/api/v1/session":
assert json.loads(request.content.decode()) == {"username": "admin", "password": "pw"}
return httpx.Response(200, json={"token": "session-token"}, request=request)
assert auth == "Bearer session-token"
return httpx.Response(200, json={"items": [_application_payload()]}, request=request)
client = ArgoCDClient(
ArgoCDConfig(base_url="https://argocd.example.com", username="admin", password="pw"),
transport=httpx.MockTransport(handler),
)
assert client.list_applications()["success"] is True
assert seen[0] == ("POST", "/api/v1/session", "")
assert seen[1][0:2] == ("GET", "/api/v1/applications")
assert seen[1][2] == "Bearer session-token"
def test_expired_session_token_is_cleared_and_retried_once() -> None:
seen: list[tuple[str, str, str]] = []
issued_tokens = iter(["expired-session-token", "fresh-session-token"])
def handler(request: httpx.Request) -> httpx.Response:
auth = request.headers.get("authorization", "")
seen.append((request.method, request.url.path, auth))
if request.method == "POST" and request.url.path == "/api/v1/session":
return httpx.Response(200, json={"token": next(issued_tokens)}, request=request)
if auth == "Bearer expired-session-token":
return httpx.Response(401, text="session expired", request=request)
assert auth == "Bearer fresh-session-token"
return httpx.Response(200, json=_application_payload(), request=request)
client = ArgoCDClient(
ArgoCDConfig(base_url="https://argocd.example.com", username="admin", password="pw"),
transport=httpx.MockTransport(handler),
)
result = client.get_application_summary("payments-api")
assert result["success"] is True
assert result["application"]["name"] == "payments-api"
assert seen == [
("POST", "/api/v1/session", ""),
("GET", "/api/v1/applications/payments-api", "Bearer expired-session-token"),
("POST", "/api/v1/session", ""),
("GET", "/api/v1/applications/payments-api", "Bearer fresh-session-token"),
]
def test_expired_session_token_final_401_redacts_retired_tokens() -> None:
seen: list[tuple[str, str, str]] = []
issued_tokens = iter(["expired-session-token", "second-expired-session-token"])
def handler(request: httpx.Request) -> httpx.Response:
auth = request.headers.get("authorization", "")
seen.append((request.method, request.url.path, auth))
if request.method == "POST" and request.url.path == "/api/v1/session":
return httpx.Response(200, json={"token": next(issued_tokens)}, request=request)
return httpx.Response(
401,
text=f"session expired for {auth} password leaked-password",
request=request,
)
client = ArgoCDClient(
ArgoCDConfig(base_url="https://argocd.example.com", username="admin", password="pw"),
transport=httpx.MockTransport(handler),
)
result = client.get_application_summary("payments-api")
assert result["success"] is False
assert "401" in result["error"]
assert "expired-session-token" not in result["error"]
assert "second-expired-session-token" not in result["error"]
assert "leaked-password" not in result["error"]
assert "[REDACTED]" in result["error"]
assert [call[0] for call in seen] == ["POST", "GET", "POST", "GET"]
def test_get_application_summary_includes_revision_history_and_operation_state() -> None:
def handler(request: httpx.Request) -> httpx.Response:
assert request.url.path == "/api/v1/applications/payments-api"
return httpx.Response(200, json=_application_payload(), request=request)
client = ArgoCDClient(
ArgoCDConfig(base_url="https://argocd.example.com", bearer_token="tok_test"),
transport=httpx.MockTransport(handler),
)
result = client.get_application_summary(
"payments-api", project="default", app_namespace="argocd"
)
assert result["success"] is True
assert result["application"]["name"] == "payments-api"
assert result["application"]["project"] == "default"
assert result["application"]["operation_phase"] == "Succeeded"
assert [h["revision"] for h in result["recent_history"]] == ["abc123", "old111"]
def test_get_application_diff_reports_drift_from_server_side_diff() -> None:
def handler(request: httpx.Request) -> httpx.Response:
assert request.url.path == "/api/v1/applications/payments-api/server-side-diff"
return httpx.Response(
200,
json={
"diffs": [
{
"group": "apps",
"kind": "Deployment",
"name": "payments-api",
"namespace": "payments",
"diff": "- replicas: 3\n+ replicas: 2",
}
]
},
request=request,
)
client = ArgoCDClient(
ArgoCDConfig(base_url="https://argocd.example.com", bearer_token="tok_test"),
transport=httpx.MockTransport(handler),
)
result = client.get_application_diff("payments-api", project="default", app_namespace="argocd")
assert result["success"] is True
assert result["drift_detected"] is True
assert result["diff_count"] == 1
assert result["diffs"][0]["kind"] == "Deployment"
def test_get_application_diff_accepts_argocd_items_response() -> None:
def handler(request: httpx.Request) -> httpx.Response:
assert request.url.path == "/api/v1/applications/payments-api/server-side-diff"
return httpx.Response(
200,
json={
"modified": True,
"items": [
{
"group": "apps",
"kind": "Deployment",
"name": "payments-api",
"namespace": "payments",
"modified": True,
"diff": "- replicas: 3\n+ replicas: 2",
}
],
},
request=request,
)
client = ArgoCDClient(
ArgoCDConfig(base_url="https://argocd.example.com", bearer_token="tok_test"),
transport=httpx.MockTransport(handler),
)
result = client.get_application_diff("payments-api")
assert result["success"] is True
assert result["drift_detected"] is True
assert result["diff_count"] == 1
assert result["diffs"][0]["kind"] == "Deployment"
def test_get_application_diff_falls_back_to_managed_resources_when_server_side_diff_empty() -> None:
seen_paths: list[str] = []
target_state = json.dumps(
{
"apiVersion": "apps/v1",
"kind": "Deployment",
"metadata": {"name": "payments-api", "namespace": "payments"},
"spec": {"replicas": 1},
},
sort_keys=True,
)
live_state = json.dumps(
{
"apiVersion": "apps/v1",
"kind": "Deployment",
"metadata": {"name": "payments-api", "namespace": "payments"},
"spec": {"replicas": 2},
},
sort_keys=True,
)
def handler(request: httpx.Request) -> httpx.Response:
seen_paths.append(request.url.path)
if request.url.path.endswith("/server-side-diff"):
return httpx.Response(200, json={"modified": False}, request=request)
assert request.url.path.endswith("/managed-resources")
return httpx.Response(
200,
json={
"items": [
{
"group": "apps",
"kind": "Deployment",
"name": "payments-api",
"namespace": "payments",
"targetState": target_state,
"normalizedLiveState": live_state,
}
]
},
request=request,
)
client = ArgoCDClient(
ArgoCDConfig(base_url="https://argocd.example.com", bearer_token="tok_test"),
transport=httpx.MockTransport(handler),
)
result = client.get_application_diff("payments-api")
assert result["success"] is True
assert result["drift_detected"] is True
assert result["diff_count"] == 1
assert result["diffs"][0]["kind"] == "Deployment"
assert "replicas" in result["diffs"][0]["diff"]
assert seen_paths == [
"/api/v1/applications/payments-api/server-side-diff",
"/api/v1/applications/payments-api/managed-resources",
]
def test_get_application_diff_fallback_ignores_equal_managed_resource_states() -> None:
state = json.dumps(
{
"apiVersion": "apps/v1",
"kind": "Deployment",
"metadata": {"name": "payments-api", "namespace": "payments"},
"spec": {"replicas": 1},
},
sort_keys=True,
)
def handler(request: httpx.Request) -> httpx.Response:
if request.url.path.endswith("/server-side-diff"):
return httpx.Response(200, json={"modified": False}, request=request)
assert request.url.path.endswith("/managed-resources")
return httpx.Response(
200,
json={
"items": [
{
"group": "apps",
"kind": "Deployment",
"name": "payments-api",
"namespace": "payments",
"targetState": state,
"normalizedLiveState": state,
}
]
},
request=request,
)
client = ArgoCDClient(
ArgoCDConfig(base_url="https://argocd.example.com", bearer_token="tok_test"),
transport=httpx.MockTransport(handler),
)
result = client.get_application_diff("payments-api")
assert result["success"] is True
assert result["drift_detected"] is False
assert result["diff_count"] == 0
assert result["diffs"] == []
def test_get_application_diff_fallback_skips_non_json_managed_resource_states() -> None:
def handler(request: httpx.Request) -> httpx.Response:
if request.url.path.endswith("/server-side-diff"):
return httpx.Response(200, json={"modified": False}, request=request)
assert request.url.path.endswith("/managed-resources")
return httpx.Response(
200,
json={
"items": [
{
"group": "",
"kind": "ConfigMap",
"name": "payments-config",
"namespace": "payments",
"targetState": "kind: ConfigMap\nmetadata:\n name: payments-config\n",
"normalizedLiveState": "kind: ConfigMap\nmetadata: {name: payments-config}\n",
}
]
},
request=request,
)
client = ArgoCDClient(
ArgoCDConfig(base_url="https://argocd.example.com", bearer_token="tok_test"),
transport=httpx.MockTransport(handler),
)
result = client.get_application_diff("payments-config")
assert result["success"] is True
assert result["drift_detected"] is False
assert result["diff_count"] == 0
def test_get_application_diff_fallback_redacts_sensitive_non_secret_json_state() -> None:
target_state = json.dumps(
{
"apiVersion": "v1",
"kind": "ConfigMap",
"metadata": {"name": "payments-config", "namespace": "payments"},
"data": {"clientSecret": "old-super-secret-value"},
},
sort_keys=True,
)
live_state = json.dumps(
{
"apiVersion": "v1",
"kind": "ConfigMap",
"metadata": {"name": "payments-config", "namespace": "payments"},
"data": {"clientSecret": "new-super-secret-value"},
},
sort_keys=True,
)
def handler(request: httpx.Request) -> httpx.Response:
if request.url.path.endswith("/server-side-diff"):
return httpx.Response(200, json={"modified": False}, request=request)
assert request.url.path.endswith("/managed-resources")
return httpx.Response(
200,
json={
"items": [
{
"group": "",
"kind": "ConfigMap",
"name": "payments-config",
"namespace": "payments",
"targetState": target_state,
"normalizedLiveState": live_state,
}
]
},
request=request,
)
client = ArgoCDClient(
ArgoCDConfig(base_url="https://argocd.example.com", bearer_token="tok_test"),
transport=httpx.MockTransport(handler),
)
result = client.get_application_diff("payments-config")
assert result["success"] is True
assert result["drift_detected"] is True
assert result["diff_count"] == 1
rendered = result["diffs"][0]["diff"]
assert "old-super-secret-value" not in rendered
assert "new-super-secret-value" not in rendered
assert rendered == "[REDACTED secret-bearing resource diff]"
def test_get_application_diff_redacts_secret_resources_and_token_like_values() -> None:
def handler(request: httpx.Request) -> httpx.Response:
return httpx.Response(
200,
json={
"diffs": [
{
"group": "",
"kind": "Secret",
"name": "payments-secret",
"namespace": "payments",
"diff": "+ opaque: dummy-sensitive-value",
},
{
"group": "",
"kind": "ConfigMap",
"name": "payments-config",
"namespace": "payments",
"diff": "+ auth: Bearer dummy-sensitive-value",
},
]
},
request=request,
)
client = ArgoCDClient(
ArgoCDConfig(base_url="https://argocd.example.com", bearer_token="tok_test"),
transport=httpx.MockTransport(handler),
)
result = client.get_application_diff("payments-api")
assert result["success"] is True
rendered = "\n".join(diff["diff"] for diff in result["diffs"])
assert "dummy-sensitive-value" not in rendered
assert "[REDACTED" in rendered
def test_http_errors_are_returned_without_leaking_tokens() -> None:
def handler(request: httpx.Request) -> httpx.Response:
return httpx.Response(403, text="forbidden for token tok_secret", request=request)
client = ArgoCDClient(
ArgoCDConfig(base_url="https://argocd.example.com", bearer_token="tok_secret"),
transport=httpx.MockTransport(handler),
)
result = client.get_application_summary("payments-api")
assert result["success"] is False
assert "403" in result["error"]
assert "tok_secret" not in result["error"]
assert "[REDACTED]" in result["error"]
def test_unauthorized_errors_are_returned_without_leaking_tokens() -> None:
def handler(request: httpx.Request) -> httpx.Response:
return httpx.Response(
401,
text="unauthorized for bearer tok_secret password leaked-password",
request=request,
)
client = ArgoCDClient(
ArgoCDConfig(base_url="https://argocd.example.com", bearer_token="tok_secret"),
transport=httpx.MockTransport(handler),
)
result = client.get_application_summary("payments-api")
assert result["success"] is False
assert "401" in result["error"]
assert "tok_secret" not in result["error"]
assert "leaked-password" not in result["error"]
assert "[REDACTED]" in result["error"]
def test_large_diffs_are_truncated_after_redaction() -> None:
large_diff = "+ replicas: 3\n" + ("+ safe-config: value\n" * 700)
def handler(request: httpx.Request) -> httpx.Response:
return httpx.Response(
200,
json={
"diffs": [
{
"group": "apps",
"kind": "Deployment",
"name": "payments-api",
"namespace": "payments",
"diff": large_diff,
}
]
},
request=request,
)
client = ArgoCDClient(
ArgoCDConfig(base_url="https://argocd.example.com", bearer_token="tok_test"),
transport=httpx.MockTransport(handler),
)
result = client.get_application_diff("payments-api")
assert result["success"] is True
rendered = result["diffs"][0]["diff"]
assert len(rendered) < len(large_diff)
assert "[truncated after 10000 chars]" in rendered
def test_list_applications_omits_empty_project_and_selector_params() -> None:
seen_requests: list[httpx.Request] = []
def handler(request: httpx.Request) -> httpx.Response:
seen_requests.append(request)
return httpx.Response(200, json={"items": []}, request=request)
client = ArgoCDClient(
ArgoCDConfig(base_url="https://argocd.example.com", bearer_token="tok_test"),
transport=httpx.MockTransport(handler),
)
result = client.list_applications(projects=["", " "], selector=" ")
assert result["success"] is True
assert seen_requests[0].url.query == b""
def test_verify_ssl_false_is_passed_to_http_client(monkeypatch: pytest.MonkeyPatch) -> None:
captured: dict[str, Any] = {}
class FakeClient:
def __init__(self, **kwargs: Any) -> None:
captured.update(kwargs)
monkeypatch.setattr("integrations.argocd.client.httpx.Client", FakeClient)
client = ArgoCDClient(
ArgoCDConfig(
base_url="https://argocd.example.com",
bearer_token="tok_test",
verify_ssl=False,
)
)
assert client._get_client() is not None
assert captured["verify"] is False