| """ |
| Tests for the static lint module. |
| """ |
| import pytest |
| from alpha_factory.deterministic.lint import lint, quick_dedup_hash |
|
|
|
|
| class TestLint: |
| def test_valid_simple_expression(self): |
| expr = "rank(ts_mean(close, 20))" |
| result = lint(expr) |
| assert result.passed |
| assert result.errors == [] |
|
|
| def test_valid_composite_expression(self): |
| expr = "0.5 * zscore(ts_mean(close, 20)) + 0.5 * zscore(rank(volume))" |
| result = lint(expr) |
| assert result.passed |
|
|
| def test_unknown_operator(self): |
| expr = "fake_operator(close, 20)" |
| result = lint(expr) |
| assert not result.passed |
| assert any("Unknown operator" in e for e in result.errors) |
|
|
| def test_lookahead_negative_delay(self): |
| expr = "ts_delay(close, -1)" |
| result = lint(expr) |
| assert not result.passed |
| assert any("Look-ahead" in e for e in result.errors) |
|
|
| def test_lookahead_future_field(self): |
| expr = "rank(future_returns)" |
| result = lint(expr) |
| assert not result.passed |
| assert any("Look-ahead" in e for e in result.errors) |
|
|
| def test_unbalanced_parens_extra_close(self): |
| expr = "rank(close))" |
| result = lint(expr) |
| assert not result.passed |
| assert any("Unbalanced" in e for e in result.errors) |
|
|
| def test_unbalanced_parens_unclosed(self): |
| expr = "rank(ts_mean(close, 20)" |
| result = lint(expr) |
| assert not result.passed |
| assert any("Unbalanced" in e for e in result.errors) |
|
|
| def test_empty_expression(self): |
| result = lint("") |
| assert not result.passed |
|
|
| def test_unit_safety_warning(self): |
| expr = "0.5 * ts_mean(close, 20) + 0.3 * volume" |
| result = lint(expr) |
| |
| assert len(result.warnings) > 0 |
|
|
| def test_high_decay_warning(self): |
| expr = "ts_decay_linear(rank(close), 50)" |
| result = lint(expr) |
| assert result.passed |
| assert any("is high" in w for w in result.warnings), f"Warnings: {result.warnings}" |
|
|
|
|
| class TestDedup: |
| def test_same_input_same_hash(self): |
| h1 = quick_dedup_hash("rank(close)", "sector", 5) |
| h2 = quick_dedup_hash("rank(close)", "sector", 5) |
| assert h1 == h2 |
|
|
| def test_different_expression_different_hash(self): |
| h1 = quick_dedup_hash("rank(close)", "sector", 5) |
| h2 = quick_dedup_hash("rank(volume)", "sector", 5) |
| assert h1 != h2 |
|
|
| def test_different_neutralization_different_hash(self): |
| h1 = quick_dedup_hash("rank(close)", "sector", 5) |
| h2 = quick_dedup_hash("rank(close)", "industry", 5) |
| assert h1 != h2 |
|
|