fix(settings): reject fractional refresh intervals

This commit is contained in:
Bu5hm4nn
2026-03-26 14:05:49 +01:00
parent 2759d9a36f
commit e860c40567
2 changed files with 22 additions and 2 deletions

View File

@@ -70,6 +70,15 @@ def settings_page(workspace_id: str) -> None:
return str(int(parsed))
return str(parsed)
def as_positive_int(value: object) -> int | None:
try:
parsed = float(value)
except (TypeError, ValueError):
return None
if parsed < 1 or not parsed.is_integer():
return None
return int(parsed)
def last_saved_status_text(config: PortfolioConfig) -> str:
return save_status_text(config).replace("Saved:", "Last saved:", 1)
@@ -78,6 +87,10 @@ def settings_page(workspace_id: str) -> None:
if parsed_loan_amount is None:
raise ValueError("Loan amount must be zero or greater")
parsed_refresh_interval = as_positive_int(refresh_interval.value)
if parsed_refresh_interval is None:
raise ValueError("Refresh interval must be a whole number of seconds")
return PortfolioConfig(
gold_value=as_positive_float(gold_value.value),
entry_price=as_positive_float(entry_price.value),
@@ -89,7 +102,7 @@ def settings_page(workspace_id: str) -> None:
ltv_warning=float(ltv_warning.value),
primary_source=str(primary_source.value),
fallback_source=str(fallback_source.value),
refresh_interval=int(refresh_interval.value),
refresh_interval=parsed_refresh_interval,
volatility_spike=float(vol_alert.value),
spot_drawdown=float(price_alert.value),
email_alerts=bool(email_alerts.value),

View File

@@ -20,8 +20,15 @@ def test_settings_reject_invalid_loan_amount_without_silent_zero_fallback() -> N
expect(page.locator("text=Settings").first).to_be_visible(timeout=15000)
expect(page.locator("text=Last saved:").first).to_be_visible(timeout=15000)
page.get_by_label("Refresh interval (seconds)").fill("7")
refresh_interval_input = page.get_by_label("Refresh interval (seconds)")
refresh_interval_input.fill("7")
expect(page.locator("text=Unsaved changes — Last saved:").first).to_be_visible(timeout=15000)
refresh_interval_input.fill("5.5")
refresh_interval_input.press("Tab")
expect(page.locator("text=INVALID").first).to_be_visible(timeout=15000)
expect(page.locator("text=Refresh interval must be a whole number of seconds").first).to_be_visible(
timeout=15000
)
page.reload(wait_until="domcontentloaded", timeout=30000)
expect(page.get_by_label("Refresh interval (seconds)")).to_have_value("5")
expect(page.locator("text=Last saved:").first).to_be_visible(timeout=15000)