from __future__ import absolute_import, unicode_literals

from uuid import uuid4

from django.conf import settings
from django.test.simple import DjangoTestSuiteRunner

from celery.task import Task
from djcelery.models import TaskState
from djcelery.utils import now


USAGE = """\
Custom test runner to allow testing of celery delayed tasks.
"""


class CeleryTestSuiteRunner(DjangoTestSuiteRunner):
    """Django test runner allowing testing of celery delayed tasks.

    All tasks are run locally, not in a worker.

    To use this runner set ``settings.TEST_RUNNER``::

        TEST_RUNNER = 'djcelery.contrib.test_runner.CeleryTestSuiteRunner'

    """
    def setup_test_environment(self, **kwargs):
        super(CeleryTestSuiteRunner, self).setup_test_environment(**kwargs)
        settings.CELERY_ALWAYS_EAGER = True
        settings.CELERY_EAGER_PROPAGATES_EXCEPTIONS = True  # Issue #75


class CeleryTestSuiteRunnerStoringResult(DjangoTestSuiteRunner):
    """This custom test suite runner make some preliminary
    monkey-patching allowing storing result of Celery task execution
    in ``djcelery.models.TaskState`` model. Tasks run eagerly.

    Exceptions is turned on. If you need to test ``on_failure``
    behavior, you should monkey-patch in your test::

        settings.CELERY_EAGER_PROPAGATES_EXCEPTIONS = False

    USAGE:

    In ``settings.py``::

        TEST_RUNNER = '''
            djcelery.contrib.test_runner.CeleryTestSuiteRunnerStoringResult
        '''.strip()

    In ``tests.py``::

        from djcelery.models import TaskState
        TaskState.object.filter(state='SUCCESS', args__contains='test')

    """
    def setup_test_environment(self, **kwargs):
        """Setting up test environment."""

        # Monkey-patch Task.on_success() method
        def on_success_patched(self, retval, task_id, args, kwargs):

            TaskState.objects.create(task_id=uuid4().hex,
                                     state='SUCCESS',
                                     name=self.name,
                                     result=retval,
                                     args=args,
                                     kwargs=kwargs,
                                     tstamp=now())
        Task.on_success = classmethod(on_success_patched)

        # Monkey-patch Task.on_failure() method
        def on_failure_patched(self, exc, task_id, args, kwargs, einfo):

            TaskState.objects.create(task_id=uuid4().hex,
                                     state='FAILURE',
                                     name=self.name,
                                     result=einfo,
                                     args=args,
                                     kwargs=kwargs,
                                     tstamp=now())
        Task.on_failure = classmethod(on_failure_patched)

        # Call parent's version
        super(CeleryTestSuiteRunnerStoringResult,
              self).setup_test_environment(**kwargs)

        # Tell celery run tasks synchronously
        settings.CELERY_ALWAYS_EAGER = True
        settings.CELERY_EAGER_PROPAGATES_EXCEPTIONS = True  # Issue #75
