import asyncio
import datetime
import logging
import sys
import tracemalloc
import uuid
from unittest import TestCase

from angelos.common.misc import Misc
from angelos.meta.testing import run_async
from angelos.meta.testing.net import FacadeContext
from angelos.net.base import PacketHandler, PacketManager, UnknownPacket, ErrorPacket, default, ext_hook, \
    DataType, Packet, r, ServerManagerMixin, ClientManagerMixin


class StubPacket(Packet, fields=("uint", "uuid", "fixed", "variable", "date"), fields_info=(
        (DataType.UINT, 100, 200), (DataType.UUID,), (DataType.BYTES_FIX, 128), (DataType.BYTES_VAR, 5, 20),
        (DataType.DATETIME,))):
    """Stub packet for testing features."""
    pass


class StubHandler(PacketHandler):
    """Base handler for stub."""
    LEVEL = 1
    RANGE = 511

    RLOW = r(RANGE)[0]

    PKT_STUB = RLOW + 1

    PACKETS = {
        PKT_STUB: StubPacket,
    }

    PROCESS = dict()

    async def process_stub(self, packet: StubPacket):
        """Handle an unknown packet response.

        This method MUST never return an unknown or error in order
        to prevent an infinite loop over the network.
        """
        print(packet.tuple)


class StubHandlerClient(StubHandler):
    """Client side stub handler."""

    PROCESS = {
        StubHandler.PKT_STUB: "process_stub",
    }


class StubHandlerServer(StubHandler):
    """Server side stub handler."""

    PROCESS = {
        StubHandler.PKT_STUB: "process_stub",
    }


class StubServer(ServerManagerMixin, PacketManager):
    """Stub protocol server."""


class StubClient(ClientManagerMixin, PacketManager):
    """Stub protocol client."""


class TestExtType(TestCase):
    """Test custom ExtTypes (DataTypes) for the protocol."""

    def test_UINT(self):
        value = 2 ** 63
        ext = default(value)
        self.assertEqual(ext.code, DataType.UINT)
        self.assertEqual(ext_hook(*ext), value)

    def test_UUID(self):
        value = uuid.uuid4()
        ext = default(value)
        self.assertEqual(ext.code, DataType.UUID)
        self.assertEqual(ext_hook(*ext), value)

    def test_BYTES_FIX(self):
        value = bytes(b"Hello, world!")
        ext = default(value)
        self.assertEqual(ext.code, DataType.BYTES_FIX)
        self.assertEqual(ext_hook(*ext), value)

    def test_BYTES_VAR(self):
        value = bytearray(b"Hello, world!")
        ext = default(value)
        self.assertEqual(ext.code, DataType.BYTES_VAR)
        self.assertEqual(ext_hook(*ext), value)

    def test_DATETIME(self):
        value = datetime.datetime.now().replace(microsecond=0)
        ext = default(value)
        self.assertEqual(ext.code, DataType.DATETIME)
        self.assertEqual(ext_hook(*ext), value)


class TestPacket(TestCase):
    def test_unpack(self):
        packet = StubPacket(
            150, uuid.uuid4(), bytes(128), bytearray(b"Hello, world!"),
            datetime.datetime.now())
        self.assertEqual(packet.tuple, StubPacket.unpack(bytes(packet)).tuple)

        with self.assertRaises(ValueError):
            StubPacket(
                99, uuid.uuid4(), bytes(128), bytearray(b"Hello, world!"),
                datetime.datetime.now())

        with self.assertRaises(ValueError):
            StubPacket(
                250, uuid.uuid4(), bytes(128), bytearray(b"Hello, world!"),
                datetime.datetime.now())

        with self.assertRaises(ValueError):
            StubPacket(
                150, uuid.uuid4(), bytes(148), bytearray(b"Hello, world!"),
                datetime.datetime.now())

        with self.assertRaises(ValueError):
            StubPacket(
                150, uuid.uuid4(), bytes(100), bytearray(b"Hello, world!"),
                datetime.datetime.now())

        with self.assertRaises(ValueError):
            StubPacket(
                150, uuid.uuid4(), bytes(128), bytearray(b"foo"),
                datetime.datetime.now())

        with self.assertRaises(ValueError):
            StubPacket(
                150, uuid.uuid4(), bytes(128), bytearray(b"Hello, world! Foo, bar, baz?"),
                datetime.datetime.now())

    def test_tuple(self):
        data = (150, uuid.uuid4(), bytes(128), bytearray(b"Hello, world!"),
                datetime.datetime.now().replace(microsecond=0))
        packet = StubPacket(*data)

        self.assertIsInstance(packet.tuple, tuple)
        self.assertEqual(packet.tuple, data)
        self.assertIs(packet.uint, 150)
        self.assertIsInstance(packet.uuid, uuid.UUID)
        self.assertIs(len(packet.fixed), 128)
        self.assertEqual(packet.variable, b"Hello, world!")

        with self.assertRaises(AttributeError):
            packet.foobar

        with self.assertRaises(ValueError):
            StubPacket(*data[:4])


class TestErrorPacket(TestCase):
    def test_error(self):
        packet = ErrorPacket(100, 200, 100, 1)
        self.assertIs(packet.type, 100)
        self.assertIs(packet.level, 200)
        self.assertIs(packet.process, 100)
        self.assertIs(packet.error, 1)


class TestUnknownPacket(TestCase):
    def test_unknown(self):
        packet = UnknownPacket(100, 200, 100)
        self.assertIs(packet.type, 100)
        self.assertIs(packet.level, 200)
        self.assertIs(packet.process, 100)


class TestPacketHandler(TestCase):
    client = None
    handler = None

    @classmethod
    def setUpClass(cls) -> None:
        """Setup test class with a facade and ten contacts."""
        tracemalloc.start()
        logging.basicConfig(stream=sys.stderr, level=logging.DEBUG)

    @run_async
    async def setUp(self) -> None:
        """Create client/server network nodes."""
        self.client = FacadeContext.create_client()
        self.handler = StubHandlerClient(PacketManager(self.client))

    def tearDown(self) -> None:
        """Clean up test network"""
        del self.client

    def test_manager(self):
        self.assertIsInstance(self.handler.manager, PacketManager)

    @run_async
    async def test_handle_packet(self):
        packet = StubPacket(
            150, uuid.uuid4(), bytes(128), bytearray(b"Hello, world!"),
            datetime.datetime.now())
        self.handler.handle_packet(StubHandler.PKT_STUB, bytes(packet))
        await Misc.sleep()

    @run_async
    async def test_process_unknown(self):
        with self.assertRaises(NotImplementedError):
            await self.handler.process_unknown(UnknownPacket(0, 0, 0))

    @run_async
    async def test_process_error(self):
        with self.assertRaises(NotImplementedError):
            await self.handler.process_error(ErrorPacket(0, 0, 0, 0))


class TestPacketManager(TestCase):
    client = None
    server = None

    @classmethod
    def setUpClass(cls) -> None:
        """Setup test class with a facade and ten contacts."""
        tracemalloc.start()
        logging.basicConfig(stream=sys.stderr, level=logging.DEBUG)

    @run_async
    async def setUp(self) -> None:
        """Create client/server network nodes."""
        self.client = FacadeContext.create_client()
        self.server = FacadeContext.create_server()

    def tearDown(self) -> None:
        """Clean up test network"""
        del self.client

    def test_facade(self):
        self.fail()

    def test_portfolio(self):
        self.fail()

    def test__add_service(self):
        self.fail()

    def test_authentication_made(self):
        self.fail()

    def test_connection_made(self):
        self.fail()

    def test_data_received(self):
        self.fail()

    def test_send_packet(self):
        self.fail()

    def test_unknown(self):
        self.fail()

    def test_error(self):
        self.fail()


class TestRig(TestCase):
    client = None
    server = None

    @classmethod
    def setUpClass(cls) -> None:
        """Setup test class with a facade and ten contacts."""
        tracemalloc.start()
        logging.basicConfig(stream=sys.stderr, level=logging.DEBUG)

    @run_async
    async def setUp(self) -> None:
        """Create client/server network nodes."""
        self.client = FacadeContext.create_client()
        self.server = FacadeContext.create_server()

    @run_async
    async def test_run(self):
        server = await StubServer.listen(self.server.facade, "127.0.0.1", 8080)
        task = asyncio.create_task(server.serve_forever())
        await asyncio.sleep(0)

        client = await StubClient.connect(self.client.facade, "127.0.0.1", 8080)
        # client.send_packet(4, 1, b"Hello, world!")
import asyncio
import datetime
import logging
import sys
import tracemalloc
import uuid
from unittest import TestCase

from angelos.common.misc import Misc
from angelos.meta.testing import run_async
from angelos.meta.testing.net import FacadeContext
from angelos.net.base import PacketHandler, PacketManager, UnknownPacket, ErrorPacket, default, ext_hook, \
    DataType, Packet, r, ServerManagerMixin, ClientManagerMixin


class StubPacket(Packet, fields=("uint", "uuid", "fixed", "variable", "date"), fields_info=(
        (DataType.UINT, 100, 200), (DataType.UUID,), (DataType.BYTES_FIX, 128), (DataType.BYTES_VAR, 5, 20),
        (DataType.DATETIME,))):
    """Stub packet for testing features."""
    pass


class StubHandler(PacketHandler):
    """Base handler for stub."""
    LEVEL = 1
    RANGE = 511

    RLOW = r(RANGE)[0]

    PKT_STUB = RLOW + 1

    PACKETS = {
        PKT_STUB: StubPacket,
    }

    PROCESS = dict()

    async def process_stub(self, packet: StubPacket):
        """Handle an unknown packet response.

        This method MUST never return an unknown or error in order
        to prevent an infinite loop over the network.
        """
        print(packet.tuple)


class StubHandlerClient(StubHandler):
    """Client side stub handler."""

    PROCESS = {
        StubHandler.PKT_STUB: "process_stub",
    }


class StubHandlerServer(StubHandler):
    """Server side stub handler."""

    PROCESS = {
        StubHandler.PKT_STUB: "process_stub",
    }


class StubServer(ServerManagerMixin, PacketManager):
    """Stub protocol server."""


class StubClient(ClientManagerMixin, PacketManager):
    """Stub protocol client."""


class TestExtType(TestCase):
    """Test custom ExtTypes (DataTypes) for the protocol."""

    def test_UINT(self):
        value = 2 ** 63
        ext = default(value)
        self.assertEqual(ext.code, DataType.UINT)
        self.assertEqual(ext_hook(*ext), value)

    def test_UUID(self):
        value = uuid.uuid4()
        ext = default(value)
        self.assertEqual(ext.code, DataType.UUID)
        self.assertEqual(ext_hook(*ext), value)

    def test_BYTES_FIX(self):
        value = bytes(b"Hello, world!")
        ext = default(value)
        self.assertEqual(ext.code, DataType.BYTES_FIX)
        self.assertEqual(ext_hook(*ext), value)

    def test_BYTES_VAR(self):
        value = bytearray(b"Hello, world!")
        ext = default(value)
        self.assertEqual(ext.code, DataType.BYTES_VAR)
        self.assertEqual(ext_hook(*ext), value)

    def test_DATETIME(self):
        value = datetime.datetime.now().replace(microsecond=0)
        ext = default(value)
        self.assertEqual(ext.code, DataType.DATETIME)
        self.assertEqual(ext_hook(*ext), value)


class TestPacket(TestCase):
    def test_unpack(self):
        packet = StubPacket(
            150, uuid.uuid4(), bytes(128), bytearray(b"Hello, world!"),
            datetime.datetime.now())
        self.assertEqual(packet.tuple, StubPacket.unpack(bytes(packet)).tuple)

        with self.assertRaises(ValueError):
            StubPacket(
                99, uuid.uuid4(), bytes(128), bytearray(b"Hello, world!"),
                datetime.datetime.now())

        with self.assertRaises(ValueError):
            StubPacket(
                250, uuid.uuid4(), bytes(128), bytearray(b"Hello, world!"),
                datetime.datetime.now())

        with self.assertRaises(ValueError):
            StubPacket(
                150, uuid.uuid4(), bytes(148), bytearray(b"Hello, world!"),
                datetime.datetime.now())

        with self.assertRaises(ValueError):
            StubPacket(
                150, uuid.uuid4(), bytes(100), bytearray(b"Hello, world!"),
                datetime.datetime.now())

        with self.assertRaises(ValueError):
            StubPacket(
                150, uuid.uuid4(), bytes(128), bytearray(b"foo"),
                datetime.datetime.now())

        with self.assertRaises(ValueError):
            StubPacket(
                150, uuid.uuid4(), bytes(128), bytearray(b"Hello, world! Foo, bar, baz?"),
                datetime.datetime.now())

    def test_tuple(self):
        data = (150, uuid.uuid4(), bytes(128), bytearray(b"Hello, world!"),
                datetime.datetime.now().replace(microsecond=0))
        packet = StubPacket(*data)

        self.assertIsInstance(packet.tuple, tuple)
        self.assertEqual(packet.tuple, data)
        self.assertIs(packet.uint, 150)
        self.assertIsInstance(packet.uuid, uuid.UUID)
        self.assertIs(len(packet.fixed), 128)
        self.assertEqual(packet.variable, b"Hello, world!")

        with self.assertRaises(AttributeError):
            packet.foobar

        with self.assertRaises(ValueError):
            StubPacket(*data[:4])


class TestErrorPacket(TestCase):
    def test_error(self):
        packet = ErrorPacket(100, 200, 100, 1)
        self.assertIs(packet.type, 100)
        self.assertIs(packet.level, 200)
        self.assertIs(packet.process, 100)
        self.assertIs(packet.error, 1)


class TestUnknownPacket(TestCase):
    def test_unknown(self):
        packet = UnknownPacket(100, 200, 100)
        self.assertIs(packet.type, 100)
        self.assertIs(packet.level, 200)
        self.assertIs(packet.process, 100)


class TestPacketHandler(TestCase):
    client = None
    handler = None

    @classmethod
    def setUpClass(cls) -> None:
        """Setup test class with a facade and ten contacts."""
        tracemalloc.start()
        logging.basicConfig(stream=sys.stderr, level=logging.DEBUG)

    @run_async
    async def setUp(self) -> None:
        """Create client/server network nodes."""
        self.client = FacadeContext.create_client()
        self.handler = StubHandlerClient(PacketManager(self.client))

    def tearDown(self) -> None:
        """Clean up test network"""
        del self.client

    def test_manager(self):
        self.assertIsInstance(self.handler.manager, PacketManager)

    @run_async
    async def test_handle_packet(self):
        packet = StubPacket(
            150, uuid.uuid4(), bytes(128), bytearray(b"Hello, world!"),
            datetime.datetime.now())
        self.handler.handle_packet(StubHandler.PKT_STUB, bytes(packet))
        await Misc.sleep()

    @run_async
    async def test_process_unknown(self):
        with self.assertRaises(NotImplementedError):
            await self.handler.process_unknown(UnknownPacket(0, 0, 0))

    @run_async
    async def test_process_error(self):
        with self.assertRaises(NotImplementedError):
            await self.handler.process_error(ErrorPacket(0, 0, 0, 0))


class TestPacketManager(TestCase):
    client = None
    server = None

    @classmethod
    def setUpClass(cls) -> None:
        """Setup test class with a facade and ten contacts."""
        tracemalloc.start()
        logging.basicConfig(stream=sys.stderr, level=logging.DEBUG)

    @run_async
    async def setUp(self) -> None:
        """Create client/server network nodes."""
        self.client = FacadeContext.create_client()
        self.server = FacadeContext.create_server()

    def tearDown(self) -> None:
        """Clean up test network"""
        del self.client

    def test_facade(self):
        self.fail()

    def test_portfolio(self):
        self.fail()

    def test__add_service(self):
        self.fail()

    def test_authentication_made(self):
        self.fail()

    def test_connection_made(self):
        self.fail()

    def test_data_received(self):
        self.fail()

    def test_send_packet(self):
        self.fail()

    def test_unknown(self):
        self.fail()

    def test_error(self):
        self.fail()


class TestRig(TestCase):
    client = None
    server = None

    @classmethod
    def setUpClass(cls) -> None:
        """Setup test class with a facade and ten contacts."""
        tracemalloc.start()
        logging.basicConfig(stream=sys.stderr, level=logging.DEBUG)

    @run_async
    async def setUp(self) -> None:
        """Create client/server network nodes."""
        self.client = FacadeContext.create_client()
        self.server = FacadeContext.create_server()

    @run_async
    async def test_run(self):
        server = await StubServer.listen(self.server.facade, "127.0.0.1", 8080)
        task = asyncio.create_task(server.serve_forever())
        await asyncio.sleep(0)

        client = await StubClient.connect(self.client.facade, "127.0.0.1", 8080)
        # client.send_packet(4, 1, b"Hello, world!")
Stub packet for testing features. Base handler for stub. Handle an unknown packet response.          This method MUST never return an unknown or error in order         to prevent an infinite loop over the network. Client side stub handler. Server side stub handler. Stub protocol server. Stub protocol client. Test custom ExtTypes (DataTypes) for the protocol. Setup test class with a facade and ten contacts. Create client/server network nodes. Clean up test network Setup test class with a facade and ten contacts. Create client/server network nodes. Clean up test network Setup test class with a facade and ten contacts. Create client/server network nodes. # client.send_packet(4, 1, b"Hello, world!")