Projet

Général

Profil

0003-tests-add-tests-for-the-multitenant-framework-8425.patch

Benjamin Dauvergne, 30 septembre 2015 14:29

Télécharger (22,7 ko)

Voir les différences:

Subject: [PATCH 3/5] tests: add tests for the multitenant framework (#8425)

tests/test_settings.py is moved in this new test suite. Tested are the
hobo_notify script and the simple creation of user objects.
 README                                |  17 +++-
 hobo/agent/test_urls.py               |   9 ++
 tests/test_multitenant.py             | 187 ----------------------------------
 tests_multitenant/conftest.py         |  54 ++++++++++
 tests_multitenant/settings.py         |  23 +++++
 tests_multitenant/test_hobo_notify.py | 101 ++++++++++++++++++
 tests_multitenant/test_objects.py     |  16 +++
 tests_multitenant/test_settings.py    | 139 +++++++++++++++++++++++++
 8 files changed, 358 insertions(+), 188 deletions(-)
 create mode 100644 hobo/agent/test_urls.py
 delete mode 100644 tests/test_multitenant.py
 create mode 100644 tests_multitenant/conftest.py
 create mode 100644 tests_multitenant/settings.py
 create mode 100644 tests_multitenant/test_hobo_notify.py
 create mode 100644 tests_multitenant/test_objects.py
 create mode 100644 tests_multitenant/test_settings.py
README
161 161
and use the email of the oldest superuser. Cron job can be created for calling
162 162
this command when regular synchronization of roles with your w.c.s.  instances
163 163
is needed. The sole option named "--delete" indicate if you want to delete
164
stale roles, default is to not delete them.
164
stale roles, default is to not delete them.  
165

  
166
Tests
167
-----
168

  
169
For testing hobo server, do in a virtualenv:
170

  
171
   pip install pytest pytest-django
172

  
173
   DJANGO_SETTINGS_MODULE=hobo.settings HOBO_SETTINGS_FILE=tests/settings.py py.tests tests
174

  
175
For testing multitenant framework, do in a virtualenv:
176

  
177
   pip install pytest pytest-django memcached mock .
178

  
179
   cd tests_multitenant ; PYTHONPATH=. DJANGO_SETTINGS_MODULE=settings py.test .
hobo/agent/test_urls.py
1
from django.conf.urls import patterns, url
2
from django.http import HttpResponse
3

  
4
def helloworld(request):
5
    return HttpResponse('Hello world')
6

  
7
urlpatterns = patterns('',
8
    url(r'^$', helloworld),
9
)
tests/test_multitenant.py
1
import threading
2
import random
3
import pytest
4

  
5
from django.apps import apps
6
import django.conf
7
from django.core.handlers.base import BaseHandler
8
from django.core.wsgi import get_wsgi_application
9
import django.db
10
from django.core.cache import cache, caches
11

  
12
@pytest.fixture
13
def multitenant_settings(settings):
14
    settings.MIDDLEWARE_CLASSES = (
15
        'hobo.multitenant.middleware.TenantMiddleware',
16
    ) + settings.MIDDLEWARE_CLASSES
17

  
18
    settings.TENANT_APPS = settings.INSTALLED_APPS
19
    settings.TENANT_MODEL = 'multitenant.Tenant'
20
    settings.DATABASE_ROUTERS = ('tenant_schemas.routers.TenantSyncRouter',)
21
    settings.DATABASES = {
22
        'default': {
23
            'ENGINE': 'tenant_schemas.postgresql_backend',
24
            'NAME': 'test%s' % str(random.random())[2:]
25
        }
26
    }
27
    settings.CACHES = {
28
        'default': {
29
            'BACKEND': 'hobo.multitenant.cache.TenantCache',
30
            # add a real Django cache backend, with its parameters if needed
31
            'REAL_BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
32
            'LOCATION': '127.0.0.1:11211',
33
        }
34
    }
35

  
36
    django.db.connections = django.db.ConnectionHandler(settings.DATABASES)
37
    apps.set_installed_apps(('hobo.multitenant', 'hobo.agent.common',) + settings.INSTALLED_APPS)
38
    return settings
39

  
40
def test_tenant_middleware(multitenant_settings, client):
41
    res = client.get('/', SERVER_NAME='invalid.example.net')
42
    assert res.status_code == 404
43
    res = client.get('/', SERVER_NAME='test.example.net')
44
    assert res.status_code != 404
45
    assert res.wsgi_request.tenant.schema_name == 'test_example_net'
46

  
47
def test_tenant_json_settings(multitenant_settings, client):
48
    from hobo.multitenant.models import Tenant
49

  
50
    django.conf.settings.clear_tenants_settings()
51

  
52
    multitenant_settings.default_settings.TENANT_SETTINGS_LOADERS = ('hobo.multitenant.settings_loaders.SettingsJSON', )
53

  
54
    # check the setting is not defined
55
    with pytest.raises(AttributeError):
56
        django.conf.settings.HOBO_TEST_VARIABLE
57

  
58
    django.db.connection.set_tenant(Tenant(domain_url='test-settings.example.net',
59
                                           schema_name='test_settings_example_net'))
60

  
61
    # check it's defined when moving into the schema
62
    assert django.conf.settings.HOBO_TEST_VARIABLE is True
63

  
64
    django.db.connection.set_schema_to_public()
65

  
66
    # check it's no longer defined after going back to the public schema
67
    with pytest.raises(AttributeError):
68
        django.conf.settings.HOBO_TEST_VARIABLE
69

  
70
def test_tenant_hobo_settings(multitenant_settings, client):
71
    from hobo.multitenant.models import Tenant
72

  
73
    django.conf.settings.clear_tenants_settings()
74

  
75
    multitenant_settings.TENANT_SETTINGS_LOADERS = ('hobo.multitenant.settings_loaders.TemplateVars', )
76

  
77
    # check the setting is not defined
78
    with pytest.raises(AttributeError):
79
        django.conf.settings.TEMPLATE_VARS
80

  
81
    django.db.connection.set_tenant(Tenant(domain_url='test-templatevars.example.net',
82
                                           schema_name='test_templatevars_example_net'))
83

  
84
    # check it's defined when moving into the schema
85
    assert django.conf.settings.TEMPLATE_VARS
86
    assert django.conf.settings.TEMPLATE_VARS['hobo_test_variable'] is True
87
    assert django.conf.settings.TEMPLATE_VARS['test_url'] == 'http://test-templatevars.example.net'
88
    assert django.conf.settings.TEMPLATE_VARS['other_url'] == 'http://other.example.net'
89
    assert django.conf.settings.TEMPLATE_VARS['site_title'] == 'Test'
90
    assert django.conf.settings.TEMPLATE_VARS['other_variable'] == 'bar'
91

  
92
    django.db.connection.set_schema_to_public()
93

  
94
    # check it's no longer defined after going back to the public schema
95
    with pytest.raises(AttributeError):
96
        django.conf.settings.TEMPLATE_VARS
97

  
98
def test_tenant_cors_settings(multitenant_settings, client):
99
    from hobo.multitenant.models import Tenant
100

  
101
    django.conf.settings.clear_tenants_settings()
102

  
103
    multitenant_settings.TENANT_SETTINGS_LOADERS = ('hobo.multitenant.settings_loaders.CORSSettings', )
104
    # check the setting is not defined
105
    with pytest.raises(AttributeError):
106
        django.conf.settings.CORS_ORIGIN_WHITELIST
107

  
108
    django.db.connection.set_tenant(Tenant(domain_url='test-templatevars.example.net',
109
                                           schema_name='test_templatevars_example_net'))
110

  
111
    # check it's defined when moving into the schema
112
    assert django.conf.settings.CORS_ORIGIN_WHITELIST
113
    assert 'http://test-templatevars.example.net' in django.conf.settings.CORS_ORIGIN_WHITELIST
114
    assert 'http://other.example.net' in django.conf.settings.CORS_ORIGIN_WHITELIST
115

  
116
    # check it's no longer defined after process_response()
117
    django.db.connection.set_schema_to_public()
118

  
119
    with pytest.raises(AttributeError):
120
        django.conf.settings.CORS_ORIGIN_WHITELIST
121

  
122
def test_multithreading(multitenant_settings, client):
123
    from hobo.multitenant.models import Tenant
124

  
125
    django.conf.settings.clear_tenants_settings()
126
    multitenant_settings.TENANT_SETTINGS_LOADERS = ('hobo.multitenant.settings_loaders.TemplateVars', )
127

  
128
    def f(template_vars=False):
129
        if template_vars:
130
            assert hasattr(django.conf.settings, 'TEMPLATE_VARS')
131
        else:
132
            assert not hasattr(django.conf.settings, 'TEMPLATE_VARS')
133

  
134
    assert not hasattr(django.conf.settings, 'TEMPLATE_VARS')
135
    t1 = threading.Thread(target=f)
136
    t1.start()
137
    t1.join()
138

  
139
    django.db.connection.set_tenant(Tenant(domain_url='test-templatevars.example.net',
140
                                           schema_name='test_templatevars_example_net'))
141

  
142
    assert hasattr(django.conf.settings, 'TEMPLATE_VARS')
143
    t2 = threading.Thread(target=f, args=(True,))
144
    t2.start()
145
    t2.join()
146

  
147
    django.db.connection.set_schema_to_public()
148

  
149
    assert not hasattr(django.conf.settings, 'TEMPLATE_VARS')
150
    t3 = threading.Thread(target=f)
151
    t3.start()
152
    t3.join()
153

  
154
def test_cache(multitenant_settings, client):
155
    from hobo.multitenant.models import Tenant
156

  
157
    # Clear caches
158
    caches._caches.caches = {}
159

  
160
    assert not hasattr(django.db.connection.get_tenant(), 'domain_url')
161
    cache.set('coin', 1)
162

  
163
    django.db.connection.set_tenant(Tenant(domain_url='test-templatevars.example.net',
164
                                           schema_name='test_templatevars_example_net'))
165
    assert cache.get('coin') is None
166
    cache.set('coin', 2)
167

  
168
    django.db.connection.set_schema_to_public()
169

  
170
    assert cache.get('coin') == 1
171

  
172
    django.db.connection.set_tenant(Tenant(domain_url='test-templatevars.example.net',
173
                                           schema_name='test_templatevars_example_net'))
174

  
175
    def f():
176
        assert cache.get('coin') == 2
177
    t1 = threading.Thread(target=f)
178
    t1.start()
179
    t1.join()
180

  
181
    django.db.connection.set_schema_to_public()
182

  
183
    def g():
184
        assert cache.get('coin') == 1
185
    t2 = threading.Thread(target=f)
186
    t2.start()
187
    t2.join()
tests_multitenant/conftest.py
1
import os
2
import tempfile
3
import shutil
4
import json
5

  
6
import pytest
7

  
8
@pytest.fixture(scope='function')
9
def tenants(db, request, settings):
10
    from hobo.multitenant.models import Tenant
11
    base = tempfile.mkdtemp('combo-tenant-base')
12
    settings.TENANT_BASE = base
13
    @pytest.mark.django_db
14
    def make_tenant(name):
15
        tenant_dir = os.path.join(base, name)
16
        os.mkdir(tenant_dir)
17
        with open(os.path.join(tenant_dir, 'unsecure'), 'w') as fd:
18
            fd.write('1')
19
        with open(os.path.join(tenant_dir, 'settings.json'), 'w') as fd:
20
            json.dump({'HOBO_TEST_VARIABLE': name}, fd)
21
        with open(os.path.join(tenant_dir, 'hobo.json'), 'w') as fd:
22
            json.dump({
23
                'variables': {
24
                    'hobo_test_variable': True,
25
                    'other_variable': 'foo',
26
                },
27
                'services': [
28
                    {'slug': 'test',
29
                     'title': 'Test',
30
                     'this': True,
31
                     'secret_key': '12345',
32
                     'base_url': 'http://%s' % name,
33
                     'saml-sp-metadata-url': 'http://%s/saml/metadata' % name,
34
                     'variables': {
35
                         'other_variable': 'bar',
36
                     }
37
                    },
38
                    {'slug': 'other',
39
                     'title': 'Other',
40
                     'base_url': 'http://other.example.net'},
41
                    ]}, fd)
42
        t = Tenant(domain_url=name,
43
                      schema_name=name.replace('-', '_').replace('.', '_'))
44
        t.create_schema()
45
        return t
46
    tenants = [make_tenant('tenant1.example.net'), make_tenant('tenant2.example.net')]
47
    def fin():
48
        from django.db import connection
49
        connection.set_schema_to_public()
50
        for t in tenants:
51
            t.delete(True)
52
        shutil.rmtree(base)
53
    request.addfinalizer(fin)
54
    return tenants
tests_multitenant/settings.py
1
import os.path
2
import __builtin__ as builtin
3
from mock import mock_open, patch
4

  
5
LANGUAGE_CODE = 'en-us'
6

  
7
INSTALLED_APPS = ('django.contrib.auth', 'django.contrib.sessions', 'django.contrib.contenttypes')
8

  
9
PROJECT_NAME = 'fake-agent'
10

  
11
with patch.object(builtin, 'file', mock_open(read_data='xxx')):
12
    execfile(os.path.join(os.path.dirname(__file__), '../debian/debian_config_common.py'))
13

  
14
TENANT_APPS = ('django.contrib.auth','django.contrib.sessions', 'django.contrib.contenttypes', 'hobo.agent.common')
15

  
16
ROOT_URLCONF = 'hobo.agent.test_urls'
17
CACHES = {
18
    'default': {
19
            'BACKEND': 'hobo.multitenant.cache.TenantCache',
20
            'REAL_BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
21
    }
22
}
23

  
tests_multitenant/test_hobo_notify.py
1
# -*- coding: utf-8 -*-
2

  
3
import pytest
4

  
5
pytestmark = pytest.mark.django_db
6

  
7

  
8
def test_hobo_notify_roles(tenants):
9
    from hobo.agent.common.management.commands.hobo_notify import Command
10
    from tenant_schemas.utils import tenant_context
11
    from django.contrib.auth.models import Group
12
    from hobo.agent.common.models import Role
13

  
14
    # test wrong audience  
15
    for tenant in tenants:
16
        with tenant_context(tenant):
17
            notification = {
18
                u'@type': u'provision',
19
                u'audience': [u'http://coin.com/saml/metadata'],
20
                u'objects': [
21
                    {
22
                        u'@type': 'role',
23
                        u'uuid': u'12345',
24
                        u'name': u'Service petite enfance',
25
                        u'slug': u'service-petite-enfance',
26
                        u'description': u'Role du service petite enfance %s' % tenant.domain_url,
27
                    }
28
                ]
29
            }
30
            Command.process_notification(tenant, notification)
31
            assert Group.objects.count() == 0
32
            assert Role.objects.count() == 0
33

  
34
    # test provision
35
    for tenant in tenants:
36
        with tenant_context(tenant):
37
            notification = {
38
                u'@type': u'provision',
39
                u'audience': [u'%s/saml/metadata' % tenant.get_base_url()],
40
                u'objects': [
41
                    {
42
                        u'@type': 'role',
43
                        u'uuid': u'12345',
44
                        u'name': u'Service petite enfance',
45
                        u'slug': u'service-petite-enfance',
46
                        u'description': u'Role du service petite enfance %s' % tenant.domain_url,
47
                    }
48
                ]
49
            }
50
            Command.process_notification(tenant, notification)
51
            assert Group.objects.count() == 1
52
            assert Role.objects.count() == 1
53
            role = Role.objects.get()
54
            assert role.uuid == u'12345'
55
            assert role.name == u'Service petite enfance'
56
            assert role.description == u'Role du service petite enfance %s' % tenant.domain_url
57

  
58
    # test full provisionning
59
    for tenant in tenants:
60
        with tenant_context(tenant):
61
            notification = {
62
                u'@type': u'provision',
63
                u'full': True,
64
                u'audience': [u'%s/saml/metadata' % tenant.get_base_url()],
65
                u'objects': [
66
                    {
67
                        u'@type': 'role',
68
                        u'uuid': u'xyz',
69
                        u'name': u'Service état civil',
70
                        u'slug': u'service-etat-civil',
71
                        u'description': u'Role du service état civil %s' % tenant.domain_url,
72
                    }
73
                ]
74
            }
75
            Command.process_notification(tenant, notification)
76
            assert Group.objects.count() == 1
77
            assert Role.objects.count() == 1
78
            role = Role.objects.get()
79
            assert role.uuid == u'xyz'
80
            assert role.name == u'Service état civil'
81
            assert role.description == u'Role du service état civil %s' % tenant.domain_url
82

  
83
    # test deprovision
84
    for tenant in tenants:
85
        with tenant_context(tenant):
86
            notification = {
87
                u'@type': u'deprovision',
88
                u'audience': [u'%s/saml/metadata' % tenant.get_base_url()],
89
                u'objects': [
90
                    {
91
                        u'@type': 'role',
92
                        u'uuid': u'xyz',
93
                        u'name': u'Service état civil',
94
                        u'slug': u'service-etat-civil',
95
                        u'description': u'Role du service état civil %s' % tenant.domain_url,
96
                    }
97
                ]
98
            }
99
            Command.process_notification(tenant, notification)
100
            assert Group.objects.count() == 0
101
            assert Role.objects.count() == 0
tests_multitenant/test_objects.py
1
import pytest
2

  
3
pytestmark = pytest.mark.django_db
4

  
5

  
6
def test_user_creation(tenants):
7
    from tenant_schemas.utils import tenant_context
8
    from django.contrib.auth import models
9

  
10
    for tenant in tenants:
11
        with tenant_context(tenant):
12
            models.User.objects.create(username=tenant.domain_url)
13
            assert models.User.objects.count() == 1
14
    for tenant in tenants:
15
        with tenant_context(tenant):
16
            assert models.User.objects.get().username == tenant.domain_url
tests_multitenant/test_settings.py
1
import threading
2
import random
3
import pytest
4

  
5
from django.apps import apps
6
import django.conf
7
from django.core.handlers.base import BaseHandler
8
from django.core.wsgi import get_wsgi_application
9
import django.db
10
from django.core.cache import cache, caches
11

  
12
from tenant_schemas.utils import tenant_context
13

  
14
def test_tenant_middleware(tenants, client):
15
    res = client.get('/', SERVER_NAME='invalid.example.net')
16
    assert res.status_code == 404
17
    for tenant in tenants:
18
        res = client.get('/', SERVER_NAME=tenant.domain_url)
19
        assert res.status_code != 404
20
        assert res.wsgi_request.tenant.schema_name == tenant.schema_name
21

  
22
def test_tenant_json_settings(tenants, settings):
23
    settings.clear_tenants_settings()
24

  
25
    settings.default_settings.TENANT_SETTINGS_LOADERS = ('hobo.multitenant.settings_loaders.SettingsJSON', )
26

  
27
    # check the setting is not defined
28
    with pytest.raises(AttributeError):
29
        settings.HOBO_TEST_VARIABLE
30

  
31
    # check that for each tenant it contains the tenant domain
32
    # it's set by the tenants fixture in conftest.py
33
    for tenant in tenants:
34
        with tenant_context(tenant):
35
            assert django.conf.settings.HOBO_TEST_VARIABLE == tenant.domain_url
36

  
37
    # check it's no longer defined after going back to the public schema
38
    with pytest.raises(AttributeError):
39
        settings.HOBO_TEST_VARIABLE
40

  
41
def test_tenant_template_vars(tenants, settings, client):
42
    from hobo.multitenant.models import Tenant
43

  
44
    django.conf.settings.clear_tenants_settings()
45

  
46
    settings.default_settings.TENANT_SETTINGS_LOADERS = ('hobo.multitenant.settings_loaders.TemplateVars', )
47

  
48
    # check the setting is not defined
49
    with pytest.raises(AttributeError):
50
        django.conf.settings.TEMPLATE_VARS
51

  
52
    for tenant in tenants:
53
        with tenant_context(tenant):
54
            # check it's defined when moving into the schema
55
            assert django.conf.settings.TEMPLATE_VARS
56
            assert django.conf.settings.TEMPLATE_VARS['hobo_test_variable'] is True
57
            assert django.conf.settings.TEMPLATE_VARS['test_url'] == tenant.get_base_url()
58
            assert django.conf.settings.TEMPLATE_VARS['other_url'] == 'http://other.example.net'
59
            assert django.conf.settings.TEMPLATE_VARS['site_title'] == 'Test'
60
            assert django.conf.settings.TEMPLATE_VARS['other_variable'] == 'bar'
61

  
62
    # check it's no longer defined after going back to the public schema
63
    with pytest.raises(AttributeError):
64
        django.conf.settings.TEMPLATE_VARS
65

  
66
def test_tenant_cors_settings(tenants, settings, client):
67
    settings.clear_tenants_settings()
68

  
69
    settings.default_settings.TENANT_SETTINGS_LOADERS = ('hobo.multitenant.settings_loaders.CORSSettings', )
70

  
71
    # check the setting is not defined
72
    with pytest.raises(AttributeError):
73
        settings.CORS_ORIGIN_WHITELIST
74

  
75
    for tenant in tenants:
76
        with tenant_context(tenant):
77
            # check it's defined when moving into the schema
78
            assert django.conf.settings.CORS_ORIGIN_WHITELIST
79
            assert tenant.get_base_url() in django.conf.settings.CORS_ORIGIN_WHITELIST
80
            assert 'http://other.example.net' in django.conf.settings.CORS_ORIGIN_WHITELIST
81

  
82
    with pytest.raises(AttributeError):
83
        django.conf.settings.CORS_ORIGIN_WHITELIST
84

  
85
def test_multithreading(tenants, settings, client):
86

  
87
    settings.clear_tenants_settings()
88
    settings.default_settings.TENANT_SETTINGS_LOADERS = ('hobo.multitenant.settings_loaders.TemplateVars', )
89

  
90
    def f(tenant=None):
91
        if not tenant is None:
92
            assert hasattr(settings, 'TEMPLATE_VARS')
93
            assert settings.TEMPLATE_VARS['test_url'] == tenant.get_base_url()
94
        else:
95
            assert not hasattr(django.conf.settings, 'TEMPLATE_VARS')
96

  
97
    assert not hasattr(django.conf.settings, 'TEMPLATE_VARS')
98
    t1 = threading.Thread(target=f)
99
    t1.start()
100
    t1.join()
101

  
102
    for tenant in tenants:
103
        with tenant_context(tenant):
104
            assert hasattr(settings, 'TEMPLATE_VARS')
105
            t2 = threading.Thread(target=f, args=(tenant,))
106
            t2.start()
107
            t2.join()
108

  
109
    assert not hasattr(django.conf.settings, 'TEMPLATE_VARS')
110
    t3 = threading.Thread(target=f)
111
    t3.start()
112
    t3.join()
113

  
114
def test_cache(tenants, client):
115
    # Clear caches
116
    caches._caches.caches = {}
117

  
118
    cache.set('coin', 1)
119

  
120
    for tenant in tenants:
121
        with tenant_context(tenant):
122
            assert cache.get('coin') is None
123
            cache.set('coin', tenant.domain_url)
124

  
125
    assert cache.get('coin') == 1
126

  
127
    for tenant in tenants:
128
        with tenant_context(tenant):
129
            def f():
130
                assert cache.get('coin') == tenant.domain_url
131
            t1 = threading.Thread(target=f)
132
            t1.start()
133
            t1.join()
134

  
135
    def g():
136
        assert cache.get('coin') == 1
137
    t2 = threading.Thread(target=g)
138
    t2.start()
139
    t2.join()
0
-