BareGit
"""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()