Files
Elydora-Python-SDK/tests/test_letta_plugin.py
T
雷电芽衣 aa4b8a8bdf chore(python): audit tests
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
2026-09-03 00:30:17 -04:00

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()