maneshtrader/web/src/tests/test_app_settings.py

145 lines
5.7 KiB
Python

from __future__ import annotations
import json
from pathlib import Path
import app
def test_save_and_load_settings_are_isolated_by_profile(tmp_path, monkeypatch) -> None:
settings_path = tmp_path / "settings.json"
legacy_path = tmp_path / "legacy.json"
monkeypatch.setattr(app, "SETTINGS_PATH", settings_path)
monkeypatch.setattr(app, "LEGACY_SETTINGS_PATH", legacy_path)
app.save_web_settings({"symbol": "AAPL", "period": "1mo"}, profile_id="alice")
app.save_web_settings({"symbol": "MSFT", "period": "3mo"}, profile_id="bob")
alice = app.load_web_settings(profile_id="alice")
bob = app.load_web_settings(profile_id="bob")
unknown = app.load_web_settings(profile_id="carol")
assert alice["symbol"] == "AAPL"
assert alice["period"] == "1mo"
assert bob["symbol"] == "MSFT"
assert bob["period"] == "3mo"
assert unknown["symbol"] == "AAPL" # fallback default
def test_load_settings_migrates_legacy_flat_payload_to_default_profile(tmp_path, monkeypatch) -> None:
settings_path = tmp_path / "settings.json"
legacy_path = tmp_path / "legacy.json"
monkeypatch.setattr(app, "SETTINGS_PATH", settings_path)
monkeypatch.setattr(app, "LEGACY_SETTINGS_PATH", legacy_path)
legacy_payload = {"symbol": "TSLA", "interval": "1h", "max_bars": 9999}
legacy_path.write_text(json.dumps(legacy_payload), encoding="utf-8")
loaded = app.load_web_settings(profile_id="default")
assert loaded["symbol"] == "TSLA"
assert loaded["interval"] == "1h"
assert loaded["max_bars"] == 5000 # normalized max cap
app.save_web_settings({"symbol": "TSLA"}, profile_id="default")
stored = json.loads(settings_path.read_text(encoding="utf-8"))
assert "profiles" in stored
assert "default" in stored["profiles"]
assert "settings" in stored["profiles"]["default"]
assert "audit" in stored["profiles"]["default"]
def test_list_web_profiles_includes_saved_profiles(tmp_path, monkeypatch) -> None:
settings_path = tmp_path / "settings.json"
legacy_path = tmp_path / "legacy.json"
monkeypatch.setattr(app, "SETTINGS_PATH", settings_path)
monkeypatch.setattr(app, "LEGACY_SETTINGS_PATH", legacy_path)
app.save_web_settings({"symbol": "AAPL"}, profile_id="zoe")
app.save_web_settings({"symbol": "AAPL"}, profile_id="alice")
profile_ids = app.list_web_profiles()
assert "default" in profile_ids
assert "alice" in profile_ids
assert "zoe" in profile_ids
def test_resolve_login_profile_prefers_session_then_query() -> None:
assert app.resolve_login_profile("alice", "bob") == "alice"
assert app.resolve_login_profile("", "bob") == "bob"
assert app.resolve_login_profile(None, None) is None
def test_profile_exists_normalizes_input() -> None:
profiles = {"default", "alice"}
assert app.profile_exists("alice", profiles) is True
assert app.profile_exists(" ", profiles) is True # normalizes to default
assert app.profile_exists("bob", profiles) is False
def test_profile_exists_is_case_insensitive() -> None:
profiles = {"default", "Alice"}
assert app.profile_exists("alice", profiles) is True
assert app.find_existing_profile_id("ALICE", profiles) == "Alice"
def test_is_profile_session_expired_after_timeout() -> None:
now_epoch = 2000.0
assert app.is_profile_session_expired(1000.0, now_epoch, timeout_sec=900) is True
assert app.is_profile_session_expired(1500.0, now_epoch, timeout_sec=900) is False
assert app.is_profile_session_expired(None, now_epoch, timeout_sec=900) is False
def test_create_profile_with_pin_requires_pin_for_verification(tmp_path, monkeypatch) -> None:
settings_path = tmp_path / "settings.json"
legacy_path = tmp_path / "legacy.json"
monkeypatch.setattr(app, "SETTINGS_PATH", settings_path)
monkeypatch.setattr(app, "LEGACY_SETTINGS_PATH", legacy_path)
app.create_profile("alice", pin="1234", now_epoch=1700000000)
assert app.profile_requires_pin("alice") is True
assert app.verify_profile_pin("alice", "1234") is True
assert app.verify_profile_pin("alice", "9999") is False
def test_audit_stamps_update_on_save_and_login(tmp_path, monkeypatch) -> None:
settings_path = tmp_path / "settings.json"
legacy_path = tmp_path / "legacy.json"
monkeypatch.setattr(app, "SETTINGS_PATH", settings_path)
monkeypatch.setattr(app, "LEGACY_SETTINGS_PATH", legacy_path)
app.create_profile("alice", now_epoch=1700000000)
app.save_web_settings({"symbol": "MSFT"}, profile_id="alice")
app.mark_profile_login("alice", now_epoch=1700000010)
audit = app.get_profile_audit("alice")
assert audit["created_at"] == 1700000000
assert audit["last_login_at"] == 1700000010
assert audit["last_symbol"] == "MSFT"
def test_load_settings_migrates_old_profiles_map_structure(tmp_path, monkeypatch) -> None:
settings_path = tmp_path / "settings.json"
legacy_path = tmp_path / "legacy.json"
monkeypatch.setattr(app, "SETTINGS_PATH", settings_path)
monkeypatch.setattr(app, "LEGACY_SETTINGS_PATH", legacy_path)
old_payload = {
"last_profile": "alice",
"profiles": {
"alice": {"symbol": "TSLA", "interval": "1d"},
"bob": {"symbol": "MSFT", "interval": "1h"},
},
}
settings_path.write_text(json.dumps(old_payload), encoding="utf-8")
alice_settings = app.load_web_settings("alice")
bob_settings = app.load_web_settings("bob")
assert alice_settings["symbol"] == "TSLA"
assert bob_settings["symbol"] == "MSFT"
app.save_web_settings({"symbol": "AAPL"}, profile_id="alice")
stored = json.loads(settings_path.read_text(encoding="utf-8"))
assert isinstance(stored["profiles"]["alice"], dict)
assert "settings" in stored["profiles"]["alice"]