Import AITURK IDE 1.0.0-beta.1 from Hermes 63279301; preserve MIT license
This commit is contained in:
@@ -0,0 +1,268 @@
|
||||
"""Relay passthrough-over-WS forwarding (Phase 5 §5.1).
|
||||
|
||||
Proves the gateway side of §5.1: a connector-forwarded passthrough request
|
||||
(Discord interaction, Twilio, …) arrives over the SAME outbound /relay WS as
|
||||
inbound messages (a hosted gateway has no public inbound port), and the relay
|
||||
adapter handles it — decoding the byte-preserved body and routing a Discord
|
||||
interaction through the normal agent path (handle_message).
|
||||
|
||||
Mirrors test_relay_interrupt.py's wiring discipline (connect() registers the
|
||||
connector->gateway handlers on the transport).
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import base64
|
||||
import json
|
||||
|
||||
import pytest
|
||||
|
||||
from gateway.config import Platform, PlatformConfig
|
||||
from gateway.relay.adapter import RelayAdapter
|
||||
from gateway.relay.descriptor import CONTRACT_VERSION, CapabilityDescriptor
|
||||
from gateway.relay.ws_transport import PassthroughForward, _passthrough_from_wire
|
||||
|
||||
from tests.gateway.relay.stub_connector import StubConnector
|
||||
|
||||
|
||||
def _desc() -> CapabilityDescriptor:
|
||||
return CapabilityDescriptor(
|
||||
contract_version=CONTRACT_VERSION,
|
||||
platform="discord",
|
||||
label="Discord",
|
||||
max_message_length=2000,
|
||||
supports_draft_streaming=False,
|
||||
supports_edit=True,
|
||||
supports_threads=True,
|
||||
markdown_dialect="discord",
|
||||
len_unit="chars",
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def adapter():
|
||||
return RelayAdapter(PlatformConfig(), _desc(), transport=StubConnector(_desc()))
|
||||
|
||||
|
||||
def _interaction_forward(payload: dict, *, profile: str | None = None) -> PassthroughForward:
|
||||
body = json.dumps(payload).encode("utf-8")
|
||||
return PassthroughForward(
|
||||
platform="discord",
|
||||
bot_id="appShared",
|
||||
method="POST",
|
||||
path="/interactions/discord/appShared",
|
||||
headers=[("content-type", "application/json")],
|
||||
body=body,
|
||||
profile=profile,
|
||||
)
|
||||
|
||||
|
||||
def test_passthrough_from_wire_byte_preserves_body():
|
||||
"""The wire frame's base64 body decodes back to the exact bytes (parity with
|
||||
the connector's toPassthroughForward)."""
|
||||
original = json.dumps({"type": 2, "data": {"name": "ping"}, "guild_id": "g1"}).encode("utf-8")
|
||||
wire = {
|
||||
"platform": "discord",
|
||||
"botId": "appShared",
|
||||
"method": "POST",
|
||||
"path": "/interactions/discord/appShared",
|
||||
"headers": [["content-type", "application/json"]],
|
||||
"bodyB64": base64.b64encode(original).decode("ascii"),
|
||||
}
|
||||
fwd = _passthrough_from_wire(wire)
|
||||
assert fwd.platform == "discord"
|
||||
assert fwd.bot_id == "appShared"
|
||||
assert fwd.body == original
|
||||
assert fwd.headers == [("content-type", "application/json")]
|
||||
|
||||
|
||||
def test_passthrough_from_wire_stamps_routed_profile():
|
||||
"""A connector-routed profile on the wire frame lands on PassthroughForward.
|
||||
|
||||
Mirrors _event_from_wire's profile stamping for the ``inbound`` frame
|
||||
(#60586) — the passthrough plane needs the same carry-through so a
|
||||
Team-Gateway's Discord interactions route to the same profile a plain
|
||||
message would.
|
||||
"""
|
||||
wire = {
|
||||
"platform": "discord",
|
||||
"botId": "appShared",
|
||||
"method": "POST",
|
||||
"path": "/interactions/discord/appShared",
|
||||
"headers": [],
|
||||
"bodyB64": "",
|
||||
"profile": "reviewer",
|
||||
}
|
||||
fwd = _passthrough_from_wire(wire)
|
||||
assert fwd.profile == "reviewer"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_connect_wires_passthrough_handler_over_ws(adapter):
|
||||
"""connect() registers the passthrough handler on the transport so a
|
||||
connector-delivered passthrough_forward frame reaches the adapter."""
|
||||
await adapter.connect()
|
||||
stub = adapter._transport
|
||||
assert stub._passthrough is not None
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_discord_interaction_routes_through_handle_message(adapter, monkeypatch):
|
||||
"""A forwarded Discord application-command interaction is decoded and routed
|
||||
through the normal agent path (handle_message) with a correct session source."""
|
||||
await adapter.connect()
|
||||
stub = adapter._transport
|
||||
|
||||
seen = []
|
||||
|
||||
async def fake_handle(event):
|
||||
seen.append(event)
|
||||
|
||||
monkeypatch.setattr(adapter, "handle_message", fake_handle)
|
||||
|
||||
fwd = _interaction_forward(
|
||||
{
|
||||
"id": "interaction-1",
|
||||
"type": 2, # APPLICATION_COMMAND
|
||||
"channel_id": "chan-9",
|
||||
"guild_id": "guild-7",
|
||||
"data": {"name": "summarize"},
|
||||
"member": {"user": {"id": "user-3", "username": "ben"}},
|
||||
}
|
||||
)
|
||||
await stub.push_passthrough(fwd, buffer_id=None)
|
||||
|
||||
assert len(seen) == 1
|
||||
ev = seen[0]
|
||||
# APPLICATION_COMMAND interactions are normalized to a leading-slash
|
||||
# command (the dispatcher's contract), not the bare registered name.
|
||||
assert ev.text == "/summarize"
|
||||
assert ev.is_command() is True
|
||||
assert ev.get_command() == "summarize"
|
||||
assert ev.source.chat_id == "chan-9"
|
||||
assert ev.source.scope_id == "guild-7"
|
||||
assert ev.source.user_id == "user-3"
|
||||
# LOGICAL platform + native-parity chat_type: the session key must match
|
||||
# the connector's capability-vault binding (interactionSessionSource →
|
||||
# buildSessionKey: platform "discord", chat_type "group") and the relay
|
||||
# text lane. Platform.RELAY / "channel" here forked the session and made
|
||||
# /sethome file the home channel under platforms.relay (invisible to cron).
|
||||
assert ev.source.platform == Platform.DISCORD
|
||||
assert ev.source.chat_type == "group"
|
||||
# Authenticated upstream-trust marker, parity with the relay text lane
|
||||
# (ws_transport._event_from_wire) — /sethome's via_relay guard keys on it.
|
||||
assert ev.source.delivered_via_upstream_relay is True
|
||||
# Scope captured so the agent's reply re-asserts scope_id for egress.
|
||||
assert adapter._scope_by_chat.get("chan-9") == "guild-7"
|
||||
# The logical platform is now recorded for egress sender selection too
|
||||
# (_capture_scope skips only the generic "relay").
|
||||
assert adapter._platform_by_chat.get("chan-9") == "discord"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_discord_interaction_stamps_routed_profile(adapter, monkeypatch):
|
||||
"""A connector-routed profile on the passthrough forward lands on the
|
||||
resulting event's SessionSource, the same way it does for a plain relayed
|
||||
message (#60586) — so a Team-Gateway's Discord slash-command/button/modal
|
||||
routes to the same profile a plain message would, instead of always
|
||||
falling back to agent:main."""
|
||||
await adapter.connect()
|
||||
stub = adapter._transport
|
||||
|
||||
seen = []
|
||||
|
||||
async def fake_handle(event):
|
||||
seen.append(event)
|
||||
|
||||
monkeypatch.setattr(adapter, "handle_message", fake_handle)
|
||||
|
||||
fwd = _interaction_forward(
|
||||
{
|
||||
"id": "interaction-2",
|
||||
"type": 2, # APPLICATION_COMMAND
|
||||
"channel_id": "chan-9",
|
||||
"guild_id": "guild-7",
|
||||
"data": {"name": "summarize"},
|
||||
"member": {"user": {"id": "user-3", "username": "ben"}},
|
||||
},
|
||||
profile="reviewer",
|
||||
)
|
||||
await stub.push_passthrough(fwd, buffer_id=None)
|
||||
|
||||
assert len(seen) == 1
|
||||
assert seen[0].source.profile == "reviewer"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_application_command_subcommand_nesting_renders_names_then_values(
|
||||
adapter, monkeypatch
|
||||
):
|
||||
"""SUB_COMMAND (type 1) appends its name, then recurses into its options."""
|
||||
await adapter.connect()
|
||||
stub = adapter._transport
|
||||
seen = []
|
||||
|
||||
async def fake_handle(event):
|
||||
seen.append(event)
|
||||
|
||||
monkeypatch.setattr(adapter, "handle_message", fake_handle)
|
||||
fwd = _interaction_forward(
|
||||
{
|
||||
"id": "i-sub",
|
||||
"type": 2,
|
||||
"channel_id": "c5",
|
||||
"guild_id": "g5",
|
||||
"data": {
|
||||
"name": "skill",
|
||||
"options": [
|
||||
{
|
||||
"name": "run",
|
||||
"type": 1, # SUB_COMMAND
|
||||
"options": [{"name": "target", "type": 3, "value": "deploy"}],
|
||||
}
|
||||
],
|
||||
},
|
||||
"member": {"user": {"id": "u5", "username": "ben"}},
|
||||
}
|
||||
)
|
||||
await stub.push_passthrough(fwd)
|
||||
assert len(seen) == 1
|
||||
ev = seen[0]
|
||||
assert ev.text == "/skill run deploy"
|
||||
assert ev.is_command() is True
|
||||
assert ev.get_command() == "skill"
|
||||
assert ev.get_command_args() == "run deploy"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_dm_interaction_keys_as_discord_dm(adapter, monkeypatch):
|
||||
"""A guild-less (DM) interaction keys as a Discord DM: logical platform,
|
||||
chat_type 'dm', and the authenticated relay marker — the /sethome-in-DM
|
||||
shape must file under platforms.discord, never platforms.relay."""
|
||||
await adapter.connect()
|
||||
stub = adapter._transport
|
||||
seen = []
|
||||
|
||||
async def fake_handle(event):
|
||||
seen.append(event)
|
||||
|
||||
monkeypatch.setattr(adapter, "handle_message", fake_handle)
|
||||
fwd = _interaction_forward(
|
||||
{
|
||||
"id": "i-dm",
|
||||
"type": 2,
|
||||
"channel_id": "dm-chan-1",
|
||||
"data": {"name": "sethome"},
|
||||
"user": {"id": "u9", "username": "ben"},
|
||||
}
|
||||
)
|
||||
await stub.push_passthrough(fwd)
|
||||
assert len(seen) == 1
|
||||
ev = seen[0]
|
||||
assert ev.source.platform == Platform.DISCORD
|
||||
assert ev.source.chat_type == "dm"
|
||||
assert ev.source.scope_id is None
|
||||
assert ev.source.user_id == "u9"
|
||||
assert ev.source.delivered_via_upstream_relay is True
|
||||
|
||||
|
||||
Reference in New Issue
Block a user