Upgrade service tests
This commit is contained in:
parent
276c02c2fd
commit
647bc43a5a
|
@ -1,4 +1,5 @@
|
||||||
from orchestra.apps.accounts.models import Account
|
from orchestra.apps.accounts.models import Account
|
||||||
|
from orchestra.core import services
|
||||||
|
|
||||||
|
|
||||||
def get_related_objects(origin, max_depth=2):
|
def get_related_objects(origin, max_depth=2):
|
||||||
|
@ -9,7 +10,6 @@ def get_related_objects(origin, max_depth=2):
|
||||||
flexibility. A more comprehensive approach may be considered if
|
flexibility. A more comprehensive approach may be considered if
|
||||||
a use-case calls for it.
|
a use-case calls for it.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def related_iterator(node):
|
def related_iterator(node):
|
||||||
for field in node._meta.virtual_fields:
|
for field in node._meta.virtual_fields:
|
||||||
if hasattr(field, 'ct_field'):
|
if hasattr(field, 'ct_field'):
|
||||||
|
@ -26,7 +26,7 @@ def get_related_objects(origin, max_depth=2):
|
||||||
return None
|
return None
|
||||||
node = models[-1]
|
node = models[-1]
|
||||||
if len(models) > 1:
|
if len(models) > 1:
|
||||||
if hasattr(node, 'account') or isinstance(node, Account):
|
if type(node) in services:
|
||||||
return node
|
return node
|
||||||
for related in related_iterator(node):
|
for related in related_iterator(node):
|
||||||
if related and related not in models:
|
if related and related not in models:
|
||||||
|
|
|
@ -14,7 +14,7 @@ from django.contrib.contenttypes.models import ContentType
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from orchestra.core import accounts
|
from orchestra.core import accounts, services
|
||||||
from orchestra.models import queryset
|
from orchestra.models import queryset
|
||||||
from orchestra.utils.python import import_class
|
from orchestra.utils.python import import_class
|
||||||
|
|
||||||
|
@ -71,6 +71,7 @@ class OrderQuerySet(models.QuerySet):
|
||||||
for order in orders:
|
for order in orders:
|
||||||
bp = service.handler.get_billing_point(order, **options)
|
bp = service.handler.get_billing_point(order, **options)
|
||||||
end = max(end, bp)
|
end = max(end, bp)
|
||||||
|
# FIXME exclude cancelled except cancelled and billed > ini
|
||||||
qs = qs | Q(
|
qs = qs | Q(
|
||||||
Q(service=service, account=account_id, registered_on__lt=end) &
|
Q(service=service, account=account_id, registered_on__lt=end) &
|
||||||
Q(Q(billed_until__isnull=True) | Q(billed_until__lt=end))
|
Q(Q(billed_until__isnull=True) | Q(billed_until__lt=end))
|
||||||
|
@ -236,27 +237,26 @@ class MetricStorage(models.Model):
|
||||||
accounts.register(Order)
|
accounts.register(Order)
|
||||||
|
|
||||||
|
|
||||||
_excluded_models = (MetricStorage, LogEntry, Order, ContentType, MigrationRecorder.Migration)
|
# TODO build a cache hash table {model: related, model: None}
|
||||||
|
|
||||||
@receiver(post_delete, dispatch_uid="orders.cancel_orders")
|
@receiver(post_delete, dispatch_uid="orders.cancel_orders")
|
||||||
def cancel_orders(sender, **kwargs):
|
def cancel_orders(sender, **kwargs):
|
||||||
if sender not in _excluded_models:
|
if sender._meta.app_label not in settings.ORDERS_EXCLUDED_APPS:
|
||||||
instance = kwargs['instance']
|
instance = kwargs['instance']
|
||||||
if hasattr(instance, 'account'):
|
if type(instance) in services:
|
||||||
for order in Order.objects.by_object(instance).active():
|
for order in Order.objects.by_object(instance).active():
|
||||||
order.cancel()
|
order.cancel()
|
||||||
else:
|
elif not hasattr(instance, 'account'):
|
||||||
related = helpers.get_related_objects(instance)
|
related = helpers.get_related_objects(instance)
|
||||||
if related and related != instance:
|
if related and related != instance:
|
||||||
Order.update_orders(related)
|
Order.update_orders(related)
|
||||||
|
|
||||||
@receiver(post_save, dispatch_uid="orders.update_orders")
|
@receiver(post_save, dispatch_uid="orders.update_orders")
|
||||||
def update_orders(sender, **kwargs):
|
def update_orders(sender, **kwargs):
|
||||||
if sender not in _excluded_models:
|
if sender._meta.app_label not in settings.ORDERS_EXCLUDED_APPS:
|
||||||
instance = kwargs['instance']
|
instance = kwargs['instance']
|
||||||
if hasattr(instance, 'account'):
|
if type(instance) in services:
|
||||||
Order.update_orders(instance)
|
Order.update_orders(instance)
|
||||||
else:
|
elif not hasattr(instance, 'account'):
|
||||||
related = helpers.get_related_objects(instance)
|
related = helpers.get_related_objects(instance)
|
||||||
if related and related != instance:
|
if related and related != instance:
|
||||||
Order.update_orders(related)
|
Order.update_orders(related)
|
||||||
|
|
|
@ -6,3 +6,16 @@ ORDERS_BILLING_BACKEND = getattr(settings, 'ORDERS_BILLING_BACKEND',
|
||||||
|
|
||||||
|
|
||||||
ORDERS_SERVICE_MODEL = getattr(settings, 'ORDERS_SERVICE_MODEL', 'services.Service')
|
ORDERS_SERVICE_MODEL = getattr(settings, 'ORDERS_SERVICE_MODEL', 'services.Service')
|
||||||
|
|
||||||
|
|
||||||
|
ORDERS_EXCLUDED_APPS = getattr(settings, 'ORDERS_EXCLUDED_APPS', (
|
||||||
|
'orders',
|
||||||
|
'admin',
|
||||||
|
'contenttypes',
|
||||||
|
'auth',
|
||||||
|
'migrations',
|
||||||
|
'sessions',
|
||||||
|
'orchestration',
|
||||||
|
'bills',
|
||||||
|
# Do not put services here (plans)
|
||||||
|
))
|
||||||
|
|
|
@ -177,8 +177,7 @@ def create_resource_relation():
|
||||||
data = self.obj.resource_set.get(resource__name=attr)
|
data = self.obj.resource_set.get(resource__name=attr)
|
||||||
except ResourceData.DoesNotExist:
|
except ResourceData.DoesNotExist:
|
||||||
model = self.obj._meta.model_name
|
model = self.obj._meta.model_name
|
||||||
resource = Resource.objects.get(content_type__model=model,
|
resource = Resource.objects.get(content_type__model=model, name=attr, is_active=True)
|
||||||
name=attr, is_active=True)
|
|
||||||
data = ResourceData(content_object=self.obj, resource=resource)
|
data = ResourceData(content_object=self.obj, resource=resource)
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
|
|
@ -1,16 +1,9 @@
|
||||||
from orchestra.apps.accounts.models import Account
|
from orchestra.apps.accounts.models import Account
|
||||||
from orchestra.apps.users.models import User
|
|
||||||
from orchestra.utils.tests import BaseTestCase, random_ascii
|
from orchestra.utils.tests import BaseTestCase, random_ascii
|
||||||
|
|
||||||
|
|
||||||
|
# TODO remove this shit
|
||||||
class BaseBillingTest(BaseTestCase):
|
class BaseBillingTest(BaseTestCase):
|
||||||
def create_account(self):
|
pass
|
||||||
account = Account.objects.create()
|
|
||||||
username = 'account_%s' % random_ascii(5)
|
|
||||||
user = User.objects.create_user(username=username, account=account)
|
|
||||||
account.user = user
|
|
||||||
account.save()
|
|
||||||
return account
|
|
||||||
|
|
||||||
|
|
||||||
# TODO web disk
|
# TODO web disk
|
||||||
|
|
|
@ -5,7 +5,7 @@ from dateutil.relativedelta import relativedelta
|
||||||
from django.contrib.contenttypes.models import ContentType
|
from django.contrib.contenttypes.models import ContentType
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
|
|
||||||
from orchestra.apps.users.models import User
|
from orchestra.apps.systemusers.models import SystemUser
|
||||||
from orchestra.utils.tests import random_ascii
|
from orchestra.utils.tests import random_ascii
|
||||||
|
|
||||||
from ... import settings
|
from ... import settings
|
||||||
|
@ -18,8 +18,8 @@ class FTPBillingTest(BaseBillingTest):
|
||||||
def create_ftp_service(self):
|
def create_ftp_service(self):
|
||||||
return Service.objects.create(
|
return Service.objects.create(
|
||||||
description="FTP Account",
|
description="FTP Account",
|
||||||
content_type=ContentType.objects.get_for_model(User),
|
content_type=ContentType.objects.get_for_model(SystemUser),
|
||||||
match='not user.is_main and user.has_posix()',
|
match='not systemuser.is_main',
|
||||||
billing_period=Service.ANUAL,
|
billing_period=Service.ANUAL,
|
||||||
billing_point=Service.FIXED_DATE,
|
billing_point=Service.FIXED_DATE,
|
||||||
is_fee=False,
|
is_fee=False,
|
||||||
|
@ -36,10 +36,7 @@ class FTPBillingTest(BaseBillingTest):
|
||||||
if not account:
|
if not account:
|
||||||
account = self.create_account()
|
account = self.create_account()
|
||||||
username = '%s_ftp' % random_ascii(10)
|
username = '%s_ftp' % random_ascii(10)
|
||||||
user = User.objects.create_user(username=username, account=account)
|
return SystemUser.objects.create_user(username, account=account)
|
||||||
POSIX = user._meta.get_field_by_name('posix')[0].model
|
|
||||||
POSIX.objects.create(user=user)
|
|
||||||
return user
|
|
||||||
|
|
||||||
def test_ftp_account_1_year_fiexed(self):
|
def test_ftp_account_1_year_fiexed(self):
|
||||||
service = self.create_ftp_service()
|
service = self.create_ftp_service()
|
||||||
|
|
|
@ -13,7 +13,7 @@ from . import BaseBillingTest
|
||||||
|
|
||||||
|
|
||||||
class BaseTrafficBillingTest(BaseBillingTest):
|
class BaseTrafficBillingTest(BaseBillingTest):
|
||||||
METRIC = 'account.resources.traffic.used'
|
TRAFFIC_METRIC = 'account.resources.traffic.used'
|
||||||
|
|
||||||
def create_traffic_service(self):
|
def create_traffic_service(self):
|
||||||
service = Service.objects.create(
|
service = Service.objects.create(
|
||||||
|
@ -23,7 +23,7 @@ class BaseTrafficBillingTest(BaseBillingTest):
|
||||||
billing_period=Service.MONTHLY,
|
billing_period=Service.MONTHLY,
|
||||||
billing_point=Service.FIXED_DATE,
|
billing_point=Service.FIXED_DATE,
|
||||||
is_fee=False,
|
is_fee=False,
|
||||||
metric=self.METRIC,
|
metric=self.TRAFFIC_METRIC,
|
||||||
pricing_period=Service.BILLING_PERIOD,
|
pricing_period=Service.BILLING_PERIOD,
|
||||||
rate_algorithm=Service.STEP_PRICE,
|
rate_algorithm=Service.STEP_PRICE,
|
||||||
on_cancel=Service.NOTHING,
|
on_cancel=Service.NOTHING,
|
||||||
|
@ -50,7 +50,7 @@ class BaseTrafficBillingTest(BaseBillingTest):
|
||||||
return self.resource
|
return self.resource
|
||||||
|
|
||||||
def report_traffic(self, account, value):
|
def report_traffic(self, account, value):
|
||||||
MonitorData.objects.create(monitor='FTPTraffic', content_object=account.user, value=value)
|
MonitorData.objects.create(monitor='FTPTraffic', content_object=account.systemusers.get(), value=value)
|
||||||
data = ResourceData.get_or_create(account, self.resource)
|
data = ResourceData.get_or_create(account, self.resource)
|
||||||
data.update()
|
data.update()
|
||||||
|
|
||||||
|
@ -90,7 +90,7 @@ class TrafficBillingTest(BaseTrafficBillingTest):
|
||||||
|
|
||||||
|
|
||||||
class TrafficPrepayBillingTest(BaseTrafficBillingTest):
|
class TrafficPrepayBillingTest(BaseTrafficBillingTest):
|
||||||
METRIC = ("max("
|
TRAFFIC_METRIC = ("max("
|
||||||
"(account.resources.traffic.used or 0) - "
|
"(account.resources.traffic.used or 0) - "
|
||||||
"getattr(account.miscellaneous.filter(is_active=True, service__name='traffic prepay').last(), 'amount', 0)"
|
"getattr(account.miscellaneous.filter(is_active=True, service__name='traffic prepay').last(), 'amount', 0)"
|
||||||
", 0)"
|
", 0)"
|
||||||
|
@ -126,8 +126,8 @@ class TrafficPrepayBillingTest(BaseTrafficBillingTest):
|
||||||
def test_traffic_prepay(self):
|
def test_traffic_prepay(self):
|
||||||
self.create_traffic_service()
|
self.create_traffic_service()
|
||||||
self.create_prepay_service()
|
self.create_prepay_service()
|
||||||
account = self.create_account()
|
|
||||||
self.create_traffic_resource()
|
self.create_traffic_resource()
|
||||||
|
account = self.create_account()
|
||||||
now = timezone.now()
|
now = timezone.now()
|
||||||
|
|
||||||
self.create_prepay(10, account=account)
|
self.create_prepay(10, account=account)
|
||||||
|
|
|
@ -5,8 +5,8 @@ from django.contrib.contenttypes.models import ContentType
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
|
|
||||||
from orchestra.apps.accounts.models import Account
|
from orchestra.apps.accounts.models import Account
|
||||||
from orchestra.apps.users.models import User
|
from orchestra.apps.systemusers.models import SystemUser
|
||||||
from orchestra.utils.tests import BaseTestCase
|
from orchestra.utils.tests import BaseTestCase, random_ascii
|
||||||
|
|
||||||
from .. import helpers
|
from .. import helpers
|
||||||
from ..models import Service, Plan
|
from ..models import Service, Plan
|
||||||
|
@ -28,22 +28,14 @@ class Order(object):
|
||||||
class HandlerTests(BaseTestCase):
|
class HandlerTests(BaseTestCase):
|
||||||
DEPENDENCIES = (
|
DEPENDENCIES = (
|
||||||
'orchestra.apps.orders',
|
'orchestra.apps.orders',
|
||||||
'orchestra.apps.users',
|
'orchestra.apps.systemusers',
|
||||||
'orchestra.apps.users.roles.posix',
|
|
||||||
)
|
)
|
||||||
|
|
||||||
def create_account(self):
|
|
||||||
account = Account.objects.create()
|
|
||||||
user = User.objects.create_user(username='rata_palida', account=account)
|
|
||||||
account.user = user
|
|
||||||
account.save()
|
|
||||||
return account
|
|
||||||
|
|
||||||
def create_ftp_service(self):
|
def create_ftp_service(self):
|
||||||
service = Service.objects.create(
|
service = Service.objects.create(
|
||||||
description="FTP Account",
|
description="FTP Account",
|
||||||
content_type=ContentType.objects.get_for_model(User),
|
content_type=ContentType.objects.get_for_model(SystemUser),
|
||||||
match='not user.is_main and user.has_posix()',
|
match='not systemuser.is_main',
|
||||||
billing_period=Service.ANUAL,
|
billing_period=Service.ANUAL,
|
||||||
billing_point=Service.FIXED_DATE,
|
billing_point=Service.FIXED_DATE,
|
||||||
is_fee=False,
|
is_fee=False,
|
||||||
|
|
|
@ -15,6 +15,7 @@ class SystemUserQuerySet(models.QuerySet):
|
||||||
user = super(SystemUserQuerySet, self).create(username=username, **kwargs)
|
user = super(SystemUserQuerySet, self).create(username=username, **kwargs)
|
||||||
user.set_password(password)
|
user.set_password(password)
|
||||||
user.save(update_fields=['password'])
|
user.save(update_fields=['password'])
|
||||||
|
return user
|
||||||
|
|
||||||
|
|
||||||
class SystemUser(models.Model):
|
class SystemUser(models.Model):
|
||||||
|
|
|
@ -2,6 +2,7 @@ from functools import partial
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.core.urlresolvers import reverse
|
from django.core.urlresolvers import reverse
|
||||||
|
from selenium.webdriver.support.select import Select
|
||||||
|
|
||||||
from orchestra.apps.accounts.models import Account
|
from orchestra.apps.accounts.models import Account
|
||||||
from orchestra.apps.orchestration.models import Server, Route
|
from orchestra.apps.orchestration.models import Server, Route
|
||||||
|
@ -9,6 +10,7 @@ from orchestra.utils.system import run
|
||||||
from orchestra.utils.tests import BaseLiveServerTestCase, random_ascii
|
from orchestra.utils.tests import BaseLiveServerTestCase, random_ascii
|
||||||
|
|
||||||
from ... import backends
|
from ... import backends
|
||||||
|
from ...models import SystemUser
|
||||||
|
|
||||||
|
|
||||||
r = partial(run, silent=True, display=False)
|
r = partial(run, silent=True, display=False)
|
||||||
|
@ -40,6 +42,9 @@ class SystemUserMixin(object):
|
||||||
def update(self):
|
def update(self):
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
|
def disable(self):
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
def test_create_systemuser(self):
|
def test_create_systemuser(self):
|
||||||
username = '%s_systemuser' % random_ascii(10)
|
username = '%s_systemuser' % random_ascii(10)
|
||||||
password = '@!?%spppP001' % random_ascii(5)
|
password = '@!?%spppP001' % random_ascii(5)
|
||||||
|
@ -55,7 +60,16 @@ class SystemUserMixin(object):
|
||||||
self.assertEqual(0, r("id %s" % username).return_code)
|
self.assertEqual(0, r("id %s" % username).return_code)
|
||||||
self.delete(username)
|
self.delete(username)
|
||||||
self.assertEqual(1, r("id %s" % username, error_codes=[0,1]).return_code)
|
self.assertEqual(1, r("id %s" % username, error_codes=[0,1]).return_code)
|
||||||
|
|
||||||
|
def test_update_systemuser(self):
|
||||||
|
pass
|
||||||
|
# TODO
|
||||||
|
|
||||||
|
def test_disable_systemuser(self):
|
||||||
|
pass
|
||||||
|
# TODO
|
||||||
|
|
||||||
|
# TODO test with ftp and ssh clients?
|
||||||
|
|
||||||
class RESTSystemUserMixin(SystemUserMixin):
|
class RESTSystemUserMixin(SystemUserMixin):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
|
@ -73,16 +87,38 @@ class RESTSystemUserMixin(SystemUserMixin):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
# TODO
|
|
||||||
class AdminSystemUserMixin(SystemUserMixin):
|
class AdminSystemUserMixin(SystemUserMixin):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(AdminSystemUserMixin, self).setUp()
|
super(AdminSystemUserMixin, self).setUp()
|
||||||
self.admin_login()
|
self.admin_login()
|
||||||
|
|
||||||
def add(self, username, password):
|
def add(self, username, password):
|
||||||
pass
|
url = self.live_server_url + reverse('admin:systemusers_systemuser_add')
|
||||||
|
self.selenium.get(url)
|
||||||
|
|
||||||
|
username_field = self.selenium.find_element_by_id('id_username')
|
||||||
|
username_field.send_keys(username)
|
||||||
|
|
||||||
|
password_field = self.selenium.find_element_by_id('id_password1')
|
||||||
|
password_field.send_keys(password)
|
||||||
|
password_field = self.selenium.find_element_by_id('id_password2')
|
||||||
|
password_field.send_keys(password)
|
||||||
|
|
||||||
|
account_input = self.selenium.find_element_by_id('id_account')
|
||||||
|
account_select = Select(account_input)
|
||||||
|
account_select.select_by_value(str(self.account.pk))
|
||||||
|
|
||||||
|
username_field.submit()
|
||||||
|
self.assertNotEqual(url, self.selenium.current_url)
|
||||||
|
|
||||||
def delete(self, username):
|
def delete(self, username):
|
||||||
|
user = SystemUser.objects.get(username=username)
|
||||||
|
url = self.live_server_url + reverse('admin:systemusers_systemuser_delete', args=(user.pk,))
|
||||||
|
self.selenium.get(url)
|
||||||
|
confirmation = self.selenium.find_element_by_name('post')
|
||||||
|
confirmation.submit()
|
||||||
|
|
||||||
|
def disable(self, username):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def update(self):
|
def update(self):
|
||||||
|
|
|
@ -12,6 +12,10 @@ from xvfbwrapper import Xvfb
|
||||||
from orchestra.apps.accounts.models import Account
|
from orchestra.apps.accounts.models import Account
|
||||||
|
|
||||||
|
|
||||||
|
def random_ascii(length):
|
||||||
|
return ''.join([random.choice(string.hexdigits) for i in range(0, length)]).lower()
|
||||||
|
|
||||||
|
|
||||||
class AppDependencyMixin(object):
|
class AppDependencyMixin(object):
|
||||||
DEPENDENCIES = ()
|
DEPENDENCIES = ()
|
||||||
|
|
||||||
|
@ -49,13 +53,15 @@ class AppDependencyMixin(object):
|
||||||
|
|
||||||
|
|
||||||
class BaseTestCase(TestCase, AppDependencyMixin):
|
class BaseTestCase(TestCase, AppDependencyMixin):
|
||||||
pass
|
def create_account(self, superuser=False):
|
||||||
|
username = '%s_superaccount' % random_ascii(5)
|
||||||
|
password = 'orchestra'
|
||||||
|
if superuser:
|
||||||
|
return Account.objects.create_superuser(username, password=password, email='orchestra@orchestra.org')
|
||||||
|
return Account.objects.create_user(username, password=password, email='orchestra@orchestra.org')
|
||||||
|
|
||||||
|
|
||||||
class BaseLiveServerTestCase(AppDependencyMixin, LiveServerTestCase):
|
class BaseLiveServerTestCase(AppDependencyMixin, LiveServerTestCase):
|
||||||
ACCOUNT_USERNAME = 'orchestra'
|
|
||||||
ACCOUNT_PASSWORD = 'orchestra'
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def setUpClass(cls):
|
def setUpClass(cls):
|
||||||
cls.vdisplay = Xvfb()
|
cls.vdisplay = Xvfb()
|
||||||
|
@ -70,11 +76,11 @@ class BaseLiveServerTestCase(AppDependencyMixin, LiveServerTestCase):
|
||||||
super(BaseLiveServerTestCase, cls).tearDownClass()
|
super(BaseLiveServerTestCase, cls).tearDownClass()
|
||||||
|
|
||||||
def create_account(self, superuser=False):
|
def create_account(self, superuser=False):
|
||||||
|
username = '%s_superaccount' % random_ascii(5)
|
||||||
|
password = 'orchestra'
|
||||||
if superuser:
|
if superuser:
|
||||||
return Account.objects.create_superuser(self.ACCOUNT_USERNAME,
|
return Account.objects.create_superuser(username, password=password, email='orchestra@orchestra.org')
|
||||||
password=self.ACCOUNT_PASSWORD, email='orchestra@orchestra.org')
|
return Account.objects.create_user(username, password=password, email='orchestra@orchestra.org')
|
||||||
return Account.objects.create_user(self.ACCOUNT_USERNAME,
|
|
||||||
password=self.ACCOUNT_PASSWORD, email='orchestra@orchestra.org')
|
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(BaseLiveServerTestCase, self).setUp()
|
super(BaseLiveServerTestCase, self).setUp()
|
||||||
|
@ -96,7 +102,3 @@ class BaseLiveServerTestCase(AppDependencyMixin, LiveServerTestCase):
|
||||||
|
|
||||||
def rest_login(self):
|
def rest_login(self):
|
||||||
self.rest.login(username=self.ACCOUNT_USERNAME, password=self.ACCOUNT_PASSWORD)
|
self.rest.login(username=self.ACCOUNT_USERNAME, password=self.ACCOUNT_PASSWORD)
|
||||||
|
|
||||||
|
|
||||||
def random_ascii(length):
|
|
||||||
return ''.join([random.choice(string.hexdigits) for i in range(0, length)]).lower()
|
|
||||||
|
|
Loading…
Reference in New Issue