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
137 lines
5.2 KiB
Python
137 lines
5.2 KiB
Python
"""Tests for FixtureAWSBackend — the synthetic AWS shim.
|
|
|
|
These pin the contract that lets RDS investigation tools serve fixture data
|
|
during synthetic scenario runs without ever reaching real boto3. The
|
|
contract has two halves:
|
|
|
|
1. ``describe_db_instances`` is foundational metadata. Every scenario carries
|
|
the DB identity in ``scenario.yml``, so the call MUST always succeed for
|
|
the scenario's own identifier. A foreign identifier mirrors the real
|
|
boto3 ``DBInstanceNotFound`` envelope so synthetic runs are honest.
|
|
2. ``describe_db_events`` is a data feed gated on ``aws_rds_events`` evidence
|
|
being declared in the scenario. Calling without that opt-in must raise
|
|
loudly; silently returning "no events" would let scenarios accidentally
|
|
teach the agent that absence of events is evidence.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import dataclasses
|
|
|
|
import pytest
|
|
|
|
from tests.synthetic.mock_aws_backend import FixtureAWSBackend
|
|
from tests.synthetic.rds_postgres.scenario_loader import (
|
|
SUITE_DIR,
|
|
ScenarioFixture,
|
|
load_scenario,
|
|
)
|
|
|
|
|
|
def _load(scenario_name: str) -> ScenarioFixture:
|
|
return load_scenario(SUITE_DIR / scenario_name)
|
|
|
|
|
|
def _without_evidence(fixture: ScenarioFixture, **fields_to_clear: object) -> ScenarioFixture:
|
|
"""Return a copy of *fixture* with the named evidence fields set to None.
|
|
|
|
Avoids the trap of "every shipped scenario declares aws_rds_events" — we
|
|
can't pick a real scenario that omits it, so synthesize one in-memory.
|
|
"""
|
|
cleared_evidence = dataclasses.replace(fixture.evidence, **fields_to_clear)
|
|
return dataclasses.replace(fixture, evidence=cleared_evidence)
|
|
|
|
|
|
def test_describe_db_instances_synthesizes_response_from_scenario_metadata() -> None:
|
|
fixture = _load("015-mysql-ec2-load-attribution")
|
|
backend = FixtureAWSBackend(fixture)
|
|
|
|
response = backend.describe_db_instances(
|
|
db_instance_identifier="orders-prod",
|
|
region="us-east-1",
|
|
)
|
|
|
|
assert response["available"] is True
|
|
assert response["db_instance_identifier"] == "orders-prod"
|
|
assert response["engine"] == "mysql"
|
|
assert response["engine_version"] == "8.0"
|
|
assert response["instance_class"] == "db.r6g.xlarge"
|
|
assert response["status"] == "available"
|
|
assert response["endpoint"]["port"] == 3306
|
|
assert "orders-prod" in response["endpoint"]["address"]
|
|
assert response["error"] is None
|
|
|
|
|
|
def test_describe_db_instances_picks_postgres_port_for_postgres_engine() -> None:
|
|
fixture = _load("001-replication-lag")
|
|
backend = FixtureAWSBackend(fixture)
|
|
|
|
response = backend.describe_db_instances(
|
|
db_instance_identifier=fixture.metadata.db_instance_identifier,
|
|
)
|
|
|
|
assert response["available"] is True
|
|
assert response["engine"].startswith("postgres")
|
|
assert response["endpoint"]["port"] == 5432
|
|
|
|
|
|
def test_describe_db_instances_returns_not_found_envelope_for_foreign_identifier() -> None:
|
|
"""Mirror the real boto3 ``DBInstanceNotFound`` semantics so synthetic
|
|
scenarios stay honest: if the agent asks about a DB the scenario didn't
|
|
define, we report it as missing rather than fabricating a successful
|
|
response.
|
|
"""
|
|
fixture = _load("015-mysql-ec2-load-attribution")
|
|
backend = FixtureAWSBackend(fixture)
|
|
|
|
response = backend.describe_db_instances(db_instance_identifier="some-other-db")
|
|
|
|
assert response["available"] is False
|
|
assert "No RDS instance" in response["error"]
|
|
assert response["db_instance_identifier"] == "some-other-db"
|
|
|
|
|
|
def test_describe_db_events_serves_aws_rds_events_fixture() -> None:
|
|
fixture = _load("015-mysql-ec2-load-attribution")
|
|
backend = FixtureAWSBackend(fixture)
|
|
|
|
response = backend.describe_db_events(
|
|
db_instance_identifier="orders-prod",
|
|
duration_minutes=120,
|
|
)
|
|
|
|
assert response["available"] is True
|
|
assert response["duration_minutes"] == 120
|
|
assert response["total_events"] == 1
|
|
event = response["events"][0]
|
|
assert "Automated backup" in event["message"]
|
|
assert event["source_type"] == "db-instance"
|
|
assert event["categories"] == ["backup"]
|
|
|
|
|
|
def test_describe_db_events_raises_when_evidence_not_declared() -> None:
|
|
"""Without ``aws_rds_events`` in ``available_evidence`` we must raise.
|
|
|
|
Returning an empty list would silently teach the agent that absence of
|
|
events is evidence — exactly the kind of false-negative we want the
|
|
synthetic suite to catch loudly during scenario authoring.
|
|
"""
|
|
fixture = _without_evidence(_load("000-healthy"), aws_rds_events=None)
|
|
backend = FixtureAWSBackend(fixture)
|
|
|
|
with pytest.raises(ValueError, match="aws_rds_events"):
|
|
backend.describe_db_events(db_instance_identifier=fixture.metadata.db_instance_identifier)
|
|
|
|
|
|
def test_describe_db_events_returns_empty_list_for_foreign_identifier() -> None:
|
|
"""A foreign identifier matches no events; this mirrors how real RDS
|
|
``describe_events`` behaves for unknown source identifiers."""
|
|
fixture = _load("015-mysql-ec2-load-attribution")
|
|
backend = FixtureAWSBackend(fixture)
|
|
|
|
response = backend.describe_db_events(db_instance_identifier="not-this-db")
|
|
|
|
assert response["available"] is True
|
|
assert response["total_events"] == 0
|
|
assert response["events"] == []
|