Import-surface docs and docstrings describe the automation as a migration script; status-doc references in CHANGELOG/ADR-009/ROADMAP point at repo files. Screenshot/verify tools write to /tmp/shopdb-shots (created on import) instead of a machine-specific directory.
114 lines
3.7 KiB
Python
114 lines
3.7 KiB
Python
"""Smoke tests pinning the baseline behavior of shopdb-flask.
|
|
|
|
These eight tests are the safety net required before any structural
|
|
refactor proceeds.
|
|
"""
|
|
|
|
import pytest
|
|
|
|
|
|
def test_app_factory_creates_app(app):
|
|
"""create_app('testing') returns a Flask app with TESTING=True."""
|
|
assert app is not None
|
|
assert app.config['TESTING'] is True
|
|
assert 'sqlite' in app.config['SQLALCHEMY_DATABASE_URI']
|
|
|
|
|
|
def test_login_with_valid_credentials_returns_tokens(client, admin_user):
|
|
"""POST /api/auth/login with valid creds returns access and refresh tokens."""
|
|
response = client.post(
|
|
'/api/auth/login',
|
|
json={'username': 'testadmin', 'password': 'testpass'},
|
|
)
|
|
assert response.status_code == 200
|
|
payload = response.get_json()
|
|
assert 'data' in payload
|
|
data = payload['data']
|
|
assert 'access_token' in data
|
|
assert 'refresh_token' in data
|
|
assert 'user' in data
|
|
assert data['user']['username'] == 'testadmin'
|
|
|
|
|
|
def test_login_with_invalid_credentials_returns_401(client, admin_user):
|
|
"""Wrong password returns 401 with the documented error envelope.
|
|
|
|
Pins the current shape: error info nested under `data.error` (not at
|
|
top level). The error_response docstring claims top-level `error` but
|
|
the implementation puts it under `data`. Pinned as-is until that
|
|
inconsistency is intentionally addressed.
|
|
"""
|
|
response = client.post(
|
|
'/api/auth/login',
|
|
json={'username': 'testadmin', 'password': 'wrongpassword'},
|
|
)
|
|
assert response.status_code == 401
|
|
payload = response.get_json()
|
|
assert payload['status'] == 'error'
|
|
assert payload['data']['error']['code'] == 'UNAUTHORIZED'
|
|
|
|
|
|
def test_login_with_missing_fields_returns_400(client):
|
|
"""Missing username or password returns 400 validation error."""
|
|
response = client.post('/api/auth/login', json={})
|
|
assert response.status_code == 400
|
|
|
|
|
|
def test_protected_route_requires_authentication(client, admin_user):
|
|
"""GET /api/users without a JWT returns 401."""
|
|
response = client.get('/api/users')
|
|
assert response.status_code == 401
|
|
|
|
|
|
def test_protected_route_works_with_jwt(client, auth_headers):
|
|
"""GET /api/users with a valid JWT returns 200."""
|
|
response = client.get('/api/users', headers=auth_headers)
|
|
assert response.status_code == 200
|
|
|
|
|
|
def test_paginated_response_shape(client, auth_headers):
|
|
"""A paginated list endpoint returns data plus pagination meta.
|
|
|
|
Uses /api/locations because it is a simple platform endpoint that
|
|
uses paginated_response. Pagination meta keys follow the naming
|
|
convention (lowercase concatenated): page, perpage, total,
|
|
totalpages, hasnext, hasprev.
|
|
"""
|
|
response = client.get('/api/locations', headers=auth_headers)
|
|
assert response.status_code == 200
|
|
payload = response.get_json()
|
|
assert 'data' in payload
|
|
assert isinstance(payload['data'], list)
|
|
assert 'meta' in payload
|
|
assert 'pagination' in payload['meta']
|
|
pagination = payload['meta']['pagination']
|
|
assert 'page' in pagination
|
|
assert 'perpage' in pagination
|
|
assert 'total' in pagination
|
|
assert 'totalpages' in pagination
|
|
|
|
|
|
def test_plugin_loader_discovers_bundled_plugins(app):
|
|
"""Plugin manager finds the bundled plugins."""
|
|
from shopdb.plugins import plugin_manager
|
|
|
|
expected_plugins = {
|
|
'computers',
|
|
'employees',
|
|
'knowledgebase',
|
|
'machines',
|
|
'network',
|
|
'notifications',
|
|
'printers',
|
|
'slides',
|
|
'usb',
|
|
}
|
|
|
|
with app.app_context():
|
|
loader = plugin_manager.loader
|
|
discovered = set(loader.discover_plugins())
|
|
|
|
assert expected_plugins.issubset(discovered), (
|
|
f'Missing bundled plugins: {expected_plugins - discovered}'
|
|
)
|