"""Post-sanitization verification smoke test. Boots the app on a fresh DB, registers a player/playlist/content, and exercises every API endpoint plus key UI routes. Fails loudly on any 5xx. """ import os import tempfile tmpdir = tempfile.mkdtemp() db_path = os.path.join(tmpdir, 'smoke.db') os.environ['DATABASE_URL'] = f'sqlite:///{db_path}' from app.app import create_app from app.extensions import db, bcrypt from app.models import Player, Playlist, Content, User, PlayerUser, HTTPSConfig app = create_app('testing') app.config['SQLALCHEMY_DATABASE_URI'] = f'sqlite:///{db_path}' app.config['CACHE_TYPE'] = 'simple' with app.app_context(): db.create_all() # prove Group/group_content are gone from metadata tables = sorted(db.metadata.tables.keys()) print('registered tables:', tables) assert 'group' not in tables, 'group table still registered!' assert 'group_content' not in tables, 'group_content still registered!' admin = User(username='smoke', password=bcrypt.generate_password_hash('smokepw').decode('utf-8'), role='admin') pl = Playlist(name='Smoke PL') db.session.add_all([admin, pl]) db.session.flush() c = Content(filename='s.png', content_type='image', duration=5) db.session.add(c) db.session.flush() pl.contents.append(c) p = Player(name='Smoke', hostname='smoke-host', auth_code='smoke-code') p.set_password('pw') p.set_quickconnect_code('qc') p.playlist_id = pl.id db.session.add(p) db.session.commit() pid = p.id code = p.auth_code auth = {'Authorization': f'Bearer {code}'} with app.test_client() as cl: # public api for url in ['/api/health', '/api/system-info', '/api/content', '/api/logs', f'/api/player-status/{pid}', '/api/playlists?hostname=smoke-host&quickconnect_code=qc']: r = cl.get(url) print(f'{r.status_code} GET {url}') assert r.status_code < 500, f'{url} -> {r.status_code}' # system-info must no longer expose a groups key if url == '/api/system-info': body = r.get_json() assert 'groups' not in body, f'groups key still present: {body}' print(' system-info keys:', sorted(body.keys())) # authed api for url in [f'/api/playlists/{pid}', f'/api/playlist-version/{pid}']: r = cl.get(url, headers=auth) print(f'{r.status_code} GET {url} (bearer)') assert r.status_code < 500 r = cl.post('/api/auth/verify', json={'auth_code': code}) print(f'{r.status_code} POST /api/auth/verify') assert r.status_code < 500 r = cl.post('/api/player-feedback', json={ 'hostname': 'smoke-host', 'quickconnect_code': 'qc', 'status': 'playing', 'message': 'ok'}) print(f'{r.status_code} POST /api/player-feedback') assert r.status_code < 500 # content listing must no longer contain group_count body = cl.get('/api/content').get_json() assert body['content'], 'expected seeded content' assert 'group_count' not in body['content'][0], body['content'][0] print(' /api/content item keys:', sorted(body['content'][0].keys())) # login then hit UI pages r = cl.post('/login', data={'username': 'smoke', 'password': 'smokepw'}, follow_redirects=True) print(f'{r.status_code} POST /login') for url in ['/', '/content/', '/content/media-library', '/players/', '/admin/', '/admin/users', '/admin/system/info']: r = cl.get(url, follow_redirects=True) print(f'{r.status_code} GET {url}') assert r.status_code < 500, f'{url} -> {r.status_code}' # import checks import app.utils as u print('utils exports ok:', hasattr(u, 'get_player_status_info')) assert not hasattr(u, 'get_group_statistics') assert not hasattr(u, 'assign_player_to_group') import importlib for mod in ['app.blueprints.content', 'app.blueprints.api', 'app.utils.group_player_management', 'app.blueprints.players']: importlib.import_module(mod) print(f'imported {mod}') print('\nšŸŽ‰ SMOKE TEST PASSED')