Source code for quade.models

from __future__ import absolute_import, division, print_function, unicode_literals

from django.conf import settings
from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import ValidationError
from django.db import models as m
from django.utils.encoding import python_2_unicode_compatible
from django_fsm.db.fields import FSMIntegerField, transition
from django_light_enums import enum
from jsonfield import JSONField

from .managers import manager, ConfigurationError
from .receivers import connect_qa_object_receiver

class ScenarioManager(m.QuerySet):

    def active(self):
        return self.filter(status=Scenario.Status.ACTIVE)

[docs]@python_2_unicode_compatible class Scenario(m.Model): """ A specific scenario that QA tests will be run against. The heart of a ``Scenario`` is the `config` attribute. `config` is a list of several fixtures, possibly with arguments, which are executed sequentially to create and modify objects in the database. """ class Meta: app_label = 'quade' ordering = ['description'] class Status(enum.Enum): INACTIVE = 0 ACTIVE = 10 objects = ScenarioManager.as_manager() REGISTRY = {} slug = m.SlugField(unique=True) status = enum.EnumField(Status, default=Status.ACTIVE) config = JSONField() description = m.TextField() created_on = m.DateTimeField(auto_now_add=True) updated_on = m.DateTimeField(auto_now=True) def __str__(self): return '#{}: {}'.format(, self.description) def activate(self): self.status = self.Status.ACTIVE def deactivate(self): self.status = self.Status.INACTIVE def delete(self): self.deactivate() def hard_delete(self, *args, **kwargs): super(Scenario, self).delete(*args, **kwargs) def save(self, *args, **kwargs): try: manager.validate(self.config) except ConfigurationError as exc: raise ValidationError("config {} contains unregistered function(s): {}".format( self.config, ','.join(exc.unregistered_functions) )) super(Scenario, self).save(*args, **kwargs)
[docs]class Record(m.Model): """ A record of setting up, and possibly executing, a particular :class:`Scenario`. """ class Meta: app_label = 'quade' class Status(enum.Enum): FAILED = -10 NOT_READY = -1 READY = 1 # Django-FSM doesn't work well with 0, because it evaluates as falsey. IN_PROGRESS = 10 DONE = 20 scenario = m.ForeignKey(Scenario, on_delete=m.PROTECT) instructions = m.TextField( blank=True, null=True, help_text="Information needed to run this particular scenario, such as usernames and login" " information." ) status = FSMIntegerField(choices=Status.choices, default=Status.NOT_READY) created_by = m.ForeignKey(settings.AUTH_USER_MODEL, related_name='+', on_delete=m.PROTECT) created_on = m.DateTimeField(auto_now_add=True) updated_on = m.DateTimeField(auto_now=True)
[docs] def execute_test(self): """ Public method for attempting to execute a test scenario. Exceptions put the record in the FAILED state and re-raise. """ try: self._execute() except Exception as ex: self._fail(exception=ex) raise
@transition(status, source=Status.NOT_READY, target=Status.READY, save=True) def _execute(self): with connect_qa_object_receiver(self): instructions = manager.execute(self.scenario.config) self.instructions = instructions @transition(status, source=Status.NOT_READY, target=Status.FAILED, save=True) def _fail(self, exception): self.instructions = repr(exception)
[docs]@python_2_unicode_compatible class RecordedObject(m.Model): """ A joiner table for creating a generic many-to-many relation between :class:`Records <Record>` and any other objects. """ class Meta: app_label = 'quade' content_type = m.ForeignKey(ContentType, on_delete=m.CASCADE) object_id = m.PositiveIntegerField() object = GenericForeignKey('content_type', 'object_id') record = m.ForeignKey(Record, related_name='recorded_objects', on_delete=m.CASCADE) def __str__(self): return "RecordedObject #{}: {}".format(, self.object)