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

215 lines
6.9 KiB
Python

from __future__ import annotations
import json
from surfaces.cli.wizard.store import (
delete_named_remote,
load_local_config,
load_named_remotes,
load_remote_ops_config,
load_remote_url,
save_local_config,
save_named_remote,
save_remote_ops_config,
update_local_llm_selection,
)
def test_save_local_config_writes_versioned_payload(tmp_path) -> None:
store_path = tmp_path / "opensre.json"
saved_path = save_local_config(
wizard_mode="quickstart",
provider="anthropic",
model="claude-opus-4-5",
api_key_env="ANTHROPIC_API_KEY",
model_env="ANTHROPIC_MODEL",
probes={
"local": {"target": "local", "reachable": True, "detail": "ok"},
"remote": {"target": "remote", "reachable": False, "detail": "down"},
},
path=store_path,
)
assert saved_path == store_path
payload = json.loads(store_path.read_text(encoding="utf-8"))
assert payload["version"] == 1
assert payload["wizard"]["mode"] == "quickstart"
assert payload["wizard"]["configured_target"] == "local"
assert payload["targets"]["local"]["provider"] == "anthropic"
assert payload["targets"]["local"]["model"] == "claude-opus-4-5"
assert "api_key" not in payload["targets"]["local"]
assert payload["probes"]["remote"]["reachable"] is False
def test_load_local_config_returns_independent_empty_payloads(tmp_path) -> None:
store_path = tmp_path / "opensre.json"
first = load_local_config(store_path)
first["targets"]["local"] = {"provider": "anthropic"}
second = load_local_config(store_path)
assert second["targets"] == {}
def test_update_local_llm_selection_merges_without_resetting_other_fields(tmp_path) -> None:
store_path = tmp_path / "opensre.json"
save_local_config(
wizard_mode="quickstart",
provider="anthropic",
model="claude-haiku",
api_key_env="ANTHROPIC_API_KEY",
model_env="ANTHROPIC_MODEL",
probes={"local": {"target": "local", "reachable": True, "detail": "ok"}},
path=store_path,
)
update_local_llm_selection(
provider="openai",
model="gpt-5-mini",
api_key_env="OPENAI_API_KEY",
model_env="OPENAI_REASONING_MODEL",
path=store_path,
)
payload = json.loads(store_path.read_text(encoding="utf-8"))
assert payload["wizard"]["mode"] == "quickstart"
assert payload["probes"]["local"]["reachable"] is True
assert payload["targets"]["local"]["provider"] == "openai"
assert payload["targets"]["local"]["model"] == "gpt-5-mini"
assert payload["targets"]["local"]["api_key_env"] == "OPENAI_API_KEY"
assert payload["targets"]["local"]["model_env"] == "OPENAI_REASONING_MODEL"
assert "updated_at" in payload["targets"]["local"]
def test_update_local_llm_selection_clears_api_key_env_for_cli_provider(tmp_path) -> None:
store_path = tmp_path / "opensre.json"
save_local_config(
wizard_mode="quickstart",
provider="anthropic",
model="claude-haiku",
api_key_env="ANTHROPIC_API_KEY",
model_env="ANTHROPIC_REASONING_MODEL",
probes={"local": {"target": "local", "reachable": True, "detail": "ok"}},
path=store_path,
)
update_local_llm_selection(
provider="claude-code",
model="",
api_key_env="",
model_env="CLAUDE_CODE_MODEL",
path=store_path,
)
payload = json.loads(store_path.read_text(encoding="utf-8"))
local = payload["targets"]["local"]
assert local["provider"] == "claude-code"
assert local["api_key_env"] == ""
assert local["model_env"] == "CLAUDE_CODE_MODEL"
def test_remote_ops_config_round_trip(tmp_path) -> None:
store_path = tmp_path / "opensre.json"
save_remote_ops_config(
provider="railway",
project="proj-a",
service="svc-a",
path=store_path,
)
loaded = load_remote_ops_config(store_path)
assert loaded == {"provider": "railway", "project": "proj-a", "service": "svc-a"}
def test_save_named_remote_persists_url(tmp_path) -> None:
store_path = tmp_path / "opensre.json"
save_named_remote("ec2", "http://1.2.3.4:8080", set_active=True, source="ec2", path=store_path)
assert load_remote_url(store_path) == "http://1.2.3.4:8080"
assert load_named_remotes(store_path) == {"ec2": "http://1.2.3.4:8080"}
def test_delete_named_remote_removes_entry_and_clears_active_url(tmp_path) -> None:
store_path = tmp_path / "opensre.json"
save_named_remote("ec2", "http://1.2.3.4:8080", set_active=True, source="ec2", path=store_path)
assert load_remote_url(store_path) == "http://1.2.3.4:8080"
delete_named_remote("ec2", store_path)
assert load_remote_url(store_path) is None
assert load_named_remotes(store_path) == {}
def test_delete_named_remote_does_not_clear_url_when_different_remote_is_active(
tmp_path,
) -> None:
store_path = tmp_path / "opensre.json"
save_named_remote("ec2", "http://1.2.3.4:8080", set_active=False, source="ec2", path=store_path)
save_named_remote(
"railway", "http://railway.app", set_active=True, source="railway", path=store_path
)
delete_named_remote("ec2", store_path)
assert load_remote_url(store_path) == "http://railway.app"
assert load_named_remotes(store_path) == {"railway": "http://railway.app"}
def test_delete_named_remote_is_noop_when_name_missing(tmp_path) -> None:
store_path = tmp_path / "opensre.json"
save_named_remote("ec2", "http://1.2.3.4:8080", set_active=True, source="ec2", path=store_path)
delete_named_remote("nonexistent", store_path)
assert load_remote_url(store_path) == "http://1.2.3.4:8080"
def test_deploy_destroy_lifecycle_updates_opensre_json(tmp_path) -> None:
"""Simulate deploy_remote + destroy_remote config-persistence without live AWS."""
store_path = tmp_path / "opensre.json"
stack_name = "opensre-ec2-remote"
remote_url = "http://54.0.0.1:8080"
# --- deploy ---
save_named_remote(stack_name, remote_url, set_active=True, source="ec2", path=store_path)
assert load_remote_url(store_path) == remote_url
remotes = load_named_remotes(store_path)
assert remotes[stack_name] == remote_url
raw = json.loads(store_path.read_text())
assert raw["remote"]["active_name"] == stack_name
# --- destroy ---
delete_named_remote(stack_name, store_path)
assert load_remote_url(store_path) is None
assert load_named_remotes(store_path) == {}
def test_remote_ops_config_clears_project_and_service(tmp_path) -> None:
store_path = tmp_path / "opensre.json"
save_remote_ops_config(
provider="railway",
project="proj-b",
service="svc-b",
path=store_path,
)
save_remote_ops_config(
provider="railway",
project=None,
service=None,
path=store_path,
)
loaded = load_remote_ops_config(store_path)
assert loaded == {"provider": "railway", "project": None, "service": None}