8939e24e69
- Add test_client.py with comprehensive client-server contract tests:
- TestAuthFlowClient: full register → login → refresh → validate → /admin/me → logout lifecycle
- TestPacksClientContract: /packs response fields matching ServerPack.java
- TestPackManifestClientContract: /pack/{name} fields matching PackManifest.java
- TestPackDiffClientContract: /pack/{name}/diff matching DiffResponse/FileInfo.java
(all-new, no-changes, outdated-file, extra-local-file scenarios)
- TestPackFileDownload: file serving, 404, path traversal security
- TestPackPermissions: auth/pass requirements for /packs and /diff
- TestLauncherVersion: /launcher/version endpoint
- TestProxyEndpoints: /proxy/status, /proxy/fabric/versions/loader
- Add logged_in_user_with_pass fixture (role=1) for pack-related tests
- Add pack_fixture: creates temp pack with mod file, scans it, cleans up
- Fix manifest test: files don't have 'url' field (only in diff response)
- Fix /pack/{name} test: endpoint is public, no auth required
Total: 67 tests passing (47 existing + 20 new)
126 lines
3.5 KiB
Python
126 lines
3.5 KiB
Python
import os
|
|
import sys
|
|
import pytest
|
|
import tempfile
|
|
import shutil
|
|
from pathlib import Path
|
|
|
|
|
|
def auth_headers(token):
|
|
"""Create Authorization headers."""
|
|
return {"Authorization": f"Bearer {token}"}
|
|
|
|
|
|
@pytest.fixture(scope="session")
|
|
def test_db_dir():
|
|
"""Temporary directory for test databases."""
|
|
d = tempfile.mkdtemp(prefix="zern_test_")
|
|
yield Path(d)
|
|
shutil.rmtree(d, ignore_errors=True)
|
|
|
|
|
|
@pytest.fixture(scope="session")
|
|
def test_app(test_db_dir):
|
|
"""Create FastAPI app with test database."""
|
|
# Patch auth module paths BEFORE importing anything
|
|
import auth
|
|
auth.AUTH_DB = test_db_dir / "auth.db"
|
|
auth.SECRET_KEY = test_db_dir / ".secret_key"
|
|
auth._rate_limit_cache.clear()
|
|
|
|
# Initialize test database
|
|
auth.init_db()
|
|
|
|
from main import app
|
|
return app
|
|
|
|
|
|
@pytest.fixture
|
|
def client(test_app):
|
|
"""TestClient instance."""
|
|
from fastapi.testclient import TestClient
|
|
return TestClient(test_app)
|
|
|
|
|
|
@pytest.fixture
|
|
def registered_user(client):
|
|
"""Register a unique test user."""
|
|
import secrets
|
|
username = f"testuser_{secrets.token_hex(4)}"
|
|
password = "TestPassword123"
|
|
|
|
resp = client.post("/auth/register", json={"username": username, "password": password})
|
|
assert resp.status_code == 200, f"Registration failed: {resp.text}"
|
|
return {"username": username, "password": password}
|
|
|
|
|
|
@pytest.fixture
|
|
def logged_in_user(client, registered_user):
|
|
"""Login and return tokens."""
|
|
resp = client.post("/auth/login", json=registered_user)
|
|
assert resp.status_code == 200, f"Login failed: {resp.text}"
|
|
data = resp.json()
|
|
return {
|
|
"username": registered_user["username"],
|
|
"password": registered_user["password"],
|
|
"access_token": data["access_token"],
|
|
"refresh_token": data["refresh_token"],
|
|
"expires_in": data["expires_in"],
|
|
"uuid": data["uuid"],
|
|
"role": data["role"],
|
|
}
|
|
|
|
|
|
@pytest.fixture
|
|
def logged_in_user_with_pass(client, registered_user):
|
|
"""Login user and give them role 1 (pass holder)."""
|
|
# Promote to pass holder
|
|
import sqlite3
|
|
import auth
|
|
conn = sqlite3.connect(str(auth.AUTH_DB))
|
|
conn.execute("UPDATE users SET role = 1 WHERE username = ?", (registered_user["username"],))
|
|
conn.commit()
|
|
conn.close()
|
|
|
|
resp = client.post("/auth/login", json=registered_user)
|
|
assert resp.status_code == 200, f"Login failed: {resp.text}"
|
|
data = resp.json()
|
|
return {
|
|
"username": registered_user["username"],
|
|
"password": registered_user["password"],
|
|
"access_token": data["access_token"],
|
|
"refresh_token": data["refresh_token"],
|
|
"expires_in": data["expires_in"],
|
|
"uuid": data["uuid"],
|
|
"role": data["role"],
|
|
}
|
|
|
|
|
|
@pytest.fixture
|
|
def admin_user(client):
|
|
"""Create and login a creator/admin user."""
|
|
import secrets
|
|
import sqlite3
|
|
import auth
|
|
|
|
username = f"admin_{secrets.token_hex(4)}"
|
|
password = "AdminPassword123"
|
|
|
|
resp = client.post("/auth/register", json={"username": username, "password": password})
|
|
assert resp.status_code == 200
|
|
|
|
# Promote to creator
|
|
conn = sqlite3.connect(str(auth.AUTH_DB))
|
|
conn.execute("UPDATE users SET role = 4 WHERE username = ?", (username,))
|
|
conn.commit()
|
|
conn.close()
|
|
|
|
resp = client.post("/auth/login", json={"username": username, "password": password})
|
|
assert resp.status_code == 200
|
|
data = resp.json()
|
|
return {
|
|
"username": username,
|
|
"access_token": data["access_token"],
|
|
"refresh_token": data["refresh_token"],
|
|
}
|