Files
hermes-agent/tests/hermes_cli/test_pool_missing_endpoint.py
ethernet f3b1399211 fix: round-5 CI backlog after the 779-commit main merge
Merge fallout (my resolution errors, all caught by CI):
- hermes_cli/backup.py + gateway.py: `theirs` on those hunks re-imported clusters HEAD had
  already moved to backup_restore.py / kept in the facade. backup.py loses the 349-line
  duplicate (main's #110179 fix is ported into backup_restore._import_db_member); the
  systemd service-unit cluster returns to gateway.py (PM's _prepare_service_launcher /
  _pm_managed_node_dirs / _systemd_command have no home in main's extraction) with main's
  utf-8-sig read. gateway_service_unit.py is dropped.
- gateway/run.py: main's plugin-update chore is not profile-scoped (the housekeeping
  ordering test pins the scope/drain sequence).
- pyproject + 30 test files: `import yaml` -> `import hermes_yaml as yaml` (pm-clean has no
  pyyaml); gateway/config._bundled_platform_manifest_name reads through hermes_yaml.
- tests re-seamed onto pm-clean's shape: residency admission (installed_engine),
  supervisor child env (binary is a constructor argument), update import guard
  (update_cmd_deps is gone; our probe already scrubs PYTHONPATH — both #115032 invariants
  pass), shallow-count git responses (stash path asks `status --porcelain -z`); dropped
  tests for retired code (_run_node_bootstrap/_ensure_tui_node, Windows resume demotion).
- tests/tools/test_local_env_blocklist.py: restore the two helpers the suite-reduction
  commit dropped and the blocklist import.

Real fixes:
- pm: classify_uv_failure/ResolutionConflict move beside the uv runner (pm.environment,
  stdlib-only). pm.workspace imports tomllib at module level and cannot load on the 3.10
  bootstrap python that streams uv output in the Docker arm64 image.
- tools/browser_tool.warm_agent_browser_npx_cache: back as a permanent definition — it is on
  the frozen old-updater surface, and the revert-scheduled compat pointer does not count.
- hermes_cli/memory_setup: the dashboard's pip row uses pm.environments.
  running_from_selected_environment for installed vs restart_required.
- scripts/windows-build-deps.ps1: export DISTUTILS_USE_SDK/MSSdk so setuptools trusts the
  primed MSVC environment instead of asking vswhere (`env -i` test runner on win32-arm64
  compiling ruamel-yaml-clib); run_tests.sh forwards them.
- tests/pm/test_windows_build_deps.py: start the protocol test from a parent env without the
  toolchain variables the runner job already exports.
- tests/conftest.py scrubs HERMES_BUNDLED_PLUGINS (Nix-wrapped hermes on the dev host);
  tests/home_io_guard.py treats sys.path site-packages under the real home as the
  interpreter's installation (PM-activated developer shell).
- tests-js: four `curly` lint errors from main's new scripts.
2026-09-21 02:47:48 -04:00

59 lines
2.6 KiB
Python

"""Endpoint-less manual credentials resolve through the public runtime ladder."""
import pytest
import hermes_yaml as yaml
from hermes_cli.auth import PROVIDER_REGISTRY, write_credential_pool
from hermes_cli.runtime_provider import resolve_runtime_provider
from hermes_constants import get_hermes_home
KEY = "test-only-not-a-real-provider-key"
def _store_entry(provider, **fields):
write_credential_pool(provider, [{
"id": "test-no-endpoint", "source": "manual", "auth_type": "api_key",
"access_token": KEY, "priority": 0, **fields,
}])
@pytest.mark.parametrize("endpoint", [None, "", "default"])
@pytest.mark.parametrize("requested", ["opencode", "opencode-zen"])
def test_missing_pool_endpoint_uses_provider_default(endpoint, requested):
default = PROVIDER_REGISTRY["opencode-zen"].inference_base_url
fields = {} if endpoint is None else {"base_url": default if endpoint == "default" else endpoint}
_store_entry("opencode-zen", **fields)
runtime = resolve_runtime_provider(requested=requested, target_model="glm-5.3")
assert runtime["base_url"] == default
assert runtime["api_key"] == KEY
assert runtime["source"] == "manual"
assert runtime["api_mode"] == "chat_completions"
@pytest.mark.parametrize("entry_url", [None, "default", "https://entry.example/v1"])
@pytest.mark.parametrize("config_provider", ["deepseek", "openai"])
def test_pool_endpoint_preserves_entry_and_provider_config_boundaries(entry_url, config_provider):
default = PROVIDER_REGISTRY["deepseek"].inference_base_url
config_url = "https://config.example/v1"
(get_hermes_home() / "config.yaml").write_text(yaml.safe_dump({
"model": {"provider": config_provider, "base_url": config_url},
}), encoding="utf-8")
fields = {} if entry_url is None else {"base_url": default if entry_url == "default" else entry_url}
_store_entry("deepseek", **fields)
runtime = resolve_runtime_provider(requested="deepseek", target_model="deepseek-chat")
expected = entry_url if entry_url and entry_url != "default" else (
config_url if config_provider == "deepseek" else default
)
assert runtime["base_url"] == expected
assert runtime["api_key"] == KEY
assert runtime["source"] == "manual"
def test_missing_endpoint_still_finalizes_anthropic_wire():
_store_entry("opencode-zen")
runtime = resolve_runtime_provider(requested="opencode-zen", target_model="claude-sonnet-4-6")
default = PROVIDER_REGISTRY["opencode-zen"].inference_base_url
assert runtime["api_mode"] == "anthropic_messages"
assert runtime["base_url"] == default.removesuffix("/v1")
assert runtime["api_key"] == KEY