docbow/tests/main/test_main.py

641 lines
23 KiB
Python

"""
This file demonstrates writing tests using the unittest module. These will pass
when you run "manage.py test".
Replace this with more appropriate tests for your application.
"""
import re
import tempfile
from django.test import TestCase
from django.test.utils import override_settings
from django.contrib.auth.models import User
from django.contrib.auth.tokens import default_token_generator
from django.core import mail, management
from django.core.files.base import ContentFile
from django.test import Client
from django.utils.encoding import force_bytes, force_text
from django.utils.http import urlsafe_base64_encode
import mock
import pytest
from docbow_project.docbow import app_settings, notification
from docbow_project.docbow.models import (
Document,
AttachedFile,
Mailbox,
Notification,
DocbowProfile,
MailingList,
generate_filename,
FileType,
FileTypeAttachedFileKind,
Delegation,
all_emails,
is_guest,
NotificationPreference,
AutomaticForwarding,
)
from docbow_project.docbow.notification import MailNotifier
from docbow_project.docbow.upload_views import file_response
from docbow_project.docbow.utils import file_match_mime_types
MEDIA_ROOT = tempfile.mkdtemp()
def create_users(num_user):
"""Creates 'num_user' User objects
with a DocbowProfile for each User
Return the User list
"""
result = []
for i in range(num_user):
result.append(User.objects.create(username='user-%s' % i, email='user-%s@example.com' % i))
result[-1].set_password('password')
result[-1].save()
DocbowProfile.objects.create(user=result[-1], personal_email='personal-email-user-%s@example.com' % i)
return result
@pytest.fixture
def users_fixture():
return create_users(10)
@pytest.fixture
def admin():
admin = User.objects.create(username='admin', email='admin@localhost', is_superuser=True, is_staff=True)
admin.set_password('admin')
admin.save()
return admin
def create_filetypes(num_filetype):
"""Creates 'num_filetype' FileType objects
Return the list of creqted FileType objects
"""
result = []
for i in range(num_filetype):
result.append(FileType.objects.create(name='filetype-%s' % i))
return result
@pytest.fixture
def filetype_fixtures():
return create_filetypes(10)
class MailingListTreeTestCase(TestCase):
'''Test mailing lists member resolution in a classical tree setup.'''
def setUp(self):
self.users = []
self.mls = []
for i in range(20):
self.users.append(User.objects.create(username='%s' % i))
sublist = []
for i in range(19, -1, -1):
self.mls.insert(0, MailingList.objects.create(name='%s' % i))
self.mls[0].members.set([self.users[i]])
self.mls[0].mailing_list_members.set(sublist)
sublist = [self.mls[0]]
def test_mailing_list_recursive(self):
"""
Tests that 1 + 1 always equals 2.
"""
for i in range(19, -1, -1):
members = self.mls[i].recursive_members()
self.assertEqual(len(members), 20 - i)
def test_mailing_list_recursive_with_origin(self):
"""
Tests that 1 + 1 always equals 2.
"""
for i in range(19, -1, -1):
mwo = self.mls[i].recursive_members_with_origin()
self.assertEqual(len(mwo), 20 - i)
for j in range(i, 20):
self.assertEqual(mwo[self.users[j]], set(self.mls[i : j + 1]))
class MailingListCycle(TestCase):
'''Test mailing lists member resolution in a cyclic setup.'''
def setUp(self):
self.users = []
self.mls = []
for i in range(20):
self.users.append(User.objects.create(username='%s' % i))
sublist = []
for i in range(19, -1, -1):
self.mls.insert(0, MailingList.objects.create(name='%s' % i))
self.mls[0].members.set([self.users[i]])
self.mls[0].mailing_list_members.set(sublist)
sublist = [self.mls[0]]
self.mls[19].mailing_list_members.set([self.mls[0]])
def test_mailing_list_recursive(self):
"""
Tests that 1 + 1 always equals 2.
"""
for i in range(19, -1, -1):
members = self.mls[i].recursive_members()
self.assertEqual(len(members), 20)
def test_mailing_list_recursive_with_origin(self):
"""
Tests that 1 + 1 always equals 2.
"""
for i in range(19, -1, -1):
mwo = self.mls[i].recursive_members_with_origin()
self.assertEqual(len(mwo), 20)
for j in range(i, 20):
self.assertEqual(mwo[self.users[j]], set(self.mls))
@override_settings(MEDIA_ROOT=MEDIA_ROOT)
class BaseTestCase(TestCase):
COUNT = 10
def setUp(self):
self.setUpUsers()
self.setUpDocuments()
def setUpUsers(self):
self.users = create_users(self.COUNT)
self.filetypes = create_filetypes(self.COUNT)
def setUpDocuments(self):
self.documents = []
for i in range(self.COUNT):
self.documents.append(
Document.objects.create(sender=self.users[(i + 2) % self.COUNT], filetype=self.filetypes[i])
)
self.documents[-1].to_user.set([self.users[i % self.COUNT], self.users[(i + 1) % self.COUNT]])
for j in range(2):
attached_file = AttachedFile(name='file%s' % j, document=self.documents[-1], kind=None)
attached_file.content.save('file%s.pdf' % j, ContentFile('coucou'))
attached_file.save()
self.documents[-1].post()
class BasicTestCase(BaseTestCase):
def test_notification_mail(self):
management.call_command('notify')
assert len(mail.outbox) == 20
outbox = sorted(mail.outbox, key=lambda m: tuple(sorted(m.to)))
MAIL_LINK_RE = re.compile('https?://[^/]+/inbox/\d+/')
for message, i in zip(outbox, range(20)):
recipient = self.users[i // 2]
if app_settings.PERSONAL_EMAIL:
emails = [recipient.docbowprofile.personal_email, recipient.email]
else:
emails = [recipient.email]
assert set(message.to) == set(emails)
match = MAIL_LINK_RE.search(message.body)
self.assertIsNotNone(match)
class UtilsTestCase(BaseTestCase):
def setUp(self):
super(UtilsTestCase, self).setUp()
self.user1, self.user2 = self.users[:2]
self.user2.docbowprofile.is_guest = True
self.user2.docbowprofile.save()
def test_generate_filename(self):
assert generate_filename(None, 'xxx.pdf') != generate_filename(None, 'xxx.pdf')
def test_all_emails(self):
assert set(all_emails(self.user1)) == set((self.user1.email, self.user1.docbowprofile.personal_email))
def test_document_manager(self):
with self.assertNumQueries(1):
# With django 1.8, there is no more
# eager loading of related AttachedFile objects
# so only one SQL query here
list(Document.objects.all())
def test_document_accessors(self):
document = self.documents[0]
filenames = set([filename.strip() for filename in document.filenames().split(',')])
self.assertEqual(filenames, set(['file0.pdf', 'file1.pdf']))
self.assertEqual(set(document.user_human_to()), set(['user-0', 'user-1']))
self.assertEqual(document.group_human_to(), [])
self.assertEqual(set(document.human_to()), set(['user-0', 'user-1']))
self.assertIsNotNone(document.filename_links())
assert set(document.to()) == set((self.user1, self.user2))
assert dict(document.to_with_origin()) == {
self.user1: set(['--direct--']),
self.user2: set(['--direct--']),
}
self.assertEqual(
sorted(map(lambda x: x.pk, document.delivered_to())), sorted([self.user1.pk, self.user2.pk])
)
def test_is_guest(self):
self.assertTrue(is_guest(self.user2))
class DelegatesTestCase(BaseTestCase):
def setUp(self):
self.setUpUsers()
self.delegate = delegate = User(username='delegate')
delegate.set_password('delegate')
delegate.save()
Delegation.objects.create(by=self.users[0], to=delegate)
Delegation.objects.create(by=self.users[1], to=delegate)
self.guest0 = User(username='user-0-1')
self.guest0.set_password('guest')
self.guest0.save()
DocbowProfile.objects.create(user=self.guest0, is_guest=True)
Delegation.objects.create(by=self.users[0], to=self.guest0)
self.guest1 = User(username='user-1-1')
self.guest1.set_password('guest')
self.guest1.save()
DocbowProfile.objects.create(user=self.guest1, is_guest=True)
Delegation.objects.create(by=self.users[1], to=self.guest1)
def test_inbox_by_document(self):
document = Document.objects.create(sender=self.users[0], filetype=self.filetypes[0])
document.to_user.set([self.users[1]])
attached_file = AttachedFile(name='file-private-flag.pdf', document=document, kind=None)
attached_file.content.save('file-private-flag.pdf', ContentFile('coucou'))
attached_file.save()
document.post()
c = Client()
c.login(username='delegate', password='delegate')
response = c.get('/inbox/%s/' % document.pk)
self.assertEqual(response.status_code, 200)
def test_private_flag(self):
document = Document.objects.create(sender=self.users[0], filetype=self.filetypes[0], private=True)
document.to_user.set([self.users[1]])
attached_file = AttachedFile(name='file-private-flag.pdf', document=document, kind=None)
attached_file.content.save('file-private-flag.pdf', ContentFile('coucou'))
attached_file.save()
document.post()
self.assertEqual(Mailbox.objects.count(), 2)
# check user-1 sees document in its outbox
c = Client()
c.login(username='user-0', password='password')
response = c.get('/outbox/')
self.assertIn('/outbox/%s/' % document.pk, force_text(response.content))
response = c.get('/outbox/%s/' % document.pk)
self.assertEqual(response.status_code, 200)
response = c.get('/inbox/')
self.assertNotIn('/inbox/%s/' % document.pk, force_text(response.content))
response = c.get('/inbox/%s/' % document.pk)
self.assertEqual(response.status_code, 302)
# check user-1 sees document in its inbox
c = Client()
c.login(username='user-1', password='password')
response = c.get('/inbox/')
self.assertIn('/inbox/%s/' % document.pk, force_text(response.content))
response = c.get('/inbox/%s/' % document.pk)
self.assertEqual(response.status_code, 200)
response = c.get('/outbox/%s/' % document.pk)
self.assertEqual(response.status_code, 302)
response = c.get('/outbox/')
self.assertNotIn('/outbox/%s/' % document.pk, force_text(response.content))
# check delegate sees nothing
c = Client()
c.login(username='delegate', password='delegate')
response = c.get('/inbox/')
self.assertNotIn('/inbox/%s/' % document.pk, force_text(response.content))
response = c.get('/inbox/%s/' % document.pk)
self.assertEqual(response.status_code, 302)
response = c.get('/outbox/')
self.assertNotIn('/outbox/%s/' % document.pk, force_text(response.content))
response = c.get('/outbox/%s/' % document.pk)
self.assertEqual(response.status_code, 302)
# check notifications
with self.assertRaises(Notification.DoesNotExist):
Notification.objects.get(user=self.delegate, document=document)
Notification.objects.get(user=self.users[1], document=document)
class DummyNotifier(notification.BaseNotifier):
notifications = []
description = 'Dummy'
key = 'dummy'
def process(self, notification):
self.notifications.append(notification)
@classmethod
def reset(cls):
cls.notifications = []
class NotificationTestCase(BaseTestCase):
COUNT = 2
def test_notification_preferences(self):
NotificationPreference.objects.create(
user=self.users[0], kind=DummyNotifier.key, filetype=self.filetypes[0], value=False
)
NotificationPreference.objects.create(
user=self.users[1], kind=DummyNotifier.key, filetype=self.filetypes[1], value=False
)
DummyNotifier.notifications = []
with mock.patch('docbow_project.docbow.notification.get_notifiers') as MockClass:
MockClass.return_value = [DummyNotifier()]
notification.process_notifications()
self.assertEqual(len(DummyNotifier.notifications), 2)
for notif in DummyNotifier.notifications:
with self.assertRaises(NotificationPreference.DoesNotExist):
NotificationPreference.objects.get(
user=notif.user, filetype=notif.document.filetype, kind=DummyNotifier.key, value=False
)
@override_settings(MEDIA_ROOT=MEDIA_ROOT)
class NotificationToDelegatesTestCase(TestCase):
def send_document(
self,
sender,
user_recipients=[],
list_recipients=[],
filetype_name='dummy filetype',
names_and_contents=(('dummy.pdf', 'dummy content'),),
):
filetype, created = FileType.objects.get_or_create(name=filetype_name)
document = Document.objects.create(sender=sender, filetype=filetype)
document.to_user.set(user_recipients)
document.to_list.set(list_recipients)
attached_files = []
for name, content in names_and_contents:
attached_file = AttachedFile(name=name, document=document, kind=None)
attached_file.content.save(name, ContentFile(content))
attached_file.save()
attached_files.append(attached_file)
document.post()
return document, attached_files
def test_notification_to_delegates(self):
DummyNotifier.reset()
sender = User.objects.create(username='sender')
recipient = User.objects.create(username='recipient')
delegate = User.objects.create(username='delegate')
Delegation.objects.create(by=recipient, to=delegate)
self.send_document(sender, [recipient])
with mock.patch('docbow_project.docbow.notification.get_notifiers') as MockClass:
MockClass.return_value = [DummyNotifier()]
notification.process_notifications()
self.assertEqual(len(DummyNotifier.notifications), 2)
self.assertEqual(
set([notif.user for notif in DummyNotifier.notifications]), set([recipient, delegate])
)
delegate.is_active = False
delegate.save()
DummyNotifier.reset()
self.send_document(sender, [recipient])
notification.process_notifications()
self.assertEqual(len(DummyNotifier.notifications), 1)
self.assertEqual(DummyNotifier.notifications[0].user, recipient)
class AddListTestCase(BaseTestCase):
def test_add_mailing_list(self):
management.call_command('add-list', 'some-ml-name')
ml_list = MailingList.objects.all()
assert len(ml_list) == 1
assert ml_list[0].name == 'some-ml-name'
@pytest.mark.django_db
def test_send_file(users_fixture, filetype_fixtures, settings, tmpdir, monkeypatch):
settings.MEDIA_ROOT = tmpdir.strpath
monkeypatch.chdir(tmpdir)
filetosend = tmpdir.join('filetosend.txt')
filetosend.write("file content")
management.call_command(
'sendfile',
'--sender',
'user-1',
'--to-user',
'user-2',
'--filetype',
'filetype-1',
'--description',
'yes we can',
'filetosend.txt',
)
docs = Document.objects.all()
assert len(docs) == 1
doc = docs[0]
assert doc.sender.get_username() == 'user-1'
assert doc.filetype.name == 'filetype-1'
attf = doc.attached_files.first()
assert attf.filename() == 'filetosend.txt'
# FIXME : way more things to assert here
@pytest.mark.django_db
def test_signals(users_fixture, settings, monkeypatch, tmpdir):
# django_journal is called through django signals (db_post_save).
# We test here that the docbow_project.docbow.signals.modified_data function
# which is connected to the db_post_dave signal, does not raised an exception.
# a MockMiddleware is used to make the function believe that an action
# of an authenticated user triggered the signal, otherwise the function
# exit too quickly
settings.MEDIA_ROOT = tmpdir.strpath
class MockMiddleware(object):
NO_USER = '-'
def get_extra(self):
return {'user': 'test-user', 'ip': '0.0.0.0'}
import docbow_project.docbow.signals
user0 = users_fixture[0]
monkeypatch.setattr(docbow_project.docbow.signals, 'middleware', MockMiddleware())
# We just need no exception raised
docbow_project.docbow.signals.modified_data(
sender=User, instance=user0, created=False, raw=False, using='default'
)
def test_file_response(tmpdir):
tmp_file = tmpdir.join('somefile')
data = b'abcdef'
with tmp_file.open('wb') as f:
f.write(data)
response = file_response(tmp_file.open())
assert response['Content-Type'] == 'application/octet-stream'
assert response.content == data
def test_mime_buffer_size_default(db):
assert app_settings.MIME_BUFFER_SIZE == 300000
def test_file_match_mime_types(tmpdir):
tmp_file = tmpdir.join('aaa.txt')
with tmp_file.open('w') as f:
f.write('aaa')
assert file_match_mime_types(tmp_file.open(), ['text/plain'], 100)
def test_filetype_attached_file_kind(db, tmpdir):
ft = FileType.objects.create(name='some file type')
fta = FileTypeAttachedFileKind.objects.create(
name='text-plain', file_type=ft, mime_types='text/plain', position=1
)
text_file = tmpdir.join('aaa.txt')
with text_file.open('w') as f:
f.write('aaa')
with text_file.open() as f:
assert fta.match_file(f)
fta.mime_types = 'image/jpeg'
fta.save()
with text_file.open() as f:
assert not fta.match_file(f)
@pytest.mark.django_db
def test_password_reset(users_fixture, settings, monkeypatch, client):
client.login(username='user-1@example.com', password='password')
import django.contrib.auth.forms
email_message = mock.Mock()
email_message_factory = mock.Mock(return_value=email_message)
monkeypatch.setattr(django.contrib.auth.forms, 'EmailMultiAlternatives', email_message_factory)
response = client.post('/accounts/password/reset/', {'identifier': 'user-1@example.com'})
assert response.status_code == 302
assert response['Location'].endswith('reset/done/')
assert email_message_factory.call_args_list[0][0][-1] == ['user-1@example.com']
email_message.send.assert_called_with()
@pytest.mark.django_db
def test_password_reset_confirm_get(users_fixture, client):
user = users_fixture[0]
uid = force_text(urlsafe_base64_encode(force_bytes(user.id)))
token = default_token_generator.make_token(user)
response = client.get('/accounts/password/reset/confirm/%s/%s/' % (uid, token))
assert response.status_code == 302
redirect_url = '/accounts/password/reset/confirm/%s/set-password/' % uid
assert response['Location'].endswith(redirect_url)
response = client.get(redirect_url)
assert 'new_password' in force_text(response.content)
@pytest.mark.django_db
def test_password_reset_confirm_post(users_fixture, client):
user = users_fixture[0]
uid = force_text(urlsafe_base64_encode(force_bytes(user.id)))
token = default_token_generator.make_token(user)
response = client.get('/accounts/password/reset/confirm/%s/%s/' % (uid, token))
assert response.status_code == 302
redirect_url = '/accounts/password/reset/confirm/%s/set-password/' % uid
assert response['Location'].endswith(redirect_url)
response = client.post(redirect_url, {'new_password1': 'newpass', 'new_password2': 'newpass'})
assert response.status_code == 302
assert response['Location'].endswith('/accounts/password/reset/complete/')
response = client.get('/accounts/password/reset/complete/')
assert response.status_code == 200
client.login(username='user-1@example.com', password='newpass')
@pytest.mark.django_db
def test_admin_notification(admin, users_fixture, filetype_fixtures, client):
user = users_fixture[0]
doc = Document.objects.create(sender=user, filetype=filetype_fixtures[0])
Notification.objects.create(document=doc)
client.login(username='admin', password='admin')
resp = client.get('/admin/docbow/notification/')
assert resp.status_code == 200
@pytest.mark.django_db
def test_admin_automaticforwarding(admin, users_fixture, filetype_fixtures, client):
user = users_fixture[0]
at = AutomaticForwarding.objects.create()
at.filetypes.add(filetype_fixtures[0])
at.originaly_to_user.add(admin)
at.forward_to_user.add(user)
client.login(username='admin', password='admin')
resp = client.get('/admin/docbow/automaticforwarding/')
assert resp.status_code == 200
@pytest.mark.django_db
def test_delegate_login(client):
recipient = User.objects.create(username='recipient')
recipient.set_password('password')
recipient.save()
delegate = User.objects.create(username='recipient-1')
delegate.set_password('password')
delegate.save()
DocbowProfile.objects.create(user=delegate, is_guest=True)
Delegation.objects.create(by=recipient, to=delegate)
response = client.post(
'/accounts/login/?next=/inbox/', {'username': 'recipient-1', 'password': 'password'}
)
assert response.status_code == 302
assert response['Location'].endswith('inbox/')
@pytest.mark.django_db
def test_delegate_login_hyphen_in_user_name(client):
recipient = User.objects.create(username='recipient-tricky')
recipient.set_password('password')
recipient.save()
delegate = User.objects.create(username='recipient-tricky-1')
delegate.set_password('password')
delegate.save()
DocbowProfile.objects.create(user=delegate, is_guest=True)
Delegation.objects.create(by=recipient, to=delegate)
response = client.post(
'/accounts/login/?next=/inbox/', {'username': 'recipient-tricky-1', 'password': 'password'}
)
assert response.status_code == 302
assert response['Location'].endswith('inbox/')
@pytest.mark.django_db
def test_mail_notifier_but_nobody_to_notify():
ctx = {'to': ['']}
notification = Notification.objects.create(ctx=ctx)
notifier = MailNotifier()
notifier.process(notification)
assert len(mail.outbox) == 0