"""Test module for Deezer flow utilities."""
from datetime import datetime
import os
from unittest import mock, TestCase
from unittest.mock import call, patch
import pytest
from feed_ingestion.flows.deezer_marketshare import config
import feed_ingestion.util.deezer_zephir_utils as zephir
_date = '2016-05-23'
class TestDeezerZephirUtils(TestCase):
"""Test class for deezer_zephir_utils."""
@pytest.fixture(autouse=True)
def set_files_dir(self):
"""Set files directory data."""
self.file_name = 'TheOrchard_201605_7221.zip'
self.local_file_path = f'/tests/util/{self.file_name}'
self.local_dir = '/tests/util'
self.source = 'source.csv'
@pytest.fixture(autouse=True)
def set_zephir_settings(self):
"""Set Zephir settings."""
self.zephir_settings = {
'username': 'test_deezer_user',
'password': 'TestPassword123',
'host': 'https://zephir.deezer.com',
'path': '/external/download?file=invoices/',
'search': '/external?path=invoices',
}
@pytest.fixture(autouse=True)
def set_search_invoice_response(self):
"""Set search invoice page response."""
self.search_invoice_response = \
'\n\n\n\n
\n' \
'TheOrchard_201605_adjustment_7221.zip\n' \
' | \n\n\n\n | \n' \
'TheOrchard_201703_6641.zip\n' \
' | \n\n\n\n | \n' \
'TheOrchard_201511_7081.zip\n' \
' | \n\n\n\n | \n' \
'TheOrchard_201605_4301.zip\n' \
' | \n\n\n\n | \n' \
'TheOrchard_201605_7221.zip\n' \
' | \n'
@patch('os.walk')
@patch('gzip.open')
@patch('shutil.copyfileobj')
@patch('builtins.open', read_data='data')
@patch('feed_ingestion.util.deezer_zephir_utils.ZipFile')
def test_repack_source_file_one_file(
self, mock_zipfile, mock_open, mock_copyfileobj,
mock_gzip_open, mock_walk):
"""Test repack_source_file for one file (str)."""
mock_walk.return_value = [
('/tests', ('util',), ('file_1.test', 'file_2.txt')),
('/tests/util', (), (self.source,)),
]
files = zephir.repack_source_file(
self.local_file_path, self.local_dir, self.source, True)
self.assertEqual(files, [{
'gzip_file': f'{self.local_dir}/{self.source}.gz',
'gzip_file_name': f'{self.source}.gz',
'source_file_name': self.source
}])
self.assertEqual(f'{self.local_dir}/{self.source}',
'/tests/util/source.csv')
@patch('os.walk')
@patch('gzip.open')
@patch('shutil.copyfileobj')
@patch('builtins.open', read_data='data')
@patch('feed_ingestion.util.deezer_zephir_utils.ZipFile')
def test_repack_source_file_multiple_files(
self, mock_zipfile, mock_open, mock_copyfileobj,
mock_gzip_open, mock_walk):
"""Test repack_source_file for source files dict."""
mock_walk.return_value = [
('/tests', ('util',), ('file_1.test', 'file_2.txt')),
('/tests/util', (), (self.source,)),
]
_files = ['file_2.txt', self.source]
files = zephir.repack_source_file(
self.local_file_path, self.local_dir, {'files': _files})
self.assertEqual(files, [{
'gzip_file':
f'{self.local_dir if source is self.source else "/tests"}'
f'/{source}.gz',
'gzip_file_name': f'{source}.gz',
'source_file_name': source} for source in _files])
self.assertEqual(f'{self.local_dir}/{self.source}',
'/tests/util/source.csv')
@patch('requests.session')
def test_log_in_to_zephir(self, mock_session):
"""Test Zephir log in."""
session = zephir.log_in_to_zephir(self.zephir_settings)
self.assertIn(call.get(
f"{self.zephir_settings.get('host')}/accounts/login/"),
session.method_calls)
self.assertIn(call.post(
f"{self.zephir_settings.get('host')}/accounts/login/",
data={
'username': self.zephir_settings.get('username'),
'password': self.zephir_settings.get('password'),
'csrfmiddlewaretoken': mock.ANY,
'next': '/',
}), session.method_calls)
self.assertIn(call.post().raise_for_status(), session.mock_calls)
@patch('requests.session')
def test_search_for_file_on_zephir(self, mock_session):
"""Test Zephir search page."""
mock_session.get().text = self.search_invoice_response
date_obj = datetime.strptime(_date, '%Y-%m-%d')
pattern = config.zip_pattern.format(date=date_obj)
files = zephir.search_for_file_on_zephir(
mock_session, self.zephir_settings, pattern)
self.assertIn(call.get(f"{self.zephir_settings.get('host')}"
f"{self.zephir_settings.get('search')}"),
mock_session.method_calls)
self.assertEqual(files, {'TheOrchard_201605_4301.zip',
'TheOrchard_201605_7221.zip',
'TheOrchard_201605_adjustment_7221.zip'})
@patch('os.path.getsize')
@patch('builtins.open')
@patch('requests.session')
@patch('feed_ingestion.util.deezer_zephir_utils.is_zipfile')
def test_download_file_from_zephir(
self, mock_is_zipfile, mock_session, mock_open, mock_getsize):
"""Test Zephir file successful downloading."""
mock_session.get().content = 'test_content'
mock_getsize.return_value = 10
local_path = zephir.download_file_from_zephir(
mock_session, self.zephir_settings, self.file_name, self.local_dir)
self.assertIn(call().__enter__().write(mock_session.get().content),
mock_open.mock_calls)
self.assertIn(call(f'{self.local_dir}/{self.file_name}', 'wb'),
mock_open.mock_calls)
self.assertIn(call.get().raise_for_status(), mock_session.mock_calls)
self.assertEqual(mock_getsize.call_count, 1)
self.assertEqual(mock_is_zipfile.call_count, 1)
self.assertEqual(local_path, f'{self.local_dir}/{self.file_name}')
@patch('os.path.getsize')
@patch('io.open')
@patch('requests.session')
def test_download_invalid_file_from_zephir(
self, mock_session, mock_io_open, mock_getsize):
"""Test writing invalid file (pattern, i.e. not .zip)."""
mock_session.get().content = b'test_content'
mock_io_open.tell.return_value = 0
mock_getsize.return_value = 10
non_zip_file_name = 'file.py'
exception = 'Zephir service have not returned a .zip file'
with self.assertRaises(FileNotFoundError) as context:
zephir.download_file_from_zephir(
mock_session, self.zephir_settings, non_zip_file_name, './')
self.assertIn(call.get(
f"{self.zephir_settings.get('host')}"
f"{self.zephir_settings.get('path')}{non_zip_file_name}"),
mock_session.method_calls)
self.assertIn(call.get().raise_for_status(), mock_session.mock_calls)
self.assertEqual(context.exception.args[0], exception)
os.remove(f'./{non_zip_file_name}')
@patch('feed_ingestion.util.deezer_zephir_utils.log_in_to_zephir')
@patch('feed_ingestion.util.deezer_zephir_utils.download_file_from_zephir')
def test_download_daily_report_from_zephir(
self, mock_download, mock_login):
"""Test downloading daily report from Zephir."""
zephir.download_daily_report_from_zephir(
self.zephir_settings, self.file_name, './')
self.assertEqual(mock_login.call_count, 1)
self.assertEqual(mock_download.call_count, 1)
self.assertIn(call(self.zephir_settings),
mock_login.mock_calls)
self.assertIn(call(
mock_login(), self.zephir_settings, self.file_name, './'),
mock_download.mock_calls)
@patch('requests.session')
@patch('feed_ingestion.util.deezer_zephir_utils.log_in_to_zephir')
@patch('feed_ingestion.util.deezer_zephir_utils.download_file_from_zephir')
def test_download_invoice_from_zephir(
self, mock_download, mock_login, mock_session):
"""Test downloading invoice from Zephir."""
# mock_search.return_value = self.search_invoice_response
mock_login().get().text = self.search_invoice_response # *
mock_download.return_value = 'file.csv.zip'
date_obj = datetime.strptime(_date, '%Y-%m-%d')
pattern = config.zip_pattern.format(date=date_obj)
files = zephir.download_invoice_from_zephir(
self.zephir_settings, pattern, './')
self.assertEqual(mock_login.call_count, 2)
# one call is for the *-marked line, the 2d is actual
self.assertIn(call(self.zephir_settings),
mock_login.mock_calls)
self.assertIn(call().get(f"{self.zephir_settings.get('host')}"
f"{self.zephir_settings.get('search')}"),
mock_login.mock_calls)
self.assertEqual(mock_download.call_count, 3)
# for all the matching three files
self.assertIn(call(
mock_login(), self.zephir_settings, self.file_name, './'),
mock_download.mock_calls) # matches three times
self.assertEqual(files, ['file.csv.zip'] * 3)