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

247 lines
9.0 KiB
Python

"""Tests for classify_integrations with multi-instance records."""
from __future__ import annotations
from integrations.catalog import classify_integrations
def _v1_grafana(endpoint: str = "https://x", api_key: str = "k") -> dict:
return {
"id": "store-grafana",
"service": "grafana",
"status": "active",
"credentials": {"endpoint": endpoint, "api_key": api_key},
}
def _v2_grafana_multi() -> dict:
return {
"id": "env-grafana",
"service": "grafana",
"status": "active",
"instances": [
{
"name": "prod",
"tags": {"env": "prod"},
"credentials": {"endpoint": "https://prod", "api_key": "kp"},
},
{
"name": "staging",
"tags": {"env": "staging"},
"credentials": {"endpoint": "https://staging", "api_key": "ks"},
},
],
}
def test_classify_single_v1_record_returns_flat_shape_unchanged() -> None:
"""Backward compat: v1 record with one instance produces the flat shape
exactly as before (no _all_*_instances sibling when single default)."""
resolved = classify_integrations([_v1_grafana()])
assert "grafana" in resolved
assert resolved["grafana"].api_key == "k"
assert resolved["grafana"].endpoint == "https://x"
# No sibling published for a single default-named instance
assert "_all_grafana_instances" not in resolved
def test_classify_single_v2_record_returns_flat_shape() -> None:
"""v2 records pass through: flat shape is still the first instance's config."""
v2_single = {
"id": "g1",
"service": "grafana",
"status": "active",
"instances": [
{"name": "default", "tags": {}, "credentials": {"endpoint": "x", "api_key": "k"}}
],
}
resolved = classify_integrations([v2_single])
assert resolved["grafana"].api_key == "k"
assert "_all_grafana_instances" not in resolved
def test_classify_multi_instance_record_exposes_sibling_all_grafana_instances() -> None:
resolved = classify_integrations([_v2_grafana_multi()])
# Default (first) view
assert resolved["grafana"].api_key == "kp"
# Sibling with all instances
assert "_all_grafana_instances" in resolved
all_instances = resolved["_all_grafana_instances"]
assert len(all_instances) == 2
assert [i["name"] for i in all_instances] == ["prod", "staging"]
def test_all_grafana_instances_each_has_config_name_tags_integration_id() -> None:
resolved = classify_integrations([_v2_grafana_multi()])
first = resolved["_all_grafana_instances"][0]
assert set(first.keys()) >= {"name", "tags", "config", "integration_id"}
assert first["name"] == "prod"
assert first["tags"] == {"env": "prod"}
assert first["config"].api_key == "kp"
assert first["integration_id"] == "env-grafana"
def test_classify_two_records_same_service_both_preserved() -> None:
"""No silent last-wins: two records for the same service both get
represented in the _all_*_instances sibling."""
record_a = {
"id": "a",
"service": "grafana",
"status": "active",
"credentials": {"endpoint": "https://a", "api_key": "ka"},
}
record_b = {
"id": "b",
"service": "grafana",
"status": "active",
"credentials": {"endpoint": "https://b", "api_key": "kb"},
}
resolved = classify_integrations([record_a, record_b])
# Flat shape is the FIRST (setdefault keeps it)
assert resolved["grafana"].api_key == "ka"
# Both are in the sibling
assert "_all_grafana_instances" in resolved
ids = [i["integration_id"] for i in resolved["_all_grafana_instances"]]
assert ids == ["a", "b"]
def test_classify_aws_with_role_arn_in_instance_credentials_works() -> None:
"""PR #527 bug #1 regression: AWS must read role_arn from instance.credentials,
not from the record's top level."""
v2_aws = {
"id": "aws-1",
"service": "aws",
"status": "active",
"instances": [
{
"name": "default",
"tags": {},
"credentials": {
"region": "us-east-1",
"role_arn": "arn:aws:iam::123456789012:role/opensre",
"external_id": "ext",
},
}
],
}
resolved = classify_integrations([v2_aws])
assert "aws" in resolved
assert resolved["aws"].role_arn == "arn:aws:iam::123456789012:role/opensre"
assert resolved["aws"].external_id == "ext"
assert resolved["aws"].region == "us-east-1"
def test_classify_with_migrated_v1_aws_record_works() -> None:
"""Backward compat: passing a v1 AWS record with top-level role_arn still
works because classify migrates on the fly."""
v1_aws = {
"id": "aws-1",
"service": "aws",
"status": "active",
"role_arn": "arn:aws:iam::123:role/r",
"external_id": "ext",
"credentials": {"region": "us-east-1"},
}
resolved = classify_integrations([v1_aws])
assert resolved["aws"].role_arn == "arn:aws:iam::123:role/r"
def test_local_and_cloud_grafana_share_all_grafana_instances_bucket() -> None:
"""Regression for Devesh36 review: a local Grafana instance (classified
as grafana_local) must be discoverable via the same _all_grafana_instances
key that selectors look up under "grafana", so a hint like
grafana_instance: "local" finds it."""
v2_mixed = {
"id": "env-grafana",
"service": "grafana",
"status": "active",
"instances": [
{
"name": "local",
"tags": {"env": "dev"},
"credentials": {"endpoint": "http://localhost:3000", "api_key": "local"},
},
{
"name": "prod",
"tags": {"env": "prod"},
"credentials": {"endpoint": "https://prod.grafana.net", "api_key": "kp"},
},
],
}
resolved = classify_integrations([v2_mixed])
# Both instances land in the same bucket under the "grafana" family key.
assert "_all_grafana_instances" in resolved
assert "_all_grafana_local_instances" not in resolved
names = [i["name"] for i in resolved["_all_grafana_instances"]]
assert set(names) == {"local", "prod"}
def test_local_grafana_without_basic_auth_keeps_credentials_empty() -> None:
"""Regression for Greptile: localhost Grafana without credentials should
remain anonymous (no injected admin:admin fallback)."""
local_only = {
"id": "env-grafana",
"service": "grafana",
"status": "active",
"credentials": {
"endpoint": "http://localhost:3000",
"api_key": "local",
},
}
resolved = classify_integrations([local_only])
assert resolved["grafana_local"].endpoint == "http://localhost:3000"
assert resolved["grafana_local"].username == ""
assert resolved["grafana_local"].password == ""
def test_classify_inactive_record_is_skipped() -> None:
inactive = {
"id": "g1",
"service": "grafana",
"status": "inactive",
"credentials": {"endpoint": "x", "api_key": "k"},
}
resolved = classify_integrations([inactive])
assert "grafana" not in resolved
def test_resolve_effective_integrations_propagates_single_non_default_instance() -> None:
"""Regression: when classify publishes _all_*_instances for a single
non-default-named instance, resolve_effective_integrations must also
propagate it so CLI/verify consumers see the instance metadata."""
from integrations.catalog import resolve_effective_integrations
single_prod = {
"id": "env-grafana",
"service": "grafana",
"status": "active",
"instances": [
{
"name": "prod", # non-default name
"tags": {"env": "prod"},
"credentials": {"endpoint": "https://p", "api_key": "kp"},
}
],
}
resolved = resolve_effective_integrations(store_integrations=[], env_integrations=[single_prod])
assert "instances" in resolved["grafana"]
assert resolved["grafana"]["instances"][0]["name"] == "prod"
def test_resolve_effective_integrations_carries_instances_through_pydantic() -> None:
"""Regression: EffectiveIntegrations.model_validate must accept the
{name, tags, config, integration_id} instance shape we build. Previously
this used list[IntegrationInstance] which would reject the extra keys
under StrictConfigModel's extra='forbid'."""
from integrations.catalog import resolve_effective_integrations
env_records = [_v2_grafana_multi()]
resolved = resolve_effective_integrations(store_integrations=[], env_integrations=env_records)
assert "grafana" in resolved
assert "instances" in resolved["grafana"]
all_inst = resolved["grafana"]["instances"]
assert [i["name"] for i in all_inst] == ["prod", "staging"]
assert "config" in all_inst[0] # shape preserved through Pydantic
assert "integration_id" in all_inst[0]