1"""tests for teal.fm scrobbling integration.""" 2 3from backend._internal.atproto.teal import ( 4 build_teal_play_record, 5 build_teal_status_record, 6) 7from backend.config import TealSettings, settings 8 9 10class TestBuildTealPlayRecord: 11 """tests for build_teal_play_record.""" 12 13 def test_builds_minimal_record(self): 14 """should build record with required fields only.""" 15 record = build_teal_play_record( 16 track_name="Test Track", 17 artist_name="Test Artist", 18 ) 19 20 assert record["$type"] == settings.teal.play_collection 21 assert record["trackName"] == "Test Track" 22 assert record["artists"] == [{"artistName": "Test Artist"}] 23 assert record["musicServiceBaseDomain"] == "plyr.fm" 24 assert record["submissionClientAgent"] == "plyr.fm/1.0" 25 assert "playedTime" in record 26 27 def test_includes_optional_fields(self): 28 """should include optional fields when provided.""" 29 record = build_teal_play_record( 30 track_name="Test Track", 31 artist_name="Test Artist", 32 duration=180, 33 album_name="Test Album", 34 origin_url="https://plyr.fm/track/123", 35 ) 36 37 assert record["duration"] == 180 38 assert record["releaseName"] == "Test Album" 39 assert record["originUrl"] == "https://plyr.fm/track/123" 40 41 42class TestBuildTealStatusRecord: 43 """tests for build_teal_status_record.""" 44 45 def test_builds_status_record(self): 46 """should build status record with item.""" 47 record = build_teal_status_record( 48 track_name="Now Playing", 49 artist_name="Cool Artist", 50 ) 51 52 assert record["$type"] == settings.teal.status_collection 53 assert "time" in record 54 assert "expiry" in record 55 assert "item" in record 56 57 item = record["item"] 58 assert item["trackName"] == "Now Playing" 59 assert item["artists"] == [{"artistName": "Cool Artist"}] 60 61 def test_expiry_is_after_time(self): 62 """should set expiry after time.""" 63 record = build_teal_status_record( 64 track_name="Test", 65 artist_name="Test", 66 ) 67 68 # expiry should be 10 minutes after time 69 assert record["expiry"] > record["time"] 70 71 72class TestTealSettings: 73 """tests for teal.fm settings configuration.""" 74 75 def test_default_play_collection(self): 76 """should have correct default teal play collection.""" 77 assert settings.teal.play_collection == "fm.teal.alpha.feed.play" 78 79 def test_default_status_collection(self): 80 """should have correct default teal status collection.""" 81 assert settings.teal.status_collection == "fm.teal.alpha.actor.status" 82 83 def test_default_enabled(self): 84 """should be enabled by default.""" 85 assert settings.teal.enabled is True 86 87 def test_resolved_scope_with_teal(self): 88 """should include teal scopes in extended scope.""" 89 scope = settings.atproto.resolved_scope_with_teal( 90 settings.teal.play_collection, settings.teal.status_collection 91 ) 92 93 assert "fm.teal.alpha.feed.play" in scope 94 assert "fm.teal.alpha.actor.status" in scope 95 # should also include base scopes 96 assert settings.atproto.track_collection in scope 97 98 def test_env_override_play_collection(self, monkeypatch): 99 """should allow overriding play collection via environment variable. 100 101 this proves we can adapt when teal.fm changes namespaces 102 (e.g., from alpha to stable: fm.teal.feed.play). 103 """ 104 monkeypatch.setenv("TEAL_PLAY_COLLECTION", "fm.teal.feed.play") 105 106 # create fresh settings to pick up env var 107 teal = TealSettings() 108 assert teal.play_collection == "fm.teal.feed.play" 109 110 def test_env_override_status_collection(self, monkeypatch): 111 """should allow overriding status collection via environment variable.""" 112 monkeypatch.setenv("TEAL_STATUS_COLLECTION", "fm.teal.actor.status") 113 114 teal = TealSettings() 115 assert teal.status_collection == "fm.teal.actor.status" 116 117 def test_env_override_enabled(self, monkeypatch): 118 """should allow disabling teal integration via environment variable.""" 119 monkeypatch.setenv("TEAL_ENABLED", "false") 120 121 teal = TealSettings() 122 assert teal.enabled is False 123 124 def test_scope_uses_configured_collections(self, monkeypatch): 125 """should use configured collections in OAuth scope. 126 127 when teal.fm graduates from alpha, we can update via env vars 128 without code changes. 129 """ 130 monkeypatch.setenv("TEAL_PLAY_COLLECTION", "fm.teal.feed.play") 131 monkeypatch.setenv("TEAL_STATUS_COLLECTION", "fm.teal.actor.status") 132 133 teal = TealSettings() 134 scope = settings.atproto.resolved_scope_with_teal( 135 teal.play_collection, teal.status_collection 136 ) 137 138 assert "fm.teal.feed.play" in scope 139 assert "fm.teal.actor.status" in scope 140 # alpha namespaces should NOT be in scope when overridden 141 assert "fm.teal.alpha.feed.play" not in scope 142 assert "fm.teal.alpha.actor.status" not in scope