Source code for common.tests.mixins
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Wed Mar 27 14:34:02 2019
@author: Paolo Cozzi <cozzi@ibba.cnr.it>
"""
import os
import shutil
import pathlib
from unittest.mock import patch, Mock
from django.urls import reverse
from django.test import Client
from django.contrib.messages import get_messages
from ..constants import LOADED, ERROR
[docs]class LoginMixinTestCase(object):
url = None
[docs] def test_unathenticated(self):
'''Non Authenticated user are directed to login page'''
login_url = reverse("login")
client = Client()
response = client.get(self.url)
self.assertRedirects(
response, '{login_url}?next={url}'.format(
login_url=login_url, url=self.url)
)
[docs]class OwnerMixinTestCase(LoginMixinTestCase):
[docs] def test_ownership(self):
"""Test a non-owner having a 404 response"""
client = Client()
client.login(username='test2', password='test2')
response = client.get(self.url)
self.assertEqual(response.status_code, 404)
[docs]class StatusMixinTestCase(object):
response = None
[docs] def test_status_code(self):
self.assertEqual(self.response.status_code, 200)
[docs]class MessageMixinTestCase(object):
[docs] def check_messages(self, response, tag, message_text):
"""Check that a response has a message of a certain type"""
# each element is an instance
# of django.contrib.messages.storage.base.Message
all_messages = [msg for msg in get_messages(response.wsgi_request)]
found = False
# I can have moltiple message, and maybe I need to find a specific one
for message in all_messages:
if tag in message.tags and message_text in message.message:
found = True
self.assertTrue(found)
[docs]class GeneralMixinTestCase(LoginMixinTestCase, StatusMixinTestCase,
MessageMixinTestCase):
pass
# methods like test_unathenticated need to be called with FormMixinTestCase
# a mixin to ensure that datasource is in place
[docs]class DataSourceMixinTestCase(object):
"""Place file in data source directory"""
# class attributes
uploaded_file = False
dst_path = None
submission_model = None
base_dir = os.path.dirname(os.path.abspath(__file__))
# the method is use to upload data into database
upload_method = None
[docs] @classmethod
def setUpClass(cls):
"""Place test data in data source if needed"""
# calling my base class setup
super().setUpClass()
# ensuring that file cryoweb_test_data_only.sql is present
submission = cls.submission_model.objects.get(pk=1)
cls.dst_path = submission.uploaded_file.path
# get datasource filename from submission object
datasource_filename = os.path.basename(submission.uploaded_file.path)
if not os.path.exists(cls.dst_path):
src_path = os.path.join(cls.base_dir, datasource_filename)
shutil.copy(src_path, cls.dst_path)
cls.uploaded_file = True
[docs] @classmethod
def tearDownClass(cls):
"""Remove test data from data source if needed"""
# remove file if I placed it for tests
if cls.uploaded_file and pathlib.Path(cls.dst_path).exists():
os.remove(cls.dst_path)
# calling my base class teardown class
super().tearDownClass()
[docs] def upload_datasource(self, message):
"""Testing uploading and importing data from crbanim to UID"""
# assert upload
self.assertTrue(self.upload_method(self.submission))
# reload submission
self.submission.refresh_from_db()
# assert submission messages
self.assertEqual(
self.submission.status,
LOADED)
self.assertIn(
message,
self.submission.message)
[docs] def check_errors(self, my_check, message):
"""Common stuff for error in file loading"""
self.assertFalse(self.upload_method(self.submission))
# reload submission
self.submission.refresh_from_db()
# test my mock method called
self.assertTrue(my_check.called)
self.assertEqual(
self.submission.status,
ERROR)
# check for two distinct messages
self.assertIn(
message,
self.submission.message)
self.assertNotIn(
"import completed for submission",
self.submission.message)
[docs]class AsyncIOMixin(object):
"""A common mixing for async objects"""
[docs] def setUp(self):
# calling my base class setup
super().setUp()
# setting channels methods
self.asyncio_mock_patcher = patch(
'asyncio.get_event_loop')
self.asyncio_mock = self.asyncio_mock_patcher.start()
# mocking asyncio return value
self.run_until = self.asyncio_mock.return_value
self.run_until.run_until_complete = Mock()
[docs] def tearDown(self):
# stopping mock objects
self.asyncio_mock_patcher.stop()
# calling base methods
super().tearDown()
# TODO: move into submission.tests (since related to submission.helpers)
[docs]class WebSocketMixin(AsyncIOMixin):
"""Override setUp to mock websocket objects"""
[docs] def setUp(self):
# calling my base class setup
super().setUp()
# another patch
self.send_msg_ws_patcher = patch(
'submissions.helpers.send_message_to_websocket')
self.send_msg_ws = self.send_msg_ws_patcher.start()
[docs] def tearDown(self):
# stopping mock objects
self.send_msg_ws_patcher.stop()
# calling base methods
super().tearDown()
[docs] def check_message(
self, message, notification_message, validation_message=None,
pk=1):
"""assert message to websocket called with parameters"""
# construct message according parameters
message = {
'message': message,
'notification_message': notification_message
}
# in case of successful data upload, a validation message is sent
if validation_message:
message['validation_message'] = validation_message
self.assertEqual(self.asyncio_mock.call_count, 1)
self.assertEqual(self.run_until.run_until_complete.call_count, 1)
self.assertEqual(self.send_msg_ws.call_count, 1)
self.send_msg_ws.assert_called_with(
message,
pk)
[docs] def check_message_not_called(self):
"""Check django channels async messages not called"""
self.assertEqual(self.asyncio_mock.call_count, 0)
self.assertEqual(self.run_until.run_until_complete.call_count, 0)
self.assertEqual(self.send_msg_ws.call_count, 0)