"""Packet-routing tests for ordered Firefox RDP requests."""
from __future__ import annotations
import asyncio
import json
import unittest
from src.rdp_connection import RdpConnection, _utf16Length
class FakeWebSocket:
"""Script an RDP peer at the text-message boundary."""
def __init__(self) -> None:
self.connected = False
self.incoming = asyncio.Queue()
self.sent = []
async def connect(self, timeout):
"""Open the fake and enqueue its root greeting."""
self.connected = True
await self.incoming.put(json.dumps({
"from": "root",
"applicationType": "browser",
}))
async def close(self):
"""Mark the fake socket closed."""
self.connected = False
async def receiveText(self):
"""Receive one queued peer message."""
return await self.incoming.get()
async def sendText(self, text):
"""Respond to supported request packets."""
packet = json.loads(text)
self.sent.append(packet)
if packet["type"] == "listTabs":
await self.incoming.put(json.dumps({
"from": "root",
"tabs": [{
"actor": "tab-1",
"title": "Fixture",
"url": "https://example.test/",
}],
}))
elif packet["type"] == "evaluateJSAsync":
await self.incoming.put(json.dumps({
"from": packet["to"],
"resultID": "result-1",
}))
await self.incoming.put(json.dumps({
"from": packet["to"],
"type": "evaluationResult",
"resultID": "result-1",
"result": {"result": '{"ok":true,"value":{"x":1}}'},
}))
class RdpConnectionTest(unittest.IsolatedAsyncioTestCase):
"""Verify greeting, actor ordering, and two-phase evaluation routing."""
async def asyncSetUp(self) -> None:
self.connection = RdpConnection("127.0.0.1", 6000, 1000)
self.websocket = FakeWebSocket()
self.connection._websocket = self.websocket
await self.connection.connect()
async def asyncTearDown(self) -> None:
await self.connection.close()
async def test_lists_tabs(self) -> None:
tabs = await self.connection.listTabs()
self.assertEqual(tabs[0].actor_id, "tab-1")
self.assertEqual(tabs[0].title, "Fixture")
async def test_routes_fast_evaluation_completion(self) -> None:
result = await self.connection.evaluateJson("console", "script")
self.assertEqual(result, {"ok": True, "value": {"x": 1}})
request = next(
packet for packet in self.websocket.sent
if packet["type"] == "evaluateJSAsync"
)
self.assertEqual(request["mapped"], {"await": True})
self.assertNotIn("options", request)
async def test_does_not_treat_actor_notification_as_reply(self) -> None:
loop = asyncio.get_running_loop()
future = loop.create_future()
self.connection._direct_future = future
self.connection._direct_actor = "target"
self.connection._routePacket({
"from": "target",
"type": "frameUpdate",
"frames": [],
})
self.assertFalse(future.done())
self.connection._routePacket({"from": "target", "value": True})
self.assertEqual(future.result(), {"from": "target", "value": True})
self.connection._direct_future = None
self.connection._direct_actor = None
def test_long_string_offsets_use_utf16_code_units(self) -> None:
self.assertEqual(_utf16Length("plain"), 5)
self.assertEqual(_utf16Length("😀"), 2)
self.assertEqual(_utf16Length("a😀b"), 4)
if __name__ == "__main__":
unittest.main()