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

225 lines
7.0 KiB
Python

"""Tests for the instance-level store APIs: upsert, remove, filter."""
from __future__ import annotations
import json
from pathlib import Path
from unittest.mock import patch
import pytest
from integrations.store import (
get_instance,
get_instances,
remove_instance,
upsert_instance,
)
@pytest.fixture
def tmp_store(tmp_path: Path):
store_file = tmp_path / "integrations.json"
with patch("integrations.store.STORE_PATH", store_file):
yield store_file
def _seed(store_file: Path, records: list[dict]) -> None:
store_file.parent.mkdir(parents=True, exist_ok=True)
store_file.write_text(json.dumps({"version": 2, "integrations": records}) + "\n")
def test_upsert_instance_appends_to_existing_record(tmp_store: Path) -> None:
_seed(
tmp_store,
[
{
"id": "g1",
"service": "grafana",
"status": "active",
"instances": [{"name": "prod", "tags": {}, "credentials": {"endpoint": "a"}}],
}
],
)
upsert_instance(
"grafana",
{"name": "staging", "tags": {"env": "staging"}, "credentials": {"endpoint": "b"}},
record_id="g1",
)
instances = get_instances("grafana")
names = {i["name"] for i in instances}
assert names == {"prod", "staging"}
def test_upsert_instance_updates_by_name_not_position(tmp_store: Path) -> None:
_seed(
tmp_store,
[
{
"id": "g1",
"service": "grafana",
"status": "active",
"instances": [
{"name": "prod", "tags": {}, "credentials": {"endpoint": "old"}},
{"name": "staging", "tags": {}, "credentials": {"endpoint": "s"}},
],
}
],
)
upsert_instance(
"grafana",
{"name": "prod", "tags": {}, "credentials": {"endpoint": "new"}},
record_id="g1",
)
prod = get_instance("grafana", name="prod")
assert prod is not None
assert prod["credentials"]["endpoint"] == "new"
# staging is still there
staging = get_instance("grafana", name="staging")
assert staging is not None
assert staging["credentials"]["endpoint"] == "s"
def test_upsert_instance_creates_new_record_when_no_record_id(tmp_store: Path) -> None:
upsert_instance(
"grafana",
{"name": "prod", "tags": {}, "credentials": {"endpoint": "p"}},
)
instances = get_instances("grafana")
assert len(instances) == 1
assert instances[0]["name"] == "prod"
def test_get_instance_by_name_returns_only_that_instance(tmp_store: Path) -> None:
"""PR #527 bug #3 regression: filtered get must not leak sibling instances."""
_seed(
tmp_store,
[
{
"id": "g1",
"service": "grafana",
"status": "active",
"instances": [
{"name": "prod", "tags": {}, "credentials": {"endpoint": "p"}},
{"name": "staging", "tags": {}, "credentials": {"endpoint": "s"}},
{"name": "dev", "tags": {}, "credentials": {"endpoint": "d"}},
],
}
],
)
prod = get_instance("grafana", name="prod")
assert prod is not None
assert prod["name"] == "prod"
assert prod["credentials"]["endpoint"] == "p"
# Result must carry ONLY the prod instance — not a record with siblings
assert "instances" not in prod # shape is the instance dict, not the parent record
assert "staging" not in json.dumps(prod)
assert "dev" not in json.dumps(prod)
def test_get_instance_by_tag(tmp_store: Path) -> None:
_seed(
tmp_store,
[
{
"id": "g1",
"service": "grafana",
"status": "active",
"instances": [
{"name": "prod", "tags": {"env": "prod"}, "credentials": {"endpoint": "p"}},
{
"name": "staging",
"tags": {"env": "staging"},
"credentials": {"endpoint": "s"},
},
],
}
],
)
match = get_instance("grafana", tags={"env": "staging"})
assert match is not None
assert match["name"] == "staging"
def test_remove_instance_persists_when_record_retains_others(tmp_store: Path) -> None:
"""PR #527 P2 regression: removing one instance must save to disk even
when the parent record is retained."""
_seed(
tmp_store,
[
{
"id": "g1",
"service": "grafana",
"status": "active",
"instances": [
{"name": "prod", "tags": {}, "credentials": {"endpoint": "p"}},
{"name": "staging", "tags": {}, "credentials": {"endpoint": "s"}},
],
}
],
)
removed = remove_instance("grafana", "prod")
assert removed is True
# Reload from disk to confirm persistence.
data = json.loads(tmp_store.read_text())
remaining_names = [i["name"] for i in data["integrations"][0]["instances"]]
assert remaining_names == ["staging"]
def test_remove_last_instance_removes_record(tmp_store: Path) -> None:
_seed(
tmp_store,
[
{
"id": "g1",
"service": "grafana",
"status": "active",
"instances": [
{"name": "prod", "tags": {}, "credentials": {"endpoint": "p"}},
],
}
],
)
removed = remove_instance("grafana", "prod")
assert removed is True
data = json.loads(tmp_store.read_text())
assert data["integrations"] == []
def test_remove_instance_returns_false_when_not_found(tmp_store: Path) -> None:
_seed(tmp_store, [])
assert remove_instance("grafana", "prod") is False
def test_get_integration_provides_flat_credentials_view(tmp_store: Path) -> None:
"""Backward compat: callers like azure_sql.py / mysql.py / postgresql.py
read ``record['credentials']`` directly. get_integration must synthesise
that view from ``instances[0].credentials`` so they keep working."""
from integrations.store import get_integration
_seed(
tmp_store,
[
{
"id": "mysql-1",
"service": "mysql",
"status": "active",
"instances": [
{
"name": "default",
"tags": {},
"credentials": {"host": "db.example.com", "database": "prod"},
}
],
}
],
)
record = get_integration("mysql")
assert record is not None
# v2 shape is still present
assert "instances" in record
# AND the flat credentials view for legacy callers
assert record["credentials"]["host"] == "db.example.com"
assert record["credentials"]["database"] == "prod"