diff --git a/src/artist_tracker/tracker.py b/src/artist_tracker/tracker.py index 3c31a8f..fce5f1c 100644 --- a/src/artist_tracker/tracker.py +++ b/src/artist_tracker/tracker.py @@ -978,8 +978,24 @@ def cmd_track(args, tracker: SpotifyReleaseTracker): # Check for redirect URI (required for OAuth) if not os.getenv('SPOTIPY_REDIRECT_URI'): # If running in a headless env without redirect URI, we can't easily do OAuth flow - # But let's try anyway, maybe the user has a cached token - pass + # Check if we have a valid token (or can refresh one) + token_valid = False + try: + auth_manager = tracker.sp.auth_manager + token_info = auth_manager.get_cached_token() + if auth_manager.validate_token(token_info): + token_valid = True + except Exception: + token_valid = False + + if not token_valid: + logger.warning("SPOTIPY_REDIRECT_URI not set and no valid cached session found.") + logger.warning("Authentication will attempt to use default: http://localhost:8888/callback") + + print("⚠️ Warning: SPOTIPY_REDIRECT_URI is not set and no valid cached session found.", file=sys.stderr) + print(" Authentication will default to 'http://localhost:8888/callback'.", file=sys.stderr) + print(" If your Spotify App is configured differently, please set SPOTIPY_REDIRECT_URI.", file=sys.stderr) + print(" You will need to copy the URL below to a browser to authorize.", file=sys.stderr) results = tracker.track_liked_songs(args.max_per_artist) diff --git a/tests/test_cli_oauth.py b/tests/test_cli_oauth.py new file mode 100644 index 0000000..fa566ec --- /dev/null +++ b/tests/test_cli_oauth.py @@ -0,0 +1,92 @@ +#!/usr/bin/env python3 +""" +Unit tests for CLI OAuth handling. +""" + +import unittest +from unittest.mock import Mock, patch +import os +import sys +from io import StringIO +from artist_tracker.tracker import cmd_track + +class TestCmdTrackOAuth(unittest.TestCase): + def setUp(self): + self.tracker = Mock() + self.args = Mock() + self.args.liked = True + self.args.artist = None + self.args.playlists = None + self.args.max_per_artist = 10 + self.args.format = 'tsv' + + # Mock auth_manager on tracker.sp + self.tracker.sp = Mock() + self.auth_manager = Mock() + self.tracker.sp.auth_manager = self.auth_manager + + # Mock track_liked_songs to return valid result structure + self.tracker.track_liked_songs.return_value = { + 'releases': [], + 'total_releases': 0, + 'artists_processed': 0 + } + + @patch('artist_tracker.tracker.os.getenv') + def test_warnings_on_missing_redirect_uri_and_no_token(self, mock_getenv): + # Simulate missing SPOTIPY_REDIRECT_URI + def getenv_side_effect(key, default=None): + if key == 'SPOTIPY_REDIRECT_URI': + return None + return default + mock_getenv.side_effect = getenv_side_effect + + # Mock auth_manager to return invalid token + self.auth_manager.get_cached_token.return_value = None + self.auth_manager.validate_token.return_value = None + + # Capture stderr + captured_stderr = StringIO() + sys.stderr = captured_stderr + + try: + cmd_track(self.args, self.tracker) + finally: + sys.stderr = sys.__stderr__ + + output = captured_stderr.getvalue() + self.assertIn("Warning: SPOTIPY_REDIRECT_URI is not set", output) + self.assertIn("default to 'http://localhost:8888/callback'", output) + + # It should still try to track + self.tracker.track_liked_songs.assert_called_once() + + @patch('artist_tracker.tracker.os.getenv') + def test_no_warning_if_token_valid(self, mock_getenv): + # Simulate missing SPOTIPY_REDIRECT_URI + def getenv_side_effect(key, default=None): + if key == 'SPOTIPY_REDIRECT_URI': + return None + return default + mock_getenv.side_effect = getenv_side_effect + + # Mock auth_manager to return VALID token + self.auth_manager.get_cached_token.return_value = {'access_token': 'valid'} + self.auth_manager.validate_token.return_value = {'access_token': 'valid'} + + # Capture stderr + captured_stderr = StringIO() + sys.stderr = captured_stderr + + try: + cmd_track(self.args, self.tracker) + finally: + sys.stderr = sys.__stderr__ + + output = captured_stderr.getvalue() + self.assertNotIn("Warning: SPOTIPY_REDIRECT_URI is not set", output) + + self.tracker.track_liked_songs.assert_called_once() + +if __name__ == '__main__': + unittest.main()