Skip to content
Open
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
25 changes: 15 additions & 10 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
language: python
python:
- "2.7"
- "3.6"
- "pypy"
- "pypy3"
# command to install dependencies
cache: pip
matrix:
include:
- env: TOX_ENV=py35
python: 3.5
- env: TOX_ENV=py36
python: 3.6
- env: TOX_ENV=py37
python: 3.7
dist: bionic
sudo: true

install:
- "pip install -r requirements.txt"
- "pip install -r requirements.txt -r dev-requirements.txt tox tox-docker"
- "pip install ."
# command to run tests
script:
- "python -m compileall ."

script: tox -vv -e $TOX_ENV
3 changes: 2 additions & 1 deletion dev-requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
astroid==2.3.3
bleach==3.1.0
certifi==2019.11.28
celery[redis]
chardet==3.0.4
docutils==0.16
idna==2.8
Expand All @@ -14,9 +15,9 @@ pkginfo==1.5.0.1
pygments==2.5.2
pylint==2.4.4
readme-renderer==24.0
redis
requests-toolbelt==0.9.1
requests==2.22.0
six==1.14.0
tqdm==4.42.1
twine==3.1.1
typed-ast==1.4.1 ; implementation_name == 'cpython' and python_version < '3.8'
Expand Down
4 changes: 4 additions & 0 deletions djcelery_model/compat.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
try:
from six import python_2_unicode_compatible
except ImportError:
from django.utils.encoding import python_2_unicode_compatible
3 changes: 2 additions & 1 deletion djcelery_model/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
from django.db.models import Q
from django.db.models.query import QuerySet
from django.contrib.contenttypes.models import ContentType
from django.utils.encoding import python_2_unicode_compatible

try:
# Django >= 1.7
Expand All @@ -21,6 +20,8 @@
from celery.utils import uuid
from celery import signals

from .compat import python_2_unicode_compatible

class ModelTaskMetaState(object):
PENDING = 0
STARTED = 1
Expand Down
7 changes: 7 additions & 0 deletions djcelery_model/tests/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
from __future__ import absolute_import, unicode_literals

# This will make sure the app is always imported when
# Django starts so that shared_task will use this app.
from .celery import app as celery_app

__all__ = ('celery_app',)
19 changes: 19 additions & 0 deletions djcelery_model/tests/celery.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
from __future__ import absolute_import, unicode_literals
import os
from celery import Celery

# set the default Django settings module for the 'celery' program.
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'djcelery_model.tests.settings')

app = Celery('project')

# Using a string here means the worker doesn't have to serialize
# the configuration object to child processes.
# - namespace='CELERY' means all celery-related configuration keys
# should have a `CELERY_` prefix.
app.config_from_object('django.conf:settings', namespace='CELERY')

# Load task modules from all registered Django app configs.
app.autodiscover_tasks()

from celery.contrib.testing.tasks import ping
70 changes: 70 additions & 0 deletions djcelery_model/tests/settings.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# -*- coding: utf-8
from __future__ import unicode_literals, absolute_import
import os

DEBUG = True
USE_TZ = True

# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz'

INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sites',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',

'djcelery_model',
'djcelery_model.tests.testapp',
]

SITE_ID = 1

MIDDLEWARE = [
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
]

TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [
os.path.join(os.path.dirname(__file__), 'templates'),
],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]

ROOT_DIR = os.path.dirname(__file__)
STATIC_ROOT = os.path.join(ROOT_DIR, 'test-static')
MEDIA_ROOT = os.path.join(ROOT_DIR, 'test-media')
MEDIA_URL = '/media/'
STATIC_URL = '/static/'

DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': ':memory:',
}
}

REDIS_PORT = os.getenv('REDIS_6379_TCP_PORT', 'port-missing-from-env')

CELERY_BROKER_URL = 'redis://localhost:%s/0' % REDIS_PORT
CELERY_RESULT_BACKEND = 'redis://localhost:%s/2' % REDIS_PORT
CELERY_TASK_TRACK_STARTED = True
CELERY_TASK_SEND_SENT_EVENT = True
CELERY_SEND_EVENTS = True
Empty file.
25 changes: 25 additions & 0 deletions djcelery_model/tests/testapp/migrations/0001_initial.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Generated by Django 2.2.7 on 2019-11-10 23:21

from django.db import migrations, models


class Migration(migrations.Migration):

initial = True

dependencies = [
]

operations = [
migrations.CreateModel(
name='JPEGFile',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('file', models.FileField(upload_to='')),
('etag', models.CharField(blank=True, max_length=255)),
],
options={
'abstract': False,
},
),
]
Empty file.
6 changes: 6 additions & 0 deletions djcelery_model/tests/testapp/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from django.db import models
from djcelery_model.models import TaskMixin

class JPEGFile(TaskMixin, models.Model):
file = models.FileField()
etag = models.CharField(max_length=255, blank=True)
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
14 changes: 14 additions & 0 deletions djcelery_model/tests/testapp/tasks.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
from __future__ import absolute_import, unicode_literals
from hashlib import sha1
from time import sleep
from celery import shared_task

from .models import JPEGFile


@shared_task
def calculate_etag(pk):
jpeg = JPEGFile.objects.get(pk=pk)
jpeg.etag = sha1(jpeg.file.read()).hexdigest()
sleep(5)
jpeg.save()
64 changes: 64 additions & 0 deletions djcelery_model/tests/testapp/tests.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
from time import sleep
from celery.contrib.testing.tasks import ping
from celery.contrib.testing.worker import start_worker
from django.contrib.staticfiles import finders
from django.core.files import File
from django.test import TestCase, TransactionTestCase

from djcelery_model.models import TaskMixin
from djcelery_model.tests import celery_app

from .models import JPEGFile
from .tasks import calculate_etag


class CeleryTestCase(TransactionTestCase):
def setUp(self):
self.worker_context = start_worker(celery_app, perform_ping_check=False)
self.worker = self.worker_context.__enter__()
self.worker.ensure_started()

def tearDown(self):
self.worker_context.__exit__(None, None, None)


class TestAppIntegrationTests(TestCase):
def test_model_is_taskmixin(self):
self.assertIsInstance(JPEGFile(), TaskMixin)


class TestAppCeleryTests(CeleryTestCase):
def test_worker(self):
result = ping.delay()
pong = result.get(timeout=10)
self.assertEqual(pong, 'pong')

def test_state_properties(self):
jpeg = JPEGFile.objects.create(
file=File(open(finders.find('testapp/flower.jpg'), 'rb'), name='flower.jpg')
)

self.assertFalse(jpeg.has_running_task)
self.assertFalse(jpeg.has_ready_task)

result = jpeg.apply_async(calculate_etag, [jpeg.pk])
self.assertTrue(jpeg.has_running_task)
self.assertFalse(jpeg.has_ready_task)
self.assertFalse(result.ready())

result.get(timeout=10)
self.assertTrue(result.ready())

# not the greatest way to wait for async stuff to happen, but we need
# the signals to complete before testing for side effects
sleep(3)

self.assertEqual(jpeg.etag, '')
self.assertFalse(jpeg.has_running_task)
self.assertTrue(jpeg.has_ready_task)

jpeg.refresh_from_db()

self.assertEqual(jpeg.etag, '80b098e6cd95b9901fa29799d48731433dfaeab0')
self.assertFalse(jpeg.has_running_task)
self.assertTrue(jpeg.has_ready_task)
12 changes: 12 additions & 0 deletions manage.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from __future__ import unicode_literals, absolute_import

import os
import sys

if __name__ == "__main__":
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "djcelery_model.tests.settings")
from django.core.management import execute_from_command_line

execute_from_command_line(sys.argv)
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
django>=1.11
celery>=4.2
six>=1.9
30 changes: 30 additions & 0 deletions runtests.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#!/usr/bin/env python

import os
import shutil
import sys
import warnings

from django.core.management import execute_from_command_line

os.environ['DJANGO_SETTINGS_MODULE'] = 'djcelery_model.tests.settings'


def runtests():
# Don't ignore DeprecationWarnings
only_djcelery_model = r'^djcelery_model(\.|$)'
warnings.filterwarnings('default', category=DeprecationWarning, module=only_djcelery_model)
warnings.filterwarnings('default', category=PendingDeprecationWarning, module=only_djcelery_model)

args = sys.argv[1:]
argv = sys.argv[:1] + ['test'] + args
try:
execute_from_command_line(argv)
finally:
from djcelery_model.tests.settings import STATIC_ROOT, MEDIA_ROOT
shutil.rmtree(STATIC_ROOT, ignore_errors=True)
shutil.rmtree(MEDIA_ROOT, ignore_errors=True)


if __name__ == '__main__':
runtests()
32 changes: 32 additions & 0 deletions tox.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
[tox]
envlist =
{py35,py36,py37}


[testenv]
setenv =
PYTHONPATH = {toxinidir}:{toxinidir}

commands =
python runtests.py --noinput {posargs}

deps =
-r{toxinidir}/requirements.txt
-r{toxinidir}/dev-requirements.txt

basepython =
py37: python3.7
py36: python3.6
py35: python3.5

docker =
redis:5.0



[docker:redis:5.0]
healthcheck_cmd = redis-cli ping | grep -q PONG
healthcheck_interval = 3
healthcheck_timeout = 3
healthcheck_retries = 30
healthcheck_start_period = 5
1 change: 1 addition & 0 deletions vagrant/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.vagrant/
23 changes: 23 additions & 0 deletions vagrant/README
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
CREATES A VAGRANT ENVIRONMENT TO FACILITATE LOCAL TESTING

========
USAGE:
========
To run tests:
cd to this directory and then issue the following commands:
vagrant up
vagrant ssh
bash /vagrant/vagrant/runtox.sh

to run with verbose output:
bash /vagrant/vagrant/runtox.sh -vv -- -v 2

to run a specific test:
bash /vagrant/vagrant/runtox.sh -vv -- -v 2 path.to.test


To clean after tests run:
rm -R /tmp/vagrant

To make migrations (after running tests to create tox environment):
/tmp/vagrant/.tox/py35/bin/python /vagrant/manage.py makemigrations
Loading