Files
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

235 lines
8.3 KiB
Python

"""Tests for the RDS investigation tools."""
from __future__ import annotations
import logging
from typing import Any
from unittest.mock import patch
from integrations.rds.tools.rds_describe_instance_tool import describe_rds_instance
from integrations.rds.tools.rds_events_tool import describe_rds_events
class _FakeAWSBackend:
"""Minimal AWSBackend stand-in that records calls for assertion."""
def __init__(self, db_response: dict[str, Any], events_response: dict[str, Any]) -> None:
self.db_response = db_response
self.events_response = events_response
self.db_calls: list[dict[str, Any]] = []
self.events_calls: list[dict[str, Any]] = []
def describe_db_instances(
self,
db_instance_identifier: str,
region: str = "",
**_: Any,
) -> dict[str, Any]:
self.db_calls.append({"db_instance_identifier": db_instance_identifier, "region": region})
return self.db_response
def describe_db_events(
self,
db_instance_identifier: str,
duration_minutes: int = 60,
region: str = "",
**_: Any,
) -> dict[str, Any]:
self.events_calls.append(
{
"db_instance_identifier": db_instance_identifier,
"duration_minutes": duration_minutes,
"region": region,
}
)
return self.events_response
@patch("integrations.rds.tools.rds_describe_instance_tool.execute_aws_sdk_call")
def test_describe_rds_instance_success(mock_call) -> None:
mock_call.return_value = {
"success": True,
"data": {
"DBInstances": [
{
"DBInstanceStatus": "available",
"Engine": "postgres",
"EngineVersion": "15.4",
"DBInstanceClass": "db.t4g.micro",
"MultiAZ": True,
"PubliclyAccessible": False,
"StorageType": "gp3",
"AllocatedStorage": 20,
"Endpoint": {"Address": "prod.abc.rds.aws", "Port": 5432},
"AvailabilityZone": "us-east-1a",
"PreferredBackupWindow": "03:00-04:00",
"BackupRetentionPeriod": 7,
}
]
},
}
result = describe_rds_instance("prod-db", region="us-east-1")
assert result["available"] is True
assert result["status"] == "available"
assert result["engine"] == "postgres"
assert result["multi_az"] is True
assert result["endpoint"]["port"] == 5432
@patch("integrations.rds.tools.rds_describe_instance_tool.execute_aws_sdk_call")
def test_describe_rds_instance_not_found(mock_call) -> None:
mock_call.return_value = {"success": True, "data": {"DBInstances": []}}
result = describe_rds_instance("missing-db")
assert result["available"] is False
assert "No RDS instance" in result["error"]
@patch("integrations.rds.tools.rds_describe_instance_tool.execute_aws_sdk_call")
def test_describe_rds_instance_aws_failure(mock_call) -> None:
mock_call.return_value = {"success": False, "error": "AccessDenied"}
result = describe_rds_instance("prod-db")
assert result["available"] is False
assert result["error"] == "Failed to describe the RDS instance. Check server logs for details."
@patch("integrations.rds.tools.rds_describe_instance_tool.execute_aws_sdk_call")
def test_describe_rds_instance_multiple_instances_warns(mock_call, caplog) -> None:
"""When AWS returns >1 instance, use the first and emit a warning."""
mock_call.return_value = {
"success": True,
"data": {
"DBInstances": [
{
"DBInstanceStatus": "available",
"Engine": "postgres",
"EngineVersion": "15.4",
"DBInstanceClass": "db.t4g.micro",
"Endpoint": {"Address": "prod.abc.rds.aws", "Port": 5432},
},
{
"DBInstanceStatus": "available",
"Engine": "mysql",
"EngineVersion": "8.0",
"DBInstanceClass": "db.r6g.large",
"Endpoint": {"Address": "replica.abc.rds.aws", "Port": 3306},
},
]
},
}
with caplog.at_level(logging.WARNING):
result = describe_rds_instance("prod-db")
assert result["available"] is True
assert result["engine"] == "postgres" # first instance used
assert "returned 2 instances" in caplog.text
@patch("integrations.rds.tools.rds_events_tool.execute_aws_sdk_call")
def test_describe_rds_events_success(mock_call) -> None:
mock_call.return_value = {
"success": True,
"data": {
"Events": [
{
"Date": "2026-05-05T12:00:00Z",
"Message": "Multi-AZ instance failover started",
"EventCategories": ["failover"],
"SourceType": "db-instance",
}
]
},
}
result = describe_rds_events("prod-db", duration_minutes=120)
assert result["available"] is True
assert result["total_events"] == 1
assert result["events"][0]["categories"] == ["failover"]
@patch("integrations.rds.tools.rds_events_tool.execute_aws_sdk_call")
def test_describe_rds_events_no_events(mock_call) -> None:
mock_call.return_value = {"success": True, "data": {"Events": []}}
result = describe_rds_events("prod-db")
assert result["available"] is True
assert result["total_events"] == 0
assert result["events"] == []
@patch("integrations.rds.tools.rds_events_tool.execute_aws_sdk_call")
def test_describe_rds_events_failure(mock_call) -> None:
mock_call.return_value = {"success": False, "error": "ThrottlingException"}
result = describe_rds_events("prod-db")
assert result["available"] is False
assert result["error"] == "Failed to describe RDS events. Check server logs for details."
# ─────────────────────────────────────────────────────────────────────────────
# Synthetic-mode short-circuit: when an aws_backend handle is provided the
# tool MUST route through it instead of boto3. This guards against the
# regression where synthetic scenarios silently leaked describe_db_instances
# calls to whatever real AWS account the developer was authenticated against.
# ─────────────────────────────────────────────────────────────────────────────
@patch("integrations.rds.tools.rds_describe_instance_tool.execute_aws_sdk_call")
def test_describe_rds_instance_short_circuits_to_aws_backend(mock_call) -> None:
backend = _FakeAWSBackend(
db_response={
"source": "rds",
"available": True,
"db_instance_identifier": "synthetic-db",
"engine": "mysql",
"error": None,
},
events_response={},
)
result = describe_rds_instance(
"synthetic-db",
region="us-east-1",
aws_backend=backend,
)
mock_call.assert_not_called()
assert backend.db_calls == [{"db_instance_identifier": "synthetic-db", "region": "us-east-1"}]
assert result["engine"] == "mysql"
@patch("integrations.rds.tools.rds_events_tool.execute_aws_sdk_call")
def test_describe_rds_events_short_circuits_to_aws_backend(mock_call) -> None:
backend = _FakeAWSBackend(
db_response={},
events_response={
"source": "rds",
"available": True,
"db_instance_identifier": "synthetic-db",
"duration_minutes": 90,
"total_events": 1,
"events": [{"date": "2026-05-05T12:00:00Z", "message": "ok"}],
"error": None,
},
)
result = describe_rds_events(
"synthetic-db",
region="us-east-1",
duration_minutes=90,
aws_backend=backend,
)
mock_call.assert_not_called()
assert backend.events_calls == [
{
"db_instance_identifier": "synthetic-db",
"duration_minutes": 90,
"region": "us-east-1",
}
]
assert result["total_events"] == 1