Compare commits

..

9 Commits

Author SHA1 Message Date
Paul Marillonnet 90f441d72d [WIP] remove unnecessary authentic redundant attributes accessor
gitea-wip/hobo/pipeline/pr-main This commit looks good Details
2023-01-31 10:08:52 +01:00
Agate b4ce8e96e8 user: allow customization of User.get_full_name() through templates (#72945) 2023-01-31 10:05:48 +01:00
Benjamin Dauvergne 842f699e8a django32: change the way Thread are made tenant aware (#67760)
gitea-wip/hobo/pipeline/pr-main This commit looks good Details
Django 3.2. changed the implementation of django.db.ConnectionHandler it
now uses asgiref.local.Local as a thread/asyncio-task local dictionnary
instead of threading.local. This new implementation use
threading.current_thread() to get a reference to the current thread
instead of threading._get_ident(), but inside __bootstrap_inner, the
_active dictionnary is not initialized and current_thread() returns a
dummy value instead of the current thread.

To work around this behaviour I made __bootstrap_inner wrap the run
method with the code needed to setup the tenant, so that it's run after
__boostrap_inner initialization of the current thread in the _active
dictionnary.
2023-01-30 14:54:37 +01:00
Emmanuel Cazenave 9bc96520ac django32: use public API to clear caches (#67760) 2023-01-30 14:53:15 +01:00
Emmanuel Cazenave b9e4dab140 django32: honor django's generated error formats (#67760) 2023-01-30 14:53:15 +01:00
Benjamin Dauvergne 3ac54aa650 django32: implement clear_tenants_settings as a global function (#67760)
Django 3.2 introduced a constraint on the access of settings object
attributes, they can only be in uppercase. As clear_tenants_settings is
only used by tests on the multitenant framework, clear_tenants_settings
is reimplemented as a global function using a global variable
_tenant_settings_wrapper to access the global multitenant aware wrapper
around the setting object.
2023-01-30 14:53:15 +01:00
Emmanuel Cazenave f05596a088 django32: check message content directly in the page (#67760)
Which is the point of interest anyway.
https://code.djangoproject.com/ticket/32191 for details on why it is not possible anymore to check the plain text in the response header.
2023-01-30 14:53:15 +01:00
Agate 4a1cfa5a16 django32: do not instanciate ServiceBase abstract model (#67760) 2023-01-30 14:53:15 +01:00
Emmanuel Cazenave dbf76af91e django32: run tests against django 3.2 (#67760) 2023-01-30 14:53:15 +01:00
23 changed files with 118 additions and 133 deletions

View File

@ -97,6 +97,25 @@ class Variable(models.Model):
self._parse_value_as_json()
def is_resolvable(url):
try:
netloc = urllib.parse.urlparse(url).netloc
if netloc and socket.gethostbyname(netloc):
return True
except socket.gaierror:
return False
def has_valid_certificate(url):
try:
requests.get(url, timeout=5, verify=True, allow_redirects=False)
return True
except requests.exceptions.SSLError:
return False
except requests.exceptions.ConnectionError:
return False
class ServiceBase(models.Model):
class Meta:
abstract = True
@ -228,23 +247,10 @@ class ServiceBase(models.Model):
return self.get_base_url_path() + '__provision__/'
def is_resolvable(self):
try:
netloc = urllib.parse.urlparse(self.base_url).netloc
if netloc and socket.gethostbyname(netloc):
return True
except socket.gaierror:
return False
return is_resolvable(self.base_url)
def has_valid_certificate(self):
if not self.is_resolvable():
return False
try:
requests.get(self.base_url, timeout=5, verify=True, allow_redirects=False)
return True
except requests.exceptions.SSLError:
return False
except requests.exceptions.ConnectionError:
return False
return has_valid_certificate(self.base_url)
def is_running(self):
if not self.is_resolvable():

View File

@ -3,18 +3,17 @@ import urllib.parse
from django.core.exceptions import ValidationError
from django.utils.translation import gettext_lazy as _
from hobo.environment.models import ServiceBase
from hobo.environment import models
def validate_service_url(url):
service = ServiceBase(title='dummy', base_url=url)
if not service.is_resolvable():
if not models.is_resolvable(url):
raise ValidationError(
_('Error: %(netloc)s is not resolvable in URL %(url)s'),
code='not-resolvable',
params={'netloc': urllib.parse.urlsplit(url).netloc, 'url': url},
)
if not service.has_valid_certificate():
if not models.has_valid_certificate(url):
raise ValidationError(
_('Error: no valid certificate for %(url)s'), code='invalid-certificate', params={'url': url}
)

View File

@ -2,19 +2,30 @@ from django.apps import AppConfig, apps
from . import settings, threads
_tenant_settings_wrapper = None
def clear_tenants_settings():
if _tenant_settings_wrapper is not None:
_tenant_settings_wrapper.__dict__['tenants_settings'] = {}
class MultitenantAppConfig(AppConfig):
name = 'hobo.multitenant'
verbose_name = 'Multitenant'
def ready(self):
global _tenant_settings_wrapper
from django import conf
from django.db import migrations
from django.db.migrations import operations
# Install tenant aware settings
if not isinstance(conf.settings._wrapped, settings.TenantSettingsWrapper):
conf.settings._wrapped = settings.TenantSettingsWrapper(conf.settings._wrapped)
_tenant_settings_wrapper = conf.settings._wrapped = settings.TenantSettingsWrapper(
conf.settings._wrapped
)
# reset settings getattr method to a cache-less version, to cancel
# https://code.djangoproject.com/ticket/27625.
conf.LazySettings.__getattr__ = lambda self, name: getattr(self._wrapped, name)

View File

@ -34,9 +34,6 @@ class TenantSettingsWrapper:
self.__dict__['tenants_settings'] = {}
self.__dict__['default_settings'] = default_settings
def clear_tenants_settings(self):
self.__dict__['tenants_settings'] = {}
@property
def loaders(self):
loaders = getattr(self.default_settings, 'TENANT_SETTINGS_LOADERS', [])

View File

@ -14,6 +14,7 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import functools
import threading
_Thread_start = threading.Thread.start
@ -28,21 +29,34 @@ def _new_start(self):
return _Thread_start(self)
def wrap_run(self, func):
if getattr(func, '_wrapped', False):
return func
@functools.wraps(func)
def wrapper():
tenant = getattr(self, 'tenant', None)
if tenant is not None:
from django.db import connection
old_tenant = connection.tenant
connection.set_tenant(self.tenant)
try:
func()
finally:
connection.set_tenant(old_tenant)
connection.close()
else:
func()
wrapper._wrapped = True
return wrapper
def _new__bootstrap_inner(self):
tenant = getattr(self, 'tenant', None)
if tenant is not None:
from django.db import connection
old_tenant = connection.tenant
connection.set_tenant(self.tenant)
try:
_Thread__bootstrap_inner(self)
finally:
connection.set_tenant(old_tenant)
connection.close()
else:
_Thread__bootstrap_inner(self)
self.run = wrap_run(self, self.run)
_Thread__bootstrap_inner(self)
def install_tenant_aware_threads():

View File

@ -4,7 +4,6 @@ import logging
import django.db
import django.template.exceptions
from django.apps import AppConfig
from django.conf import settings
from django.contrib.auth import get_user_model
from . import utils
@ -29,36 +28,6 @@ def get_full_name(user):
def cached_extra_attributes(user):
if 'authentic2' in settings.INSTALLED_APPS:
from django.contrib.contenttypes.models import ContentType
try:
from authentic2.custom_user.models import User
from authentic2.models import Attribute, AttributeValue
except ImportError:
return
try:
user_ct = ContentType.objects.get_for_model(User)
except ContentType.DoesNotExist:
return
extra_attributes = {}
# we're in authentic, attributes are looked up differently
for attribute in Attribute.objects.filter(disabled=False):
atvs = AttributeValue.objects.filter(
content_type=user_ct,
object_id=user.id,
attribute=attribute,
)
if not atvs:
continue
if not attribute.multiple:
atv = atvs.first()
extra_attributes[attribute.name] = atv.to_python()
else:
extra_attributes[attribute.name] = [atv.to_python() for atv in atvs]
return extra_attributes
try:
return user.extra_attributes.data
except django.db.models.ObjectDoesNotExist:
@ -83,9 +52,6 @@ class UserNameConfig(AppConfig):
User.original_get_full_name = User.get_full_name
# to replace the rendering everywhere in a consistent manner
User.get_full_name = get_full_name
# for easier access in templates
User.cached_extra_attributes = functools.cached_property(cached_extra_attributes)
User.cached_extra_attributes.__set_name__(User, 'cached_extra_attributes')
# to avoid performance/recursion issues
User.__str__ = User.original_get_full_name

View File

@ -148,7 +148,7 @@ setup(
'Programming Language :: Python',
],
install_requires=[
'django>=2.2, <2.3',
'django>=2.2, <3.3',
'gadjo',
'celery<4' if sys.version_info < (3, 7) else 'celery>=4',
'django-mellon',

View File

@ -1 +1 @@
{{ user.first_name }} {{ user.cached_extra_attributes.foo }}
{{ user.first_name }} {{ user.attributes.foo }}

View File

@ -9,6 +9,7 @@ from django.contrib.contenttypes.models import ContentType
from django.core.management import call_command
from django.core.management.base import CommandError
from hobo.environment import models as environment_models
from hobo.environment.management.commands.cook import Command
from hobo.environment.models import (
Authentic,
@ -57,8 +58,8 @@ def test_check_action(command, monkeypatch):
command.server_action = 'mock a server_action handler (ex: hobo-create)'
action, action_args = 'server-action', {'url': 'https://test.org/'}
monkeypatch.setattr(ServiceBase, 'is_resolvable', lambda x: True)
monkeypatch.setattr(ServiceBase, 'has_valid_certificate', lambda x: True)
monkeypatch.setattr(environment_models, 'is_resolvable', lambda x: True)
monkeypatch.setattr(environment_models, 'has_valid_certificate', lambda x: True)
command.check_action(action, action_args)
assert True
@ -68,8 +69,8 @@ def test_check_action_unknown_action(command, monkeypatch):
command.server_action = 'mock a server_action handler (ex: hobo-create)'
action, action_args = 'not-a-server-action', {'url': 'https://test.org/'}
monkeypatch.setattr(ServiceBase, 'is_resolvable', lambda x: True)
monkeypatch.setattr(ServiceBase, 'has_valid_certificate', lambda x: True)
monkeypatch.setattr(environment_models, 'is_resolvable', lambda x: True)
monkeypatch.setattr(environment_models, 'has_valid_certificate', lambda x: True)
with pytest.raises(CommandError) as e_info:
command.check_action(action, action_args)
assert 'Unknown action not-a-server-action' in str(e_info.value)
@ -80,8 +81,8 @@ def test_check_action_unresolvable(command, monkeypatch):
command.server_action = 'mock a server_action handler (ex: hobo-create)'
action, action_args = 'server-action', {'url': 'https://test.org/'}
monkeypatch.setattr(ServiceBase, 'is_resolvable', lambda x: False)
monkeypatch.setattr(ServiceBase, 'has_valid_certificate', lambda x: True)
monkeypatch.setattr(environment_models, 'is_resolvable', lambda x: False)
monkeypatch.setattr(environment_models, 'has_valid_certificate', lambda x: True)
with pytest.raises(CommandError) as e_info:
command.check_action(action, action_args)
assert 'test.org is not resolvable in URL https://test.org/' in str(e_info.value)
@ -92,8 +93,8 @@ def test_check_action_invalid_certificat(command, monkeypatch):
command.server_action = 'mock a server_action handler (ex: hobo-create)'
action, action_args = 'server-action', {'url': 'https://test.org/'}
monkeypatch.setattr(ServiceBase, 'is_resolvable', lambda x: True)
monkeypatch.setattr(ServiceBase, 'has_valid_certificate', lambda x: False)
monkeypatch.setattr(environment_models, 'is_resolvable', lambda x: True)
monkeypatch.setattr(environment_models, 'has_valid_certificate', lambda x: False)
with pytest.raises(CommandError) as e_info:
command.check_action(action, action_args)
assert 'no valid certificate for https://test.org/' in str(e_info.value)

View File

@ -8,6 +8,7 @@ from django.utils import timezone
from test_manager import login
from webtest import Upload
from hobo.environment import models as environment_models
from hobo.environment.models import AVAILABLE_SERVICES, Combo, Passerelle, ServiceBase, Variable
from hobo.environment.utils import get_installed_services_dict
from hobo.profile.models import AttributeDefinition
@ -143,13 +144,13 @@ def test_service_creation_url_validation(app, admin_user, monkeypatch):
response = form.submit()
assert 'not resolvable' in response
monkeypatch.setattr(ServiceBase, 'is_resolvable', lambda x: True)
monkeypatch.setattr(environment_models, 'is_resolvable', lambda x: True)
form = response.form
response = form.submit()
assert 'no valid certificate' in response
assert not Combo.objects.exists()
monkeypatch.setattr(ServiceBase, 'has_valid_certificate', lambda x: True)
monkeypatch.setattr(environment_models, 'has_valid_certificate', lambda x: True)
form = response.form
response = form.submit()
assert Combo.objects.exists()

View File

@ -19,8 +19,8 @@ def test_theme_view(mocked_random, app, admin_user, fake_themes):
assert Variable.objects.filter(name='theme')[0].value == 'alfortville'
assert Variable.objects.filter(name='foo')[0].value == 'bar'
assert resp.location == '/theme/'
assert "The theme has been changed" in dict(resp.headers)['Set-Cookie']
resp = resp.follow()
assert "The theme has been changed" in str(resp.html)
assert resp.form['theme'].value == 'alfortville'
resp.form['theme'].value = 'publik'

View File

@ -20,13 +20,13 @@ def test_user_original_get_full_name(user):
def test_user_cached_extra_attributes(user):
assert user.cached_extra_attributes == {'foo': 'bar'}
assert user.attributes == {'foo': 'bar'}
def test_user_cached_extra_attributes_missing_fallbacks_to_empty_dict(user):
user.extra_attributes.delete()
user.refresh_from_db()
assert user.cached_extra_attributes == {}
assert user.attributes == {}
def test_user_get_full_name_from_template(user):

View File

@ -1 +1 @@
{{ user.first_name }} {{ user.cached_extra_attributes.nicknames.0 }} {{ user.cached_extra_attributes.nicknames.2 }}
{{ user.first_name }} {{ user.attributes.nicknames.0 }} {{ user.attributes.nicknames.2 }}

View File

@ -41,6 +41,7 @@ def test_get_full_name_from_template_utils_from_multiple_attrs(db, tenant, setti
user.attributes.nicknames = ['Milly', 'Molly', 'Minnie']
user.attributes.foo = 'bar'
user.save()
user.refresh_from_db()
templates_conf_individual = settings.TEMPLATES.copy()
templates_conf_individual[0]['DIRS'] = TEMPLATES_DIR_CONFIG_INDIVIDUAL
@ -80,6 +81,7 @@ def test_get_full_name_from_template_accessor_from_multiple_attrs(db, tenant, se
user.attributes.nicknames = ['Milly', 'Molly', 'Minnie']
user.attributes.foo = 'bar'
user.save()
user.refresh_from_db()
templates_conf_individual = settings.TEMPLATES.copy()
templates_conf_individual[0]['DIRS'] = TEMPLATES_DIR_CONFIG_INDIVIDUAL

View File

@ -1,5 +1,7 @@
import pytest
from hobo.multitenant.apps import clear_tenants_settings
@pytest.fixture
def make_tenant(tmp_path, transactional_db, settings, request):
@ -111,11 +113,13 @@ def make_tenant(tmp_path, transactional_db, settings, request):
@pytest.fixture
def tenants(make_tenant):
clear_tenants_settings()
return [make_tenant('tenant1.example.net'), make_tenant('tenant2.example.net')]
@pytest.fixture
def tenant(make_tenant):
clear_tenants_settings()
return make_tenant('tenant.example.net')

View File

@ -22,12 +22,12 @@ def test_internalipmiddleware(app, tenants, settings):
settings.DEBUG_PROPAGATE_EXCEPTIONS = False
app.get('/?raise', status=404)
response = app.get('/?raise', status=500, extra_environ={'HTTP_HOST': tenants[0].domain_url})
assert response.text == '<h1>Server Error (500)</h1>'
assert 'Server Error (500)' in response.text
settings.INTERNAL_IPS = ['127.0.0.1']
response = app.get('/?raise', status=500, extra_environ={'HTTP_HOST': tenants[0].domain_url})
assert 'You\'re seeing this error because you have' in response.text
assert 'seeing this error because you have' in response.text
def test_samesite_middleware(app, tenants, settings):

View File

@ -39,8 +39,6 @@ def test_tenant_middleware(tenants, client, settings):
def test_tenant_json_settings(tenants, settings):
settings.clear_tenants_settings()
with utilities.patch_default_settings(
settings, TENANT_SETTINGS_LOADERS=('hobo.multitenant.settings_loaders.SettingsJSON',)
):
@ -67,8 +65,6 @@ def test_tenant_json_settings(tenants, settings):
def test_tenant_template_vars(tenants, settings, client):
django.conf.settings.clear_tenants_settings()
with utilities.patch_default_settings(
settings, TENANT_SETTINGS_LOADERS=('hobo.multitenant.settings_loaders.TemplateVars',)
):
@ -99,8 +95,6 @@ def test_tenant_template_vars(tenants, settings, client):
def test_tenant_settings_vars(tenants, settings, client):
django.conf.settings.clear_tenants_settings()
with utilities.patch_default_settings(
settings, TENANT_SETTINGS_LOADERS=('hobo.multitenant.settings_loaders.SettingsVars',)
):
@ -127,8 +121,6 @@ def test_tenant_settings_vars(tenants, settings, client):
def test_tenant_cors_settings(tenants, settings, client):
settings.clear_tenants_settings()
with utilities.patch_default_settings(
settings, TENANT_SETTINGS_LOADERS=('hobo.multitenant.settings_loaders.CORSSettings',)
):
@ -148,8 +140,6 @@ def test_tenant_cors_settings(tenants, settings, client):
def test_tenant_theme_settings(tenants, settings, client):
django.conf.settings.clear_tenants_settings()
with utilities.patch_default_settings(
settings,
TENANT_SETTINGS_LOADERS=(
@ -183,7 +173,6 @@ def test_shared_secret():
def test_known_services(tenants, settings):
from hobo.multitenant.settings_loaders import KnownServices
settings.clear_tenants_settings()
settings.SETTINGS_MODULE = 'fake.settings'
for tenant in tenants:
@ -223,8 +212,6 @@ def test_known_services(tenants, settings):
def test_legacy_urls_mapping(tenants, settings):
settings.clear_tenants_settings()
settings.SETTINGS_MODULE = 'fake.settings'
for tenant in tenants:
@ -235,8 +222,6 @@ def test_legacy_urls_mapping(tenants, settings):
def test_unique_cookies(tenants, settings):
settings.clear_tenants_settings()
cookie_names = set()
for tenant in tenants:
with tenant_context(tenant):
@ -247,8 +232,6 @@ def test_unique_cookies(tenants, settings):
def test_tenant_json_settings_reload(tenants, settings, freezer):
settings.clear_tenants_settings()
with utilities.patch_default_settings(
settings, TENANT_SETTINGS_LOADERS=('hobo.multitenant.settings_loaders.SettingsJSON',)
):

View File

@ -49,7 +49,6 @@ def test_all_tenants(handle, tenants):
def test_all_tenants_disable_cron(handle, tenants, settings):
from django.core.management import execute_from_command_line
settings.clear_tenants_settings()
settings.DISABLE_CRON_JOBS = True
handle.side_effect = RecordTenant()
execute_from_command_line(['manage.py', 'tenant_command', 'clearsessions', '--all-tenants'])
@ -61,7 +60,6 @@ def test_all_tenants_disable_cron(handle, tenants, settings):
def test_all_tenants_disable_cron_for_specific_tenant(handle, tenants, settings):
from django.core.management import execute_from_command_line
settings.clear_tenants_settings()
disabled_tenant = tenants[0]
with open(os.path.join(disabled_tenant.get_directory(), 'settings.json'), 'w') as fd:
json.dump(
@ -78,7 +76,6 @@ def test_all_tenants_disable_cron_for_specific_tenant(handle, tenants, settings)
def test_all_tenants_global_disable_cron_with_force_job(handle, tenants, settings):
from django.core.management import execute_from_command_line
settings.clear_tenants_settings()
settings.DISABLE_CRON_JOBS = True
handle.side_effect = RecordTenant()
execute_from_command_line(

View File

@ -23,8 +23,6 @@ from tenant_schemas.utils import tenant_context
def test_thread(tenants, settings, client):
settings.clear_tenants_settings()
with utilities.patch_default_settings(
settings, TENANT_SETTINGS_LOADERS=('hobo.multitenant.settings_loaders.TemplateVars',)
):
@ -56,7 +54,8 @@ def test_thread(tenants, settings, client):
def test_cache(tenants, client):
# Clear caches
caches._caches.caches = {}
for c in caches.all():
c.clear()
cache.set('coin', 1)
@ -86,7 +85,6 @@ def test_cache(tenants, client):
def test_timer_thread(tenants, settings, client):
settings.clear_tenants_settings()
with utilities.patch_default_settings(
settings, TENANT_SETTINGS_LOADERS=('hobo.multitenant.settings_loaders.TemplateVars',)
):

View File

@ -5,18 +5,18 @@ from django.core.management import call_command, load_command_class
from django.core.management.base import CommandError
from hobo.deploy.utils import get_hobo_json
from hobo.environment.models import ServiceBase
from hobo.environment import models as environment_models
def test_cook(db, fake_notify, monkeypatch):
monkeypatch.setattr(ServiceBase, 'is_resolvable', lambda x: True)
monkeypatch.setattr(ServiceBase, 'has_valid_certificate', lambda x: True)
monkeypatch.setattr(environment_models, 'is_resolvable', lambda x: True)
monkeypatch.setattr(environment_models, 'has_valid_certificate', lambda x: True)
call_command('cook', 'tests_schemas/recipe.json')
assert len(fake_notify) == 3
def test_cook_unresolvable(db, fake_notify, monkeypatch):
monkeypatch.setattr(ServiceBase, 'is_resolvable', lambda x: False)
monkeypatch.setattr(environment_models, 'is_resolvable', lambda x: False)
with pytest.raises(CommandError) as e_info:
call_command('cook', 'tests_schemas/recipe.json')
assert 'is not resolvable' in str(e_info.value)
@ -26,8 +26,8 @@ def test_cook_example(db, fake_notify, monkeypatch, fake_themes):
"""hobo/cook (before rabbitmq) scenario having templates.
the resulting JSON may be helpfull to manually invoque hobo-deploy (after rabbitmq)
"""
monkeypatch.setattr(ServiceBase, 'is_resolvable', lambda x: True)
monkeypatch.setattr(ServiceBase, 'has_valid_certificate', lambda x: True)
monkeypatch.setattr(environment_models, 'is_resolvable', lambda x: True)
monkeypatch.setattr(environment_models, 'has_valid_certificate', lambda x: True)
call_command('cook', 'tests_schemas/example_recipe.json')
# notify_agents was call

View File

@ -5,15 +5,15 @@ from django.core.management import call_command
from django.core.management.base import CommandError
from tenant_schemas.utils import tenant_context
from hobo.environment.models import Passerelle, ServiceBase
from hobo.environment import models as environment_models
from hobo.environment.utils import get_installed_services, get_or_create_local_hobo
from hobo.multitenant.middleware import TenantMiddleware, TenantNotFound
@pytest.fixture()
def hobo_tenant(db, fake_notify, monkeypatch, fake_themes):
monkeypatch.setattr(ServiceBase, 'is_resolvable', lambda x: True)
monkeypatch.setattr(ServiceBase, 'has_valid_certificate', lambda x: True)
monkeypatch.setattr(environment_models, 'is_resolvable', lambda x: True)
monkeypatch.setattr(environment_models, 'has_valid_certificate', lambda x: True)
yield call_command('cook', 'tests_schemas/example_recipe.json')
call_command('delete_tenant', 'hobo-instance-name.dev.signalpublik.com')
@ -34,8 +34,8 @@ def test_unknown_service(hobo_tenant):
def test_rename_hobo_service_succes(db, fake_notify, monkeypatch, fake_themes):
monkeypatch.setattr(ServiceBase, 'is_resolvable', lambda x: True)
monkeypatch.setattr(ServiceBase, 'has_valid_certificate', lambda x: True)
monkeypatch.setattr(environment_models, 'is_resolvable', lambda x: True)
monkeypatch.setattr(environment_models, 'has_valid_certificate', lambda x: True)
call_command('cook', 'tests_schemas/example_recipe.json')
assert TenantMiddleware.get_tenant_by_hostname('hobo-instance-name.dev.signalpublik.com')
call_command(

View File

@ -5,15 +5,15 @@ from django.core.management import call_command
from django.core.management.base import CommandError
from tenant_schemas.utils import tenant_context
from hobo.environment.models import Passerelle, ServiceBase
from hobo.environment import models as environment_models
from hobo.environment.utils import get_installed_services
from hobo.multitenant.middleware import TenantMiddleware
@pytest.fixture()
def hobo_tenant(db, fake_notify, monkeypatch, fake_themes):
monkeypatch.setattr(ServiceBase, 'is_resolvable', lambda x: True)
monkeypatch.setattr(ServiceBase, 'has_valid_certificate', lambda x: True)
monkeypatch.setattr(environment_models, 'is_resolvable', lambda x: True)
monkeypatch.setattr(environment_models, 'has_valid_certificate', lambda x: True)
yield call_command('cook', 'tests_schemas/example_recipe.json')
call_command('delete_tenant', 'hobo-instance-name.dev.signalpublik.com')
@ -35,7 +35,7 @@ def test_secondary_service(hobo_tenant):
tenant = TenantMiddleware.get_tenant_by_hostname('hobo-instance-name.dev.signalpublik.com')
with tenant_context(tenant):
assert get_installed_services()
Passerelle.objects.create(
environment_models.Passerelle.objects.create(
title='other passerelle',
slug='other-passerelle',
base_url='https://other-passerelle-instance-name.dev.signalpublik.com',
@ -56,8 +56,8 @@ def test_secondary_service(hobo_tenant):
def test_rename_service_succes(hobo_tenant, monkeypatch):
tenant = TenantMiddleware.get_tenant_by_hostname('hobo-instance-name.dev.signalpublik.com')
with tenant_context(tenant):
assert Passerelle.objects.count() == 1
passerelle_service = Passerelle.objects.first()
assert environment_models.Passerelle.objects.count() == 1
passerelle_service = environment_models.Passerelle.objects.first()
assert (
passerelle_service.get_base_url_path() == 'https://passerelle-instance-name.dev.signalpublik.com/'
)
@ -69,8 +69,8 @@ def test_rename_service_succes(hobo_tenant, monkeypatch):
'https://passerelle-instance-name.dev.signalpublik.com/',
'https://new-passerelle-instance-name.dev.signalpublik.com/',
)
assert Passerelle.objects.count() == 1
passerelle_service = Passerelle.objects.first()
assert environment_models.Passerelle.objects.count() == 1
passerelle_service = environment_models.Passerelle.objects.first()
assert (
passerelle_service.get_base_url_path()
== 'https://new-passerelle-instance-name.dev.signalpublik.com/'

View File

@ -6,7 +6,8 @@
min_version = 4
toxworkdir = {env:TMPDIR:/tmp}/tox-{env:USER}/hobo/{env:BRANCH_NAME:}
envlist =
py3-django22-coverage-{hobo,authentic,multipublik,multitenant,schemas,passerelle}
py3-django22-{hobo,authentic,multipublik,multitenant,schemas,passerelle}
py3-django32-{hobo,authentic,multipublik,multitenant,schemas,passerelle}
code-style
[testenv]
@ -45,6 +46,11 @@ setenv =
passerelle: TEST_DIRECTORY=tests_passerelle/
deps:
django22: django>=2.2,<2.3
django22: psycopg2-binary<2.9
django22: psycopg2<2.9
django32: django>=3.2,<3.3
django32: psycopg2-binary
django32: psycopg
pytest!=6.0.0
pytest-cov
pytest-django