Phase 1-2 (PLAN_MASTERBUS_FX_RACK_VST): tests cho render_fx_chain + API fx-render; plan HOÀN THÀNH Phase 0-4
- test_native_render: render_fx_chain builtin gain e2e (bridge --render-fx) - test_plugin_api: POST /api/v1/plugins/fx-render builtin gain - PLAN_MASTERBUS_FX_RACK_VST.md: cap nhat trang thai cac phase (0-4 da implement tu cac commit truoc + phan nay)
This commit is contained in:
@@ -66,3 +66,30 @@ def test_plugins_bridge_branch_dispatch(monkeypatch):
|
||||
with pytest.raises(HTTPException) as ei:
|
||||
pl._render_midi_notes_pedalboard("__no_such__", [], bpm=120.0, sample_rate=44100)
|
||||
assert ei.value.status_code == 404
|
||||
def test_render_fx_chain_builtin_gain_e2e():
|
||||
"""render_fx_chain (PLAN_MASTERBUS_FX_RACK_VST Phase 1): builtin gain
|
||||
+6dB qua bridge --render-fx, không cần VST. Output tồn tại, đúng sr,
|
||||
RMS gấp đôi input."""
|
||||
import math
|
||||
import numpy as np
|
||||
import soundfile as sf
|
||||
exe = native_render.find_bridge_exe()
|
||||
if not exe:
|
||||
pytest.skip("bridge exe missing")
|
||||
sr = 44100
|
||||
t = np.linspace(0, 1.0, sr, endpoint=False)
|
||||
data = (0.1 * np.sin(2 * np.pi * 440 * t)).astype(np.float32)
|
||||
with tempfile.TemporaryDirectory() as td:
|
||||
inp = os.path.join(td, "in.wav")
|
||||
sf.write(inp, data, sr, subtype="FLOAT")
|
||||
out = native_render.render_fx_chain(
|
||||
input_wav=inp,
|
||||
fx_chain=[{"type": "builtin", "id": "gain", "params": {"db": 6.0}}],
|
||||
sample_rate=sr,
|
||||
)
|
||||
assert os.path.isfile(out)
|
||||
d, _ = sf.read(out, dtype="float32", always_2d=True)
|
||||
rms = float(np.sqrt(np.mean(d ** 2)))
|
||||
assert rms > 0.09 # ~2x 0.05
|
||||
info = sf.info(out)
|
||||
assert info.samplerate == sr
|
||||
|
||||
@@ -30,6 +30,47 @@ def get_admin_token():
|
||||
|
||||
|
||||
class TestPluginAPI:
|
||||
def test_fx_render_builtin_gain(self):
|
||||
"""POST /api/v1/plugins/fx-render với builtin gain (bridge --render-fx),
|
||||
không cần VST — output WAV mới trong PROCESSED_DIR."""
|
||||
import numpy as np
|
||||
import soundfile as sf
|
||||
from app.core import native_render
|
||||
from app.config import settings
|
||||
exe = native_render.find_bridge_exe()
|
||||
if not exe:
|
||||
pytest.skip("bridge exe missing")
|
||||
token = get_admin_token()
|
||||
if not token:
|
||||
pytest.skip("no admin token")
|
||||
h = {"Authorization": f"Bearer {token}"}
|
||||
sr = 44100
|
||||
t = np.linspace(0, 0.5, int(sr * 0.5), endpoint=False)
|
||||
data = (0.05 * np.sin(2 * np.pi * 440 * t)).astype(np.float32)
|
||||
os.makedirs(settings.PROCESSED_DIR, exist_ok=True)
|
||||
inp = os.path.join(settings.PROCESSED_DIR, "fx_api_in_test.wav")
|
||||
sf.write(inp, data, sr, subtype="FLOAT")
|
||||
try:
|
||||
resp = client.post("/api/v1/plugins/fx-render", headers=h, json={
|
||||
"input_file_id": "fx_api_in_test.wav",
|
||||
"fx_chain": [{"type": "builtin", "id": "gain", "params": {"db": 6.0}}],
|
||||
"sample_rate": sr,
|
||||
})
|
||||
assert resp.status_code == 200, resp.text[-500:]
|
||||
body = resp.json()
|
||||
assert body["success"] is True
|
||||
assert os.path.isfile(body["path"])
|
||||
d, _ = sf.read(body["path"], dtype="float32", always_2d=True)
|
||||
assert float(np.max(np.abs(d))) > 0.07 # ~2x 0.05
|
||||
finally:
|
||||
client.cookies.clear() # không làm hỏng test requires_auth kế tiếp
|
||||
for f in (inp, os.path.join(settings.PROCESSED_DIR, "fx_api_out_test.wav")):
|
||||
if os.path.exists(f):
|
||||
try:
|
||||
os.remove(f)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def test_list_plugins_requires_auth(self):
|
||||
resp = client.get("/api/v1/plugins/available")
|
||||
assert resp.status_code in (401, 403)
|
||||
|
||||
Reference in New Issue
Block a user