Split the three oversized test files into support modules and topic files, evaluate the rmdir dir_fd capability once at import, and drop unused imports. Claude-Session: https://claude.ai/code/session_01Agoc9bRVE9KL7mmWtey9Vq
395 lines
13 KiB
Python
395 lines
13 KiB
Python
from __future__ import annotations
|
|
|
|
import json
|
|
import os
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
from elydora import cli
|
|
from elydora.plugins import letta
|
|
from elydora.plugins.registry import SUPPORTED_AGENTS
|
|
from letta_support import (
|
|
AGENT_ID,
|
|
VALID_PRIVATE_KEY,
|
|
assert_managed_triple,
|
|
legacy_group,
|
|
prepare_fixture,
|
|
run_cli,
|
|
write_json,
|
|
write_text,
|
|
)
|
|
|
|
|
|
def test_letta_is_registered_in_the_sdk_and_cli() -> None:
|
|
assert SUPPORTED_AGENTS["letta"] == {
|
|
"name": "Letta Code",
|
|
"hook_event": "PreToolUse/PostToolUse/PostToolUseFailure",
|
|
"config_path": "~/.letta/settings.json",
|
|
}
|
|
assert cli.PLUGIN_MAP["letta"] is letta.LettaPlugin
|
|
|
|
|
|
def test_install_preserves_every_source_and_is_idempotent(
|
|
monkeypatch: pytest.MonkeyPatch,
|
|
tmp_path: Path,
|
|
capsys: pytest.CaptureFixture[str],
|
|
) -> None:
|
|
global_source = (
|
|
"{\r\n"
|
|
' "theme": "dark",\r\n'
|
|
' "hooks": {\r\n'
|
|
' "FutureEvent": [null],\r\n'
|
|
' "PreToolUse": [{ "matcher": "Bash", "hooks": '
|
|
'[{ "type": "command", "command": "user-hook", "quiet": true }] }]\r\n'
|
|
" }\r\n"
|
|
"}\r\n"
|
|
)
|
|
fixture = prepare_fixture(
|
|
monkeypatch,
|
|
tmp_path,
|
|
global_settings=global_source,
|
|
project_settings={"owner": "project"},
|
|
local_settings={"owner": "local"},
|
|
)
|
|
project_before = fixture.source(fixture.project_path)
|
|
local_before = fixture.source(fixture.local_path)
|
|
|
|
fixture.install()
|
|
first_source = fixture.source()
|
|
settings = fixture.settings()
|
|
assert "\r\n" in first_source
|
|
assert settings["theme"] == "dark"
|
|
assert settings["hooks"]["FutureEvent"] == [None]
|
|
assert settings["hooks"]["PreToolUse"][0]["hooks"][0]["command"] == "user-hook"
|
|
assert_managed_triple(settings)
|
|
assert fixture.source(fixture.project_path) == project_before
|
|
assert fixture.source(fixture.local_path) == local_before
|
|
for file_path in (
|
|
fixture.guard_path,
|
|
fixture.audit_path,
|
|
fixture.runtime_config_path,
|
|
fixture.private_key_path,
|
|
):
|
|
assert file_path.is_file()
|
|
|
|
fixture.install()
|
|
assert fixture.source() == first_source
|
|
output = capsys.readouterr().out
|
|
assert "run /hooks" in output
|
|
assert "restart active sessions" in output
|
|
|
|
|
|
def test_letta_validates_known_schemas_and_preserves_future_events(
|
|
monkeypatch: pytest.MonkeyPatch, tmp_path: Path
|
|
) -> None:
|
|
fixture = prepare_fixture(monkeypatch, tmp_path, global_settings={
|
|
"hooks": {
|
|
"FutureEvent": [None],
|
|
"PreToolUse": [{
|
|
"matcher": "[",
|
|
"hooks": [{
|
|
"type": "command",
|
|
"command": "user-command",
|
|
"timeout": 0,
|
|
"quiet": False,
|
|
"futureField": True,
|
|
}],
|
|
}],
|
|
"Stop": [{
|
|
"hooks": [{
|
|
"type": "prompt",
|
|
"prompt": "Evaluate $ARGUMENTS",
|
|
"model": "fast",
|
|
"timeout": 30,
|
|
}],
|
|
}],
|
|
},
|
|
})
|
|
fixture.install()
|
|
settings = fixture.settings()
|
|
assert settings["hooks"]["FutureEvent"] == [None]
|
|
assert settings["hooks"]["PreToolUse"][0]["matcher"] == "["
|
|
assert settings["hooks"]["PreToolUse"][0]["hooks"][0]["futureField"] is True
|
|
assert settings["hooks"]["Stop"][0]["hooks"][0]["type"] == "prompt"
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"source",
|
|
[
|
|
"{ malformed",
|
|
"[]",
|
|
'{ "theme": true, }',
|
|
'{ "theme": true // comment\n }',
|
|
'{ "hooks": {}, "hooks": {} }',
|
|
'{ "hooks": null }',
|
|
'{ "hooks": [] }',
|
|
'{ "hooks": { "disabled": "yes" } }',
|
|
'{ "hooks": { "PreToolUse": null } }',
|
|
'{ "hooks": { "PreToolUse": [null] } }',
|
|
'{ "hooks": { "PreToolUse": [{ "hooks": [] }] } }',
|
|
'{ "hooks": { "Stop": [{ "matcher": "*", "hooks": [] }] } }',
|
|
'{ "hooks": { "PreToolUse": [{ "matcher": "*" }] } }',
|
|
'{ "hooks": { "Stop": [{ "hooks": [null] }] } }',
|
|
'{ "hooks": { "Stop": [{ "hooks": [{ "type": "http" }] }] } }',
|
|
'{ "hooks": { "PreToolUse": [{ "matcher": "*", "hooks": [{ "type": "command" }] }] } }',
|
|
'{ "hooks": { "Stop": [{ "hooks": [{ "type": "prompt" }] }] } }',
|
|
'{ "hooks": { "Stop": [{ "hooks": [{ "type": "prompt", "prompt": "x", "model": 1 }] }] } }',
|
|
'{ "hooks": { "Stop": [{ "hooks": [{ "type": "command", "command": "x", "quiet": 1 }] }] } }',
|
|
'{ "hooks": { "Stop": [{ "hooks": [{ "type": "command", "command": "x", "timeout": -1 }] }] } }',
|
|
],
|
|
)
|
|
def test_install_rejects_malformed_global_settings_before_every_write(
|
|
monkeypatch: pytest.MonkeyPatch, tmp_path: Path, source: str
|
|
) -> None:
|
|
fixture = prepare_fixture(
|
|
monkeypatch, tmp_path, global_settings=source
|
|
)
|
|
with pytest.raises((OSError, ValueError), match="Letta Code"):
|
|
fixture.install()
|
|
assert fixture.source() == source
|
|
assert not fixture.agent_dir.exists()
|
|
|
|
|
|
@pytest.mark.parametrize("kind", ["project", "local"])
|
|
def test_install_surfaces_malformed_read_only_sources(
|
|
monkeypatch: pytest.MonkeyPatch, tmp_path: Path, kind: str
|
|
) -> None:
|
|
options = (
|
|
{"project_settings": "{ malformed"}
|
|
if kind == "project"
|
|
else {"local_settings": "{ malformed"}
|
|
)
|
|
fixture = prepare_fixture(monkeypatch, tmp_path, **options)
|
|
label = "project-local" if kind == "local" else kind
|
|
with pytest.raises(ValueError, match=f"Letta Code {label}"):
|
|
fixture.install()
|
|
assert not fixture.global_path.exists()
|
|
assert not fixture.agent_dir.exists()
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
("global_settings", "project_settings", "local_settings", "enabled"),
|
|
[
|
|
({"hooks": {"disabled": True}}, {}, {}, False),
|
|
({}, {"hooks": {"disabled": True}}, {}, False),
|
|
({}, {}, {"hooks": {"disabled": True}}, False),
|
|
(
|
|
{"hooks": {"disabled": False}},
|
|
{"hooks": {"disabled": True}},
|
|
{"hooks": {"disabled": True}},
|
|
True,
|
|
),
|
|
],
|
|
)
|
|
def test_hooks_disabled_uses_official_precedence(
|
|
monkeypatch: pytest.MonkeyPatch,
|
|
tmp_path: Path,
|
|
global_settings: object,
|
|
project_settings: object,
|
|
local_settings: object,
|
|
enabled: bool,
|
|
) -> None:
|
|
fixture = prepare_fixture(
|
|
monkeypatch,
|
|
tmp_path,
|
|
global_settings=global_settings,
|
|
project_settings=project_settings,
|
|
local_settings=local_settings,
|
|
)
|
|
if enabled:
|
|
fixture.install()
|
|
assert_managed_triple(fixture.settings())
|
|
else:
|
|
with pytest.raises(ValueError, match="hooks.disabled"):
|
|
fixture.install()
|
|
assert not fixture.agent_dir.exists()
|
|
|
|
|
|
def test_install_migrates_exact_legacy_and_preserves_lookalikes(
|
|
monkeypatch: pytest.MonkeyPatch, tmp_path: Path
|
|
) -> None:
|
|
fixture = prepare_fixture(monkeypatch, tmp_path)
|
|
guard_legacy = legacy_group(fixture.guard_path, "PreToolUse")
|
|
guard_lookalike = legacy_group(fixture.guard_path, "PreToolUse")
|
|
guard_lookalike["hooks"][0]["quiet"] = True
|
|
audit_legacy = legacy_group(fixture.audit_path, "PostToolUse")
|
|
write_json(fixture.global_path, {
|
|
"owner": "user",
|
|
"hooks": {
|
|
"PreToolUse": [guard_legacy, guard_lookalike],
|
|
"PostToolUse": [audit_legacy],
|
|
},
|
|
})
|
|
fixture.install()
|
|
settings = fixture.settings()
|
|
assert_managed_triple(settings)
|
|
assert any(
|
|
group["hooks"][0].get("quiet") is True
|
|
for group in settings["hooks"]["PreToolUse"]
|
|
)
|
|
managed = next(
|
|
group for group in settings["hooks"]["PreToolUse"]
|
|
if group["hooks"][0].get("timeout") == 10_000
|
|
)
|
|
managed["userField"] = "preserve-group"
|
|
managed["hooks"].append({"type": "command", "command": "user-command"})
|
|
write_json(fixture.global_path, settings)
|
|
|
|
fixture.plugin.uninstall(AGENT_ID)
|
|
remaining = fixture.settings()
|
|
assert remaining["owner"] == "user"
|
|
assert any(
|
|
group["hooks"][0].get("quiet") is True
|
|
for group in remaining["hooks"]["PreToolUse"]
|
|
)
|
|
assert any(
|
|
handler.get("command") == "user-command"
|
|
for group in remaining["hooks"]["PreToolUse"]
|
|
for handler in group["hooks"]
|
|
)
|
|
assert "PostToolUse" not in remaining["hooks"]
|
|
assert "PostToolUseFailure" not in remaining["hooks"]
|
|
|
|
|
|
def test_uninstall_removes_owned_file_and_preserves_user_settings(
|
|
monkeypatch: pytest.MonkeyPatch, tmp_path: Path
|
|
) -> None:
|
|
user = prepare_fixture(
|
|
monkeypatch,
|
|
tmp_path / "user",
|
|
global_settings={"theme": "dark", "hooks": {"Notification": []}},
|
|
)
|
|
user.install()
|
|
user.plugin.uninstall(AGENT_ID)
|
|
assert user.settings() == {
|
|
"theme": "dark",
|
|
"hooks": {"Notification": []},
|
|
}
|
|
|
|
owned = prepare_fixture(monkeypatch, tmp_path / "owned")
|
|
owned.install()
|
|
owned.plugin.uninstall(AGENT_ID)
|
|
assert not owned.global_path.exists()
|
|
|
|
|
|
def test_status_requires_effective_contract_and_strict_runtime_identity(
|
|
monkeypatch: pytest.MonkeyPatch, tmp_path: Path
|
|
) -> None:
|
|
fixture = prepare_fixture(monkeypatch, tmp_path, project_settings={})
|
|
fixture.install()
|
|
assert fixture.plugin.status()["installed"] is True
|
|
write_json(fixture.project_path, {"hooks": {"disabled": True}})
|
|
assert fixture.plugin.status()["installed"] is False
|
|
write_json(fixture.project_path, {})
|
|
settings = fixture.settings()
|
|
settings["hooks"]["PostToolUseFailure"].append(
|
|
settings["hooks"]["PostToolUseFailure"][-1]
|
|
)
|
|
write_json(fixture.global_path, settings)
|
|
assert fixture.plugin.status()["installed"] is False
|
|
|
|
fixture.install()
|
|
write_text(fixture.private_key_path, "invalid")
|
|
with pytest.raises(ValueError, match="private key"):
|
|
fixture.plugin.status()
|
|
fixture.install()
|
|
write_text(fixture.audit_path, "tampered")
|
|
assert fixture.plugin.status()["installed"] is False
|
|
fixture.install()
|
|
runtime = json.loads(fixture.runtime_config_path.read_text())
|
|
runtime["agent_name"] = "other"
|
|
write_json(fixture.runtime_config_path, runtime)
|
|
with pytest.raises(ValueError, match="runtime identity"):
|
|
fixture.plugin.status()
|
|
|
|
|
|
def test_preflight_rejects_unverifiable_runtime_and_invalid_agent_id(
|
|
monkeypatch: pytest.MonkeyPatch, tmp_path: Path
|
|
) -> None:
|
|
identity = prepare_fixture(monkeypatch, tmp_path / "identity")
|
|
write_text(identity.audit_path, "unverified")
|
|
with pytest.raises(ValueError, match="without config.json"):
|
|
identity.install()
|
|
assert not identity.global_path.exists()
|
|
|
|
invalid = prepare_fixture(monkeypatch, tmp_path / "invalid")
|
|
invalid.config["agent_id"] = "../escape"
|
|
with pytest.raises(ValueError, match="Invalid agent ID"):
|
|
invalid.install()
|
|
assert not invalid.global_path.exists()
|
|
|
|
|
|
def test_letta_cli_install_status_and_uninstall_end_to_end(
|
|
monkeypatch: pytest.MonkeyPatch, tmp_path: Path
|
|
) -> None:
|
|
fixture = prepare_fixture(
|
|
monkeypatch, tmp_path, global_settings={"theme": "dark"}
|
|
)
|
|
key_file = tmp_path / "install-private.key"
|
|
token_file = tmp_path / "install-token.txt"
|
|
write_text(key_file, VALID_PRIVATE_KEY + "\n")
|
|
write_text(token_file, "token-1\n")
|
|
install = run_cli(fixture, [
|
|
"install",
|
|
"--agent", "letta",
|
|
"--org_id", "org-1",
|
|
"--agent_id", AGENT_ID,
|
|
"--kid", "kid-1",
|
|
"--private_key_file", str(key_file),
|
|
"--token_file", str(token_file),
|
|
"--base_url", "http://127.0.0.1:9",
|
|
])
|
|
assert install.returncode == 0, install.stderr
|
|
assert_managed_triple(fixture.settings())
|
|
status = run_cli(fixture, ["status"])
|
|
assert status.returncode == 0, status.stderr
|
|
assert "Letta Code" in status.stdout and "[installed]" in status.stdout
|
|
uninstall = run_cli(fixture, [
|
|
"uninstall", "--agent", "letta", "--agent_id", AGENT_ID
|
|
])
|
|
assert uninstall.returncode == 0, uninstall.stderr
|
|
assert fixture.settings() == {"theme": "dark"}
|
|
assert not fixture.agent_dir.exists()
|
|
|
|
|
|
@pytest.mark.skipif(
|
|
os.name != "nt", reason="Windows resolves HOME and USERPROFILE separately"
|
|
)
|
|
def test_windows_cli_separates_letta_home_from_runtime_home(
|
|
monkeypatch: pytest.MonkeyPatch, tmp_path: Path
|
|
) -> None:
|
|
fixture = prepare_fixture(
|
|
monkeypatch, tmp_path, global_settings={"theme": "dark"}
|
|
)
|
|
runtime_home = tmp_path / "windows profile"
|
|
runtime_agent_dir = runtime_home / ".elydora" / AGENT_ID
|
|
monkeypatch.setenv("USERPROFILE", str(runtime_home))
|
|
key_file = tmp_path / "install-private.key"
|
|
write_text(key_file, VALID_PRIVATE_KEY + "\n")
|
|
|
|
install = run_cli(fixture, [
|
|
"install",
|
|
"--agent", "letta",
|
|
"--org_id", "org-1",
|
|
"--agent_id", AGENT_ID,
|
|
"--kid", "kid-1",
|
|
"--private_key_file", str(key_file),
|
|
"--base_url", "http://127.0.0.1:9",
|
|
])
|
|
assert install.returncode == 0, install.stderr
|
|
assert_managed_triple(fixture.settings())
|
|
assert (runtime_agent_dir / "guard.py").is_file()
|
|
assert (runtime_agent_dir / "hook.py").is_file()
|
|
|
|
status = run_cli(fixture, ["status"])
|
|
assert status.returncode == 0, status.stderr
|
|
assert "Letta Code" in status.stdout and "[installed]" in status.stdout
|
|
|
|
uninstall = run_cli(fixture, [
|
|
"uninstall", "--agent", "letta", "--agent_id", AGENT_ID
|
|
])
|
|
assert uninstall.returncode == 0, uninstall.stderr
|
|
assert fixture.settings() == {"theme": "dark"}
|
|
assert not runtime_agent_dir.exists()
|