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,211 @@
|
||||
"""Extended API endpoint tests."""
|
||||
import pytest
|
||||
import json
|
||||
from musicround.models import db, User, Song, Tag
|
||||
|
||||
|
||||
def _create_user_and_login(app, client, username='apiuser', email='api@example.com'):
|
||||
"""Helper: create a user and log in."""
|
||||
with app.app_context():
|
||||
existing = User.query.filter_by(username=username).first()
|
||||
if not existing:
|
||||
user = User(username=username, email=email)
|
||||
user.password = 'ApiPass123!'
|
||||
db.session.add(user)
|
||||
db.session.commit()
|
||||
client.post('/users/login', data={'username': username, 'password': 'ApiPass123!'})
|
||||
|
||||
|
||||
def _create_song(app, title='Test Song', artist='Test Artist', genre='Rock'):
|
||||
"""Helper: create and persist a Song, returning its id."""
|
||||
with app.app_context():
|
||||
song = Song(title=title, artist=artist, genre=genre)
|
||||
db.session.add(song)
|
||||
db.session.commit()
|
||||
return song.id
|
||||
|
||||
|
||||
class TestTagsApi:
|
||||
"""Tests for /api/tags endpoints."""
|
||||
|
||||
def test_get_tags_empty(self, app, client):
|
||||
"""Test GET /api/tags returns empty tags list when no tags exist."""
|
||||
response = client.get('/api/tags')
|
||||
assert response.status_code == 200
|
||||
data = response.get_json()
|
||||
assert 'tags' in data
|
||||
assert data['tags'] == []
|
||||
|
||||
def test_create_tag(self, app, client):
|
||||
"""Test POST /api/tags creates a new tag."""
|
||||
_create_user_and_login(app, client, 'tagcreate', 'tagcreate@example.com')
|
||||
response = client.post(
|
||||
'/api/tags',
|
||||
data=json.dumps({'name': 'NewTag'}),
|
||||
content_type='application/json',
|
||||
)
|
||||
assert response.status_code == 201
|
||||
data = response.get_json()
|
||||
assert 'tag' in data
|
||||
assert data['tag']['name'] == 'NewTag'
|
||||
|
||||
def test_create_tag_duplicate(self, app, client):
|
||||
"""Test POST /api/tags returns existing tag if duplicate name."""
|
||||
_create_user_and_login(app, client, 'tagdup', 'tagdup@example.com')
|
||||
# Create first
|
||||
client.post('/api/tags', data=json.dumps({'name': 'DupTag'}),
|
||||
content_type='application/json')
|
||||
# Create second with same name
|
||||
response = client.post('/api/tags', data=json.dumps({'name': 'DupTag'}),
|
||||
content_type='application/json')
|
||||
# Should return 200 (existing tag, not 201)
|
||||
assert response.status_code == 200
|
||||
data = response.get_json()
|
||||
assert 'tag' in data
|
||||
|
||||
def test_create_tag_missing_name(self, app, client):
|
||||
"""Test POST /api/tags returns 400 when name is missing."""
|
||||
_create_user_and_login(app, client, 'tagnoname', 'tagnoname@example.com')
|
||||
response = client.post('/api/tags', data=json.dumps({}),
|
||||
content_type='application/json')
|
||||
assert response.status_code == 400
|
||||
|
||||
def test_get_tags_after_creation(self, app, client):
|
||||
"""Test GET /api/tags returns created tags."""
|
||||
_create_user_and_login(app, client, 'taglist', 'taglist@example.com')
|
||||
# Create a tag
|
||||
with app.app_context():
|
||||
tag = Tag(name='ListableTag')
|
||||
db.session.add(tag)
|
||||
db.session.commit()
|
||||
|
||||
response = client.get('/api/tags')
|
||||
data = response.get_json()
|
||||
tag_names = [t['name'] for t in data['tags']]
|
||||
assert 'ListableTag' in tag_names
|
||||
|
||||
|
||||
class TestSongTagsApi:
|
||||
"""Tests for /api/songs/<id>/tags endpoints."""
|
||||
|
||||
def test_get_song_tags_empty(self, app, client):
|
||||
"""Test GET /api/songs/<id>/tags returns empty list for song with no tags."""
|
||||
song_id = _create_song(app, 'TagSong1', 'Artist')
|
||||
response = client.get(f'/api/songs/{song_id}/tags')
|
||||
assert response.status_code == 200
|
||||
data = response.get_json()
|
||||
assert 'tags' in data
|
||||
assert data['tags'] == []
|
||||
|
||||
def test_add_tag_to_song(self, app, client):
|
||||
"""Test POST /api/songs/<id>/tags adds a tag to a song."""
|
||||
_create_user_and_login(app, client, 'addtaguser', 'addtag@example.com')
|
||||
song_id = _create_song(app, 'TagSong2', 'Artist2')
|
||||
|
||||
# Create a tag first
|
||||
with app.app_context():
|
||||
tag = Tag(name='AddableTag')
|
||||
db.session.add(tag)
|
||||
db.session.commit()
|
||||
tag_id = tag.id
|
||||
|
||||
response = client.post(
|
||||
f'/api/songs/{song_id}/tags',
|
||||
data=json.dumps({'tag_id': tag_id}),
|
||||
content_type='application/json',
|
||||
)
|
||||
assert response.status_code in (200, 201)
|
||||
|
||||
def test_add_tag_by_name(self, app, client):
|
||||
"""Test POST /api/songs/<id>/tags creates and adds tag by name."""
|
||||
_create_user_and_login(app, client, 'tagbynameuser', 'tagbyname@example.com')
|
||||
song_id = _create_song(app, 'TagSong3', 'Artist3')
|
||||
response = client.post(
|
||||
f'/api/songs/{song_id}/tags',
|
||||
data=json.dumps({'tag_name': 'BrandNewTag'}),
|
||||
content_type='application/json',
|
||||
)
|
||||
assert response.status_code in (200, 201)
|
||||
|
||||
def test_get_songs_by_tag(self, app, client):
|
||||
"""Test GET /api/tags/<tag_id> returns songs for that tag."""
|
||||
with app.app_context():
|
||||
tag = Tag(name='SongsByTag')
|
||||
song = Song(title='Tagged Song', artist='Artist', genre='Pop')
|
||||
db.session.add_all([tag, song])
|
||||
db.session.commit()
|
||||
song.tags.append(tag)
|
||||
db.session.commit()
|
||||
tag_id = tag.id
|
||||
|
||||
response = client.get(f'/api/tags/{tag_id}')
|
||||
assert response.status_code == 200
|
||||
|
||||
def test_remove_tag_from_song(self, app, client):
|
||||
"""Test DELETE /api/songs/<id>/tags/<tag_id> removes a tag."""
|
||||
_create_user_and_login(app, client, 'removetaguser', 'removetag@example.com')
|
||||
with app.app_context():
|
||||
tag = Tag(name='RemovableTag')
|
||||
song = Song(title='RemoveTagSong', artist='A', genre='Pop')
|
||||
db.session.add_all([tag, song])
|
||||
db.session.commit()
|
||||
song.tags.append(tag)
|
||||
db.session.commit()
|
||||
song_id = song.id
|
||||
tag_id = tag.id
|
||||
|
||||
response = client.delete(f'/api/songs/{song_id}/tags/{tag_id}')
|
||||
assert response.status_code in (200, 204)
|
||||
|
||||
|
||||
class TestSongSearchApi:
|
||||
"""Tests for /api/songs/search endpoint."""
|
||||
|
||||
def test_search_songs_authenticated(self, app, client):
|
||||
"""Test song search returns results when authenticated."""
|
||||
_create_user_and_login(app, client, 'searchapiuser', 'searchapi@example.com')
|
||||
# Create songs to search
|
||||
with app.app_context():
|
||||
songs = [
|
||||
Song(title='Searchable Rock Song', artist='Rock Band', genre='Rock'),
|
||||
Song(title='Another Pop Song', artist='Pop Star', genre='Pop'),
|
||||
]
|
||||
db.session.add_all(songs)
|
||||
db.session.commit()
|
||||
|
||||
response = client.get('/api/songs/search?q=Rock')
|
||||
assert response.status_code == 200
|
||||
data = response.get_json()
|
||||
assert isinstance(data, list)
|
||||
assert any('Rock' in song.get('title', '') or 'Rock' in song.get('artist', '')
|
||||
for song in data)
|
||||
|
||||
def test_search_songs_short_query(self, app, client):
|
||||
"""Test song search returns empty for too-short query."""
|
||||
_create_user_and_login(app, client, 'shortquery', 'shortq@example.com')
|
||||
response = client.get('/api/songs/search?q=r')
|
||||
assert response.status_code == 200
|
||||
data = response.get_json()
|
||||
assert data == []
|
||||
|
||||
def test_search_songs_empty_query(self, app, client):
|
||||
"""Test song search returns empty for no query."""
|
||||
_create_user_and_login(app, client, 'emptyquery', 'emptyq@example.com')
|
||||
response = client.get('/api/songs/search?q=')
|
||||
assert response.status_code == 200
|
||||
data = response.get_json()
|
||||
assert data == []
|
||||
|
||||
def test_search_songs_by_artist(self, app, client):
|
||||
"""Test song search works for artist name."""
|
||||
_create_user_and_login(app, client, 'artistsearch', 'artistsearch@example.com')
|
||||
with app.app_context():
|
||||
song = Song(title='Unique Title XYZ', artist='SpecificArtistABC', genre='Jazz')
|
||||
db.session.add(song)
|
||||
db.session.commit()
|
||||
|
||||
response = client.get('/api/songs/search?q=SpecificArtistABC')
|
||||
assert response.status_code == 200
|
||||
data = response.get_json()
|
||||
assert len(data) >= 1
|
||||
assert any(s['artist'] == 'SpecificArtistABC' for s in data)
|
||||
Reference in New Issue
Block a user