"""Participant handler tests."""
import json
import textwrap
from unittest.mock import MagicMock, call, patch
from uuid import UUID
import boto3
import pytest
from neo4j.time import DateTime as Neo4jDateTime
from owsrequest import context
from owsrequest.constants import headers as owsrequest_headers
from owsrequest.utils import mock_request
from owsresponse import response
from participant import config
from participant.api import app
from participant.constants import service as service_constants
from participant.handlers import base as base_handlers
from participant.handlers import label_participant as label_participant_handlers
from participant.logic import label_participant as label_participant_logic
mock_label_participant_response = {
'name': 'L.G. Wise',
'vendorId': 8759,
'subaccountId': 0,
'spotifyId': '5xaT2601i5MC4h6yZa7EOD',
'id': 786281,
'appleMusicId': 111000530,
'createdAt': Neo4jDateTime.from_iso_format('2020-01-01T00:00:00.000000000+00:00'),
'modifiedAt': Neo4jDateTime.from_iso_format('2020-02-01T00:00:00.000000000+00:00'),
}
mock_label_participant_response_json = {
'name': 'L.G. Wise',
'vendorId': 8759,
'subaccountId': 0,
'spotifyId': '5xaT2601i5MC4h6yZa7EOD',
'id': 786281,
'appleMusicId': 111000530,
'createdAt': '2020-01-01T00:00:00.000000000+00:00',
'modifiedAt': '2020-02-01T00:00:00.000000000+00:00',
}
@pytest.mark.parametrize(
(
'test_description',
'label_participant_id',
'flask_g',
'get_label_participant_by_id_status',
'get_label_participant_by_id_message',
'expected_get_label_participant_by_id_calls',
'expected_response_status',
'expected_response_json',
),
[
(
'Test label participant get by id, no context, success.',
123,
{},
222,
mock_label_participant_response,
[call(123)],
222,
mock_label_participant_response_json,
),
],
)
def test_get_label_participant_by_id(
test_description,
label_participant_id,
flask_g,
get_label_participant_by_id_status,
get_label_participant_by_id_message,
expected_get_label_participant_by_id_calls,
expected_response_status,
expected_response_json,
mocker,
test_client,
):
"""Test participant get by id."""
mocker.patch.object(
label_participant_logic,
'get_label_participant_by_id',
return_value=response.Response(
mock_label_participant_response, status=get_label_participant_by_id_status
),
autospec=True,
)
with app.test_request_context():
handler_response = label_participant_handlers.get_label_participant_by_id(
label_participant_id
)
result = json.loads(handler_response.data.decode())
assert (
label_participant_logic.get_label_participant_by_id.mock_calls
) == expected_get_label_participant_by_id_calls
assert handler_response.status_code == expected_response_status
assert result == expected_response_json
mock_label_participants_by_name_response = {
'items': [
{'name': 'The Moors', 'vendorId': 24503, 'subaccountId': 0, 'id': 355008},
{
'name': 'The Latin Brothers',
'vendorId': 24503,
'subaccountId': 0,
'id': 514843,
'createdAt': Neo4jDateTime.from_iso_format(
'2020-01-01T00:00:00.000000000+00:00'
),
'modifiedAt': Neo4jDateTime.from_iso_format(
'2020-02-01T00:00:00.000000000+00:00'
),
},
{
'name': 'Eddy and The Antillan Group',
'vendorId': 24503,
'subaccountId': 0,
'id': 20511754,
'createdAt': Neo4jDateTime.from_iso_format(
'2020-01-01T00:00:00.000000000+00:00'
),
'modifiedAt': Neo4jDateTime.from_iso_format(
'2020-02-01T00:00:00.000000000+00:00'
),
},
],
'pagination': {'total_records': 7},
}
mock_label_participants_by_name_response_json = {
'items': [
{'name': 'The Moors', 'vendorId': 24503, 'subaccountId': 0, 'id': 355008},
{
'name': 'The Latin Brothers',
'vendorId': 24503,
'subaccountId': 0,
'id': 514843,
'createdAt': '2020-01-01T00:00:00.000000000+00:00',
'modifiedAt': '2020-02-01T00:00:00.000000000+00:00',
},
{
'name': 'Eddy and The Antillan Group',
'vendorId': 24503,
'subaccountId': 0,
'id': 20511754,
'createdAt': '2020-01-01T00:00:00.000000000+00:00',
'modifiedAt': '2020-02-01T00:00:00.000000000+00:00',
},
],
'pagination': {'total_records': 7},
}
def test_label_participants_search(mocker, test_client):
"""Test label participant search success."""
mock = mocker.patch(
'participant.logic.label_participant.search_label_participant',
return_value=response.Response(mock_label_participants_by_name_response),
)
name = 'the'
vendor_id = 24503
subaccount_id = 777
role = 'true'
handler_response = test_client.get(
f'/label-participants/search?name={name}'
+ f'&vendor_id={vendor_id}&subaccount_id={subaccount_id}&role={role}'
)
result = json.loads(handler_response.data.decode())
mock.assert_called_with(
{'name': name, 'vendor_id': vendor_id, 'subaccount_id': subaccount_id}, role
)
assert handler_response.status_code == 200
assert result == mock_label_participants_by_name_response_json
def test_label_participants_search_bad_name_results_in_400(mocker, test_client):
"""Test label participant search with bad name."""
mock = mocker.patch('participant.logic.label_participant.search_label_participant')
name = ''
vendor_id = 24503
handler_response = test_client.get(
f'/label-participants/search?name={name}&vendor_id={vendor_id}'
)
result = json.loads(handler_response.data.decode())
mock.assert_not_called()
assert handler_response.status_code == 400
assert result['code'] == 'invalid_usage'
assert result['message'] == {'subaccount_id': ['Field may not be null.']}
def test_label_participants_search_bad_vendor_id_results_in_400(mocker, test_client):
"""Test label participant search with bad data."""
mock = mocker.patch('participant.logic.label_participant.search_label_participant')
name = 'the'
vendor_id = None
handler_response = test_client.get(
f'/label-participants/search?name={name}&vendor_id={vendor_id}'
)
result = json.loads(handler_response.data.decode())
mock.assert_not_called()
assert handler_response.status_code == 400
assert result['code'] == 'invalid_usage'
assert result['message'] == {
'subaccount_id': ['Field may not be null.'],
'vendor_id': ['Not a valid integer.'],
}
@pytest.mark.parametrize(
'params',
[
{
'name': 'Josh and the Supremes',
'vendor_id': 3232,
'subaccount_id': 12345,
'spotify_id': None,
'apple_music_id': None,
},
{
'name': 'Josh and the Supremes',
'vendor_id': 3232,
'subaccount_id': 12345,
'spotify_id': 'spotify-id',
'apple_music_id': None,
},
{
'name': 'Josh and the Supremes',
'vendor_id': 3232,
'subaccount_id': 12345,
'spotify_id': None,
'apple_music_id': 'apple-music-id',
},
{
'name': 'Josh and the Supremes',
'vendor_id': 3232,
'subaccount_id': 12345,
'spotify_id': 'spotify-id',
'apple_music_id': 'apple-music-id',
},
],
)
def test_label_participant_create_success(mocker, test_client, params):
"""Test label participant create success."""
request_params = {key: str(value) for key, value in params.items() if value}
create_label_participant_response_mock = {'mock': 'response'}
status = 333
mock = mocker.patch(
'participant.logic.label_participant.create_label_participant',
return_value=response.Response(
create_label_participant_response_mock, status=status
),
)
handler_response = test_client.post('/label-participants', json=request_params)
result = json.loads(handler_response.data.decode())
mock.assert_called_with(**params)
assert handler_response.status_code == status
assert result == create_label_participant_response_mock
def test_label_participant_create_400_on_bad_input(mocker, test_client):
"""Test label participant create with bad data."""
name = 'Josh and the Supremes'
vendor_id = 'non integer'
subaccount_id = 'non integer'
mock_create_label_participant_request = {
'name': f'{name}',
'vendor_id': f'{vendor_id}',
'subaccount_id': f'{subaccount_id}',
}
mock_create_label_participant_response = {
'code': 'validation_error',
'message': {
'error': {
'subaccount_id': ['Not a valid integer.'],
'vendor_id': ['Not a valid integer.'],
}
},
}
mock = mocker.patch('participant.logic.label_participant.create_label_participant')
handler_response = test_client.post(
'/label-participants', json=mock_create_label_participant_request
)
result = json.loads(handler_response.data.decode())
# mock not called since input validator short-circuits on bad data
mock.assert_not_called()
assert handler_response.status_code == 400
assert result == mock_create_label_participant_response
# flake8: noqa
@pytest.mark.parametrize(
('assigned_to_email', 'expected_list_of_email', 'participant_id'),
[
(
'applesauce.bananas@theorchard.com',
['applesauce.bananas@theorchard.com'],
334455,
),
(
'foo@theorchard.com',
['foo@theorchard.com'],
'a87122db-3d92-41fb-a3c9-9d5b35f3f6f8',
),
],
)
def test_contact(
mocker, test_client, assigned_to_email, expected_list_of_email, participant_id
):
"""Test contact."""
mock_request.get(
service_constants.OWS_ACCOUNT,
service_constants.OWS_ACCOUNT_VENDOR_DOCUMENT_RESOURCE.format(vendor_id=123),
{'assigned_to_email': assigned_to_email},
200,
)
mocker.patch.object(
context,
'get_request_context_from_headers',
autospec=True,
return_value=MagicMock(
context_type=None,
identity_id=None,
profile_id=678,
profile_type=owsrequest_headers.PROFILE_TYPE_LABEL,
),
)
mocker.patch.object(
base_handlers,
'get_vendor_and_subaccount',
autospec=True,
return_value=(123, 678),
)
mocker.patch.object(boto3, 'client', autospec=True)
with app.test_request_context():
test_client.post(
'/contact',
json={
'participant_id': participant_id,
'participant_name': 'Name',
'message': 'a message about something',
'vendor_id': 123,
'subaccount_id': 678,
'product_id': 123,
'product_name': 'product name',
'upc': 'UPC',
'current_artist_name': 'current',
'new_artist_name': 'new',
},
query_string={'vendor_id': 123, 'subaccount_id': 678},
)
boto3.client.return_value.send_email.assert_called_once_with(
Source='info@theorchard.com',
Destination={
'ToAddresses': expected_list_of_email,
'CcAddresses': config.BASE_EMAIL_CC,
},
Message={
'Subject': {
'Data': 'Missing or Incorrect Artist Profile',
'Charset': 'UTF-8',
},
'Body': {
'Html': {
'Charset': 'UTF-8',
'Data': textwrap.dedent(
f"""
Greetings Label Manager,
Your label listed below has notified us that there's missing or incorrect information regarding an artist in their roster.