#  Copyright 2022 The Debusine developers
#  See the AUTHORS file at the top-level directory of this distribution
#
#  This file is part of Debusine. It is subject to the license terms
#  in the LICENSE file found in the top-level directory of this
#  distribution. No part of Debusine, including this file, may be copied,
#  modified, propagated, or distributed except according to the terms
#  contained in the LICENSE file.
"""Common test-helper code involving django usage."""
import asyncio
from unittest.util import safe_repr

from asgiref.sync import async_to_sync

from channels.layers import get_channel_layer

from debusine.db.models import Token


class DatabaseHelpersMixin:
    """
    Collection of database-related methods to help write unit tests.

    This mixin-class provides different methods to setup various database
    entries using the corresponding Django models.
    """

    def create_token_enabled(self):
        """Return an enabled Token."""
        token = Token.objects.create()
        token.enable()
        return token


class ChannelsHelpersMixin:
    """
    Channel-related methods to help writing unit tests.

    Provides methods to setup a channel and assert messages or lack of messages.
    """

    def create_channel(self, group_name) -> dict:
        """
        Create a channel and add it to the group named ``group_name``.

        Return dict with layer and name.
        """
        channel_layer = get_channel_layer()
        channel_name = async_to_sync(channel_layer.new_channel)()
        async_to_sync(channel_layer.group_add)(group_name, channel_name)

        return {"layer": channel_layer, "name": channel_name}

    def assert_channel_nothing_received(self, channel: dict):
        """Assert that nothing is received in channel."""
        try:
            received = async_to_sync(asyncio.wait_for)(
                channel["layer"].receive(channel["name"]), timeout=0.1
            )
        except asyncio.exceptions.TimeoutError:
            pass
        else:
            self.fail("Expected nothing. Received: '%s'" % safe_repr(received))

    def assert_channel_received(self, channel: dict, data):
        """Assert that data is received in channel_layer, channel_name."""
        try:
            self.assertEqual(
                async_to_sync(asyncio.wait_for)(
                    (channel["layer"].receive)(channel["name"]), timeout=0.1
                ),
                data,
            )
        except asyncio.exceptions.TimeoutError:
            self.fail("Expected '%s' received nothing" % safe_repr(data))
