async def test_connect_no_auth_data(opsdroid, caplog, tmpdir):
    caplog.set_level(logging.INFO)
    get_response = amock.Mock()
    get_response.status = 200
    get_response.json = amock.CoroutineMock()
    get_response.json.return_value = {"data": [{"id": "test-bot"}]}

    connector = ConnectorTwitch(connector_config, opsdroid=opsdroid)
    connector.auth_file = AUTH_FILE
    connector.webhook = amock.CoroutineMock()
    connector.request_oauth_token = amock.CoroutineMock()

    opsdroid.web_server = amock.Mock()

    with amock.patch(
            "aiohttp.ClientSession.get",
            new=amock.CoroutineMock()) as patched_request, amock.patch(
                "os.path") as mocked_file:
        patched_request.return_value = asyncio.Future()
        patched_request.return_value.set_result(get_response)

        mocked_file.isfile = amock.Mock(return_value=False)

        await connector.connect()

        assert "No previous authorization data found" in caplog.text
        assert connector.request_oauth_token.called
        assert opsdroid.web_server.web_app.router.add_get.called
        assert connector.webhook.called
async def test_create_clip_failure(opsdroid, caplog):
    caplog.set_level(logging.DEBUG)
    connector = ConnectorTwitch(connector_config, opsdroid=opsdroid)
    connector.send_message = amock.CoroutineMock()

    post_response = amock.Mock()
    post_response.status = 200
    post_response.json = amock.CoroutineMock()
    post_response.json.return_value = {"data": [{"id": "clip123"}]}

    get_response = amock.Mock()
    get_response.status = 404

    with amock.patch("aiohttp.ClientSession.post",
                     new=amock.CoroutineMock()) as mocked_post, amock.patch(
                         "aiohttp.ClientSession.get",
                         new=amock.CoroutineMock()) as mocked_get:

        mocked_post.return_value = asyncio.Future()
        mocked_post.return_value.set_result(post_response)

        mocked_get.return_value = asyncio.Future()
        mocked_get.return_value.set_result(get_response)

        await connector.create_clip()

        assert "Failed to create Twitch clip" in caplog.text
async def test_stream_started_event(opsdroid, caplog):
    caplog.set_level(logging.DEBUG)
    connector = ConnectorTwitch(connector_config, opsdroid=opsdroid)
    connector.validate_request = amock.CoroutineMock(return_value=True)
    connector.listen = amock.CoroutineMock()

    mock_request = amock.CoroutineMock()
    mock_request.json = amock.CoroutineMock()
    mock_request.json.return_value = {
        "data": [{
            "started_at": "just now",
            "title": "Testing with pytest!",
            "viewer_count": 1,
        }]
    }

    stream_start_event = twitch_event.StreamStarted("Testing with pytest", 1,
                                                    "just now")

    twitch_event.StreamStarted = amock.Mock()

    resp = await connector.twitch_webhook_handler(mock_request)

    assert "Broadcaster went live event received by Twitch." in caplog.text
    assert connector.is_live
    assert resp.status == 200
    assert twitch_event.StreamStarted.called
    assert connector.listen.called
    assert "Testing with pytest" in stream_start_event.title
    assert 1 == stream_start_event.viewers
    assert "just now" in stream_start_event.started_at
async def test_gift_subscription_event(opsdroid, caplog):
    caplog.set_level(logging.DEBUG)
    connector = ConnectorTwitch(connector_config, opsdroid=opsdroid)
    connector.validate_request = amock.CoroutineMock(return_value=True)

    mock_request = amock.CoroutineMock()
    mock_request.json = amock.CoroutineMock()
    mock_request.json.return_value = {
        "data": [{
            "event_type": "subscriptions.subscribe",
            "event_data": {
                "user_name": "lucky_mc_luck",
                "gifter_name": "Awesome gifter",
                "is_gift": True,
            },
        }]
    }

    twitch_event.UserSubscribed = amock.Mock()

    resp = await connector.twitch_webhook_handler(mock_request)

    assert "Gifted subscriber event received by Twitch." in caplog.text
    assert resp.status == 200
    assert twitch_event.UserSubscribed.called
def test_init(opsdroid):
    connector = ConnectorTwitch(connector_config, opsdroid=opsdroid)
    assert connector.default_target == "test"
    assert connector.name == "twitch"
    assert connector.token is None
    assert connector.websocket is None
    assert connector.user_id is None
    assert connector.reconnections == 0
async def test_send_handshake(opsdroid):
    connector = ConnectorTwitch(connector_config, opsdroid=opsdroid)

    connector.websocket = amock.Mock()
    connector.websocket.send_str = amock.CoroutineMock()

    await connector.send_handshake()

    assert connector.websocket.send_str.called
async def test_listen(opsdroid):
    connector = ConnectorTwitch(connector_config, opsdroid=opsdroid)

    with amock.patch("aiohttp.ClientSession.ws_connect",
                     new=amock.CoroutineMock()) as mocked_websocket:
        mocked_websocket.side_effect = Exception()

        with contextlib.suppress(Exception):
            await connector.listen()
async def test_disconnect_websockets(opsdroid):
    connector = ConnectorTwitch(connector_config, opsdroid=opsdroid)

    connector.websocket = web.WebSocketResponse()

    resp = await connector.disconnect_websockets()

    assert not connector.websocket
    assert not connector.is_live
    assert not resp
async def test_handle_challenge_error(opsdroid, caplog):
    caplog.set_level(logging.DEBUG)

    connector = ConnectorTwitch(connector_config, opsdroid=opsdroid)

    req = make_mocked_request("GET", "/connector/twitch")

    resp = await connector.handle_challenge(req)

    assert "Failed to get challenge" in caplog.text
    assert resp.status == 500
async def test_ban_user(opsdroid, caplog):
    caplog.set_level(logging.DEBUG)
    connector = ConnectorTwitch(connector_config, opsdroid=opsdroid)
    ban_event = opsdroid_events.BanUser(user="******")

    connector.send_message = amock.CoroutineMock()

    await connector.ban_user(ban_event)

    assert connector.send_message.called
    assert "bot_mc_spam_bot" in caplog.text
async def test_handle_message_authentication_failed(opsdroid):
    message = ":tmi.twitch.tv NOTICE * :Login authentication failed"

    connector = ConnectorTwitch(connector_config, opsdroid=opsdroid)

    connector.refresh_token = amock.CoroutineMock()

    with pytest.raises(ConnectionError):
        await connector._handle_message(message)

        assert connector.refresh_token.called
async def test_send_message_event(opsdroid, caplog):
    caplog.set_level(logging.DEBUG)
    connector = ConnectorTwitch(connector_config, opsdroid=opsdroid)
    message_event = opsdroid_events.Message(text="Hello world!")

    connector.send_message = amock.CoroutineMock()

    await connector._send_message(message_event)

    assert connector.send_message.called
    assert "Hello world!" in caplog.text
async def test_handle_message_left_event(opsdroid):
    message = ":[email protected] PART #channel"

    left_event = opsdroid_events.LeaveRoom(user="******")

    connector = ConnectorTwitch(connector_config, opsdroid=opsdroid)

    opsdroid.parse = amock.CoroutineMock()

    await connector._handle_message(message)
    assert opsdroid.parse.called
    assert "username" in left_event.user
def test_base_url_twitch_config(opsdroid):
    config = {
        "code": "yourcode",
        "channel": "test",
        "client-id": "client-id",
        "client-secret": "client-secret",
        "forward-url": "http://my-awesome-url",
    }

    connector = ConnectorTwitch(config, opsdroid=opsdroid)

    assert connector.base_url == "http://my-awesome-url"
async def test_handle_message_chat_message(opsdroid, caplog):
    caplog.set_level(logging.DEBUG)
    message = "@badge-info=;badges=;client-nonce=jiwej12;color=;display-name=user;emotes=;flags=0-81:;id=jdias-9212;mod=0;room-id=123;subscriber=0;tmi-sent-ts=1592943868712;turbo=0;user-id=123;user-type= :[email protected] PRIVMSG #channel :Hello world!"

    connector = ConnectorTwitch(connector_config, opsdroid=opsdroid)

    opsdroid.parse = amock.CoroutineMock()

    await connector._handle_message(message)

    assert "Got message from Twitch" in caplog.text
    assert opsdroid.parse.called
async def test_handle_challenge(opsdroid, caplog):
    caplog.set_level(logging.DEBUG)

    connector = ConnectorTwitch(connector_config, opsdroid=opsdroid)

    req = make_mocked_request(
        "GET", "/connector/twitch?hub.challenge=testchallenge123")

    resp = await connector.handle_challenge(req)

    assert "Failed to get challenge" not in caplog.text
    assert "testchallenge123" in resp.text
async def test_disconnect(opsdroid):
    connector_config["always-listening"] = True
    connector = ConnectorTwitch(connector_config, opsdroid=opsdroid)

    connector.disconnect_websockets = amock.CoroutineMock()
    connector.webhook = amock.CoroutineMock()

    await connector.disconnect()

    assert connector.is_live is True
    assert connector.disconnect_websockets.called
    assert connector.webhook.called
async def test_invalid_post_request_webhook(opsdroid):
    connector = ConnectorTwitch(connector_config, opsdroid=opsdroid)
    connector.validate_request = amock.CoroutineMock(return_value=False)

    mock_request = amock.CoroutineMock()
    mock_request.json = amock.CoroutineMock()
    mock_request.json.return_value = {"data": [{"test": "test"}]}

    resp = await connector.twitch_webhook_handler(mock_request)

    assert "Unauthorized" in resp.text
    assert resp.status == 401
def test_base_url_web_config(opsdroid):
    config = {
        "code": "yourcode",
        "channel": "test",
        "client-id": "client-id",
        "client-secret": "client-secret",
    }

    opsdroid.config["web"] = {"base-url": "http://my-awesome-url"}

    connector = ConnectorTwitch(config, opsdroid=opsdroid)

    assert connector.base_url == "http://my-awesome-url"
async def test_get_user_id_unauthorized(opsdroid):
    connector = ConnectorTwitch(connector_config, opsdroid=opsdroid)
    get_response = amock.Mock()
    get_response.status = 401

    with amock.patch("aiohttp.ClientSession.get",
                     new=amock.CoroutineMock()) as patched_request:
        patched_request.return_value = asyncio.Future()
        patched_request.return_value.set_result(get_response)

        with pytest.raises(ConnectionError) as exception:
            await connector.get_user_id("theflyingdev", "token", "client-id")
            assert "Unauthorized" in exception.message
async def test_validate_request(opsdroid):
    connector = ConnectorTwitch(connector_config, opsdroid=opsdroid)
    request = amock.CoroutineMock()
    request.headers = {
        "x-hub-signature":
        "sha256=fcfa24b327e3467f1586cc1ace043c016cabfe9c15dabc0020aca45440338be9"
    }

    request.read = amock.CoroutineMock()
    request.read.return_value = b'{"test": "test"}'

    validation = await connector.validate_request(request, "test")

    assert validation
async def test_get_user_id_failure(opsdroid, caplog):
    connector = ConnectorTwitch(connector_config, opsdroid=opsdroid)
    get_response = amock.Mock()
    get_response.status = 404
    get_response.json = amock.CoroutineMock()

    with amock.patch("aiohttp.ClientSession.get",
                     new=amock.CoroutineMock()) as patched_request:
        patched_request.return_value = asyncio.Future()
        patched_request.return_value.set_result(get_response)

        await connector.get_user_id("theflyingdev", "token", "client-id")

        assert "Unable to receive broadcaster id - Error" in caplog.text
async def test_connect_refresh_token(opsdroid):
    connector = ConnectorTwitch(connector_config, opsdroid=opsdroid)
    connector.auth_file = AUTH_FILE
    connector.webhook = amock.CoroutineMock()
    connector.get_user_id = amock.CoroutineMock(side_effect=ConnectionError)
    connector.refresh_token = amock.CoroutineMock()

    opsdroid.web_server = amock.Mock()

    with pytest.raises(ConnectionError):
        await connector.connect()

        assert connector.webhook.called
        assert opsdroid.web_server.web_app.router.add_get.called
        assert connector.refresh_token.called
def test_save_authentication_data(opsdroid, tmpdir):
    connector = ConnectorTwitch(connector_config, opsdroid=opsdroid)
    connector.auth_file = AUTH_FILE

    connector.save_authentication_data({
        "access_token": "token123",
        "refresh_token": "refresh_token123"
    })

    details = connector.get_authorization_data()

    assert details == {
        "access_token": "token123",
        "refresh_token": "refresh_token123"
    }
async def test_webhook_subscribers(opsdroid):
    connector = ConnectorTwitch(connector_config, opsdroid=opsdroid)

    post_response = amock.Mock()
    post_response.status = 200
    post_response.json = amock.CoroutineMock()

    with amock.patch("aiohttp.ClientSession.post",
                     new=amock.CoroutineMock()) as mocked_session:

        mocked_session.return_value = asyncio.Future()
        mocked_session.return_value.set_result(post_response)

        await connector.webhook("subscribers", "subscribe")

        assert mocked_session.called
async def test_get_user_id(opsdroid):
    connector = ConnectorTwitch(connector_config, opsdroid=opsdroid)
    get_response = amock.Mock()
    get_response.status = 200
    get_response.json = amock.CoroutineMock()
    get_response.json.return_value = {"data": [{"id": "test-bot"}]}

    with amock.patch("aiohttp.ClientSession.get",
                     new=amock.CoroutineMock()) as patched_request:
        patched_request.return_value = asyncio.Future()
        patched_request.return_value.set_result(get_response)

        response = await connector.get_user_id("theflyingdev", "token",
                                               "client-id")

        assert response == "test-bot"
async def test_stream_ended_event(opsdroid):
    connector = ConnectorTwitch(connector_config, opsdroid=opsdroid)
    connector.validate_request = amock.CoroutineMock(return_value=True)
    connector.disconnect_websockets = amock.CoroutineMock()

    mock_request = amock.CoroutineMock()
    mock_request.json = amock.CoroutineMock()
    mock_request.json.return_value = {"data": []}

    twitch_event.StreamEnded = amock.Mock()

    resp = await connector.twitch_webhook_handler(mock_request)

    assert not connector.is_live
    assert resp.status == 200
    assert twitch_event.StreamEnded.called
async def test_connect_websocket(opsdroid, caplog):
    connector = ConnectorTwitch(connector_config, opsdroid=opsdroid)
    caplog.set_level(logging.INFO)

    with amock.patch("aiohttp.ClientSession") as mocked_session:

        mocked_session.ws_connect = amock.CoroutineMock()

        connector.send_handshake = amock.CoroutineMock()
        connector.get_messages_loop = amock.CoroutineMock()

        await connector.connect_websocket()

        assert "Connecting to Twitch IRC Server." in caplog.text
        assert connector.websocket
        assert connector.send_handshake.called
        assert connector.get_messages_loop.called
async def test_listen_reconnect(opsdroid, caplog):
    caplog.set_level(logging.DEBUG)
    connector = ConnectorTwitch(connector_config, opsdroid=opsdroid)
    connector.connect_websocket = amock.CoroutineMock(
        side_effect=ConnectionError)

    with amock.patch(
            "aiohttp.ClientSession.ws_connect",
            new=amock.CoroutineMock()) as mocked_websocket, amock.patch(
                "asyncio.sleep") as mocked_sleep:
        mocked_websocket.side_effect = Exception()

        with contextlib.suppress((Exception)):
            await connector.listen()

            assert mocked_sleep.called
            assert None in caplog.text
            assert connector.reconnections == 1
async def test_webhook_failure(opsdroid, caplog):
    connector = ConnectorTwitch(connector_config, opsdroid=opsdroid)

    caplog.set_level(logging.DEBUG)

    post_response = amock.Mock()
    post_response.status = 500
    post_response.json = amock.CoroutineMock()

    with amock.patch("aiohttp.ClientSession.post",
                     new=amock.CoroutineMock()) as mocked_session:

        mocked_session.return_value = asyncio.Future()
        mocked_session.return_value.set_result(post_response)

        await connector.webhook("subscribers", "subscribe")

        assert "Error:" in caplog.text