def test_sleeps_between_connection_attempts(mocker, mock_sleep, mock_connect,
                                            db_config):
    mock_uniform = mocker.patch(
        "random.uniform",
        autospec=True,
        side_effect=[1000, 2000, 3000, 4000, 5000],
    )
    mock_connect.side_effect = READ_ERROR
    db = DatabaseWrapper({
        **db_config,
        "CONNECTION_RETRY": {
            "MAX_ATTEMPTS": 6,
            "WAIT_MIN": 15,
            "WAIT_MAX": 100,
            "WAIT_MULTIPLIER": 10,
            "WAIT_EXP_BASE": 2,
        },
    })
    with pytest.raises(pyodbc.Error):
        db.get_new_connection(db.get_connection_params())
    assert mock_uniform.call_args_list == [
        call(15, 15),
        call(15, 20),
        call(15, 40),
        call(15, 80),
        call(15, 100),
    ]
    assert mock_sleep.call_args_list == [
        call(1), call(2), call(3), call(4),
        call(5)
    ]
def test_get_new_connection_breaks_early_if_connection_succeeds(
        mock_sleep, mock_connect, db_config):
    mock_connect.side_effect = [READ_ERROR, Mock()]
    db = DatabaseWrapper({
        **db_config, "CONNECTION_RETRY": {
            "MAX_ATTEMPTS": 3
        }
    })
    db.get_new_connection(db.get_connection_params())
    assert mock_connect.call_count == 2
    assert mock_sleep.call_count == 1
def test_only_retries_certain_errors(mock_sleep, mock_connect, db_config,
                                     error, expected_retries):
    mock_connect.side_effect = error
    db = DatabaseWrapper({
        **db_config, "CONNECTION_RETRY": {
            "MAX_ATTEMPTS": 2
        }
    })
    with pytest.raises(pyodbc.Error):
        db.get_new_connection(db.get_connection_params())
    assert mock_connect.call_count == 1 + expected_retries  # attempts = 1 + retries
def test_errors_to_retry_can_be_overridden(mock_sleep, mock_connect, db_config,
                                           error, expected_retries):
    mock_connect.side_effect = error
    db = DatabaseWrapper({
        **db_config,
        "CONNECTION_RETRY": {
            "MAX_ATTEMPTS": 2,
            "ERRORS": ["-12345"],
        },
    })
    with pytest.raises(pyodbc.Error):
        db.get_new_connection(db.get_connection_params())
    assert mock_connect.call_count == 1 + expected_retries
def test_get_new_connection_retries_up_to_MAX_ATTEMPTS(mock_sleep,
                                                       mock_connect,
                                                       db_config):
    mock_connect.side_effect = READ_ERROR
    db = DatabaseWrapper({
        **db_config, "CONNECTION_RETRY": {
            "MAX_ATTEMPTS": 3
        }
    })
    with pytest.raises(pyodbc.Error):
        db.get_new_connection(db.get_connection_params())
    assert mock_connect.call_count == 3
    assert mock_sleep.call_count == 2
def test_DatabaseWrapper_get_new_connection_calls_pyodbc_connect(
        mock_connect, db_config):
    db = DatabaseWrapper({
        "SERVER": "server1",
        "NAME": "db1",
        "USER": "******",
        "PASSWORD": "******",
        "OPTIONS": {
            "CONN_TIMEOUT": 120
        },
    })
    db.get_new_connection(db.get_connection_params())
    assert mock_connect.called is True
    connection_string = mock_connect.call_args[0][0]
    parts = connection_string.split(";")
    assert "Server=server1" in parts
    assert "Database=db1" in parts
    assert "Uid=user1" in parts
    assert "Pwd=password1" in parts
    assert mock_connect.call_args[1]['timeout'] == 120
def test_get_new_connection_doesnt_retry_by_default(mock_connect, db_config):
    mock_connect.side_effect = READ_ERROR
    db = DatabaseWrapper(db_config)
    with pytest.raises(pyodbc.Error):
        db.get_new_connection(db.get_connection_params())
    assert mock_connect.call_count == 1