Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixed async tests #22

Merged
merged 8 commits into from
Oct 5, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,11 @@ env:

install:
- "pip install \"Django${DJANGO_SPEC}\" \"celery${CELERY_SPEC}\""
- pip install .
- pip install -r requirements.txt -e .

services:
- postgresql
- rabbitmq

addons:
postgresql: "9.5"
Expand All @@ -27,4 +28,4 @@ before_script:
- psql -c "create user tenant_celery with password 'qwe123'" -U postgres
- psql -c "alter role tenant_celery createdb" -U postgres

script: "cd test_app && python manage.py test tenant_schemas_celery"
script: "./run-tests"
4 changes: 4 additions & 0 deletions requirements.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
pip-tools>=3.1.0
pytest>=3.8.2
pytest-django>=3.4.3

16 changes: 16 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#
# This file is autogenerated by pip-compile
# To update, run:
#
# pip-compile --output-file requirements.txt requirements.in
#
atomicwrites==1.2.1 # via pytest
attrs==18.2.0 # via pytest
click==7.0 # via pip-tools
more-itertools==4.3.0 # via pytest
pip-tools==3.1.0
pluggy==0.7.1 # via pytest
py==1.6.0 # via pytest
pytest-django==3.4.3
pytest==3.8.2
six==1.11.0 # via more-itertools, pip-tools, pytest
29 changes: 29 additions & 0 deletions run-tests
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#!/usr/bin/env python3
import subprocess
import os


def main():
new_environ = os.environ.copy()
new_environ["DJANGO_SETTINGS_MODULE"] = "test_app.settings"
cwd = os.path.abspath(os.path.join(os.path.dirname(__file__), "test_app"))

celery_proc = subprocess.Popen(
["celery", "worker", "-A", "tenant_schemas_celery.test_app:app", "-l", "INFO"],
env=new_environ.copy(),
cwd=cwd,
)
try:
subprocess.check_call(
["pytest", "../tenant_schemas_celery/tests.py"],
env=new_environ.copy(),
cwd=cwd,
)

finally:
celery_proc.terminate()
celery_proc.wait()


if __name__ == "__main__":
main()
2 changes: 1 addition & 1 deletion tenant_schemas_celery/compat.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@
from django_tenants.test.cases import TenantTestCase
from django_tenants.utils import get_public_schema_name, get_tenant_model

except ImportError as e:
except ImportError:
from tenant_schemas.test.cases import TenantTestCase
from tenant_schemas.utils import get_public_schema_name, get_tenant_model
14 changes: 14 additions & 0 deletions tenant_schemas_celery/tasks.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
@app.task
def update_task(model_id, name):
dummy = DummyModel.objects.get(pk=model_id)
dummy.name = name
dummy.save()

@app.task(bind=True)
def update_retry_task(self, model_id, name):
connection.close()
if update_retry_task.request.retries:
return update_task(model_id, name)

# Don't throw the Retry exception.
self.retry(throw=False)
15 changes: 15 additions & 0 deletions tenant_schemas_celery/test_app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
try:
from .app import CeleryApp
except ImportError:
app = None
else:
app = CeleryApp('testapp')

class CeleryConfig:
BROKER_URL = 'amqp://'
CELERY_RESULT_BACKEND = 'rpc://'
CELERY_RESULT_PERSISTENT = False
CELERY_ALWAYS_EAGER = False

app.config_from_object(CeleryConfig)
app.autodiscover_tasks(['tenant_schemas_celery'], 'test_tasks')
30 changes: 30 additions & 0 deletions tenant_schemas_celery/test_tasks.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
from __future__ import absolute_import

from test_app.tenant.models import DummyModel

from .test_app import app


class DoesNotExist(Exception):
pass


@app.task
def update_task(model_id, name):
try:
dummy = DummyModel.objects.get(pk=model_id)

except DummyModel.DoesNotExist:
raise DoesNotExist()

dummy.name = name
dummy.save()


@app.task(bind=True)
def update_retry_task(self, model_id, name):
if update_retry_task.request.retries:
return update_task(model_id, name)

# Don't throw the Retry exception.
self.retry(countdown=0.1)
178 changes: 89 additions & 89 deletions tenant_schemas_celery/tests.py
Original file line number Diff line number Diff line change
@@ -1,126 +1,126 @@
from unittest import skipIf
from __future__ import absolute_import

import pytest
import time

from django.db import connection
from django.db.models.fields import FieldDoesNotExist
from tenant_schemas.utils import schema_context, tenant_context

from test_app.shared.models import Client
from test_app.tenant.models import DummyModel
from .compat import get_public_schema_name, TenantTestCase
from .compat import get_public_schema_name
from .test_tasks import update_task, update_retry_task, DoesNotExist

try:
from .app import CeleryApp
except ImportError:
app = None
else:
app = CeleryApp('testapp')

class CeleryConfig:
CELERY_ALWAYS_EAGER = True
CELERY_EAGER_PROPAGATES_EXCEPTIONS = True
@pytest.fixture
def setup_tenant_test(transactional_db):
kwargs1 = {}
kwargs2 = {}

app.config_from_object(CeleryConfig)
data = {}

@app.task
def update_task(model_id, name):
dummy = DummyModel.objects.get(pk=model_id)
dummy.name = name
dummy.save()
try:
Client._meta.get_field('domain_url')
except FieldDoesNotExist:
pass
else:
kwargs1 = {'domain_url': 'test1.test.com'}
kwargs2 = {'domain_url': 'test2.test.com'}

@app.task
def update_retry_task(model_id, name):
if update_retry_task.request.retries:
return update_task(model_id, name)
tenant1 = data['tenant1'] = Client(name='test1', schema_name='test1', **kwargs1)
tenant1.save()

# Don't throw the Retry exception.
update_retry_task.retry(throw=False)
tenant2 = data['tenant2'] = Client(name='test2', schema_name='test2', **kwargs2)
tenant2.save()

connection.set_tenant(tenant1)
DummyModel.objects.all().delete()
data['dummy1'] = DummyModel.objects.create(name='test1')

@skipIf(app is None, 'Celery is not available.')
class CeleryTasksTests(TenantTestCase):
@classmethod
def setUpClass(cls):
pass
connection.set_tenant(tenant2)
DummyModel.objects.all().delete()
data['dummy2'] = DummyModel.objects.create(name='test2')

@classmethod
def tearDownClass(cls):
pass
connection.set_schema_to_public()

def setUp(self):
kwargs1 = {}
kwargs2 = {}
try:
yield data

try:
Client._meta.get_field('domain_url')
except FieldDoesNotExist:
pass
else:
kwargs1 = {'domain_url': 'test1.test.com'}
kwargs2 = {'domain_url': 'test2.test.com'}
finally:
connection.set_schema_to_public()

self.tenant1 = Client(name='test1', schema_name='test1', **kwargs1)
self.tenant1.save()

self.tenant2 = Client(name='test2', schema_name='test2', **kwargs2)
self.tenant2.save()
def test_should_update_model(setup_tenant_test):
dummy1, dummy2 = setup_tenant_test['dummy1'], setup_tenant_test['dummy2']

connection.set_tenant(self.tenant1)
self.dummy1 = DummyModel.objects.create(name='test1')
# We should be in public schema where dummies don't exist.
for dummy in dummy1, dummy2:
# Test both async and local versions.
with pytest.raises(DoesNotExist):
update_task.apply_async(args=(dummy.pk, 'updated-name')).get()

connection.set_tenant(self.tenant2)
self.dummy2 = DummyModel.objects.create(name='test2')
with pytest.raises(DoesNotExist):
update_task.apply(args=(dummy.pk, 'updated-name')).get()

connection.set_schema_to_public()
connection.set_tenant(setup_tenant_test['tenant1'])
update_task.apply_async(args=(dummy1.pk, 'updated-name')).get()
assert connection.schema_name == setup_tenant_test['tenant1'].schema_name

def tearDown(self):
connection.set_schema_to_public()
# The task restores the schema from before running the task, so we are
# using the `tenant1` tenant now.
model_count = DummyModel.objects.filter(name='updated-name').count()
assert model_count == 1

def test_basic_model_update(self):
# We should be in public schema where dummies don't exist.
for dummy in self.dummy1, self.dummy2:
# Test both async and local versions.
with self.assertRaises(DummyModel.DoesNotExist):
update_task.apply_async(args=(dummy.pk, 'updated-name'))
connection.set_tenant(setup_tenant_test['tenant2'])
model_count = DummyModel.objects.filter(name='updated-name').count()
assert model_count == 0

with self.assertRaises(DummyModel.DoesNotExist):
update_task.apply(args=(dummy.pk, 'updated-name'))

connection.set_tenant(self.tenant1)
update_task.apply_async(args=(self.dummy1.pk, 'updated-name'))
self.assertEqual(connection.schema_name, self.tenant1.schema_name)
def test_task_retry(setup_tenant_test):
dummy1 = setup_tenant_test['dummy1']

# The task restores the schema from before running the task, so we are
# using the `tenant1` tenant now.
model_count = DummyModel.objects.filter(name='updated-name').count()
self.assertEqual(model_count, 1)
# Schema name should persist through retry attempts.
connection.set_tenant(setup_tenant_test['tenant1'])
update_retry_task.apply_async(args=(dummy1.pk, 'updated-name')).get()

connection.set_tenant(self.tenant2)
for _ in range(19):
model_count = DummyModel.objects.filter(name='updated-name').count()
self.assertEqual(model_count, 0)
try:
assert model_count == 1

def test_task_retry(self):
# Schema name should persist through retry attempts.
connection.set_tenant(self.tenant1)
update_retry_task.apply_async(args=(self.dummy1.pk, 'updated-name'))
except AssertionError:
# Wait for the retried task to finish.
time.sleep(0.1)

model_count = DummyModel.objects.filter(name='updated-name').count()
self.assertEqual(model_count, 1)
else:
break

model_count = DummyModel.objects.filter(name='updated-name').count()
assert model_count == 1


def test_restoring_schema_name(setup_tenant_test):
dummy1 = setup_tenant_test['dummy1']
dummy2 = setup_tenant_test['dummy2']

with tenant_context(setup_tenant_test['tenant1']):
update_task.apply_async(args=(dummy1.pk, 'updated-name')).get()

assert connection.schema_name == get_public_schema_name()

connection.set_tenant(setup_tenant_test['tenant1'])

def test_restoring_schema_name(self):
with tenant_context(self.tenant1):
update_task.apply_async(args=(self.dummy1.pk, 'updated-name'))
self.assertEqual(connection.schema_name, get_public_schema_name())
with tenant_context(setup_tenant_test['tenant2']):
update_task.apply_async(args=(dummy2.pk, 'updated-name')).get()

connection.set_tenant(self.tenant1)
assert connection.schema_name == setup_tenant_test['tenant1'].schema_name

with tenant_context(self.tenant2):
update_task.apply_async(args=(self.dummy2.pk, 'updated-name'))
self.assertEqual(connection.schema_name, self.tenant1.schema_name)
connection.set_tenant(setup_tenant_test['tenant2'])

connection.set_tenant(self.tenant2)
# The model does not exist in the public schema.
with self.assertRaises(DummyModel.DoesNotExist):
with schema_context(get_public_schema_name()):
update_task.apply_async(args=(self.dummy2.pk, 'updated-name'))
# The model does not exist in the public schema.
with pytest.raises(DoesNotExist):
with schema_context(get_public_schema_name()):
update_task.apply_async(args=(dummy2.pk, 'updated-name')).get()

self.assertEqual(connection.schema_name, self.tenant2.schema_name)
assert connection.schema_name == setup_tenant_test['tenant2'].schema_name
3 changes: 3 additions & 0 deletions test_app/test_app/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,9 @@
'NAME': 'tenant_celery',
'PASSWORD': 'qwe123',
'USER': 'tenant_celery',
'TEST': {
'NAME': 'tenant_celery',
}
}
}

Expand Down