def test_execute_raises_if_shlex_wold_block(): """ Check if execute raises ValueError when None is passed instead of the command. That would cause ``shlex.split`` to read from stdin. """ with pytest.raises(ValueError): execute(None, [])
def test_execute_pre_checks_fail(popen_mock, invalid_check, checks_count): """Test if ``execute`` raises ``PreChecksFailed`` if pre-execute checks fail.""" failing_pre_checks = [lambda: False] * checks_count with pytest.raises(PreChecksFailed): # Exception coming from the pre-checks - we don't get to the point of running post-checks. execute(FAKE_COMMAND, [invalid_check], pre_checks=failing_pre_checks, interval=0.1, timeout=0.1) assert not popen_mock.called, 'After pre-checks failed, the command should not be executed.'
def test_execute_raises_when_process_exits(): """Check if ``SubprocessExited`` is thrown if the process exits.""" process_mock = Mock() process_mock.poll.return_value = 12 exiting_popen_mock = Mock(return_value=process_mock) with pytest.raises(SubprocessExited): execute(FAKE_COMMAND, [lambda: exiting_popen_mock.called], popen=exiting_popen_mock) assert exiting_popen_mock.popen_called
def test_execute_accepts_strings(popen_mock): """Check that if the command is a string, it is split in the shell way.""" string_command = 'command --arg "quoted_val" --arg2 not_quoted_val --flag -d=\'sth_else\'' list_command = ['command', '--arg', 'quoted_val', '--arg2', 'not_quoted_val', '--flag', '-d=sth_else'] execute(string_command, [], popen=popen_mock) assert popen_mock.called_with(list_command) execute(list_command, [], popen=popen_mock) assert popen_mock.call_args_list[0] == popen_mock.call_args_list[1]
def test_execute_waits_for_the_post_checks_to_turn_ok(popen_mock): """Check if ``execute`` will poll the checks until they resolve OK or timeout occurs.""" check = MagicMock(side_effect=[False, False, False, True]) sleep_mock = Mock() execute(FAKE_COMMAND, [check], pre_checks=[lambda: True], sleep_fn=sleep_mock, popen=popen_mock) assert popen_mock.called assert (check.call_count == 4, 'The check function should return False 3 times as a post-check and once - and the last time - True.') assert sleep_mock.call_count == 3, 'Sleeping should take place after each failed check.'
def test_execute_pre_checks_defaults(popen_mock): """Test if negated post-checks are used as pre-checks by default.""" with pytest.raises(PreChecksFailed): # We expect the failure to come from pre-checks. # A negated version of the passed check will be used as a pre-check. execute(FAKE_COMMAND, [lambda: True], timeout=0.1, popen=popen_mock) assert not popen_mock.called, 'We shouldn\'t be able to run the command after pre-checks failed.' # Positive case - the check fn returns False before Popen and True after - just as in the ideal world: execute(FAKE_COMMAND, [lambda: popen_mock.called], timeout=0.1, popen=popen_mock) assert popen_mock.called
def test_execute_pre_checks_pass(popen_mock, checks_count, initial_failures_count): """ Check if succeeding pre-checks cause the command to be executed. Check if initially failing pre-checks are polled until they pass. """ pre_checks = [ chain([lambda: False] * initial_failures_count, cycle([lambda: True])).next for _ in range(checks_count) ] execute(FAKE_COMMAND, [lambda: True], pre_checks=pre_checks, popen=popen_mock) assert popen_mock.called, 'The command should be executed.'
def test_execute_accepts_strings(popen_mock): """Check that if the command is a string, it is split in the shell way.""" string_command = 'command --arg "quoted_val" --arg2 not_quoted_val --flag -d=\'sth_else\'' list_command = [ 'command', '--arg', 'quoted_val', '--arg2', 'not_quoted_val', '--flag', '-d=sth_else' ] execute(string_command, [], popen=popen_mock) assert popen_mock.called_with(list_command) execute(list_command, [], popen=popen_mock) assert popen_mock.call_args_list[0] == popen_mock.call_args_list[1]
def test_execute_post_checks_fail(popen_mock, process_mock): """Check if ``PostChecksFailed`` is raised when post checks fail.""" with pytest.raises(PostChecksFailed): # One check that will fail as a post-check and pass as a negated pre-check. killer_mock = Mock() execute(FAKE_COMMAND, [lambda: False], kill_fn=killer_mock, timeout=0.1, popen=popen_mock) killer_mock.assert_called_once_with(process_mock) with pytest.raises(PostChecksFailed): # Separate failing post-check and passing pre-check. execute(FAKE_COMMAND, [lambda: not popen_mock.called], pre_checks=[lambda: True], kill_fn=killer_mock, timeout=0.1, popen=popen_mock) assert killer_mock.call_count == 2
def test_execute_waits_for_the_post_checks_to_turn_ok(popen_mock): """Check if ``execute`` will poll the checks until they resolve OK or timeout occurs.""" check = MagicMock(side_effect=[False, False, False, True]) sleep_mock = Mock() execute(FAKE_COMMAND, [check], pre_checks=[lambda: True], sleep_fn=sleep_mock, popen=popen_mock) assert popen_mock.called assert ( check.call_count == 4, 'The check function should return False 3 times as a post-check and once - and the last time - True.' ) assert sleep_mock.call_count == 3, 'Sleeping should take place after each failed check.'
def test_execute_popen_called(popen_mock): """Check if the ``execute`` function calls ``Popen`` when checks are OK.""" process = execute(FAKE_COMMAND, [lambda: popen_mock.called], popen=popen_mock) assert popen_mock.popen_called # This actually checks if the mocked Popen object was returned. assert process.poll() is None
def test_execute_same_service_sequentially(): """Check if the pre-checks detect remains of the previous process.""" port = port_for.select_random() command = [SERVICE, 'tcp', '--port', str(port)] process = execute(command, [check_tcp(port)]) with pytest.raises(PreChecksFailed): # The previous process still in place. execute(command, [check_tcp(port)], timeout=1) process.terminate() # The process will take >2 seconds to tear down. # Now, as the previous process is dead, the `execute` should succeed. another_process = execute(command, [check_tcp(port)], timeout=5) another_process.kill()
def test_execute_process_killed(): """ Check if the process gets killed after post checks fail. We spawn a process that listens TCP on some port and supply a failing check so that the process should get killed. We check if the process got killed by checking if nothing listens on TCP after PostChecksFailed is raised. """ port = port_for.select_random() passing_check = check_tcp(port) def failing_check_for_testing_purposes(): return False with pytest.raises(PostChecksFailed) as checks_failed: execute([SERVICE, 'tcp', '--port', str(port)], [passing_check, failing_check_for_testing_purposes], timeout=1) assert 'function failing_check_for_testing_purposes' in str(checks_failed.value) # The process will be killed and nothing will be listening on that port. wait_until(lambda: passing_check() is False)
def test_execute_check_unix(tmpdir, delay): """Check the executor with the unix socket check.""" socket_file = str(tmpdir / 'temp_unix_socket') check = check_unix(socket_file) assert check() is False process = execute( [SERVICE, '--delay', str(delay), 'unix', '--socket-file', socket_file], [check_unix(socket_file)], timeout=1 + delay) assert check() is True assert process.poll() is None process.kill()
def test_execute_check_tcp(delay): """Check the executor with the TCP check.""" port = port_for.select_random() check = check_tcp(port) assert check() is False process = execute( [SERVICE, '--delay', str(delay), 'tcp', '--port', str(port)], [check_tcp(port)], timeout=1 + delay) assert check() is True assert process.poll() is None # Still running. process.kill()
def test_execute_process_killed(): """ Check if the process gets killed after post checks fail. We spawn a process that listens TCP on some port and supply a failing check so that the process should get killed. We check if the process got killed by checking if nothing listens on TCP after PostChecksFailed is raised. """ port = port_for.select_random() passing_check = check_tcp(port) def failing_check_for_testing_purposes(): return False with pytest.raises(PostChecksFailed) as checks_failed: execute([SERVICE, 'tcp', '--port', str(port)], [passing_check, failing_check_for_testing_purposes], timeout=1) assert 'function failing_check_for_testing_purposes' in str( checks_failed.value) # The process will be killed and nothing will be listening on that port. wait_until(lambda: passing_check() is False)
def test_execute_failing_checks(): """Test if failing checks result in ``PostChecksFailed`` being thrown.""" port = check_tcp(port_for.select_random()) tcp_check = check_tcp(port) unix_check = check_unix('no_such_sock') http_check = check_http('http://127.0.0.1:%s' % port) with pytest.raises(PostChecksFailed): execute('sleep 10', [tcp_check], timeout=1) with pytest.raises(PostChecksFailed): execute('sleep 10', [unix_check], timeout=1) with pytest.raises(PostChecksFailed): execute('sleep 10', [http_check], timeout=1) with pytest.raises(PostChecksFailed): # 3 failing checks at once. execute('sleep 10', [http_check, unix_check, tcp_check], timeout=1)
def test_execute_check_http(delay): """Check the executor with the HTTP HEAD request check.""" port = port_for.select_random() good_path = '/good_path' check = check_http('http://127.0.0.1:%s%s' % (port, good_path)) check_bad_path = check_http('http://127.0.0.1:%s/bad_path' % port) # This request will not return 200. assert check() is False assert check_bad_path() is False process = execute( [SERVICE, '--delay', str(delay), 'http', '--port', str(port), '--path', good_path], [check], timeout=1 + delay) assert check() is True assert check_bad_path() is False assert process.poll() is None process.kill()
def test_execute_check_http(delay): """Check the executor with the HTTP HEAD request check.""" port = port_for.select_random() good_path = '/good_path' check = check_http('http://127.0.0.1:%s%s' % (port, good_path)) check_bad_path = check_http('http://127.0.0.1:%s/bad_path' % port) # This request will not return 200. assert check() is False assert check_bad_path() is False process = execute([ SERVICE, '--delay', str(delay), 'http', '--port', str(port), '--path', good_path ], [check], timeout=1 + delay) assert check() is True assert check_bad_path() is False assert process.poll() is None process.kill()