xref: /unit/test/test_configuration.py (revision 1848:4bd548074e2c)
1import pytest
2
3from unit.control import TestControl
4
5
6class TestConfiguration(TestControl):
7    prerequisites = {'modules': {'python': 'any'}}
8
9    def test_json_empty(self):
10        assert 'error' in self.conf(''), 'empty'
11
12    def test_json_leading_zero(self):
13        assert 'error' in self.conf('00'), 'leading zero'
14
15    def test_json_unicode(self):
16        assert 'success' in self.conf(
17            u"""
18            {
19                "ap\u0070": {
20                    "type": "\u0070ython",
21                    "processes": { "spare": 0 },
22                    "path": "\u002Fapp",
23                    "module": "wsgi"
24                }
25            }
26            """,
27            'applications',
28        ), 'unicode'
29
30        assert self.conf_get('applications') == {
31            "app": {
32                "type": "python",
33                "processes": {"spare": 0},
34                "path": "/app",
35                "module": "wsgi",
36            }
37        }, 'unicode get'
38
39    def test_json_unicode_2(self):
40        assert 'success' in self.conf(
41            {
42                "приложение": {
43                    "type": "python",
44                    "processes": {"spare": 0},
45                    "path": "/app",
46                    "module": "wsgi",
47                }
48            },
49            'applications',
50        ), 'unicode 2'
51
52        assert 'приложение' in self.conf_get('applications'), 'unicode 2 get'
53
54    def test_json_unicode_number(self):
55        assert 'success' in self.conf(
56            u"""
57            {
58                "app": {
59                    "type": "python",
60                    "processes": { "spare": \u0030 },
61                    "path": "/app",
62                    "module": "wsgi"
63                }
64            }
65            """,
66            'applications',
67        ), 'unicode number'
68
69    def test_json_utf8_bom(self):
70        assert 'success' in self.conf(
71            b"""\xEF\xBB\xBF
72            {
73                "app": {
74                    "type": "python",
75                    "processes": {"spare": 0},
76                    "path": "/app",
77                    "module": "wsgi"
78                }
79            }
80            """,
81            'applications',
82        ), 'UTF-8 BOM'
83
84    def test_json_comment_single_line(self):
85        assert 'success' in self.conf(
86            b"""
87            // this is bridge
88            {
89                "//app": {
90                    "type": "python", // end line
91                    "processes": {"spare": 0},
92                    // inside of block
93                    "path": "/app",
94                    "module": "wsgi"
95                }
96                // double //
97            }
98            // end of json \xEF\t
99            """,
100            'applications',
101        ), 'single line comments'
102
103    def test_json_comment_multi_line(self):
104        assert 'success' in self.conf(
105            b"""
106            /* this is bridge */
107            {
108                "/*app": {
109                /**
110                 * multiple lines
111                 **/
112                    "type": "python",
113                    "processes": /* inline */ {"spare": 0},
114                    "path": "/app",
115                    "module": "wsgi"
116                    /*
117                    // end of block */
118                }
119                /* blah * / blah /* blah */
120            }
121            /* end of json \xEF\t\b */
122            """,
123            'applications',
124        ), 'multi line comments'
125
126    def test_json_comment_invalid(self):
127        assert 'error' in self.conf(b'/{}', 'applications'), 'slash'
128        assert 'error' in self.conf(b'//{}', 'applications'), 'comment'
129        assert 'error' in self.conf(b'{} /', 'applications'), 'slash end'
130        assert 'error' in self.conf(b'/*{}', 'applications'), 'slash star'
131        assert 'error' in self.conf(b'{} /*', 'applications'), 'slash star end'
132
133    def test_applications_open_brace(self):
134        assert 'error' in self.conf('{', 'applications'), 'open brace'
135
136    def test_applications_string(self):
137        assert 'error' in self.conf('"{}"', 'applications'), 'string'
138
139    @pytest.mark.skip('not yet, unsafe')
140    def test_applications_type_only(self):
141        assert 'error' in self.conf(
142            {"app": {"type": "python"}}, 'applications'
143        ), 'type only'
144
145    def test_applications_miss_quote(self):
146        assert 'error' in self.conf(
147            """
148            {
149                app": {
150                    "type": "python",
151                    "processes": { "spare": 0 },
152                    "path": "/app",
153                    "module": "wsgi"
154                }
155            }
156            """,
157            'applications',
158        ), 'miss quote'
159
160    def test_applications_miss_colon(self):
161        assert 'error' in self.conf(
162            """
163            {
164                "app" {
165                    "type": "python",
166                    "processes": { "spare": 0 },
167                    "path": "/app",
168                    "module": "wsgi"
169                }
170            }
171            """,
172            'applications',
173        ), 'miss colon'
174
175    def test_applications_miss_comma(self):
176        assert 'error' in self.conf(
177            """
178            {
179                "app": {
180                    "type": "python"
181                    "processes": { "spare": 0 },
182                    "path": "/app",
183                    "module": "wsgi"
184                }
185            }
186            """,
187            'applications',
188        ), 'miss comma'
189
190    def test_applications_skip_spaces(self):
191        assert 'success' in self.conf(
192            b'{ \n\r\t}', 'applications'
193        ), 'skip spaces'
194
195    def test_applications_relative_path(self):
196        assert 'success' in self.conf(
197            {
198                "app": {
199                    "type": "python",
200                    "processes": {"spare": 0},
201                    "path": "../app",
202                    "module": "wsgi",
203                }
204            },
205            'applications',
206        ), 'relative path'
207
208    @pytest.mark.skip('not yet, unsafe')
209    def test_listeners_empty(self):
210        assert 'error' in self.conf(
211            {"*:7080": {}}, 'listeners'
212        ), 'listener empty'
213
214    def test_listeners_no_app(self):
215        assert 'error' in self.conf(
216            {"*:7080": {"pass": "applications/app"}}, 'listeners'
217        ), 'listeners no app'
218
219    def test_listeners_wildcard(self):
220        assert 'success' in self.conf(
221            {
222                "listeners": {"*:7080": {"pass": "applications/app"}},
223                "applications": {
224                    "app": {
225                        "type": "python",
226                        "processes": {"spare": 0},
227                        "path": "/app",
228                        "module": "wsgi",
229                    }
230                },
231            }
232        ), 'listeners wildcard'
233
234    def test_listeners_explicit(self):
235        assert 'success' in self.conf(
236            {
237                "listeners": {"127.0.0.1:7080": {"pass": "applications/app"}},
238                "applications": {
239                    "app": {
240                        "type": "python",
241                        "processes": {"spare": 0},
242                        "path": "/app",
243                        "module": "wsgi",
244                    }
245                },
246            }
247        ), 'explicit'
248
249    def test_listeners_explicit_ipv6(self):
250        assert 'success' in self.conf(
251            {
252                "listeners": {"[::1]:7080": {"pass": "applications/app"}},
253                "applications": {
254                    "app": {
255                        "type": "python",
256                        "processes": {"spare": 0},
257                        "path": "/app",
258                        "module": "wsgi",
259                    }
260                },
261            }
262        ), 'explicit ipv6'
263
264    @pytest.mark.skip('not yet, unsafe')
265    def test_listeners_no_port(self):
266        assert 'error' in self.conf(
267            {
268                "listeners": {"127.0.0.1": {"pass": "applications/app"}},
269                "applications": {
270                    "app": {
271                        "type": "python",
272                        "processes": {"spare": 0},
273                        "path": "/app",
274                        "module": "wsgi",
275                    }
276                },
277            }
278        ), 'no port'
279
280    def test_json_application_name_large(self):
281        name = "X" * 1024 * 1024
282
283        assert 'success' in self.conf(
284            {
285                "listeners": {"*:7080": {"pass": "applications/" + name}},
286                "applications": {
287                    name: {
288                        "type": "python",
289                        "processes": {"spare": 0},
290                        "path": "/app",
291                        "module": "wsgi",
292                    }
293                },
294            }
295        )
296
297    @pytest.mark.skip('not yet')
298    def test_json_application_many(self):
299        apps = 999
300
301        conf = {
302            "applications": {
303                "app-"
304                + str(a): {
305                    "type": "python",
306                    "processes": {"spare": 0},
307                    "path": "/app",
308                    "module": "wsgi",
309                }
310                for a in range(apps)
311            },
312            "listeners": {
313                "*:" + str(7000 + a): {"pass": "applications/app-" + str(a)}
314                for a in range(apps)
315            },
316        }
317
318        assert 'success' in self.conf(conf)
319
320    def test_json_application_many2(self):
321        conf = {
322            "applications": {
323                "app-"
324                + str(a): {
325                    "type": "python",
326                    "processes": {"spare": 0},
327                    "path": "/app",
328                    "module": "wsgi",
329                }
330                # Larger number of applications can cause test fail with default
331                # open files limit due to the lack of file descriptors.
332                for a in range(100)
333            },
334            "listeners": {"*:7080": {"pass": "applications/app-1"}},
335        }
336
337        assert 'success' in self.conf(conf)
338
339    def test_unprivileged_user_error(self, is_su, skip_alert):
340        skip_alert(r'cannot set user "root"', r'failed to apply new conf')
341        if is_su:
342            pytest.skip('unprivileged tests')
343
344        assert 'error' in self.conf(
345            {
346                "app": {
347                    "type": "external",
348                    "processes": 1,
349                    "executable": "/app",
350                    "user": "root",
351                }
352            },
353            'applications',
354        ), 'setting user'
355