Files
web-page-backend/packs-lab/tests/test_routes.py

103 lines
3.4 KiB
Python

"""routes.py 통합 테스트 (DSM, supabase는 mock)."""
import os
import time
import uuid
from datetime import datetime, timezone
from unittest.mock import AsyncMock, MagicMock, patch
import pytest
from fastapi.testclient import TestClient
# 테스트용 환경변수 (auth import 전)
os.environ["BACKEND_HMAC_SECRET"] = "test-secret-32-bytes-XXXXXXXXXXXX"
os.environ["DSM_HOST"] = "https://test.synology.me:5001"
os.environ["DSM_USER"] = "test"
os.environ["DSM_PASS"] = "test"
os.environ["SUPABASE_URL"] = "https://placeholder.supabase.co"
os.environ["SUPABASE_SERVICE_KEY"] = "placeholder-key"
from app import auth # noqa: E402
from app.main import app # noqa: E402
client = TestClient(app)
def _signed(body: bytes) -> dict:
ts = str(int(time.time()))
sig = auth._sign(ts.encode() + b"." + body)
return {"X-Timestamp": ts, "X-Signature": sig, "Content-Type": "application/json"}
def test_health():
r = client.get("/health")
assert r.status_code == 200
assert r.json()["service"] == "packs-lab"
@patch("app.routes.create_share_link", new_callable=AsyncMock)
def test_sign_link_success(mock_share):
mock_share.return_value = ("https://test.synology.me:5001/d/s/abc", datetime.now(timezone.utc))
# Windows에서는 절대경로 resolve 결과가 C:\... 로 prefix되므로 PACK_BASE_DIR도 동일하게 패치
from pathlib import Path
abs_resolved = Path("/volume1/docker/webpage/media/packs/master/x.mp4").resolve()
base_resolved = Path(str(abs_resolved).rsplit("master", 1)[0].rstrip("\\/"))
with patch("app.routes.PACK_BASE_DIR", base_resolved):
body = b'{"file_path":"/volume1/docker/webpage/media/packs/master/x.mp4","expires_in_seconds":14400}'
r = client.post("/api/packs/sign-link", content=body, headers=_signed(body))
assert r.status_code == 200
assert "url" in r.json()
def test_sign_link_no_hmac():
r = client.post("/api/packs/sign-link", json={"file_path": "/x"})
assert r.status_code == 401
def test_sign_link_path_outside_base():
body = b'{"file_path":"/etc/passwd","expires_in_seconds":14400}'
r = client.post("/api/packs/sign-link", content=body, headers=_signed(body))
assert r.status_code == 400
def test_upload_invalid_token():
r = client.post(
"/api/packs/upload",
files={"file": ("x.pdf", b"abc", "application/pdf")},
headers={"Authorization": "Bearer invalid"},
)
assert r.status_code == 401
def test_upload_no_auth():
r = client.post(
"/api/packs/upload",
files={"file": ("x.pdf", b"abc", "application/pdf")},
)
assert r.status_code == 401
@patch("app.routes._supabase")
def test_list_success(mock_sb):
mock_table = MagicMock()
mock_table.select.return_value = mock_table
mock_table.is_.return_value = mock_table
mock_table.order.return_value = mock_table
mock_table.execute.return_value = MagicMock(data=[
{
"id": str(uuid.uuid4()),
"min_tier": "starter",
"label": "테스트",
"file_path": "/volume1/.../x.pdf",
"filename": "x.pdf",
"size_bytes": 100,
"sort_order": 0,
"uploaded_at": "2026-05-02T12:00:00+00:00",
}
])
mock_sb.return_value.table.return_value = mock_table
body = b''
r = client.get("/api/packs/list", headers=_signed(body))
assert r.status_code == 200
assert len(r.json()) == 1