Files
gh-christianlouis-quizzical…/tests/test_api_extended.py
T
2026-03-12 01:16:36 +00:00

212 lines
8.4 KiB
Python

"""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)