Browse Source

Merge pull request #50 from buxx/dev/_/41/radicale

Tracim 8 years ago
parent
commit
38ce4ea2d9

+ 1 - 0
.travis.yml View File

@@ -34,3 +34,4 @@ script: "cd ${TRAVIS_BUILD_DIR}/tracim && nosetests -c ${TRAVIS_BUILD_DIR}/traci
34 34
 
35 35
 after_success:
36 36
 - coveralls
37
+

+ 1 - 0
install/requirements.txt View File

@@ -52,3 +52,4 @@ who-ldap==3.1.0
52 52
 unicode-slugify==0.1.3
53 53
 Radicale==1.1.1
54 54
 icalendar==3.10
55
+caldav==0.4.0

+ 9 - 2
tracim/test.ini View File

@@ -9,6 +9,9 @@ debug = true
9 9
 # email_to = you@yourdomain.com
10 10
 smtp_server = localhost
11 11
 error_email_from = turbogears@localhost
12
+radicale.server.port = 15232
13
+radicale.client.port = 15232
14
+radicale.server.filesystem.folder = /tmp/tracim_tests_radicale_fs
12 15
 
13 16
 [server:main]
14 17
 use = egg:gearbox#wsgiref
@@ -16,7 +19,7 @@ host = 127.0.0.1
16 19
 port = 8080
17 20
 
18 21
 [app:main]
19
-sqlalchemy.url = postgresql://postgres:dummy@127.0.0.1:5432/tracim_test?client_encoding=utf8
22
+sqlalchemy.url = mysql+oursql://tracim:tracim@localhost/tracim_test
20 23
 use = config:development.ini
21 24
 
22 25
 [app:main_without_authn]
@@ -24,7 +27,7 @@ use = main
24 27
 skip_authentication = True
25 28
 
26 29
 [app:ldap]
27
-sqlalchemy.url = postgresql://postgres:dummy@127.0.0.1:5432/tracim_test?client_encoding=utf8
30
+sqlalchemy.url = mysql+oursql://tracim:tracim@localhost/tracim_test
28 31
 auth_type = ldap
29 32
 ldap_url = ldap://localhost:3333
30 33
 ldap_base_dn = dc=directory,dc=fsf,dc=org
@@ -36,4 +39,8 @@ ldap_tls = False
36 39
 ldap_group_enabled = False
37 40
 use = config:development.ini
38 41
 
42
+[app:radicale]
43
+sqlalchemy.url = mysql+oursql://tracim:tracim@localhost/tracim_test
44
+use = config:development.ini
45
+
39 46
 # Add additional test specific configuration options as necessary.

+ 3 - 1
tracim/tracim/config/app_cfg.py View File

@@ -217,7 +217,9 @@ class CFG(object):
217 217
         ]
218 218
 
219 219
         self.RADICALE_SERVER_HOST = tg.config.get('radicale.server.host', '0.0.0.0')
220
-        self.RADICALE_SERVER_PORT = tg.config.get('radicale.server.port', 5232)
220
+        self.RADICALE_SERVER_PORT = int(
221
+            tg.config.get('radicale.server.port', 5232)
222
+        )
221 223
         # Note: Other parameters needed to work in SSL (cert file, etc)
222 224
         self.RADICALE_SERVER_SSL = asbool(tg.config.get('radicale.server.ssl', False))
223 225
         self.RADICALE_SERVER_FILE_SYSTEM_FOLDER = tg.config.get(

+ 3 - 3
tracim/tracim/fixtures/ldap.py View File

@@ -46,11 +46,11 @@ ldap_test_server_fixtures = {
46 46
         },
47 47
         {
48 48
             'objectclass': ['account', 'top'],
49
-            'dn': 'cn=lawrence-not-real-email@fsf.org,ou=people,dc=directory,dc=fsf,dc=org',
49
+            'dn': 'cn=lawrence-not-real-email@fsf.local,ou=people,dc=directory,dc=fsf,dc=org',
50 50
             'attributes': {
51
-                'uid': 'lawrence-not-real-email@fsf.org',
51
+                'uid': 'lawrence-not-real-email@fsf.local',
52 52
                 'userPassword': 'foobarbaz',
53
-                'mail': 'lawrence-not-real-email@fsf.org',
53
+                'mail': 'lawrence-not-real-email@fsf.local',
54 54
                 'pubname': 'Lawrence Lessig',
55 55
             }
56 56
         },

+ 8 - 1
tracim/tracim/fixtures/users_and_groups.py View File

@@ -43,7 +43,14 @@ class Test(Fixture):
43 43
 
44 44
         lawrence = model.User()
45 45
         lawrence.display_name = 'Lawrence L.'
46
-        lawrence.email = 'lawrence-not-real-email@fsf.org'
46
+        lawrence.email = 'lawrence-not-real-email@fsf.local'
47 47
         lawrence.password = 'foobarbaz'
48 48
         self._session.add(lawrence)
49 49
         g2.users.append(lawrence)
50
+
51
+        bob = model.User()
52
+        bob.display_name = 'Bob i.'
53
+        bob.email = 'bob@fsf.local'
54
+        bob.password = 'foobarbaz'
55
+        self._session.add(bob)
56
+        g2.users.append(bob)

+ 49 - 3
tracim/tracim/lib/calendar.py View File

@@ -24,13 +24,51 @@ CALENDAR_WORKSPACE_PATH_RE = 'workspace\/([0-9]+).ics'
24 24
 CALENDAR_TYPE_USER = UserCalendar
25 25
 CALENDAR_TYPE_WORKSPACE = WorkspaceCalendar
26 26
 
27
+CALENDAR_BASE_URL_TEMPLATE = '{proto}://{domain}:{port}'
27 28
 CALENDAR_USER_URL_TEMPLATE = \
28
-    '{proto}://{domain}:{port}/user/{id}.ics#{slug}'
29
+    CALENDAR_BASE_URL_TEMPLATE + '/user/{id}.ics{extra}/'
29 30
 CALENDAR_WORKSPACE_URL_TEMPLATE = \
30
-    '{proto}://{domain}:{port}/workspace/{id}.ics#{slug}'
31
+    CALENDAR_BASE_URL_TEMPLATE + '/workspace/{id}.ics{extra}/'
31 32
 
32 33
 
33 34
 class CalendarManager(object):
35
+    @staticmethod
36
+    def get_base_url():
37
+        from tracim.config.app_cfg import CFG
38
+        cfg = CFG.get_instance()
39
+
40
+        return CALENDAR_BASE_URL_TEMPLATE.format(
41
+            proto='https' if cfg.RADICALE_CLIENT_SSL else 'http',
42
+            domain=cfg.RADICALE_CLIENT_HOST or '127.0.0.1',
43
+            port=str(cfg.RADICALE_CLIENT_PORT)
44
+        )
45
+
46
+    @staticmethod
47
+    def get_user_calendar_url(user_id: int, extra: str=''):
48
+        from tracim.config.app_cfg import CFG
49
+        cfg = CFG.get_instance()
50
+
51
+        return CALENDAR_USER_URL_TEMPLATE.format(
52
+            proto='https' if cfg.RADICALE_CLIENT_SSL else 'http',
53
+            domain=cfg.RADICALE_CLIENT_HOST or '127.0.0.1',
54
+            port=str(cfg.RADICALE_CLIENT_PORT),
55
+            id=str(user_id),
56
+            extra=extra,
57
+        )
58
+
59
+    @staticmethod
60
+    def get_workspace_calendar_url(workspace_id: int, extra: str=''):
61
+        from tracim.config.app_cfg import CFG
62
+        cfg = CFG.get_instance()
63
+
64
+        return CALENDAR_WORKSPACE_URL_TEMPLATE.format(
65
+            proto='https' if cfg.RADICALE_CLIENT_SSL else 'http',
66
+            domain=cfg.RADICALE_CLIENT_HOST or '127.0.0.1',
67
+            port=str(cfg.RADICALE_CLIENT_PORT),
68
+            id=str(workspace_id),
69
+            extra=extra,
70
+        )
71
+
34 72
     def __init__(self, user: User):
35 73
         self._user = user
36 74
 
@@ -223,7 +261,15 @@ class CalendarManager(object):
223 261
             content: Content,
224 262
             event: iCalendarEvent,
225 263
             event_name: str,
226
-    ) -> Content:
264
+    ) -> None:
265
+        """
266
+        Populate Content content instance from iCalendarEvent event attributes.
267
+        :param content: content to populate
268
+        :param event: event with data to insert in content
269
+        :param event_name: Event name (ID) like
270
+        20160602T083511Z-18100-1001-1-71_Bastien-20160602T083516Z.ics
271
+        :return: given content
272
+        """
227 273
         content.label = event.get('summary')
228 274
         content.description = event.get('description')
229 275
         content.properties = {

+ 81 - 7
tracim/tracim/lib/daemons.py View File

@@ -1,14 +1,15 @@
1 1
 import threading
2 2
 from wsgiref.simple_server import make_server
3
-
4 3
 import signal
5 4
 
5
+import collections
6
+import transaction
7
+
6 8
 from radicale import Application as RadicaleApplication
7
-from radicale import HTTPServer as RadicaleHTTPServer
8
-from radicale import HTTPSServer as RadicaleHTTPSServer
9
+from radicale import HTTPServer as BaseRadicaleHTTPServer
10
+from radicale import HTTPSServer as BaseRadicaleHTTPSServer
9 11
 from radicale import RequestHandler as RadicaleRequestHandler
10 12
 from radicale import config as radicale_config
11
-from tg import TGApp
12 13
 
13 14
 from tracim.lib.base import logger
14 15
 from tracim.lib.exceptions import AlreadyRunningDaemon
@@ -60,7 +61,7 @@ class DaemonsManager(object):
60 61
 
61 62
     def stop_all(self, *args, **kwargs) -> None:
62 63
         """
63
-        Stop all started daemons and w<ait for them.
64
+        Stop all started daemons and wait for them.
64 65
         """
65 66
         logger.info(self, 'Stopping all daemons')
66 67
         for name, daemon in self._running_daemons.items():
@@ -73,18 +74,83 @@ class DaemonsManager(object):
73 74
 
74 75
         self._running_daemons = {}
75 76
 
77
+    def execute_in_thread(self, thread_name, callback):
78
+        self._running_daemons[thread_name].append_thread_callback(callback)
79
+
80
+
81
+class TracimSocketServerMixin(object):
82
+    """
83
+    Mixin to use with socketserver.BaseServer who add _after_serve_actions
84
+    method executed after end of server execution.
85
+    """
86
+    def __init__(self, *args, **kwargs):
87
+        super().__init__(*args, **kwargs)
88
+        self._daemon_execute_callbacks = []
89
+
90
+    def append_thread_callback(self, callback: collections.Callable) -> None:
91
+        """
92
+        Add callback to self._daemon_execute_callbacks. See service_actions
93
+        function to their usages.
94
+        :param callback: callback to execute in daemon
95
+        """
96
+        self._daemon_execute_callbacks.append(callback)
97
+
98
+    def serve_forever(self, *args, **kwargs):
99
+        super().serve_forever(*args, **kwargs)
100
+        # After serving (in case of stop) do following:
101
+        self._after_serve_actions()
102
+
103
+    def _after_serve_actions(self):
104
+        """
105
+        Override (and call super if needed) to execute actions when server
106
+        finish it's job.
107
+        """
108
+        pass
109
+
110
+    def service_actions(self):
111
+        if len(self._daemon_execute_callbacks):
112
+            try:
113
+                while True:
114
+                    self._daemon_execute_callbacks.pop()()
115
+            except IndexError:
116
+                pass  # Finished to iter
117
+
76 118
 
77 119
 class Daemon(threading.Thread):
78 120
     """
79 121
     Thread who contains daemon. You must implement start and stop methods to
80 122
     manage daemon life correctly.
81 123
     """
82
-    def run(self):
124
+    def run(self) -> None:
125
+        """
126
+        Place here code who have to be executed in Daemon.
127
+        """
83 128
         raise NotImplementedError()
84 129
 
85
-    def stop(self):
130
+    def stop(self) -> None:
131
+        """
132
+        Place here code who stop your daemon
133
+        """
86 134
         raise NotImplementedError()
87 135
 
136
+    def append_thread_callback(self, callback: collections.Callable) -> None:
137
+        """
138
+        Place here the logic who permit to execute a callback in your daemon.
139
+        To get an exemple of that, take a look at
140
+        socketserver.BaseServer#service_actions  and how we use it in
141
+        tracim.lib.daemons.TracimSocketServerMixin#service_actions .
142
+        :param callback: callback to execute in your thread.
143
+        """
144
+        raise NotImplementedError()
145
+
146
+
147
+class RadicaleHTTPSServer(TracimSocketServerMixin, BaseRadicaleHTTPSServer):
148
+    pass
149
+
150
+
151
+class RadicaleHTTPServer(TracimSocketServerMixin, BaseRadicaleHTTPServer):
152
+    pass
153
+
88 154
 
89 155
 class RadicaleDaemon(Daemon):
90 156
     def __init__(self, *args, **kwargs):
@@ -132,3 +198,11 @@ class RadicaleDaemon(Daemon):
132 198
             RadicaleHTTPSServer if cfg.RADICALE_SERVER_SSL else RadicaleHTTPServer,
133 199
             RadicaleRequestHandler
134 200
         )
201
+
202
+    def append_thread_callback(self, callback: collections.Callable) -> None:
203
+        """
204
+        Give the callback to running server through
205
+        tracim.lib.daemons.TracimSocketServerMixin#append_thread_callback
206
+        :param callback: callback to execute in daemon
207
+        """
208
+        self._server.append_thread_callback(callback)

+ 1 - 1
tracim/tracim/model/auth.py View File

@@ -159,7 +159,7 @@ class User(DeclarativeBase):
159 159
             domain=cfg.RADICALE_CLIENT_HOST or request.domain,
160 160
             port=cfg.RADICALE_CLIENT_PORT,
161 161
             id=self.user_id,
162
-            slug=slugify(self.get_display_name(
162
+            extra='#' + slugify(self.get_display_name(
163 163
                 remove_email_part=True
164 164
             ), only_ascii=True)
165 165
         )

+ 1 - 1
tracim/tracim/model/data.py View File

@@ -78,7 +78,7 @@ class Workspace(DeclarativeBase):
78 78
             domain=cfg.RADICALE_CLIENT_HOST or tg.request.domain,
79 79
             port=cfg.RADICALE_CLIENT_PORT,
80 80
             id=self.workspace_id,
81
-            slug=slugify(self.label)
81
+            extra='#' + slugify(self.label),
82 82
         )
83 83
 
84 84
     def get_user_role(self, user: User) -> int:

+ 50 - 1
tracim/tracim/tests/__init__.py View File

@@ -3,6 +3,8 @@
3 3
 import argparse
4 4
 import os
5 5
 import time
6
+
7
+import shutil
6 8
 from os import getcwd
7 9
 
8 10
 import ldap3
@@ -11,6 +13,7 @@ import transaction
11 13
 from gearbox.commands.setup_app import SetupAppCommand
12 14
 from ldap_test import LdapServer
13 15
 from nose.tools import eq_
16
+from nose.tools import make_decorator
14 17
 from nose.tools import ok_
15 18
 from paste.deploy import loadapp
16 19
 from sqlalchemy.engine import reflection
@@ -28,11 +31,14 @@ from who_ldap import make_connection
28 31
 
29 32
 from tracim.fixtures import FixturesLoader
30 33
 from tracim.fixtures.users_and_groups import Base as BaseFixture
34
+from tracim.fixtures.users_and_groups import Test as TestFixture
35
+from tracim.config.app_cfg import daemons
31 36
 from tracim.lib.base import logger
32 37
 from tracim.lib.content import ContentApi
33 38
 from tracim.lib.workspace import WorkspaceApi
34 39
 from tracim.model import DBSession, Content
35
-from tracim.model.data import Workspace, ContentType, ContentRevisionRO
40
+from tracim.model.data import Workspace
41
+from tracim.model.data import ContentType
36 42
 
37 43
 __all__ = ['setup_app', 'setup_db', 'teardown_db', 'TestController']
38 44
 
@@ -239,6 +245,7 @@ class TestController(object):
239 245
     def tearDown(self):
240 246
         """Tear down test fixture for each functional test method."""
241 247
         DBSession.close()
248
+        daemons.execute_in_thread('radicale', lambda: transaction.commit())
242 249
         teardown_db()
243 250
 
244 251
 
@@ -344,3 +351,45 @@ class BaseTestThread(BaseTest):
344 351
                                                parent=folder,
345 352
                                                owner=user)
346 353
         return thread
354
+
355
+
356
+class TestCalendar(TestController):
357
+    fixtures = [BaseFixture, TestFixture]
358
+    application_under_test = 'radicale'
359
+
360
+    def setUp(self):
361
+        super().setUp()
362
+        self._clear_radicale_fs()
363
+
364
+    @staticmethod
365
+    def _clear_radicale_fs():
366
+        radicale_fs_path = config.radicale.server.filesystem.folder
367
+        try:
368
+            files = os.listdir(radicale_fs_path)
369
+            for file in files:
370
+                shutil.rmtree('{0}/{1}'.format(radicale_fs_path, file))
371
+        except FileNotFoundError:
372
+            pass  # Dir not exists yet, no need to clear it
373
+
374
+
375
+def not_raises(*exceptions):
376
+    """
377
+    Test must not raise one of expected exceptions to pass.
378
+    """
379
+    valid = ' or '.join([e.__name__ for e in exceptions])
380
+
381
+    def decorate(func):
382
+        name = func.__name__
383
+
384
+        def newfunc(*arg, **kw):
385
+            try:
386
+                func(*arg, **kw)
387
+            except exceptions as exc:
388
+                message = '{0} raise {1} exception and should be not'\
389
+                    .format(name, exc)
390
+                raise AssertionError(message)
391
+            except:
392
+                raise
393
+        newfunc = make_decorator(func)(newfunc)
394
+        return newfunc
395
+    return decorate

+ 205 - 0
tracim/tracim/tests/functional/test_calendar.py View File

@@ -0,0 +1,205 @@
1
+import time
2
+
3
+import caldav
4
+import transaction
5
+from caldav.lib.error import AuthorizationError
6
+from nose.tools import eq_
7
+from nose.tools import ok_
8
+from nose.tools import raises
9
+import requests
10
+from requests.exceptions import ConnectionError
11
+from sqlalchemy.orm.exc import NoResultFound
12
+
13
+from tracim.config.app_cfg import daemons
14
+from tracim.lib.calendar import CalendarManager
15
+from tracim.lib.workspace import WorkspaceApi
16
+from tracim.model import DBSession
17
+from tracim.tests import TestCalendar as BaseTestCalendar
18
+from tracim.tests import not_raises
19
+from tracim.model.auth import User
20
+from tracim.model.data import Content
21
+
22
+
23
+class TestCalendar(BaseTestCalendar):
24
+    def setUp(self):
25
+        super().setUp()
26
+        time.sleep(3)  # TODO - 20160606 - Bastien: sleep to wait ...
27
+        # ... radicale daemon started. We should lock something somewhere !
28
+
29
+    def test_func__radicale_connectivity__ok__nominal_case(self):
30
+        radicale_base_url = CalendarManager.get_base_url()
31
+
32
+        try:
33
+            response = requests.get(radicale_base_url)
34
+            eq_(response.status_code, 401, 'Radicale http response should be '
35
+                                           '401, its {0}'
36
+                .format(response.status_code))
37
+        except ConnectionError as exc:
38
+            ok_(False, 'Unable to contact radicale on HTTP: {0}'.format(exc))
39
+
40
+    @not_raises(AuthorizationError)
41
+    def test_func__radicale_auth__ok__as_lawrence(self):
42
+        radicale_base_url = CalendarManager.get_base_url()
43
+        client = caldav.DAVClient(
44
+            radicale_base_url,
45
+            username='lawrence-not-real-email@fsf.local',
46
+            password='foobarbaz'
47
+        )
48
+        client.propfind()
49
+
50
+    @raises(AuthorizationError)
51
+    def test_func__radicale_auth__fail__as_john_doe(self):
52
+        radicale_base_url = CalendarManager.get_base_url()
53
+        client = caldav.DAVClient(
54
+            radicale_base_url,
55
+            username='john.doe@foo.local',
56
+            password='nopasswd'
57
+        )
58
+        client.propfind()
59
+
60
+    @not_raises(AuthorizationError)
61
+    def test_func__rights_read_user_calendar__ok__as_lawrence(self):
62
+        radicale_base_url = CalendarManager.get_base_url()
63
+        client = caldav.DAVClient(
64
+            radicale_base_url,
65
+            username='lawrence-not-real-email@fsf.local',
66
+            password='foobarbaz'
67
+        )
68
+        user = DBSession.query(User).filter(
69
+            User.email == 'lawrence-not-real-email@fsf.local'
70
+        ).one()
71
+        user_calendar_url = CalendarManager.get_user_calendar_url(user.user_id)
72
+        caldav.Calendar(
73
+            parent=client,
74
+            client=client,
75
+            url=user_calendar_url
76
+        ).events()
77
+
78
+    @raises(AuthorizationError)
79
+    def test_func__rights_read_user_calendar__fail__as_john_doe(self):
80
+        radicale_base_url = CalendarManager.get_base_url()
81
+        client = caldav.DAVClient(
82
+            radicale_base_url,
83
+            username='john.doe@foo.local',
84
+            password='nopasswd'
85
+        )
86
+        other_user = DBSession.query(User).filter(
87
+            User.email == 'admin@admin.admin'
88
+        ).one()
89
+        user_calendar_url = CalendarManager.get_user_calendar_url(other_user.user_id)
90
+        caldav.Calendar(
91
+            parent=client,
92
+            client=client,
93
+            url=user_calendar_url
94
+        ).events()
95
+
96
+    @not_raises(AuthorizationError)
97
+    def test_func__rights_read_workspace_calendar__ok__as_owner(self):
98
+        lawrence = DBSession.query(User).filter(
99
+            User.email == 'lawrence-not-real-email@fsf.local'
100
+        ).one()
101
+        workspace = WorkspaceApi(lawrence).create_workspace(
102
+            'workspace_1',
103
+            save_now=False
104
+        )
105
+        workspace.calendar_enabled = True
106
+        DBSession.flush()
107
+
108
+        workspace_calendar_url = CalendarManager.get_workspace_calendar_url(
109
+            workspace.workspace_id
110
+        )
111
+
112
+        transaction.commit()
113
+
114
+        radicale_base_url = CalendarManager.get_base_url()
115
+        client = caldav.DAVClient(
116
+            radicale_base_url,
117
+            username='lawrence-not-real-email@fsf.local',
118
+            password='foobarbaz'
119
+        )
120
+        caldav.Calendar(
121
+            parent=client,
122
+            client=client,
123
+            url=workspace_calendar_url
124
+        ).events()
125
+
126
+    @raises(AuthorizationError)
127
+    def test_func__rights_read_workspace_calendar__fail__as_unauthorized(self):
128
+        lawrence = DBSession.query(User).filter(
129
+            User.email == 'lawrence-not-real-email@fsf.local'
130
+        ).one()
131
+        workspace = WorkspaceApi(lawrence).create_workspace(
132
+            'workspace_1',
133
+            save_now=False
134
+        )
135
+        workspace.calendar_enabled = True
136
+        DBSession.flush()
137
+
138
+        workspace_calendar_url = CalendarManager.get_workspace_calendar_url(
139
+            workspace.workspace_id
140
+        )
141
+
142
+        transaction.commit()
143
+
144
+        radicale_base_url = CalendarManager.get_base_url()
145
+        client = caldav.DAVClient(
146
+            radicale_base_url,
147
+            username='bob@fsf.local',
148
+            password='foobarbaz'
149
+        )
150
+        caldav.Calendar(
151
+            parent=client,
152
+            client=client,
153
+            url=workspace_calendar_url
154
+        ).events()
155
+
156
+    def test_func__event_create__ok__nominal_case(self):
157
+        lawrence = DBSession.query(User).filter(
158
+            User.email == 'lawrence-not-real-email@fsf.local'
159
+        ).one()
160
+        radicale_base_url = CalendarManager.get_base_url()
161
+        client = caldav.DAVClient(
162
+            radicale_base_url,
163
+            username='lawrence-not-real-email@fsf.local',
164
+            password='foobarbaz'
165
+        )
166
+        user_calendar_url = CalendarManager.get_user_calendar_url(
167
+            lawrence.user_id
168
+        )
169
+        user_calendar = caldav.Calendar(
170
+            parent=client,
171
+            client=client,
172
+            url=user_calendar_url
173
+        )
174
+
175
+        event_ics = """BEGIN:VCALENDAR
176
+VERSION:2.0
177
+PRODID:-//Example Corp.//CalDAV Client//EN
178
+BEGIN:VEVENT
179
+UID:1234567890
180
+DTSTAMP:20100510T182145Z
181
+DTSTART:20100512T170000Z
182
+DTEND:20100512T180000Z
183
+SUMMARY:This is an event
184
+LOCATION:Here
185
+END:VEVENT
186
+END:VCALENDAR
187
+"""
188
+        user_calendar.add_event(event_ics)
189
+        user_calendar.save()
190
+
191
+        daemons.execute_in_thread('radicale', lambda: transaction.commit())
192
+        # TODO - 20160606 - Bastien: lock should be better here ?
193
+        time.sleep(3)  # Wait for be sure transaction commited in daemon
194
+        transaction.commit()
195
+        try:
196
+            event = DBSession.query(Content).filter(
197
+                Content.label == 'This is an event'
198
+            ).one()
199
+        except NoResultFound:
200
+            ok_(False, 'Content record should exist for '
201
+                       '"This is an event" label')
202
+
203
+        eq_(event.properties['location'], 'Here')
204
+        eq_(event.properties['start'], '2010-05-12 18:00:00+0000')
205
+        eq_(event.properties['end'], '2010-05-12 17:00:00+0000')

+ 4 - 4
tracim/tracim/tests/functional/test_ldap_authentication.py View File

@@ -44,13 +44,13 @@ class TestAuthentication(LDAPTest, TracimTestController):
44 44
 
45 45
     def test_ldap_attributes_sync(self):
46 46
         # User is already know in database
47
-        eq_(1, DBSession.query(User).filter(User.email == 'lawrence-not-real-email@fsf.org').count())
47
+        eq_(1, DBSession.query(User).filter(User.email == 'lawrence-not-real-email@fsf.local').count())
48 48
 
49 49
         # His display name is Lawrence L.
50
-        lawrence = DBSession.query(User).filter(User.email == 'lawrence-not-real-email@fsf.org').one()
50
+        lawrence = DBSession.query(User).filter(User.email == 'lawrence-not-real-email@fsf.local').one()
51 51
         eq_('Lawrence L.', lawrence.display_name)
52 52
 
53 53
         # After connexion with LDAP, his display_name is updated (see ldap fixtures)
54
-        self._connect_user('lawrence-not-real-email@fsf.org', 'foobarbaz')
55
-        lawrence = DBSession.query(User).filter(User.email == 'lawrence-not-real-email@fsf.org').one()
54
+        self._connect_user('lawrence-not-real-email@fsf.local', 'foobarbaz')
55
+        lawrence = DBSession.query(User).filter(User.email == 'lawrence-not-real-email@fsf.local').one()
56 56
         eq_('Lawrence Lessig', lawrence.display_name)

+ 7 - 7
tracim/tracim/tests/functional/test_ldap_restrictions.py View File

@@ -21,8 +21,8 @@ class TestAuthentication(LDAPTest, TracimTestController):
21 21
         Password change is disabled
22 22
         :return:
23 23
         """
24
-        lawrence = DBSession.query(User).filter(User.email == 'lawrence-not-real-email@fsf.org').one()
25
-        self._connect_user('lawrence-not-real-email@fsf.org', 'foobarbaz')
24
+        lawrence = DBSession.query(User).filter(User.email == 'lawrence-not-real-email@fsf.local').one()
25
+        self._connect_user('lawrence-not-real-email@fsf.local', 'foobarbaz')
26 26
         home = self.app.get('/home/',)
27 27
 
28 28
         # HTML button is not here
@@ -45,8 +45,8 @@ class TestAuthentication(LDAPTest, TracimTestController):
45 45
         Some fields (email) are not editable on user interface: they are managed by LDAP
46 46
         :return:
47 47
         """
48
-        lawrence = DBSession.query(User).filter(User.email == 'lawrence-not-real-email@fsf.org').one()
49
-        self._connect_user('lawrence-not-real-email@fsf.org', 'foobarbaz')
48
+        lawrence = DBSession.query(User).filter(User.email == 'lawrence-not-real-email@fsf.local').one()
49
+        self._connect_user('lawrence-not-real-email@fsf.local', 'foobarbaz')
50 50
 
51 51
         edit = self.app.get('/user/5/edit')
52 52
 
@@ -60,7 +60,7 @@ class TestAuthentication(LDAPTest, TracimTestController):
60 60
         ok_('readonly' not in name_input.attrs)
61 61
 
62 62
         # If we force edit of user, "email" field will be not updated
63
-        eq_(lawrence.email, 'lawrence-not-real-email@fsf.org')
63
+        eq_(lawrence.email, 'lawrence-not-real-email@fsf.local')
64 64
         eq_(lawrence.display_name, 'Lawrence L.')
65 65
 
66 66
         try_post_user = self.app.post(
@@ -73,6 +73,6 @@ class TestAuthentication(LDAPTest, TracimTestController):
73 73
 
74 74
         eq_(try_post_user.status_code, 302, "Code should be 302, but is %d" % try_post_user.status_code)
75 75
 
76
-        lawrence = DBSession.query(User).filter(User.email == 'lawrence-not-real-email@fsf.org').one()
77
-        eq_(lawrence.email, 'lawrence-not-real-email@fsf.org', "email should be unmodified")
76
+        lawrence = DBSession.query(User).filter(User.email == 'lawrence-not-real-email@fsf.local').one()
77
+        eq_(lawrence.email, 'lawrence-not-real-email@fsf.local', "email should be unmodified")
78 78
         eq_(lawrence.display_name, 'Lawrence Lessig YEAH', "Name should be updated")

+ 1 - 2
tracim/tracim/tests/library/test_helpers.py View File

@@ -17,12 +17,11 @@ from tracim.model.serializers import DictLikeClass
17 17
 
18 18
 from tracim.tests import TestStandard
19 19
 
20
-config = CFG.get_instance()
21
-
22 20
 
23 21
 class TestHelpers(TestStandard):
24 22
 
25 23
     def test_is_item_still_editable(self):
24
+        config = CFG.get_instance()
26 25
         item = DictLikeClass()
27 26
 
28 27
         config.DATA_UPDATE_ALLOWED_DURATION = 0

+ 4 - 4
tracim/tracim/tests/library/test_ldap_without_ldap_groups.py View File

@@ -68,12 +68,12 @@ class TestContentApi(LDAPTest, TestStandard):
68 68
         LDAP don't manage groups here: We must retrieve internal groups of tested user
69 69
         :return:
70 70
         """
71
-        lawrence = DBSession.query(User).filter(User.email == 'lawrence-not-real-email@fsf.org').one()
71
+        lawrence = DBSession.query(User).filter(User.email == 'lawrence-not-real-email@fsf.local').one()
72 72
         managers = DBSession.query(Group).filter(Group.group_name == 'managers').one()
73 73
         lawrence_identity = {'user': lawrence}
74 74
 
75 75
         # Lawrence is in fixtures: he is in managers group
76
-        self._check_db_user('lawrence-not-real-email@fsf.org', 1)
76
+        self._check_db_user('lawrence-not-real-email@fsf.local', 1)
77 77
         assert lawrence in managers.users
78 78
         assert False is ini_conf_to_bool(config.get('ldap_group_enabled', False))
79 79
         assert ['managers'] == config.get('sa_auth').authmetadata.get_groups(
@@ -95,12 +95,12 @@ class TestContentApi(LDAPTest, TestStandard):
95 95
         LDAP don't manage groups here: We must retrieve internal groups permission of tested user
96 96
         :return:
97 97
         """
98
-        lawrence = DBSession.query(User).filter(User.email == 'lawrence-not-real-email@fsf.org').one()
98
+        lawrence = DBSession.query(User).filter(User.email == 'lawrence-not-real-email@fsf.local').one()
99 99
         managers = DBSession.query(Group).filter(Group.group_name == 'managers').one()
100 100
         lawrence_identity = {'user': lawrence}
101 101
 
102 102
         # Lawrence is in fixtures: he is in managers group
103
-        self._check_db_user('lawrence-not-real-email@fsf.org', 1)
103
+        self._check_db_user('lawrence-not-real-email@fsf.local', 1)
104 104
         assert lawrence in managers.users
105 105
         assert False is ini_conf_to_bool(config.get('ldap_group_enabled', False))
106 106