1import re 2 3import pytest 4from unit.applications.lang.ruby import TestApplicationRuby 5 6 7class TestRubyApplication(TestApplicationRuby): 8 prerequisites = {'modules': {'ruby': 'all'}} 9 10 def test_ruby_application(self): 11 self.load('variables') 12 13 body = 'Test body string.' 14 15 resp = self.post( 16 headers={ 17 'Host': 'localhost', 18 'Content-Type': 'text/html', 19 'Custom-Header': 'blah', 20 'Connection': 'close', 21 }, 22 body=body, 23 ) 24 25 assert resp['status'] == 200, 'status' 26 headers = resp['headers'] 27 header_server = headers.pop('Server') 28 assert re.search(r'Unit/[\d\.]+', header_server), 'server header' 29 assert ( 30 headers.pop('Server-Software') == header_server 31 ), 'server software header' 32 33 date = headers.pop('Date') 34 assert date[-4:] == ' GMT', 'date header timezone' 35 assert ( 36 abs(self.date_to_sec_epoch(date) - self.sec_epoch()) < 5 37 ), 'date header' 38 39 assert headers == { 40 'Connection': 'close', 41 'Content-Length': str(len(body)), 42 'Content-Type': 'text/html', 43 'Request-Method': 'POST', 44 'Request-Uri': '/', 45 'Http-Host': 'localhost', 46 'Server-Protocol': 'HTTP/1.1', 47 'Custom-Header': 'blah', 48 'Rack-Version': '13', 49 'Rack-Url-Scheme': 'http', 50 'Rack-Multithread': 'false', 51 'Rack-Multiprocess': 'true', 52 'Rack-Run-Once': 'false', 53 'Rack-Hijack-Q': 'false', 54 'Rack-Hijack': '', 55 'Rack-Hijack-IO': '', 56 }, 'headers' 57 assert resp['body'] == body, 'body' 58 59 def test_ruby_application_query_string(self): 60 self.load('query_string') 61 62 resp = self.get(url='/?var1=val1&var2=val2') 63 64 assert ( 65 resp['headers']['Query-String'] == 'var1=val1&var2=val2' 66 ), 'Query-String header' 67 68 def test_ruby_application_query_string_empty(self): 69 self.load('query_string') 70 71 resp = self.get(url='/?') 72 73 assert resp['status'] == 200, 'query string empty status' 74 assert resp['headers']['Query-String'] == '', 'query string empty' 75 76 def test_ruby_application_query_string_absent(self): 77 self.load('query_string') 78 79 resp = self.get() 80 81 assert resp['status'] == 200, 'query string absent status' 82 assert resp['headers']['Query-String'] == '', 'query string absent' 83 84 @pytest.mark.skip('not yet') 85 def test_ruby_application_server_port(self): 86 self.load('server_port') 87 88 assert ( 89 self.get()['headers']['Server-Port'] == '7080' 90 ), 'Server-Port header' 91 92 def test_ruby_application_status_int(self): 93 self.load('status_int') 94 95 assert self.get()['status'] == 200, 'status int' 96 97 def test_ruby_application_input_read_empty(self): 98 self.load('input_read_empty') 99 100 assert self.get()['body'] == '', 'read empty' 101 102 def test_ruby_application_input_read_parts(self): 103 self.load('input_read_parts') 104 105 assert ( 106 self.post(body='0123456789')['body'] == '012345678' 107 ), 'input read parts' 108 109 def test_ruby_application_input_read_buffer(self): 110 self.load('input_read_buffer') 111 112 assert ( 113 self.post(body='0123456789')['body'] == '0123456789' 114 ), 'input read buffer' 115 116 def test_ruby_application_input_read_buffer_not_empty(self): 117 self.load('input_read_buffer_not_empty') 118 119 assert ( 120 self.post(body='0123456789')['body'] == '0123456789' 121 ), 'input read buffer not empty' 122 123 def test_ruby_application_input_gets(self): 124 self.load('input_gets') 125 126 body = '0123456789' 127 128 assert self.post(body=body)['body'] == body, 'input gets' 129 130 def test_ruby_application_input_gets_2(self): 131 self.load('input_gets') 132 133 assert ( 134 self.post(body='01234\n56789\n')['body'] == '01234\n' 135 ), 'input gets 2' 136 137 def test_ruby_application_input_gets_all(self): 138 self.load('input_gets_all') 139 140 body = '\n01234\n56789\n\n' 141 142 assert self.post(body=body)['body'] == body, 'input gets all' 143 144 def test_ruby_application_input_each(self): 145 self.load('input_each') 146 147 body = '\n01234\n56789\n\n' 148 149 assert self.post(body=body)['body'] == body, 'input each' 150 151 @pytest.mark.skip('not yet') 152 def test_ruby_application_input_rewind(self): 153 self.load('input_rewind') 154 155 body = '0123456789' 156 157 assert self.post(body=body)['body'] == body, 'input rewind' 158 159 @pytest.mark.skip('not yet') 160 def test_ruby_application_syntax_error(self, skip_alert): 161 skip_alert( 162 r'Failed to parse rack script', 163 r'syntax error', 164 r'new_from_string', 165 r'parse_file', 166 ) 167 self.load('syntax_error') 168 169 assert self.get()['status'] == 500, 'syntax error' 170 171 def test_ruby_application_errors_puts(self): 172 self.load('errors_puts') 173 174 self.get() 175 176 assert ( 177 self.wait_for_record(r'\[error\].+Error in application') 178 is not None 179 ), 'errors puts' 180 181 def test_ruby_application_errors_puts_int(self): 182 self.load('errors_puts_int') 183 184 self.get() 185 186 assert ( 187 self.wait_for_record(r'\[error\].+1234567890') is not None 188 ), 'errors puts int' 189 190 def test_ruby_application_errors_write(self): 191 self.load('errors_write') 192 193 self.get() 194 195 assert ( 196 self.wait_for_record(r'\[error\].+Error in application') 197 is not None 198 ), 'errors write' 199 200 def test_ruby_application_errors_write_to_s_custom(self): 201 self.load('errors_write_to_s_custom') 202 203 assert self.get()['status'] == 200, 'errors write to_s custom' 204 205 def test_ruby_application_errors_write_int(self): 206 self.load('errors_write_int') 207 208 self.get() 209 210 211 assert ( 212 self.wait_for_record(r'\[error\].+1234567890') is not None 213 ), 'errors write int' 214 215 def test_ruby_application_at_exit(self): 216 self.load('at_exit') 217 218 self.get() 219 220 assert 'success' in self.conf({"listeners": {}, "applications": {}}) 221 222 assert ( 223 self.wait_for_record(r'\[error\].+At exit called\.') is not None 224 ), 'at exit' 225 226 def test_ruby_application_header_custom(self): 227 self.load('header_custom') 228 229 resp = self.post(body="\ntc=one,two\ntc=three,four,\n\n") 230 231 assert resp['headers']['Custom-Header'] == [ 232 '', 233 'tc=one,two', 234 'tc=three,four,', 235 '', 236 '', 237 ], 'header custom' 238 239 @pytest.mark.skip('not yet') 240 def test_ruby_application_header_custom_non_printable(self): 241 self.load('header_custom') 242 243 assert ( 244 self.post(body='\b')['status'] == 500 245 ), 'header custom non printable' 246 247 def test_ruby_application_header_status(self): 248 self.load('header_status') 249 250 assert self.get()['status'] == 200, 'header status' 251 252 @pytest.mark.skip('not yet') 253 def test_ruby_application_header_rack(self): 254 self.load('header_rack') 255 256 assert self.get()['status'] == 500, 'header rack' 257 258 def test_ruby_application_body_empty(self): 259 self.load('body_empty') 260 261 assert self.get()['body'] == '', 'body empty' 262 263 def test_ruby_application_body_array(self): 264 self.load('body_array') 265 266 assert self.get()['body'] == '0123456789', 'body array' 267 268 def test_ruby_application_body_large(self): 269 self.load('mirror') 270 271 body = '0123456789' * 1000 272 273 assert self.post(body=body)['body'] == body, 'body large' 274 275 @pytest.mark.skip('not yet') 276 def test_ruby_application_body_each_error(self): 277 self.load('body_each_error') 278 279 assert self.get()['status'] == 500, 'body each error status' 280 281 assert ( 282 self.wait_for_record(r'\[error\].+Failed to run ruby script') 283 is not None 284 ), 'body each error' 285 286 def test_ruby_application_body_file(self): 287 self.load('body_file') 288 289 assert self.get()['body'] == 'body\n', 'body file' 290 291 def test_ruby_keepalive_body(self): 292 self.load('mirror') 293 294 assert self.get()['status'] == 200, 'init' 295 296 body = '0123456789' * 500 297 (resp, sock) = self.post( 298 headers={ 299 'Host': 'localhost', 300 'Connection': 'keep-alive', 301 'Content-Type': 'text/html', 302 }, 303 start=True, 304 body=body, 305 read_timeout=1, 306 ) 307 308 assert resp['body'] == body, 'keep-alive 1' 309 310 body = '0123456789' 311 resp = self.post( 312 headers={ 313 'Host': 'localhost', 314 'Connection': 'close', 315 'Content-Type': 'text/html', 316 }, 317 sock=sock, 318 body=body, 319 ) 320 321 assert resp['body'] == body, 'keep-alive 2' 322 323 def test_ruby_application_constants(self): 324 self.load('constants') 325 326 resp = self.get() 327 328 assert resp['status'] == 200, 'status' 329 330 headers = resp['headers'] 331 assert len(headers['X-Copyright']) > 0, 'RUBY_COPYRIGHT' 332 assert len(headers['X-Description']) > 0, 'RUBY_DESCRIPTION' 333 assert len(headers['X-Engine']) > 0, 'RUBY_ENGINE' 334 assert len(headers['X-Engine-Version']) > 0, 'RUBY_ENGINE_VERSION' 335 assert len(headers['X-Patchlevel']) > 0, 'RUBY_PATCHLEVEL' 336 assert len(headers['X-Platform']) > 0, 'RUBY_PLATFORM' 337 assert len(headers['X-Release-Date']) > 0, 'RUBY_RELEASE_DATE' 338 assert len(headers['X-Revision']) > 0, 'RUBY_REVISION' 339 assert len(headers['X-Version']) > 0, 'RUBY_VERSION' 340 341 def test_ruby_application_threads(self): 342 self.load('threads') 343 344 assert 'success' in self.conf( 345 '4', 'applications/threads/threads' 346 ), 'configure 4 threads' 347 348 socks = [] 349 350 for i in range(4): 351 (_, sock) = self.get( 352 headers={ 353 'Host': 'localhost', 354 'X-Delay': '2', 355 'Connection': 'close', 356 }, 357 no_recv=True, 358 start=True, 359 ) 360 361 socks.append(sock) 362 363 threads = set() 364 365 for sock in socks: 366 resp = self.recvall(sock).decode('utf-8') 367 368 self.log_in(resp) 369 370 resp = self._resp_to_dict(resp) 371 372 assert resp['status'] == 200, 'status' 373 374 threads.add(resp['headers']['X-Thread']) 375 376 assert resp['headers']['Rack-Multithread'] == 'true', 'multithread' 377 378 sock.close() 379 380 assert len(socks) == len(threads), 'threads differs' 381