ReMe/reme2/mcp/test/__main__.py
huangsen 6a5561f86a feat: add file_graph component and integrate with LinkedFileParser
- Add file_graph import to component registry
- Register FILE_GRAPH enum in ComponentEnum
- Implement BaseFileGraph integration in LinkedFileParser
- Replace FileEdge with FileLink for better semantic clarity
- Add lazy resolution of file_graph from app_context
- Update file watcher logging to reflect links instead of edges

refactor: streamline MCP transport layer architecture

- Remove redundant step shells from reme2/mcp/steps/
- Consolidate all @R.register components to reme2.memory package
- Update server.py to import reme2.memory directly
- Revise README.md to document new architecture
- Simplify module dependencies and import structure
2026-05-14 11:35:02 +08:00

76 lines
2.4 KiB
Python

"""CLI runner: `python -m reme2.mcp.test`
Boots one Application per profile against a fresh temp vault, runs the
profile's CHECKS list in order, prints a per-check status line plus a
final summary. Exit code is the number of failed checks across both
profiles.
Per-profile suites are intentionally sequential — later checks rely on
side-effects from earlier ones (e.g. `sync.append` after `sync.create`).
Cross-profile work happens in independent vaults.
"""
from __future__ import annotations
import asyncio
import shutil
import sys
import traceback
from . import test_expert, test_service
from ._helpers import AppContext, make_context
SUITES = {
"expert": test_expert.CHECKS,
"service": test_service.CHECKS,
}
async def _run_suite(profile: str, checks: list[tuple[str, callable]]) -> tuple[int, int]:
"""Run one suite. Returns (passed, total)."""
print(f"\n========== profile: {profile} ==========")
ctx: AppContext | None = None
tmp = None
passed = 0
try:
ctx, tmp = await make_context(profile)
print(f" bootstrapped: vault={ctx.vault}, jobs={len(ctx.jobs)}, " f"file_store={len(ctx.file_store)}")
for label, fn in checks:
try:
summary = await fn(ctx)
print(f" ✓ {label:32s} {summary}")
passed += 1
except Exception as e:
print(f" ✗ {label:32s} {type(e).__name__}: {e}")
traceback.print_exc()
except Exception as e:
print(f" ✗ bootstrap failed: {type(e).__name__}: {e}")
traceback.print_exc()
finally:
if ctx is not None:
try:
await ctx.app.close()
except Exception as e: # noqa: BLE001
print(f" (warn) app.close failed: {e}")
if tmp is not None:
shutil.rmtree(tmp, ignore_errors=True)
return passed, len(checks)
async def _main() -> int:
summary: dict[str, tuple[int, int]] = {}
for profile, checks in SUITES.items():
summary[profile] = await _run_suite(profile, checks)
print("\n========== summary ==========")
failed = 0
for profile, (passed, total) in summary.items():
marker = "✓" if passed == total else "✗"
print(f" {marker} {profile}: {passed}/{total}")
failed += total - passed
return 0 if failed == 0 else failed
if __name__ == "__main__":
sys.exit(asyncio.run(_main()))