浏览代码

Radicale: tests: fix for multiples tests + test auth

Bastien Sevajol (Algoo) 9 年前
父节点
当前提交
0f361f6552

+ 1 - 0
install/requirements.txt 查看文件

52
 unicode-slugify==0.1.3
52
 unicode-slugify==0.1.3
53
 Radicale==1.1.1
53
 Radicale==1.1.1
54
 icalendar==3.10
54
 icalendar==3.10
55
+caldav==0.4.0

+ 53 - 4
tracim/tracim/lib/daemons.py 查看文件

1
 import threading
1
 import threading
2
 from wsgiref.simple_server import make_server
2
 from wsgiref.simple_server import make_server
3
 import signal
3
 import signal
4
+
5
+import collections
4
 import transaction
6
 import transaction
5
 
7
 
6
 from radicale import Application as RadicaleApplication
8
 from radicale import Application as RadicaleApplication
59
 
61
 
60
     def stop_all(self, *args, **kwargs) -> None:
62
     def stop_all(self, *args, **kwargs) -> None:
61
         """
63
         """
62
-        Stop all started daemons and w<ait for them.
64
+        Stop all started daemons and wait for them.
63
         """
65
         """
64
         logger.info(self, 'Stopping all daemons')
66
         logger.info(self, 'Stopping all daemons')
65
         for name, daemon in self._running_daemons.items():
67
         for name, daemon in self._running_daemons.items():
72
 
74
 
73
         self._running_daemons = {}
75
         self._running_daemons = {}
74
 
76
 
77
+    def execute_in_thread(self, thread_name, callback):
78
+        self._running_daemons[thread_name].append_thread_callback(callback)
79
+
75
 
80
 
76
 class TracimSocketServerMixin(object):
81
 class TracimSocketServerMixin(object):
77
     """
82
     """
78
     Mixin to use with socketserver.BaseServer who add _after_serve_actions
83
     Mixin to use with socketserver.BaseServer who add _after_serve_actions
79
     method executed after end of server execution.
84
     method executed after end of server execution.
80
     """
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
+
81
     def serve_forever(self, *args, **kwargs):
98
     def serve_forever(self, *args, **kwargs):
82
         super().serve_forever(*args, **kwargs)
99
         super().serve_forever(*args, **kwargs)
83
         # After serving (in case of stop) do following:
100
         # After serving (in case of stop) do following:
88
         Override (and call super if needed) to execute actions when server
105
         Override (and call super if needed) to execute actions when server
89
         finish it's job.
106
         finish it's job.
90
         """
107
         """
91
-        transaction.commit()
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
92
 
117
 
93
 
118
 
94
 class Daemon(threading.Thread):
119
 class Daemon(threading.Thread):
96
     Thread who contains daemon. You must implement start and stop methods to
121
     Thread who contains daemon. You must implement start and stop methods to
97
     manage daemon life correctly.
122
     manage daemon life correctly.
98
     """
123
     """
99
-    def run(self):
124
+    def run(self) -> None:
125
+        """
126
+        Place here code who have to be executed in Daemon.
127
+        """
100
         raise NotImplementedError()
128
         raise NotImplementedError()
101
 
129
 
102
-    def stop(self):
130
+    def stop(self) -> None:
131
+        """
132
+        Place here code who stop your daemon
133
+        """
134
+        raise NotImplementedError()
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
+        """
103
         raise NotImplementedError()
144
         raise NotImplementedError()
104
 
145
 
105
 
146
 
157
             RadicaleHTTPSServer if cfg.RADICALE_SERVER_SSL else RadicaleHTTPServer,
198
             RadicaleHTTPSServer if cfg.RADICALE_SERVER_SSL else RadicaleHTTPServer,
158
             RadicaleRequestHandler
199
             RadicaleRequestHandler
159
         )
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)

+ 15 - 2
tracim/tracim/tests/__init__.py 查看文件

28
 
28
 
29
 from tracim.fixtures import FixturesLoader
29
 from tracim.fixtures import FixturesLoader
30
 from tracim.fixtures.users_and_groups import Base as BaseFixture
30
 from tracim.fixtures.users_and_groups import Base as BaseFixture
31
-from tracim.config.app_cfg import daemons
31
+from tracim.fixtures.users_and_groups import Test as TestFixture
32
+from tracim.config.app_cfg import daemons, start_daemons
32
 from tracim.lib.base import logger
33
 from tracim.lib.base import logger
33
 from tracim.lib.content import ContentApi
34
 from tracim.lib.content import ContentApi
34
 from tracim.lib.workspace import WorkspaceApi
35
 from tracim.lib.workspace import WorkspaceApi
35
 from tracim.model import DBSession, Content
36
 from tracim.model import DBSession, Content
36
 from tracim.model.data import Workspace, ContentType, ContentRevisionRO
37
 from tracim.model.data import Workspace, ContentType, ContentRevisionRO
38
+from tracim.lib.calendar import CALENDAR_BASE_URL
37
 
39
 
38
 __all__ = ['setup_app', 'setup_db', 'teardown_db', 'TestController']
40
 __all__ = ['setup_app', 'setup_db', 'teardown_db', 'TestController']
39
 
41
 
240
     def tearDown(self):
242
     def tearDown(self):
241
         """Tear down test fixture for each functional test method."""
243
         """Tear down test fixture for each functional test method."""
242
         DBSession.close()
244
         DBSession.close()
243
-        daemons.stop_all()
245
+        daemons.execute_in_thread('radicale', lambda: transaction.commit())
244
         teardown_db()
246
         teardown_db()
245
 
247
 
246
 
248
 
349
 
351
 
350
 
352
 
351
 class TestCalendar(TestController):
353
 class TestCalendar(TestController):
354
+    fixtures = [BaseFixture, TestFixture]
352
     application_under_test = 'radicale'
355
     application_under_test = 'radicale'
353
 
356
 
354
     def setUp(self):
357
     def setUp(self):
364
                 os.remove('{0}/{1}'.format(radicale_fs_path, file))
367
                 os.remove('{0}/{1}'.format(radicale_fs_path, file))
365
         except FileNotFoundError:
368
         except FileNotFoundError:
366
             pass  # Dir not exists yet, no need to clear it
369
             pass  # Dir not exists yet, no need to clear it
370
+
371
+    def _get_base_url(self):
372
+        from tracim.config.app_cfg import CFG
373
+        cfg = CFG.get_instance()
374
+
375
+        return CALENDAR_BASE_URL.format(
376
+            proto='https' if cfg.RADICALE_CLIENT_SSL else 'http',
377
+            domain=cfg.RADICALE_CLIENT_HOST or '127.0.0.1',
378
+            port=str(cfg.RADICALE_CLIENT_PORT)
379
+        )

+ 14 - 9
tracim/tracim/tests/functional/test_calendar.py 查看文件

1
 import time
1
 import time
2
 
2
 
3
+import caldav
4
+from caldav.lib.error import AuthorizationError
3
 from nose.tools import eq_, ok_
5
 from nose.tools import eq_, ok_
4
 import requests
6
 import requests
5
 from requests.exceptions import ConnectionError
7
 from requests.exceptions import ConnectionError
6
 
8
 
7
-from tracim.lib.calendar import CALENDAR_BASE_URL
8
 from tracim.tests import TestCalendar as BaseTestCalendar
9
 from tracim.tests import TestCalendar as BaseTestCalendar
9
 
10
 
10
 
11
 
11
 class TestCalendar(BaseTestCalendar):
12
 class TestCalendar(BaseTestCalendar):
12
-    def test_unit__radicale_connectivity__ok__nominal_case(self):
13
-        from tracim.config.app_cfg import CFG
14
-        cfg = CFG.get_instance()
13
+    def test_func__radicale_connectivity__ok__nominal_case(self):
14
+        radicale_base_url = self._get_base_url()
15
 
15
 
16
-        radicale_base_url = CALENDAR_BASE_URL.format(
17
-            proto='https' if cfg.RADICALE_CLIENT_SSL else 'http',
18
-            domain=cfg.RADICALE_CLIENT_HOST or '127.0.0.1',
19
-            port=str(cfg.RADICALE_CLIENT_PORT)
20
-        )
21
         try:
16
         try:
22
             time.sleep(2)  # TODO - 20160606 - Bastien: sleep to wait ...
17
             time.sleep(2)  # TODO - 20160606 - Bastien: sleep to wait ...
23
             # ... radicale daemon started. We should lock something somewhere !
18
             # ... radicale daemon started. We should lock something somewhere !
25
             eq_(response.status_code, 401, 'Radicale http response is 401')
20
             eq_(response.status_code, 401, 'Radicale http response is 401')
26
         except ConnectionError:
21
         except ConnectionError:
27
             ok_(False, 'Unable to contact radicale on HTTP')
22
             ok_(False, 'Unable to contact radicale on HTTP')
23
+
24
+    def test_func__radicale_auth__ok__as_lawrence(self):
25
+        client = caldav.DAVClient('http://127.0.0.1:15232',
26
+                                  username='lawrence-not-real-email@fsf.local',
27
+                                  password='foobarbaz')
28
+        try:
29
+            client.propfind()
30
+            ok_(True, 'No auth error when communicate with radicale server')
31
+        except AuthorizationError:
32
+            ok_(False, 'AuthorizationError when communicate with radicale')