AGENTS.md@mcps/unreal_mcp/tests/unit/plugin · git:20261104.ed35fa4 · 2026-11-04 · sha256 06896ea45c03f487
AGENTS.md@mcps/unreal_mcp/tests/unit/plugin git:20261104.ed35fa4A
Immutable. This exact content never changes and is served at /api/v1/blob/06896ea45c03f487.
# PLUGIN SOURCE-CONTRACT TESTS Vitest tests that read C++/C# **source text** and assert required or forbidden patterns. They never compile Unreal and run in CI (`npm run test:unit`). Only structural rules and security wiring are gated here; implementation spelling is not. ## WHERE TO LOOK | Contract | Test file | What it enforces | |----------|-----------|-------------------| | 250 pure-line ceiling, no split artifacts, local includes resolve | `source_structure_contracts.test.ts` | Every plugin `.cpp/.cs/.h` ≤ 250 **pure** lines (non-blank, non-`#`/`//`); rejects `Common.*`, `Part\d+`, `.incl`; every `#include "Mcp..."` resolves | | ≤25 source files per folder | `source_structure.test.ts` | Any folder under `Private/`. At cap (25, counted 2026-10-26): `MCP/Gateway`, `MCP/Transport`, `MCP/Execute`, `Foundation`, `Domains/ControlEditor`, `Domains/ControlActor`, `Domains/Environment/Inspection`, `Domains/BlueprintGraph`, `Domains/ControlActor/Placement` (2026-10-27); at 24: `MCP/Generated`, `Tests`, `Domains/Sequence`, `Domains/LevelStructure`, `Domains/AssetWorkflow/Operations`, `Domains/AnimationAuthoring` | | Includes are self-sufficient | `source_include_self_sufficiency.test.ts` | Users of shared JSON helpers, the asset registry reads and the path refusal wording include a header that declares them (no unity-blob borrowing) | | Token, bind, socket, SavePackage | `security_contracts.test.ts` | Constant-time token compare on both transports; non-loopback bind refused without `RequireCapabilityToken`; replies never reach an unrelated socket; import sources stay inside the project; no `UPackage::SavePackage` outside the Safety wrappers | | Pre-queue gate | `prequeue-gate-contracts.test.ts` | Both transports authorize before enqueueing; the gate and the dispatcher resolve the same action | | Browser-origin WS upgrade | `websocket_origin_contracts.test.ts` | Origin rejected before the 101 (code 4403) | | Reflection targets | `reflection_target_contracts.test.ts` | get/set_object_property refuse `/Script` targets (outermost package) before reading or writing, and report `OBJECT_NOT_ADDRESSABLE` | | Response identity redaction | `response_identity_redaction_contracts.test.ts` | Identity keys are redacted before a reply leaves; console_command output and the launch_build log tail go through the per-line sanitizer | | Path refusals and registered mounts | `path_refusal_contracts.test.ts` | A refused content path is worded once (`McpPathRefusalMessage`) with the helper's own reason, never as traversal unless it was; a mounted root is a folder like `/Game`; a reply keeps a registered mount readable but hides host roots, and the inline log device never asks the engine for mounts | | Fab bridge | `fab_bridge_security_contracts.test.ts` | No arbitrary script reaches the page; credentials never reach a reply or a log | | Fab page scripts | `fab_add_script_behaviour.test.ts`, `fab_details_script_behaviour.test.ts`, `fab_search_script_behaviour.test.ts` (with `fab-page-script.ts`) | The page JavaScript is lifted out of the C++ raw literals and run in a `vm` against a scripted `fetch`: format, tier and engine-build selection, reply shape, refusals, and that no token or signed URL is reported | | Fab imports | `fab_import_operations_contracts.test.ts`, `fab_import_cancel_contracts.test.ts`, `fab_library_sync_contracts.test.ts`, `fab_listing_details_contracts.test.ts`, `fab_relocate_contracts.test.ts`, `fab_page_readiness_contracts.test.ts` | The add answers when Fab accepts, one import runs at a time, a cancel is only Fab's own button or Interchange's own cancel, an empty library is synced with one fixed command, a relocation touches only what the import created in its own folders and is all or nothing | | Python diagnostics | `execute_python_diagnostics_contracts.test.ts` | Logs carry `codeSha256`, never the code | | Behaviour recipe files | `behaviour_recipes.test.ts` | Every `Resources/Recipes/<Domain>/<Name>.json` uses only the fields the plugin accepts, fills every placeholder it declares, calls no Blueprint-dependent function without `memberClass`, and ships (FilterPlugin.ini keeps `/Resources`) | ## CONVENTIONS - Use `countPureLines()` and `sliceBetween()` from `plugin-contract-fixtures.ts`; do not hand-roll them. - Tests read files with `fs.readFileSync` + regex; they never import plugin code. - Add a test here only for a structural or security invariant. Behaviour belongs in an integration case (`tests/mcp-tools/`). ## ANTI-PATTERNS - Weakening a security contract to make CI pass; fix the source. - Adding a 26th source file to a capped folder; split into a subdirectory first. - Pinning implementation spelling (exact log strings, symbol names) as a "contract".