Merge pull request 'fix: gateway config debt - validation, defaults, fallback chain checks (#328)' (#381) from fix/gateway-config-debt-328 into main
Some checks failed
Forge CI / smoke-and-build (push) Has been cancelled
Some checks failed
Forge CI / smoke-and-build (push) Has been cancelled
This commit was merged in pull request #381.
This commit is contained in:
@@ -249,3 +249,111 @@ class TestConfigIssueDataclass:
|
||||
a = ConfigIssue("error", "msg", "hint")
|
||||
b = ConfigIssue("error", "msg", "hint")
|
||||
assert a == b
|
||||
|
||||
|
||||
class TestFallbackProvidersValidation:
|
||||
"""fallback_providers must be a list of dicts with provider + model."""
|
||||
|
||||
def test_non_list(self):
|
||||
"""fallback_providers as string should error."""
|
||||
issues = validate_config_structure({
|
||||
"fallback_providers": "openrouter:google/gemini-3-flash-preview",
|
||||
})
|
||||
errors = [i for i in issues if i.severity == "error"]
|
||||
assert any("fallback_providers" in i.message and "list" in i.message for i in errors)
|
||||
|
||||
def test_dict_instead_of_list(self):
|
||||
"""fallback_providers as dict should error."""
|
||||
issues = validate_config_structure({
|
||||
"fallback_providers": {"provider": "openrouter", "model": "test"},
|
||||
})
|
||||
errors = [i for i in issues if i.severity == "error"]
|
||||
assert any("fallback_providers" in i.message and "dict" in i.message for i in errors)
|
||||
|
||||
def test_entry_missing_provider(self):
|
||||
"""Entry without provider should warn."""
|
||||
issues = validate_config_structure({
|
||||
"fallback_providers": [{"model": "google/gemini-3-flash-preview"}],
|
||||
})
|
||||
assert any("missing 'provider'" in i.message for i in issues)
|
||||
|
||||
def test_entry_missing_model(self):
|
||||
"""Entry without model should warn."""
|
||||
issues = validate_config_structure({
|
||||
"fallback_providers": [{"provider": "openrouter"}],
|
||||
})
|
||||
assert any("missing 'model'" in i.message for i in issues)
|
||||
|
||||
def test_entry_not_dict(self):
|
||||
"""Non-dict entries should warn."""
|
||||
issues = validate_config_structure({
|
||||
"fallback_providers": ["not-a-dict"],
|
||||
})
|
||||
assert any("not a dict" in i.message for i in issues)
|
||||
|
||||
def test_valid_entries(self):
|
||||
"""Valid fallback_providers should produce no fallback-related issues."""
|
||||
issues = validate_config_structure({
|
||||
"fallback_providers": [
|
||||
{"provider": "openrouter", "model": "google/gemini-3-flash-preview"},
|
||||
{"provider": "gemini", "model": "gemini-2.5-flash"},
|
||||
],
|
||||
})
|
||||
fb_issues = [i for i in issues if "fallback_providers" in i.message]
|
||||
assert len(fb_issues) == 0
|
||||
|
||||
def test_empty_list_no_issues(self):
|
||||
"""Empty list is valid (fallback disabled)."""
|
||||
issues = validate_config_structure({
|
||||
"fallback_providers": [],
|
||||
})
|
||||
fb_issues = [i for i in issues if "fallback_providers" in i.message]
|
||||
assert len(fb_issues) == 0
|
||||
|
||||
|
||||
class TestSessionResetValidation:
|
||||
"""session_reset.idle_minutes must be positive."""
|
||||
|
||||
def test_zero_idle_minutes(self):
|
||||
"""idle_minutes=0 should warn."""
|
||||
issues = validate_config_structure({
|
||||
"session_reset": {"idle_minutes": 0},
|
||||
})
|
||||
assert any("idle_minutes=0" in i.message for i in issues)
|
||||
|
||||
def test_negative_idle_minutes(self):
|
||||
"""idle_minutes=-5 should warn."""
|
||||
issues = validate_config_structure({
|
||||
"session_reset": {"idle_minutes": -5},
|
||||
})
|
||||
assert any("idle_minutes=-5" in i.message for i in issues)
|
||||
|
||||
def test_string_idle_minutes(self):
|
||||
"""idle_minutes as string should warn."""
|
||||
issues = validate_config_structure({
|
||||
"session_reset": {"idle_minutes": "abc"},
|
||||
})
|
||||
assert any("idle_minutes=" in i.message for i in issues)
|
||||
|
||||
def test_valid_idle_minutes(self):
|
||||
"""Valid idle_minutes should not warn."""
|
||||
issues = validate_config_structure({
|
||||
"session_reset": {"idle_minutes": 1440},
|
||||
})
|
||||
idle_issues = [i for i in issues if "idle_minutes" in i.message]
|
||||
assert len(idle_issues) == 0
|
||||
|
||||
def test_invalid_at_hour(self):
|
||||
"""at_hour=25 should warn."""
|
||||
issues = validate_config_structure({
|
||||
"session_reset": {"at_hour": 25},
|
||||
})
|
||||
assert any("at_hour=25" in i.message for i in issues)
|
||||
|
||||
def test_valid_at_hour(self):
|
||||
"""Valid at_hour should not warn."""
|
||||
issues = validate_config_structure({
|
||||
"session_reset": {"at_hour": 4},
|
||||
})
|
||||
hour_issues = [i for i in issues if "at_hour" in i.message]
|
||||
assert len(hour_issues) == 0
|
||||
|
||||
Reference in New Issue
Block a user