Passed
Push — master ( e8584f...303446 )
by Emmanuel
04:55
created

cli_test   A

Complexity

Total Complexity 27

Size/Duplication

Total Lines 324
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 226
dl 0
loc 324
rs 10
c 0
b 0
f 0
wmc 27

2 Functions

Rating   Name   Duplication   Size   Complexity  
A exec_cmd() 0 8 1
A stop_remove_container() 0 8 2

20 Methods

Rating   Name   Duplication   Size   Complexity  
A CliTest.test_restart_stopped() 0 24 1
A CliTest.test_start_single() 0 21 2
A CliTest.test_console_no_ct() 0 7 1
A CliTest.test_bad_arg() 0 7 1
A CliTest.test_no_arg() 0 4 1
A CliTest.tearDownClass() 0 8 1
A CliTest.test_pull() 0 2 1
A CliTest.test_restart_started() 0 27 1
A CliTest.test_recreate() 0 2 1
A CliTest.test_console() 0 14 1
A CliTest.test_start() 0 25 2
A CliTest.test_mysql() 0 7 1
A CliTest.test_bad_config() 0 8 1
A CliTest.test_debug_mode() 0 8 1
A CliTest.test_exec_php() 0 16 2
A CliTest._proxy_start_check_not_in_network() 0 9 1
A CliTest.test_pull_recreate() 0 2 1
A CliTest.test_stop_single() 0 35 2
A CliTest.test_status() 0 23 1
A CliTest.test_stop() 0 15 1
1
import os
2
import subprocess
3
import sys
4
import unittest
5
6
from click.testing import CliRunner
7
from stakkr.docker_actions import get_client as docker_client, _container_in_network as ct_in_network
8
from stakkr.cli import stakkr
9
10
base_dir = os.path.abspath(os.path.dirname(__file__))
11
sys.path.insert(0, base_dir + '/../')
12
13
14
class CliTest(unittest.TestCase):
15
    cmd_base = ['stakkr', '-c', base_dir + '/static/config_valid.ini']
16
17
    def test_no_arg(self):
18
        result = CliRunner().invoke(stakkr)
19
        self.assertEqual(0, result.exit_code)
20
        self.assertEqual('Usage: stakkr [OPTIONS] COMMAND [ARGS]', result.output[:38])
21
22
    def test_bad_arg(self):
23
        result = CliRunner().invoke(stakkr, ['hello-world'])
24
        self.assertEqual(2, result.exit_code)
25
        self.assertEqual('Usage: stakkr [OPTIONS] COMMAND [ARGS]', result.output[:38])
26
27
        res_out = result.output[-38:].strip()
28
        self.assertEqual('Error: No such command "hello-world".', res_out)
29
30
    def test_pull(self):
31
        exec_cmd(self.cmd_base + ['start', '--pull'])
32
33
    def test_recreate(self):
34
        exec_cmd(self.cmd_base + ['start', '--recreate'])
35
36
    def test_pull_recreate(self):
37
        exec_cmd(self.cmd_base + ['start', '--pull', '--recreate'])
38
39
    def test_debug_mode(self):
40
        exec_cmd(self.cmd_base + ['start', '-r'])
41
42
        res = exec_cmd(self.cmd_base + ['--debug', 'console', 'portainer'])
43
        self.assertEqual(res['stdout'], '')
44
        regex = r'.*OSError: Could not find a shell for that container*'
45
        self.assertRegex(res['stderr'], regex)
46
        self.assertIs(res['status'], 1)
47
48
    def test_bad_config(self):
49
        res = exec_cmd([
50
            'stakkr', '-c', base_dir + '/static/config_invalid.ini', 'start'])
51
        self.assertRegex(res['stderr'], r'.*Failed validating.*')
52
        self.assertRegex(res['stderr'], r'.*project_name.*Missing.*')
53
        self.assertRegex(res['stderr'], r'.*php\.version.*unacceptable.*')
54
        self.assertEqual(res['stdout'], '')
55
        self.assertIs(res['status'], 1)
56
57
    def test_console_no_ct(self):
58
        exec_cmd(self.cmd_base + ['start'])
59
60
        res = exec_cmd(self.cmd_base + ['console'])
61
        self.assertRegex(res['stderr'], r'Usage: stakkr console \[OPTIONS\] CONTAINER.*')
62
        self.assertEqual(res['stdout'], '')
63
        self.assertIs(res['status'], 2)
64
65
    def test_console(self):
66
        exec_cmd(self.cmd_base + ['start'])
67
68
        res = exec_cmd(self.cmd_base + ['console', 'mysql'])
69
        self.assertRegex(res['stderr'], r'.*Invalid value: invalid choice: mysql\. \(choose from.*')
70
        self.assertEqual(res['stdout'], '')
71
        self.assertIs(res['status'], 2)
72
73
        exec_cmd(self.cmd_base + ['stop'])
74
75
        res = exec_cmd(self.cmd_base + ['console', 'php'])
76
        self.assertEqual(res['stdout'], '')
77
        self.assertRegex(res['stderr'], r'.*Have you started your server with the start action.*')
78
        self.assertIs(res['status'], 1)
79
80
    def test_exec_php(self):
81
        exec_cmd(self.cmd_base + ['start'])
82
83
        # Check for PHP Version
84
        # TODO make it work under windows (problem with TTY)
85
        if os.name != 'nt':
86
            res = exec_cmd(self.cmd_base + ['exec', '--no-tty', 'php', 'php', '-v'])
87
            self.assertRegex(res['stdout'], r'.*The PHP Group.*', 'stderr was : ' + res['stderr'])
88
            self.assertEqual(res['stderr'], '')
89
            self.assertIs(res['status'], 0)
90
91
        exec_cmd(self.cmd_base + ['stop'])
92
        res = exec_cmd(self.cmd_base + ['exec', 'php', 'php', '-v'])
93
        self.assertEqual(res['stdout'], '')
94
        self.assertRegex(res['stderr'], r'.*Have you started your server with the start action.*')
95
        self.assertIs(res['status'], 1)
96
97
    def test_mysql(self):
98
        exec_cmd(self.cmd_base + ['start'])
99
100
        res = exec_cmd(self.cmd_base + ['mysql', '--version'])
101
        self.assertEqual(res['stdout'], '')
102
        self.assertRegex(res['stderr'], r'.*Invalid value: invalid choice: mysql.*')
103
        self.assertIs(res['status'], 2)
104
105
    def test_restart_stopped(self):
106
        self._proxy_start_check_not_in_network()
107
108
        exec_cmd(self.cmd_base + ['stop'])
109
110
        # Restart
111
        res = exec_cmd(self.cmd_base + ['restart'])
112
        self.assertEqual(res['stderr'], '')
113
        regex = r'RESTARTING.*your stakkr services.*STOPPING.*STARTING.*your stakkr services.*For Maildev.*'
114
        self.assertRegex(res['stdout'], regex)
115
        self.assertIs(res['status'], 0)
116
117
        # Proxy is in network now
118
        self.assertIs(True, ct_in_network('proxy_stakkr', 'test_stakkr'))
119
120
        # Check it's fine
121
        res = exec_cmd(self.cmd_base + ['status'])
122
        self.assertEqual(res['stderr'], '')
123
        self.assertRegex(res['stdout'], r'Container\s*IP\s*Url\s*Image.*')
124
        self.assertRegex(res['stdout'], '.*maildev.test.localhost.*')
125
        self.assertRegex(res['stdout'], '.*test_maildev.*')
126
        self.assertRegex(res['stdout'], '.*test_php.*')
127
        self.assertRegex(res['stdout'], '.*No traefik rule.*php.*')
128
        self.assertIs(res['status'], 0)
129
130
    def test_restart_started(self):
131
        exec_cmd(self.cmd_base + ['stop'])
132
        self._proxy_start_check_not_in_network()
133
134
        exec_cmd(self.cmd_base + ['start'])
135
        # Proxy is in network now
136
        self.assertIs(True, ct_in_network('proxy_stakkr', 'test_stakkr'))
137
138
        # Restart
139
        res = exec_cmd(self.cmd_base + ['restart'])
140
        self.assertEqual(res['stderr'], '')
141
        regex = r'RESTARTING.*your stakkr services.*STOPPING.*your stakkr services.*STARTING.*your stakkr services.*For Maildev.*'
142
        self.assertRegex(res['stdout'], regex)
143
        self.assertIs(res['status'], 0)
144
145
        # Check it's fine
146
        res = exec_cmd(self.cmd_base + ['status'])
147
        self.assertEqual(res['stderr'], '')
148
        self.assertRegex(res['stdout'], r'Container\s*IP\s*Url\s*Image.*')
149
        self.assertRegex(res['stdout'], '.*maildev.test.localhost.*')
150
        self.assertRegex(res['stdout'], '.*test_maildev.*')
151
        self.assertRegex(res['stdout'], '.*test_php.*')
152
        self.assertRegex(res['stdout'], '.*No traefik rule.*php.*')
153
        self.assertIs(res['status'], 0)
154
155
        # Proxy is in network
156
        self.assertIs(True, ct_in_network('proxy_stakkr', 'test_stakkr'))
157
158
    def test_start(self):
159
        self._proxy_start_check_not_in_network()
160
161
        exec_cmd(self.cmd_base + ['stop'])
162
163
        cmd = self.cmd_base + ['start']
164
165
        # Start
166
        res = exec_cmd(cmd)
167
        self.assertEqual(res['stderr'], '')
168
        self.assertRegex(res['stdout'], r'\[STARTING\].*your stakkr services.*For Maildev.*')
169
        # Problem with scrutinizer : can't get a correct status
170
        # TODO check and fix it later
171
        if 'SCRUTINIZER_API_ENDPOINT' not in os.environ:
172
            self.assertIs(res['status'], 0)
173
174
        # Proxy is in network now
175
        self.assertIs(True, ct_in_network('proxy_stakkr', 'test_stakkr'))
176
177
        # Again ....
178
        res = exec_cmd(cmd)
179
        self.assertEqual(res['stderr'], '')
180
        regex = r'\[STARTING\].*your stakkr services.*\[INFO\] stakkr is already started.*'
181
        self.assertRegex(res['stdout'], regex)
182
        self.assertIs(res['status'], 0)
183
184
    def test_start_single(self):
185
        exec_cmd(self.cmd_base + ['stop'])
186
187
        cmd = self.cmd_base + ['start', 'php']
188
189
        # Start
190
        res = exec_cmd(cmd)
191
        self.assertEqual(res['stderr'], '')
192
        self.assertRegex(res['stdout'], r'\[STARTING\].*your stakkr services.*edyan/php.*')
193
        self.assertNotRegex(res['stdout'], r'\[STARTING\].*your stakkr services.*For Maildev.*')
194
        # Problem with scrutinizer : can't get a correct status
195
        # TODO check and fix it later
196
        if 'SCRUTINIZER_API_ENDPOINT' not in os.environ:
197
            self.assertIs(res['status'], 0)
198
199
        # Again ....
200
        res = exec_cmd(cmd)
201
        self.assertEqual(res['stderr'], '')
202
        regex = r'\[STARTING\].*your stakkr services.*\[INFO\] service php is already started.*'
203
        self.assertRegex(res['stdout'], regex)
204
        self.assertIs(res['status'], 0)
205
206
    def test_stop_single(self):
207
        exec_cmd(self.cmd_base + ['stop'])
208
209
        # Start
210
        res = exec_cmd(cmd=self.cmd_base + ['start', 'php'])
211
        self.assertEqual(res['stderr'], '')
212
        self.assertRegex(res['stdout'], r'\[STARTING\].*your stakkr services.*edyan/php.*')
213
        self.assertNotRegex(res['stdout'], r'\[STARTING\].*your stakkr services.*For Maildev.*')
214
        # Problem with scrutinizer : can't get a correct status
215
        # TODO check and fix it later
216
        if 'SCRUTINIZER_API_ENDPOINT' not in os.environ:
217
            self.assertIs(res['status'], 0)
218
219
        # Stop the wrong one
220
        res = exec_cmd(self.cmd_base + ['stop', 'maildev'])
221
        self.assertEqual(res['stderr'], '')
222
        self.assertRegex(res['stdout'], r'\[STOPPING\].*your stakkr services.*')
223
        self.assertIs(res['status'], 0)
224
225
        res = exec_cmd(self.cmd_base + ['status'])
226
        self.assertEqual(res['stderr'], '')
227
        self.assertRegex(res['stdout'], r'.*edyan/php.*')
228
        self.assertIs(res['status'], 0)
229
230
        # Stop the right one
231
        res = exec_cmd(self.cmd_base + ['stop', 'php'])
232
        self.assertEqual(res['stderr'], '')
233
        self.assertRegex(res['stdout'], r'\[STOPPING\].*your stakkr services.*')
234
        self.assertIs(res['status'], 0)
235
236
        res = exec_cmd(self.cmd_base + ['status'])
237
        self.assertEqual(res['stderr'], '')
238
        self.assertEqual(res['stdout'], r'[INFO] stakkr is currently stopped')
239
        self.assertNotRegex(res['stdout'], r'.*edyan/php.*')
240
        self.assertIs(res['status'], 0)
241
242
    def test_status(self):
243
        exec_cmd(self.cmd_base + ['stop'])
244
        exec_cmd(self.cmd_base + ['start'])
245
246
        cmd = self.cmd_base + ['status']
247
248
        # Status OK
249
        res = exec_cmd(cmd)
250
        self.assertEqual(res['stderr'], '')
251
        self.assertRegex(res['stdout'], r'Container\s*IP\s*Url\s*Image.*')
252
        self.assertRegex(res['stdout'], '.*maildev.test.localhost.*')
253
        self.assertRegex(res['stdout'], '.*test_maildev.*')
254
        self.assertRegex(res['stdout'], '.*test_php.*')
255
        self.assertRegex(res['stdout'], '.*No traefik rule.*php.*')
256
        self.assertIs(res['status'], 0)
257
258
        exec_cmd(self.cmd_base + ['stop'])
259
260
        # Status not ok , it has been stopped
261
        res = exec_cmd(cmd)
262
        self.assertEqual(res['stdout'], r'[INFO] stakkr is currently stopped')
263
        self.assertEqual(res['stderr'], '')
264
        self.assertIs(res['status'], 0)
265
266
    def test_stop(self):
267
        exec_cmd(self.cmd_base + ['start'])
268
        cmd = self.cmd_base + ['stop']
269
270
        # Stop OK
271
        res = exec_cmd(cmd)
272
        self.assertEqual(res['stderr'], '')
273
        self.assertRegex(res['stdout'], r'\[STOPPING\].*your stakkr services.*')
274
        self.assertIs(res['status'], 0)
275
276
        # Stop Error : it has been stopped already
277
        res = exec_cmd(cmd)
278
        self.assertRegex(res['stdout'], r'\[STOPPING\].*your stakkr services.*')
279
        self.assertRegex(res['stderr'], r'.*Have you started your server with the start action.*')
280
        self.assertIs(res['status'], 1)
281
282
    def tearDownClass():
283
        cli = CliTest()
284
285
        exec_cmd(cli.cmd_base + ['stop'])
286
287
        stop_remove_container('test_maildev')
288
        stop_remove_container('test_php')
289
        stop_remove_container('test_portainer')
290
291
    def _proxy_start_check_not_in_network(self):
292
        from stakkr.proxy import Proxy
293
        # First start Proxy to verify it'll be added in the network
294
        Proxy().stop()
295
        Proxy().start()
296
        # Proxy is not connected to network
297
        self.assertIs(
298
            False,
299
            ct_in_network('proxy_stakkr', 'test_stakkr'))
300
301
302
def exec_cmd(cmd: list):
303
    p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
304
    stdout, stderr = p.communicate()
305
    status = p.returncode
306
    stdout = stdout.decode().strip().replace('\n', '')
307
    stderr = stderr.decode().strip().replace('\n', '')
308
309
    return {'stdout': stdout, 'stderr': stderr, 'status': status}
310
311
312
def stop_remove_container(ct_name: str):
313
    from docker.errors import NotFound
314
    try:
315
        ct = docker_client().containers.get(ct_name)
316
        ct.stop()
317
        ct.remove()
318
    except NotFound:
319
        pass
320
321
322
if __name__ == "__main__":
323
    unittest.main()
324