import io import time from unittest import mock import pytest import main SAMPLE_TIMEZONE_CSV = """Etc/UTC,ZZ,UTC,0,0,0 America/New_York,US,EST,0,-18000,0 Europe/London,GB,BST,0,0,1 """ SAMPLE_COUNTRY_CSV = """ZZ,Unknown US,United States GB,United Kingdom """ def test_load_timezones_and_countries(monkeypatch): tzs = main.load_timezones() countries = main.load_countries() assert any(t['zone_name'] == 'America/New_York' for t in tzs) assert any(c['country_code'] == 'US' for c in countries) def test_get_tz_and_country_info(): timezones = [{'zone_name': 'A/B', 'country_code': 'US'}] countries = [{'country_code': 'US', 'country_name': 'United States'}] assert main.get_tz_info('A/B', timezones)['zone_name'] == 'A/B' assert main.get_country_info('US', countries)[ 'country_name'] == 'United States' assert main.get_tz_info('X/Y', timezones) is None assert main.get_country_info('XX', countries) is None def test_create_embed_all_types(monkeypatch): # Prevent create_embed from trying to read actual CSV files by patching loaders monkeypatch.setattr(main, 'load_timezones', lambda: [ {'zone_name': 'Etc/UTC', 'country_code': 'ZZ'}]) monkeypatch.setattr(main, 'load_countries', lambda: [ {'country_code': 'ZZ', 'country_name': 'Nowhere'}]) # reminder emb = main.create_embed('reminder') assert emb['title'] == 'Reminder' assert '5 minute' in emb['description'] assert emb['color'] == 0xe67e22 # reminder_halftime emb = main.create_embed('reminder_halftime') assert emb['title'] == 'Reminder halftime' assert 'Half-time in 5 minutes' in emb['description'] # halftime (should include image) monkeypatch.setattr(main, 'where_is_it_420', lambda tzs, cs, **kwargs: []) emb = main.create_embed('halftime') assert emb['title'] == 'Halftime' assert emb['image'] is not None # 420 (should include image and appended tz info string when list empty) monkeypatch.setattr(main, 'where_is_it_420', lambda tzs, cs, **kwargs: []) emb = main.create_embed('420') assert emb['title'] == '420' assert emb['image'] is not None assert "not 4:20" in emb['description'] or "It's 4:20" in emb['description'] # unknown emb = main.create_embed('nope') assert emb['description'] == 'Unknown notification type' def test_where_is_it_420(monkeypatch): # Limit timezones to a predictable set monkeypatch.setattr(main.pytz, 'all_timezones', ['Etc/UTC']) tzs = [{'zone_name': 'Etc/UTC', 'country_code': 'ZZ'}] countries = [{'country_code': 'ZZ', 'country_name': 'Nowhere'}] class FakeDatetime: @staticmethod def now(tz): class R: hour = 4 return R() monkeypatch.setattr(main, 'datetime', FakeDatetime) monkeypatch.setattr(main, 'get_tz_info', lambda name, t: tzs[0] if name == 'Etc/UTC' else None) monkeypatch.setattr(main, 'get_country_info', lambda code, c: countries[0] if code == 'ZZ' else None) res = main.where_is_it_420(tzs, countries) assert res == ['Nowhere'] def test_main_exits_quickly(monkeypatch): # Patch send_notification so it doesn't perform network monkeypatch.setattr(main, 'send_notification', lambda x: None) # Don't start dashboard during this test monkeypatch.setattr(main, 'start_dashboard', lambda: None) # Make schedule.run_pending raise KeyboardInterrupt to exit loop monkeypatch.setattr(main.schedule, 'run_pending', lambda: ( _ for _ in ()).throw(KeyboardInterrupt())) # Patch time.sleep to no-op monkeypatch.setattr(main.time, 'sleep', lambda s: None) # Ensure WEBHOOK_URL present to avoid early return monkeypatch.setenv('DISCORD_WEBHOOK_URL', 'http://example.com/webhook') main.WEBHOOK_URL = 'http://example.com/webhook' # Should exit quickly due to KeyboardInterrupt from run_pending main.main() def test_get_next_scheduled_event(): # 10:14 -> next is 10:15 reminder now = main.datetime(2025, 1, 1, 10, 14, 30) nxt = main.get_next_scheduled_event(now) assert nxt["type"] == "reminder" assert nxt["at"].hour == 10 and nxt["at"].minute == 15 # 10:50:01 -> next is 11:15 reminder now = main.datetime(2025, 1, 1, 10, 50, 1) nxt = main.get_next_scheduled_event(now) assert nxt["type"] == "reminder" assert nxt["at"].hour == 11 and nxt["at"].minute == 15 def test_split_tz_name(): assert main.split_tz_name("America/New_York") == ("America", "New_York") assert main.split_tz_name("America/Argentina/Buenos_Aires") == ( "America", "Argentina/Buenos_Aires", ) assert main.split_tz_name("UTC") == ("UTC", "")