FIX: 11 bugs bảo mật/ổn định (static mount chặn dotfile, delete traversal, cleanup giữ clips serverFileId, upload whitelist, password strength, auth audio endpoints, pedalboard==0.9.19, vendor CDN local) + FEATURE: Carla bridge preview/export MIDI notes âm VSTi (POST /midi-render, /carla-play-notes, nút Preview VSTi/Export MIDI->Audio; pedalboard 0.9.19 raw MIDI bytes; SONICFORGE_STORAGE_DIR cô lập test storage)

This commit is contained in:
2026-08-10 18:46:54 +07:00
parent 67ad2b8e4b
commit 61cb4b846f
33 changed files with 1097 additions and 103 deletions
+70 -3
View File
@@ -11,8 +11,11 @@ client = TestClient(app)
def get_admin_token():
resp = client.post("/api/v1/auth/login", json={"username": "admin", "password": "admin123"})
if resp.status_code == 200:
# test_auth_and_quota.py có thể đã rotate password; thử cả 2.
for pwd in ("admin123", "admin_new_password_2026"):
resp = client.post("/api/v1/auth/login", json={"username": "admin", "password": pwd})
if resp.status_code != 200:
continue
token = resp.json()["access_token"]
# Admin is seeded with must_change_password=1; the app blocks music
# processing until the first password change. Complete that flow here
@@ -20,7 +23,7 @@ def get_admin_token():
user = resp.json()["user"]
if user.get("must_change_password"):
r = client.post("/api/v1/auth/change-password", headers={"Authorization": f"Bearer {token}"},
json={"old_password": "admin123", "new_password": "admin123"})
json={"old_password": pwd, "new_password": pwd})
if r.status_code == 200:
token = r.json()["access_token"]
return token
@@ -145,3 +148,67 @@ class TestPluginAPI:
client.post("/api/v1/plugins/dirs", headers=h, json={"plugin_dirs": [d1]})
g = client.get("/api/v1/plugins/dirs", headers=h)
assert g.json()["plugin_dirs"] == [d1]
class TestMidiRenderVSTi:
"""Preview/export MIDI notes với âm VSTi — feature Carla bridge → pedalboard."""
def test_midi_render_requires_auth(self):
client.cookies.clear() # TestClient giữ cookie login từ test trước
resp = client.post("/api/v1/plugins/midi-render",
json={"instrument_id": "x", "notes": [{"pitch": 60}]})
assert resp.status_code == 401
def test_midi_render_no_notes(self):
token = get_admin_token()
if not token:
pytest.skip("Cannot get admin token")
resp = client.post("/api/v1/plugins/midi-render", headers={"Authorization": f"Bearer {token}"},
json={"instrument_id": "x", "notes": []})
assert resp.status_code == 400
def test_midi_render_unknown_instrument(self):
token = get_admin_token()
if not token:
pytest.skip("Cannot get admin token")
resp = client.post("/api/v1/plugins/midi-render", headers={"Authorization": f"Bearer {token}"},
json={"instrument_id": "NoSuchPluginXYZ",
"notes": [{"pitch": 60, "start_beat": 0, "duration_beats": 1, "velocity": 0.8}]})
assert resp.status_code == 404
assert "VSTi" in resp.json()["detail"]
def test_midi_render_requires_pedalboard(self):
# Nếu pedalboard thiếu → 501 (không crash)
import app.api.v1.plugins as plugins_mod
token = get_admin_token()
if not token:
pytest.skip("Cannot get admin token")
if not plugins_mod.HAS_PEDALBOARD:
resp = client.post("/api/v1/plugins/midi-render", headers={"Authorization": f"Bearer {token}"},
json={"instrument_id": "x",
"notes": [{"pitch": 60, "start_beat": 0, "duration_beats": 1}]})
assert resp.status_code == 501
else:
pytest.skip("pedalboard present — render path covered by unknown-instrument test")
def test_carla_play_notes_requires_auth(self):
client.cookies.clear() # TestClient giữ cookie login từ test trước
resp = client.post("/api/v1/plugins/carla-play-notes",
json={"notes": [{"pitch": 60}], "bpm": 120})
assert resp.status_code == 401
def test_carla_play_notes_no_notes(self):
token = get_admin_token()
if not token:
pytest.skip("Cannot get admin token")
resp = client.post("/api/v1/plugins/carla-play-notes", headers={"Authorization": f"Bearer {token}"},
json={"notes": [], "bpm": 120})
assert resp.status_code == 400
def test_carla_play_notes_no_carla(self):
# Máy test không có Carla local → 409 hướng dẫn định vị/mở Carla.
token = get_admin_token()
if not token:
pytest.skip("Cannot get admin token")
resp = client.post("/api/v1/plugins/carla-play-notes", headers={"Authorization": f"Bearer {token}"},
json={"notes": [{"pitch": 60, "start_beat": 0, "duration_beats": 1}], "bpm": 120})
assert resp.status_code == 409