Files
Agentswarm/scripts/test-redis-connection-config.py
FastheiandClaude Opus 4.8 35aab3a643 feat(redis): 加 REDIS_CLUSTER 支持 OSS Cluster 端点(heicode-rd 必需)
heicode-rd(Azure Redis Enterprise)database clusteringPolicy=OSSCluster,
裸 redis.Redis 客户端在多分片下 keys()/跨 slot 操作会误路由/抛 MOVED。

- REDIS_CLUSTER truthy → 用 redis.asyncio.cluster.RedisCluster(URL 或
  host/port 两种入参,密码/TLS 同样支持)。cluster 模式无 DB select,
  REDIS_DB 被忽略(仅逻辑 DB0)。
- 不设时维持 standalone 行为,完全向后兼容。
- 测试加 cluster 用例;manifest/DELIVERY 补 REDIS_CLUSTER 说明。

验证:连接配置单测 4 项 + REDIS_FAKE 回退 + test-runtime-contract /
test-contract-freeze / test-merge-smoke 全 PASS。

影响范围:仅 agent_swarm orchestrator 连接层;不改契约/计费/审计/密钥落地。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-12 17:00:37 +08:00

123 lines
4.4 KiB
Python

#!/usr/bin/env python3
"""Unit test for RedisClient connection config (TLS / password / REDIS_URL).
Validates that env config maps to the right redis-py connection kwargs WITHOUT
opening a connection — so it runs anywhere, no live Redis needed. Covers:
- default plaintext in-cluster behaviour (backward compat)
- discrete REDIS_PASSWORD / REDIS_SSL
- REDIS_URL precedence + rediss:// (TLS)
- logs never leak the URL/password (credential-safe target description)
Run: python scripts/test-redis-connection-config.py
"""
import importlib
import os
import sys
REPO_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
if REPO_ROOT not in sys.path:
sys.path.insert(0, REPO_ROOT)
REDIS_ENV_VARS = (
"REDIS_URL", "REDIS_HOST", "REDIS_PORT", "REDIS_DB",
"REDIS_PASSWORD", "REDIS_SSL", "REDIS_FAKE", "ALLOW_MEMORY_STORE",
)
def _fresh_client(env):
"""Reset the relevant env, apply `env`, and return a freshly built RedisClient."""
for k in REDIS_ENV_VARS:
os.environ.pop(k, None)
os.environ.update(env)
import orchestrator.redis_client as rc
importlib.reload(rc)
return rc.RedisClient(), rc
def _conn_kwargs(client_obj):
"""Pull the connection kwargs redis-py resolved for the built client."""
real = client_obj._make_real_client()
return real.connection_pool.connection_kwargs, real
def test_default_plaintext():
c, _ = _fresh_client({})
kw, real = _conn_kwargs(c)
assert kw.get("host") == "redis-service", kw
assert kw.get("port") == 6379, kw
assert "password" not in kw or kw["password"] is None, kw
# No TLS: connection class must not be the SSL one.
assert "SSL" not in real.connection_pool.connection_class.__name__, kw
assert "TLS=False" in c._target_desc()
print("ok: default plaintext (backward compatible)")
def test_discrete_password_and_ssl():
c, _ = _fresh_client({
"REDIS_HOST": "heicode-rd.redisenterprise.cache.azure.net",
"REDIS_PORT": "10000",
"REDIS_PASSWORD": "s3cr3t-from-secret",
"REDIS_SSL": "1",
})
kw, real = _conn_kwargs(c)
assert kw.get("host") == "heicode-rd.redisenterprise.cache.azure.net", kw
assert kw.get("port") == 10000, kw
assert kw.get("password") == "s3cr3t-from-secret", kw
assert "SSL" in real.connection_pool.connection_class.__name__, "expected TLS connection class"
# target description must NOT leak the password
assert "s3cr3t" not in c._target_desc(), c._target_desc()
assert "TLS=True" in c._target_desc()
print("ok: discrete REDIS_PASSWORD + REDIS_SSL → authenticated TLS")
def test_url_precedence_and_tls():
c, _ = _fresh_client({
"REDIS_URL": "rediss://:url-password@heicode-rd.example.net:10000/0",
# discrete vars set too — URL must win
"REDIS_HOST": "redis-service",
"REDIS_SSL": "",
})
kw, real = _conn_kwargs(c)
assert kw.get("host") == "heicode-rd.example.net", kw
assert kw.get("port") == 10000, kw
assert kw.get("password") == "url-password", kw
assert "SSL" in real.connection_pool.connection_class.__name__, "rediss:// must enable TLS"
desc = c._target_desc()
assert "url-password" not in desc and "heicode-rd.example.net" not in desc, desc
assert "TLS=True" in desc
print("ok: REDIS_URL precedence + rediss:// TLS, credentials not logged")
def test_cluster_mode():
# OSS Cluster endpoint (e.g. Azure Redis Enterprise clusteringPolicy=OSSCluster):
# must build a cluster-aware client so keys()/cross-slot routing is correct.
c, _ = _fresh_client({
"REDIS_CLUSTER": "1",
"REDIS_HOST": "heicode-rd.southeastasia.redis.azure.net",
"REDIS_PORT": "10000",
"REDIS_PASSWORD": "key-from-secret",
})
real = c._make_real_client() # constructs lazily; does NOT connect
assert type(real).__name__ == "RedisCluster", type(real).__name__
assert "cluster" in c._target_desc(), c._target_desc()
assert "key-from-secret" not in c._target_desc(), c._target_desc()
print("ok: REDIS_CLUSTER → RedisCluster client (OSS Cluster safe)")
def main():
try:
import redis # noqa: F401
except Exception:
print("SKIP: redis-py not installed; connection-config test requires it")
return 0
test_default_plaintext()
test_discrete_password_and_ssl()
test_url_precedence_and_tls()
test_cluster_mode()
print("\nALL PASS: redis connection config")
return 0
if __name__ == "__main__":
sys.exit(main())