import pytest
import linreg_core
class TestErrorMessageQuality:
def test_empty_input_error_message(self):
with pytest.raises(Exception) as exc_info:
linreg_core.ols_regression([], [], ["Intercept"])
error_msg = str(exc_info.value)
assert len(error_msg) > 0, "Error message should not be empty"
assert any(term in error_msg.lower() for term in
["empty", "insufficient", "data", "observation", "input", "invalid"])
def test_dimension_mismatch_error_message(self):
y = [1.0, 2.0, 3.0]
x = [[1.0, 2.0]]
with pytest.raises(Exception) as exc_info:
linreg_core.ols_regression(y, x, ["Intercept", "X1"])
error_msg = str(exc_info.value)
assert any(term in error_msg.lower() for term in
["dimension", "mismatch", "size", "length", "insufficient"])
def test_insufficient_data_error_message(self):
y = [1.0, 2.0]
x = [[1.0, 2.0], [3.0, 4.0]]
with pytest.raises(Exception) as exc_info:
linreg_core.ols_regression(y, x, ["Intercept", "X1", "X2"])
error_msg = str(exc_info.value)
assert len(error_msg) > 0
def test_singular_matrix_handled_gracefully(self):
y = [1.0, 2.0, 3.0, 4.0, 5.0]
x1 = [1.0, 2.0, 3.0, 4.0, 5.0]
x2 = [2.0, 4.0, 6.0, 8.0, 10.0]
result = linreg_core.ols_regression(y, [x1, x2], ["Intercept", "X1", "X2"])
assert result is not None
assert len(result.coefficients) == 3
import math
assert any(math.isnan(c) for c in result.coefficients)
def test_nan_value_error_message(self):
y = [1.0, float('nan'), 3.0, 4.0, 5.0]
x = [[1.0, 2.0, 3.0, 4.0, 5.0]]
with pytest.raises(Exception) as exc_info:
linreg_core.ols_regression(y, x, ["Intercept", "X1"])
error_msg = str(exc_info.value)
assert any(term in error_msg.lower() for term in
["nan", "invalid", "finite", "number"])
def test_inf_value_error_message(self):
y = [1.0, float('inf'), 3.0, 4.0, 5.0]
x = [[1.0, 2.0, 3.0, 4.0, 5.0]]
with pytest.raises(Exception) as exc_info:
linreg_core.ols_regression(y, x, ["Intercept", "X1"])
error_msg = str(exc_info.value)
assert any(term in error_msg.lower() for term in
["inf", "infinite", "invalid", "finite"])
def test_constant_predictor_handled_gracefully(self):
y = [1.0, 2.0, 3.0, 4.0, 5.0]
x = [[1.0, 1.0, 1.0, 1.0, 1.0]]
result = linreg_core.ols_regression(y, x, ["Intercept", "X1"])
assert result is not None
assert len(result.coefficients) == 2
import math
assert any(math.isnan(c) for c in result.coefficients)
def test_negative_lambda_error_message(self):
y = [1.0, 2.0, 3.0, 4.0, 5.0]
x = [[1.0, 2.0, 3.0, 4.0, 5.0]]
with pytest.raises(Exception) as exc_info:
linreg_core.ridge_regression(y, x, lambda_=-1.0)
error_msg = str(exc_info.value)
assert any(term in error_msg.lower() for term in
["invalid", "lambda", "parameter", "positive"])
class TestExceptionTypes:
def test_linreg_error_exists(self):
assert hasattr(linreg_core, 'LinregError')
def test_data_validation_error_exists(self):
assert hasattr(linreg_core, 'DataValidationError')
def test_error_is_catchable(self):
try:
linreg_core.ols_regression([], [], ["Intercept"])
except linreg_core.LinregError:
caught = True
except Exception:
caught = True
assert caught, "Error should be catchable"
def test_error_has_message_attribute(self):
try:
linreg_core.ols_regression([], [], ["Intercept"])
except Exception as e:
has_message = hasattr(e, 'message') or len(str(e)) > 0
assert has_message, "Error should have a message or string representation"
class TestErrorMessageContext:
def test_ridge_insufficient_data_context(self):
y = [1.0]
x = [[1.0]]
with pytest.raises(Exception) as exc_info:
linreg_core.ridge_regression(y, x)
error_msg = str(exc_info.value)
assert len(error_msg) > 10
def test_lasso_convergence_error_message(self):
y = [1.0, 2.0, 3.0, 4.0, 5.0]
x = [1.0, 2.0, 3.0, 4.0, 5.0]
result = linreg_core.lasso_regression(y, [x], lambda_val=0.001, max_iter=1)
assert hasattr(result, 'converged')
def test_stats_empty_data_error_message(self):
with pytest.raises(Exception) as exc_info:
linreg_core.stats_mean([])
error_msg = str(exc_info.value)
assert any(term in error_msg.lower() for term in
["empty", "data", "value", "invalid"])
def test_correlation_length_mismatch_message(self):
x = [1.0, 2.0, 3.0]
y = [1.0, 2.0]
with pytest.raises(Exception) as exc_info:
linreg_core.stats_correlation(x, y)
error_msg = str(exc_info.value)
assert any(term in error_msg.lower() for term in
["length", "size", "dimension", "mismatch"])
class TestErrorRecoverability:
def test_error_allows_retry_with_correct_data(self):
with pytest.raises(Exception):
linreg_core.ols_regression([], [], ["Intercept"])
y = [1.0, 2.0, 3.0, 4.0, 5.0]
x = [[1.0, 2.0, 3.0, 4.0, 5.0]]
result = linreg_core.ols_regression(y, x, ["Intercept", "X1"])
assert result is not None
assert len(result.coefficients) == 2
def test_singular_matrix_recoverable_by_removing_predictor(self):
import math
y = [1.0, 2.0, 3.0, 4.0, 5.0]
x1 = [1.0, 2.0, 3.0, 4.0, 5.0]
x2 = [2.0, 4.0, 6.0, 8.0, 10.0]
result_both = linreg_core.ols_regression(y, [x1, x2], ["Intercept", "X1", "X2"])
assert any(math.isnan(c) for c in result_both.coefficients)
result_one = linreg_core.ols_regression(y, [x1], ["Intercept", "X1"])
assert result_one.r_squared > 0.9
assert not any(math.isnan(c) for c in result_one.coefficients)
class TestErrorMessageLanguage:
def test_error_messages_are_english(self):
errors_to_test = [
lambda: linreg_core.ols_regression([], [], ["Intercept"]),
lambda: linreg_core.ols_regression([1], [[1]], ["Intercept", "X1"]),
lambda: linreg_core.stats_mean([]),
]
for error_func in errors_to_test:
try:
error_func()
except Exception as e:
msg = str(e).lower()
has_vowels = any(vowel in msg for vowel in "aeiou")
assert has_vowels or len(msg) == 0, f"Error message seems too cryptic: {msg}"
def test_error_messages_avoid_internal_terminology(self):
with pytest.raises(Exception) as exc_info:
linreg_core.ols_regression([], [], ["Intercept"])
error_msg = str(exc_info.value).lower()
internal_terms = ["pyo3", "pyresult", "bound", "rust", "nak", "panic"]
for term in internal_terms:
assert term not in error_msg, f"Error message contains internal term: {term}"
def test_error_messages_not_too_long(self):
with pytest.raises(Exception) as exc_info:
linreg_core.ols_regression([], [], ["Intercept"])
error_msg = str(exc_info.value)
assert len(error_msg) < 500, "Error message too long"
def test_error_messages_not_too_short(self):
with pytest.raises(Exception) as exc_info:
linreg_core.ols_regression([], [], ["Intercept"])
error_msg = str(exc_info.value)
assert len(error_msg) >= 10, "Error message too short"
class TestSpecificErrorScenarios:
def test_multiple_high_vif_predictors_error(self):
import random
random.seed(42)
n = 50
x1 = [random.gauss(0, 1) for _ in range(n)]
x2 = [x + random.gauss(0, 0.01) for x in x1] x3 = [x * 2 + random.gauss(0, 0.01) for x in x1]
y = [x1[i] + x2[i] + random.gauss(0, 0.1) for i in range(n)]
try:
result = linreg_core.ols_regression(y, [x1, x2, x3], ["Intercept", "X1", "X2", "X3"])
assert any(vif > 100 for vif in result.vif), "Expected high VIF for collinear data"
except Exception as e:
error_msg = str(e).lower()
assert any(term in error_msg for term in
["singular", "multicollinear", "correlation"])
def test_single_observation_error_message(self):
y = [5.0]
x = [[2.0]]
with pytest.raises(Exception) as exc_info:
linreg_core.ols_regression(y, x, ["Intercept", "X1"])
error_msg = str(exc_info.value).lower()
assert any(term in error_msg for term in
["insufficient", "observation", "data", "sample"])
def test_two_observations_three_predictors_error(self):
y = [1.0, 2.0]
x1 = [1.0, 2.0]
x2 = [2.0, 4.0]
x3 = [3.0, 6.0]
with pytest.raises(Exception) as exc_info:
linreg_core.ols_regression(y, [x1, x2, x3], ["Intercept", "X1", "X2", "X3"])
error_msg = str(exc_info.value)
assert any(term in error_msg.lower() for term in
["insufficient", "observation", "predictor", "data"])
class TestCsvErrorMessages:
def test_empty_csv_error_message(self):
result = linreg_core.parse_csv("")
assert result.n_rows == 0
def test_malformed_csv_doesnt_crash(self):
csv_content = """a,b,c
1,2,3
4,5
6,7,8,9"""
result = linreg_core.parse_csv(csv_content)
assert result is not None
def test_csv_with_only_headers(self):
csv_content = "x,y,z"
result = linreg_core.parse_csv(csv_content)
assert result.n_rows == 0
assert result.headers == ["x", "y", "z"]
class TestDiagnosticsErrorMessages:
def test_diagnostic_with_insufficient_data(self):
y = [1.0, 2.0]
x = [[1.0, 2.0]]
try:
result = linreg_core.breusch_pagan_test(y, x)
assert hasattr(result, 'p_value')
except Exception as e:
error_msg = str(e)
assert len(error_msg) > 10
def test_durbin_watson_with_single_obs(self):
y = [1.0]
x = [[1.0]]
with pytest.raises(Exception) as exc_info:
linreg_core.durbin_watson_test(y, x)
error_msg = str(exc_info.value)
assert len(error_msg) > 5