Add comprehensive test suite to reach 31% code coverage
Co-authored-by: christianlouis <361235+christianlouis@users.noreply.github.com>
This commit is contained in:
@@ -0,0 +1,188 @@
|
||||
"""Final targeted tests to push coverage to 30%."""
|
||||
import pytest
|
||||
from musicround.models import db, User, Song
|
||||
|
||||
|
||||
def _login(app, client, username='final_user', email='final@example.com'):
|
||||
"""Helper: create and log in a user."""
|
||||
with app.app_context():
|
||||
existing = User.query.filter_by(username=username).first()
|
||||
if not existing:
|
||||
user = User(username=username, email=email)
|
||||
user.password = 'FinalPass123!'
|
||||
db.session.add(user)
|
||||
db.session.commit()
|
||||
client.post('/users/login', data={'username': username, 'password': 'FinalPass123!'})
|
||||
|
||||
|
||||
class TestFilterPlaylistsByKeywords:
|
||||
"""Tests for the pure filter_playlists_by_keywords function in import_routes."""
|
||||
|
||||
def test_empty_playlists(self, app):
|
||||
"""Test filtering an empty list returns empty."""
|
||||
with app.app_context():
|
||||
from musicround.routes.import_routes import filter_playlists_by_keywords
|
||||
result = filter_playlists_by_keywords([], ['hits'])
|
||||
assert result == []
|
||||
|
||||
def test_empty_keywords(self, app):
|
||||
"""Test filtering with no keywords returns no matches."""
|
||||
with app.app_context():
|
||||
from musicround.routes.import_routes import filter_playlists_by_keywords
|
||||
playlists = [{'name': 'Top Hits 2024'}, {'name': 'Chill Vibes'}]
|
||||
result = filter_playlists_by_keywords(playlists, [])
|
||||
assert result == []
|
||||
|
||||
def test_matching_keyword(self, app):
|
||||
"""Test filtering by a matching keyword."""
|
||||
with app.app_context():
|
||||
from musicround.routes.import_routes import filter_playlists_by_keywords
|
||||
playlists = [
|
||||
{'name': 'Top Hits 2024'},
|
||||
{'name': 'Chill Vibes'},
|
||||
{'name': 'Greatest Hits Ever'},
|
||||
]
|
||||
result = filter_playlists_by_keywords(playlists, ['hits'])
|
||||
assert len(result) == 2
|
||||
names = [p['name'] for p in result]
|
||||
assert 'Top Hits 2024' in names
|
||||
assert 'Greatest Hits Ever' in names
|
||||
|
||||
def test_case_insensitive(self, app):
|
||||
"""Test that filtering is case-insensitive."""
|
||||
with app.app_context():
|
||||
from musicround.routes.import_routes import filter_playlists_by_keywords
|
||||
playlists = [{'name': 'CLASSIC ROCK'}, {'name': 'Modern Pop'}]
|
||||
result = filter_playlists_by_keywords(playlists, ['classic'])
|
||||
assert len(result) == 1
|
||||
assert result[0]['name'] == 'CLASSIC ROCK'
|
||||
|
||||
def test_multiple_keywords(self, app):
|
||||
"""Test filtering by multiple keywords (OR logic)."""
|
||||
with app.app_context():
|
||||
from musicround.routes.import_routes import filter_playlists_by_keywords
|
||||
playlists = [
|
||||
{'name': 'Best of Jazz'},
|
||||
{'name': 'Chill Rock'},
|
||||
{'name': 'Sunday Morning'},
|
||||
]
|
||||
result = filter_playlists_by_keywords(playlists, ['jazz', 'rock'])
|
||||
assert len(result) == 2
|
||||
|
||||
def test_debug_info_updated(self, app):
|
||||
"""Test that debug_info is populated when provided."""
|
||||
with app.app_context():
|
||||
from musicround.routes.import_routes import filter_playlists_by_keywords
|
||||
playlists = [{'name': 'Best Jazz Playlist'}]
|
||||
debug_info = {'matched_keywords': {}}
|
||||
filter_playlists_by_keywords(playlists, ['jazz'], debug_info=debug_info)
|
||||
assert 'jazz' in debug_info['matched_keywords']
|
||||
assert debug_info['matched_keywords']['jazz'] == 1
|
||||
|
||||
|
||||
class TestSafeFilename:
|
||||
"""Tests for safe_filename in rounds.py."""
|
||||
|
||||
def test_simple_name(self, app):
|
||||
"""Test safe_filename with a simple string."""
|
||||
from musicround.routes.rounds import safe_filename
|
||||
result = safe_filename('My Round')
|
||||
assert result == 'My_Round'
|
||||
|
||||
def test_removes_special_chars(self, app):
|
||||
"""Test safe_filename removes special characters."""
|
||||
from musicround.routes.rounds import safe_filename
|
||||
result = safe_filename('Round: 2024!')
|
||||
assert '!' not in result
|
||||
assert ':' not in result
|
||||
|
||||
def test_preserves_alphanumeric(self, app):
|
||||
"""Test safe_filename preserves alphanumeric characters."""
|
||||
from musicround.routes.rounds import safe_filename
|
||||
result = safe_filename('Round123')
|
||||
assert 'Round123' in result
|
||||
|
||||
def test_strips_whitespace(self, app):
|
||||
"""Test safe_filename strips leading/trailing whitespace."""
|
||||
from musicround.routes.rounds import safe_filename
|
||||
result = safe_filename(' spaces ')
|
||||
assert not result.startswith('_')
|
||||
assert not result.endswith('_')
|
||||
|
||||
|
||||
class TestDeezerRoutes:
|
||||
"""Tests for Deezer route endpoints."""
|
||||
|
||||
def test_deezer_search_page_loads(self, app, client):
|
||||
"""Test that the Deezer search page is accessible."""
|
||||
response = client.get('/deezer-search')
|
||||
assert response.status_code == 200
|
||||
|
||||
def test_deezer_album_page_loads(self, app, client):
|
||||
"""Test that the Deezer album import page is accessible."""
|
||||
response = client.get('/import-deezer-album')
|
||||
assert response.status_code == 200
|
||||
|
||||
def test_deezer_track_page_loads(self, app, client):
|
||||
"""Test that the Deezer track import page is accessible."""
|
||||
response = client.get('/import-deezer-track')
|
||||
assert response.status_code == 200
|
||||
|
||||
def test_deezer_playlist_page_loads(self, app, client):
|
||||
"""Test that the Deezer playlist import page is accessible."""
|
||||
response = client.get('/import-deezer-playlist')
|
||||
assert response.status_code in (200, 302)
|
||||
|
||||
|
||||
class TestImportRoutesAccess:
|
||||
"""Tests for basic import route access."""
|
||||
|
||||
def test_official_playlists_requires_login(self, client):
|
||||
"""Test that import official playlists requires authentication."""
|
||||
response = client.get('/import/official-playlists')
|
||||
assert response.status_code in (200, 302)
|
||||
|
||||
def test_direct_official_playlists_requires_login(self, client):
|
||||
"""Test that direct official playlists requires authentication."""
|
||||
response = client.get('/import/direct-official-playlists')
|
||||
assert response.status_code in (200, 302)
|
||||
|
||||
def test_import_songs_page_accessible(self, app, client):
|
||||
"""Test that import official playlists requires Spotify or redirects."""
|
||||
_login(app, client)
|
||||
response = client.get('/import/official-playlists')
|
||||
# Without Spotify token, it may redirect elsewhere; still a valid response
|
||||
assert response.status_code in (200, 302)
|
||||
|
||||
def test_queue_status_requires_login(self, client):
|
||||
"""Test that queue status requires authentication."""
|
||||
response = client.get('/import/queue-status')
|
||||
assert response.status_code == 302
|
||||
|
||||
|
||||
class TestCoreViewSongs:
|
||||
"""Tests for view-songs with actual data."""
|
||||
|
||||
def test_view_songs_with_songs_in_db(self, app, client):
|
||||
"""Test view-songs returns songs that are in the database."""
|
||||
_login(app, client)
|
||||
with app.app_context():
|
||||
song = Song(title='Visible Song', artist='Visible Artist', genre='Rock', year=2020)
|
||||
db.session.add(song)
|
||||
db.session.commit()
|
||||
|
||||
response = client.get('/view-songs')
|
||||
assert response.status_code == 200
|
||||
assert b'Visible Song' in response.data
|
||||
|
||||
def test_view_songs_shows_artists(self, app, client):
|
||||
"""Test view-songs shows artist names."""
|
||||
_login(app, client)
|
||||
with app.app_context():
|
||||
song = Song(title='Artist Test', artist='Known Artist XYZ', genre='Pop')
|
||||
db.session.add(song)
|
||||
db.session.commit()
|
||||
|
||||
response = client.get('/view-songs')
|
||||
assert response.status_code == 200
|
||||
assert b'Known Artist XYZ' in response.data
|
||||
Reference in New Issue
Block a user