Compare commits
9 Commits
0c14103e4b
...
90f441d72d
Author | SHA1 | Date |
---|---|---|
Paul Marillonnet | 90f441d72d | |
Agate | b4ce8e96e8 | |
Benjamin Dauvergne | 842f699e8a | |
Emmanuel Cazenave | 9bc96520ac | |
Emmanuel Cazenave | b9e4dab140 | |
Benjamin Dauvergne | 3ac54aa650 | |
Emmanuel Cazenave | f05596a088 | |
Agate | 4a1cfa5a16 | |
Emmanuel Cazenave | dbf76af91e |
|
@ -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():
|
||||
|
|
|
@ -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}
|
||||
)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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', [])
|
||||
|
|
|
@ -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():
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
2
setup.py
2
setup.py
|
@ -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',
|
||||
|
|
|
@ -1 +1 @@
|
|||
{{ user.first_name }} {{ user.cached_extra_attributes.foo }}
|
||||
{{ user.first_name }} {{ user.attributes.foo }}
|
|
@ -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)
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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 }}
|
|
@ -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
|
||||
|
|
|
@ -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')
|
||||
|
||||
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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',)
|
||||
):
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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',)
|
||||
):
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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/'
|
||||
|
|
8
tox.ini
8
tox.ini
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue