ReMe/tests/integration/test_codex_plugin_install.py
Sen Huang 749c653f23
feat(integrations): add automatic memory plugins for Claude Code and Codex (#575)
* feat(integrations): add native Claude Code and Codex memory plugins

* docs(integrations): make plugin installation the coding agent entry point

* feat(integrations): configure plugin connections from user settings

* refactor(codex): use MCP tools and native plugin settings

* fix(codex): restore plugin hooks on current runtimes

* docs(codex): expand setup guide and reserve screenshot slots

* feat(codex): add configurable memory consolidation and status

* feat(codex): merge connection checks into a tabbed status app

* docs(codex): diagnose blocked status panel sandbox loading

* fix(codex): simplify status panel layout for narrow cards

* docs(codex): focus guides on connecting and using ReMe

* docs(codex): specify validated CLI and desktop version requirements

* feat(codex): add ReMe branding to plugin icons

* docs(codex): prepare manual screenshot workflow

* fix(codex): simplify ReMe status layout and activity

* docs(codex): explain Hook loading and local project context

* fix(codex): keep status refresh clear of host close control

* fix(codex): retain status resources for active MCP connections

* docs(codex): add real desktop screenshots to usage guides

* fix(integrations): address memory capture and consolidation review

* fix(codex): preserve capture endpoint during background delivery
2026-10-12 20:12:47 +08:00

102 lines
3.7 KiB
Python

"""Opt-in native plugin discovery regression, without model calls or hook execution.
Run with REME_CODEX_BIN=/path/to/codex pytest tests/integration/test_codex_plugin_install.py.
The selected runtime must support the hooks/list app-server API.
"""
# pylint: disable=missing-function-docstring
import json
import os
import queue
import subprocess
import threading
from pathlib import Path
import pytest
MARKETPLACE = Path(__file__).resolve().parents[2] / "integrations/codex"
EVENTS = {"sessionStart", "sessionEnd", "userPromptSubmit", "stop", "subagentStop"}
def test_installed_plugin_exposes_all_hooks_before_trust(tmp_path):
binary = os.environ.get("REME_CODEX_BIN")
if not binary:
pytest.skip("Set REME_CODEX_BIN to run the native Codex installation check")
home = tmp_path / "codex-home"
home.mkdir()
env = {**os.environ, "CODEX_HOME": str(home)}
for args in (
["plugin", "marketplace", "add", str(MARKETPLACE)],
["plugin", "add", "reme@reme-codex"],
):
result = subprocess.run([binary, *args], env=env, capture_output=True, text=True, check=False, timeout=60)
assert result.returncode == 0, result.stderr
with (
(tmp_path / "app-server.log").open("w", encoding="utf-8") as stderr,
subprocess.Popen(
[binary, "app-server"],
env=env,
cwd=tmp_path,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=stderr,
text=True,
) as process,
):
responses = queue.Queue()
def receive():
for line in process.stdout:
responses.put(json.loads(line))
reader = threading.Thread(target=receive, daemon=True)
reader.start()
def request(request_id, method, params):
process.stdin.write(json.dumps({"id": request_id, "method": method, "params": params}) + "\n")
process.stdin.flush()
while True:
response = responses.get(timeout=30)
if response.get("id") == request_id:
assert "error" not in response, response
return response["result"]
try:
request(
0,
"initialize",
{
"clientInfo": {"name": "reme-install-test", "version": "1.0"},
"capabilities": {"experimentalApi": True},
},
)
process.stdin.write(json.dumps({"method": "initialized", "params": {}}) + "\n")
process.stdin.flush()
details = request(
1,
"plugin/read",
{"marketplacePath": str(MARKETPLACE / ".agents/plugins/marketplace.json"), "pluginName": "reme"},
)["plugin"]
assert details["summary"]["installed"] and details["summary"]["enabled"]
assert details["mcpServers"] == ["reme"]
assert {hook["eventName"] for hook in details["hooks"]} == EVENTS
entries = request(2, "hooks/list", {"cwds": [str(tmp_path)]})["data"]
assert len(entries) == 1
assert not entries[0]["errors"]
hooks = [hook for hook in entries[0]["hooks"] if hook.get("pluginId") == "reme@reme-codex"]
assert len(hooks) == 7
assert {hook["eventName"] for hook in hooks} == EVENTS
assert all(hook["enabled"] and hook["trustStatus"] == "untrusted" for hook in hooks)
finally:
process.stdin.close()
process.terminate()
try:
process.wait(timeout=10)
except subprocess.TimeoutExpired:
process.kill()
process.wait(timeout=10)
reader.join(timeout=5)
process.stdout.close()