test(contracts): tests mirror tui_gateway/; the runtime-artifact spoof test asserts the new 4000
tests/contracts -> tests/tui_gateway/contracts (tree-layout rule: tests mirror a source package). test_rpc_params_cannot_spoof_runtime_artifacts: forged owner_transport / owner_session_record / owner_token keys are now refused at the wire (4000 + key path) instead of silently dropped before the handler; the invariant (no steer reaches the agent) is unchanged and asserted directly.
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
// GENERATED by scripts/gen_gateway_contracts.py from tui_gateway/contracts — DO NOT EDIT.
|
||||
// Regenerate: .venv/bin/python scripts/gen_gateway_contracts.py
|
||||
// tests/contracts/test_generated.py fails when this file is stale.
|
||||
// tests/tui_gateway/contracts/test_generated.py fails when this file is stale.
|
||||
/* eslint-disable */
|
||||
// ── Types ──
|
||||
/** Any method the desktop may route to a named profile (``requestGatewayForProfile`` adds ``profile``). */
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
* `./gateway-contract.generated.ts` carries `RpcMethods` (client→server method → params/result),
|
||||
* `ServerRequestMap` (server→client request → params/result), `GatewayEventMap` (notification
|
||||
* type → payload) and every value shape. `scripts/gen_gateway_contracts.py` regenerates it and
|
||||
* `tests/contracts/test_generated.py` fails when the committed file is stale, so a field the
|
||||
* `tests/tui_gateway/contracts/test_generated.py` fails when the committed file is stale, so a field the
|
||||
* backend stops sending fails `tsc` here instead of drifting.
|
||||
*
|
||||
* This module adds only what the wire does not carry: the client-local synthetic events the TUI
|
||||
|
||||
@@ -96,7 +96,7 @@ _PY_RELEVANT_SITE = (
|
||||
# Editing only the JSON in an apps/-only PR would otherwise skip the one test
|
||||
# that can catch the drift, so these force the Python lane too.
|
||||
_PY_RELEVANT_CONTRACT_FILES = {
|
||||
# tests/contracts/test_generated.py (rendered from tui_gateway/contracts)
|
||||
# tests/tui_gateway/contracts/test_generated.py (rendered from tui_gateway/contracts)
|
||||
"apps/shared/src/gateway-contract.generated.ts",
|
||||
"apps/shared/src/gateway-contract.openrpc.json",
|
||||
# tests/hermes_cli/test_desktop_slash_registry.py
|
||||
|
||||
@@ -34,7 +34,7 @@ OPENRPC_OUT = ROOT / "apps" / "shared" / "src" / "gateway-contract.openrpc.json"
|
||||
HEADER = (
|
||||
"// GENERATED by scripts/gen_gateway_contracts.py from tui_gateway/contracts — DO NOT EDIT.\n"
|
||||
"// Regenerate: .venv/bin/python scripts/gen_gateway_contracts.py\n"
|
||||
"// tests/contracts/test_generated.py fails when this file is stale.\n"
|
||||
"// tests/tui_gateway/contracts/test_generated.py fails when this file is stale.\n"
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -715,8 +715,11 @@ class TestSubagentSteerRPC:
|
||||
transport=owner_transport,
|
||||
session_record=owner_record,
|
||||
)
|
||||
assert envelope["result"]["status"] == "queued"
|
||||
assert agent.steered == ["ignore serialized capabilities"]
|
||||
# The wire contract refuses unknown keys outright, so a forged runtime artifact never
|
||||
# reaches the handler (before contracts: silently ignored, steer still queued).
|
||||
assert envelope["error"]["code"] == 4000
|
||||
assert "owner_transport" in envelope["error"]["message"]
|
||||
assert agent.steered == []
|
||||
finally:
|
||||
_unregister_subagent("sid-rpc-param-spoof")
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
REPO = Path(__file__).resolve().parents[2]
|
||||
REPO = Path(__file__).resolve().parents[3]
|
||||
GEN = REPO / "scripts" / "gen_gateway_contracts.py"
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@ has a `Params` + `Result` model, every server→client request a `Params` + `Res
|
||||
result or an emitted payload does not match its model (production only logs). `apps/shared/src/
|
||||
gateway-contract.generated.ts` (`RpcMethods`, `ServerRequestMap`, `BackendGatewayEventMap` + every value
|
||||
shape) and `gateway-contract.openrpc.json` are rendered by `scripts/gen_gateway_contracts.py`;
|
||||
`tests/contracts/test_generated.py` fails when they are stale, so the loop is: change the model →
|
||||
`tests/tui_gateway/contracts/test_generated.py` fails when they are stale, so the loop is: change the model →
|
||||
regenerate → `tsc` shows every consumer the field moved. `apps/shared/src/gateway-events.ts` only adds
|
||||
the client-local synthetic events and the `GatewayEvent` envelope on top.
|
||||
New question for the user = `_ask("<method>", sid, params, timeout)` in the emitter, a handler in
|
||||
|
||||
@@ -4,7 +4,7 @@ Python is the single source of truth for the JSON-RPC wire: every client→serve
|
||||
(params + result), every server→client request (params + result) and every notification
|
||||
payload is a Pydantic model declared in this package. ``scripts/gen_gateway_contracts.py``
|
||||
renders them into ``apps/shared/src/gateway-contract.generated.ts`` and
|
||||
``apps/shared/src/gateway-contract.openrpc.json``; ``tests/contracts/test_generated.py``
|
||||
``apps/shared/src/gateway-contract.openrpc.json``; ``tests/tui_gateway/contracts/test_generated.py``
|
||||
regenerates in memory and diffs the committed files, so a model edited without regenerating
|
||||
fails CI on the Python side, and TS that reads a phantom field fails ``tsc``.
|
||||
|
||||
|
||||
@@ -769,7 +769,7 @@ def _err(rid, code: int, msg: str, data=None) -> dict:
|
||||
|
||||
def register_method(name: str, fn) -> None:
|
||||
"""The ONE registration seam (``@method`` here and ``HandlerRegistry.install`` for the split
|
||||
modules). ``tests/contracts/test_generated.py::test_every_method_has_a_contract`` and the
|
||||
modules). ``tests/tui_gateway/contracts/test_generated.py::test_every_method_has_a_contract`` and the
|
||||
generator's ``assert_complete`` fail when a registered name has no contract."""
|
||||
_methods[name] = fn
|
||||
|
||||
|
||||
Reference in New Issue
Block a user