import pytest from finbot.config import Settings from finbot.limits import WINDOW_SECONDS, RateLimiter def settings(**overrides): base = dict( telegram_bot_token="", telegram_webhook_secret="", allowed_telegram_user_ids="", hf_token="", hf_dataset_repo="", open_access=False, ) base.update(overrides) return Settings(**base) class TestAuthorisation: def test_empty_allowlist_authorises_nobody(self): # The safe default: a forgotten allowlist must not mean "open to all". assert settings().is_authorised(42) is False def test_allowlist_match(self): s = settings(allowed_telegram_user_ids="42, 99") assert s.is_authorised(42) assert s.is_authorised(99) assert not s.is_authorised(7) def test_open_access_authorises_anyone(self): s = settings(open_access=True) assert s.is_authorised(1) assert s.is_authorised(123456789) def test_open_access_ignores_the_allowlist(self): s = settings(open_access=True, allowed_telegram_user_ids="42") assert s.is_authorised(7) def test_malformed_ids_are_dropped_not_fatal(self): s = settings(allowed_telegram_user_ids="42,,abc, 99 ,") assert s.allowed_user_ids == frozenset({42, 99}) def test_open_access_removes_the_allowlist_warning(self): assert any("ALLOWED" in g for g in settings().missing_required()) assert not any( "ALLOWED" in g for g in settings(open_access=True).missing_required() ) class TestRateLimiter: def test_allows_up_to_the_cap(self): limiter = RateLimiter(3) assert [limiter.check(1) for _ in range(3)] == [True, True, True] def test_blocks_past_the_cap(self): limiter = RateLimiter(2) limiter.check(1) limiter.check(1) assert limiter.check(1) is False def test_users_are_independent(self): limiter = RateLimiter(1) assert limiter.check(1) is True assert limiter.check(2) is True assert limiter.check(1) is False def test_zero_disables_the_cap(self): limiter = RateLimiter(0) assert not limiter.enabled assert all(limiter.check(1) for _ in range(500)) def test_negative_disables_the_cap(self): assert not RateLimiter(-1).enabled def test_window_expires(self): limiter = RateLimiter(1) assert limiter.check(1, now=1000.0) is True assert limiter.check(1, now=1000.0) is False # Just past the 24h window, the old hit no longer counts. assert limiter.check(1, now=1000.0 + WINDOW_SECONDS + 1) is True def test_remaining(self): limiter = RateLimiter(3) assert limiter.remaining(1) == 3 limiter.check(1) assert limiter.remaining(1) == 2 def test_remaining_is_unlimited_when_disabled(self): assert RateLimiter(0).remaining(1) == -1 @pytest.mark.parametrize("cap", [1, 5, 20]) def test_never_exceeds_cap(self, cap): limiter = RateLimiter(cap) granted = sum(1 for _ in range(cap * 3) if limiter.check(1, now=500.0)) assert granted == cap