Import AITURK IDE 1.0.0-beta.1 from Hermes 63279301; preserve MIT license
This commit is contained in:
@@ -0,0 +1,204 @@
|
||||
"""Tests for the terminal environment provider registry + ABC.
|
||||
|
||||
Mirrors tests/agent/test_image_gen_registry.py in structure. Covers:
|
||||
- registration / re-registration / type & name validation
|
||||
- reserved built-in name rejection
|
||||
- scoped registrations (multiplexed-gateway profiles)
|
||||
- classification helpers (provider_flag, plugin_strip_env_keys)
|
||||
- restore_registration semantics (plugin unload)
|
||||
- fail-soft behavior for raising providers
|
||||
"""
|
||||
|
||||
import pytest
|
||||
|
||||
from agent.terminal_env_provider import TerminalEnvironmentProvider
|
||||
from agent import terminal_env_registry as reg
|
||||
|
||||
|
||||
class _Env:
|
||||
def execute(self, command, **kwargs):
|
||||
return {"output": "", "exit_code": 0}
|
||||
|
||||
def cleanup(self):
|
||||
pass
|
||||
|
||||
|
||||
class _Provider(TerminalEnvironmentProvider):
|
||||
name = "testbox"
|
||||
display_name = "TestBox"
|
||||
is_remote = True
|
||||
is_container = True
|
||||
session_isolated_when_nonpersistent = True
|
||||
|
||||
@property
|
||||
def cache_path_base(self):
|
||||
return "~/.hermes"
|
||||
|
||||
@property
|
||||
def strip_env_keys(self):
|
||||
return frozenset({"TESTBOX_TOKEN", "TESTBOX_SECRET"})
|
||||
|
||||
def is_available(self):
|
||||
return True
|
||||
|
||||
def create_environment(self, *, cwd, timeout, task_id="default",
|
||||
image=None, container_config=None, **kwargs):
|
||||
return _Env()
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def _clean_registry():
|
||||
reg._reset_for_tests()
|
||||
yield
|
||||
reg._reset_for_tests()
|
||||
|
||||
|
||||
def test_register_and_get():
|
||||
p = _Provider()
|
||||
reg.register_provider(p)
|
||||
assert reg.get_provider("testbox") is p
|
||||
assert reg.get_provider("TESTBOX") is p # case-insensitive lookup
|
||||
assert reg.plugin_backend_names() == ["testbox"]
|
||||
|
||||
|
||||
def test_rejects_non_provider():
|
||||
with pytest.raises(TypeError):
|
||||
reg.register_provider(object())
|
||||
|
||||
|
||||
def test_rejects_empty_name():
|
||||
class Bad(_Provider):
|
||||
name = " "
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
reg.register_provider(Bad())
|
||||
|
||||
|
||||
@pytest.mark.parametrize("reserved", sorted(reg.BUILTIN_BACKEND_NAMES))
|
||||
def test_rejects_builtin_names(reserved):
|
||||
class Shadow(_Provider):
|
||||
name = reserved
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
reg.register_provider(Shadow())
|
||||
assert reg.get_provider(reserved) is None
|
||||
|
||||
|
||||
def test_reregistration_overwrites():
|
||||
p1, p2 = _Provider(), _Provider()
|
||||
reg.register_provider(p1)
|
||||
reg.register_provider(p2)
|
||||
assert reg.get_provider("testbox") is p2
|
||||
|
||||
|
||||
def test_scoped_registration_isolated():
|
||||
p_global, p_scoped = _Provider(), _Provider()
|
||||
reg.register_provider(p_global)
|
||||
reg.register_provider(p_scoped, scope="profile-a")
|
||||
assert reg.get_provider("testbox", scope="profile-a") is p_scoped
|
||||
# Different scope falls back to global
|
||||
assert reg.get_provider("testbox", scope="profile-b") is p_global
|
||||
|
||||
|
||||
def test_provider_flag_reads_attributes():
|
||||
reg.register_provider(_Provider())
|
||||
assert reg.provider_flag("testbox", "is_remote") is True
|
||||
assert reg.provider_flag("testbox", "is_container") is True
|
||||
assert reg.provider_flag("testbox", "session_isolated_when_nonpersistent") is True
|
||||
assert reg.provider_flag("testbox", "cache_path_base", None) == "~/.hermes"
|
||||
assert reg.provider_flag("testbox", "skip_container_guards") is True
|
||||
|
||||
|
||||
def test_provider_flag_unknown_backend_returns_default():
|
||||
assert reg.provider_flag("missing", "is_remote", False) is False
|
||||
assert reg.provider_flag("missing", "cache_path_base", None) is None
|
||||
|
||||
|
||||
def test_provider_flag_fail_soft_on_raising_property():
|
||||
class Broken(_Provider):
|
||||
name = "broken"
|
||||
|
||||
@property
|
||||
def cache_path_base(self):
|
||||
raise RuntimeError("boom")
|
||||
|
||||
reg.register_provider(Broken())
|
||||
assert reg.provider_flag("broken", "cache_path_base", None) is None
|
||||
|
||||
|
||||
def test_plugin_strip_env_keys_union():
|
||||
class Other(_Provider):
|
||||
name = "other"
|
||||
|
||||
@property
|
||||
def strip_env_keys(self):
|
||||
return frozenset({"OTHER_KEY"})
|
||||
|
||||
reg.register_provider(_Provider())
|
||||
reg.register_provider(Other())
|
||||
keys = reg.plugin_strip_env_keys()
|
||||
assert keys == frozenset({"TESTBOX_TOKEN", "TESTBOX_SECRET", "OTHER_KEY"})
|
||||
|
||||
|
||||
def test_plugin_strip_env_keys_fail_soft():
|
||||
class Broken(_Provider):
|
||||
name = "broken"
|
||||
|
||||
@property
|
||||
def strip_env_keys(self):
|
||||
raise RuntimeError("boom")
|
||||
|
||||
reg.register_provider(Broken())
|
||||
assert reg.plugin_strip_env_keys() == frozenset()
|
||||
|
||||
|
||||
def test_restore_registration_unregisters():
|
||||
p = _Provider()
|
||||
reg.register_provider(p)
|
||||
assert reg.restore_registration("testbox", p, None) is True
|
||||
assert reg.get_provider("testbox") is None
|
||||
|
||||
|
||||
def test_restore_registration_noop_when_replaced():
|
||||
p1, p2 = _Provider(), _Provider()
|
||||
reg.register_provider(p1)
|
||||
reg.register_provider(p2) # p1 replaced
|
||||
assert reg.restore_registration("testbox", p1, None) is False
|
||||
assert reg.get_provider("testbox") is p2
|
||||
|
||||
|
||||
def test_restore_registration_restores_previous():
|
||||
p1, p2 = _Provider(), _Provider()
|
||||
reg.register_provider(p1)
|
||||
reg.register_provider(p2)
|
||||
assert reg.restore_registration("testbox", p2, p1) is True
|
||||
assert reg.get_provider("testbox") is p1
|
||||
|
||||
|
||||
def test_abc_defaults():
|
||||
p = _Provider()
|
||||
assert p.skip_container_guards is True # defaults to is_container
|
||||
assert "TestBox" in p.env_description
|
||||
assert p.probe() == ("ready", "")
|
||||
assert p.setup_instructions() == []
|
||||
rows = p.doctor_checks()
|
||||
assert rows and rows[0][0] is True
|
||||
|
||||
|
||||
def test_probe_needs_setup_when_unavailable():
|
||||
class Off(_Provider):
|
||||
name = "offbox"
|
||||
|
||||
def is_available(self):
|
||||
return False
|
||||
|
||||
status, detail = Off().probe()
|
||||
assert status == "needs_setup"
|
||||
assert detail
|
||||
|
||||
|
||||
def test_registry_generation_bumps():
|
||||
g0 = reg.registry_generation()
|
||||
reg.register_provider(_Provider())
|
||||
g1 = reg.registry_generation()
|
||||
assert g1 != g0
|
||||
Reference in New Issue
Block a user