cff3950670
- Suggestion chips bar: context-aware (amber for rearrange, indigo for creation) - Typeahead dropdown: keyword matching on ≥2 chars, Tab to select - getSelectedMidiItemInfo helper for context detection - Backend: 4 rearrange presets in ai_presets.py - PromptTemplateManager: getContextualSuggestions(hasSelectedItem)
146 lines
4.9 KiB
Python
146 lines
4.9 KiB
Python
import os, json, time
|
|
import pytest
|
|
from fastapi.testclient import TestClient
|
|
from app.main import app
|
|
from app.config import settings
|
|
from app.core.auth import create_token
|
|
|
|
client = TestClient(app)
|
|
|
|
TEST_DATA_FILE = os.path.join(settings.PROCESSED_DIR, "ai_presets.json")
|
|
if os.path.exists(TEST_DATA_FILE):
|
|
os.remove(TEST_DATA_FILE)
|
|
|
|
|
|
def _auth_header():
|
|
token = create_token("test_user_presets", "testuser", "standard", False)
|
|
return {"Authorization": f"Bearer {token}"}
|
|
|
|
|
|
def test_list_presets_defaults():
|
|
res = client.get("/api/v1/ai/presets")
|
|
assert res.status_code == 200
|
|
data = res.json()
|
|
assert data["success"] is True
|
|
assert len(data["presets"]) == 7
|
|
names = [p["name"] for p in data["presets"]]
|
|
assert "Epic Orchestra Intro (8 Bars)" in names
|
|
assert "Pop Piano Chords (4 Bars)" in names
|
|
assert "Cyberpunk Synthwave (8 Bars)" in names
|
|
assert "Jazz Swing Variation" in names
|
|
assert "80s Synthwave Arpeggiator" in names
|
|
|
|
|
|
def test_create_user_preset():
|
|
preset = {
|
|
"id": "preset_test_my_beat",
|
|
"name": "My Beat (4 Bars)",
|
|
"keywords": ["my beat", "custom beat", "beat của tôi"],
|
|
"category": "Hip Hop / Beat",
|
|
"default_bars": 4,
|
|
"default_bpm": 100,
|
|
"default_scale": "A Minor",
|
|
"system_instruction_template": "Generate a 4-bar hip hop beat with kick, snare, hi-hat.",
|
|
"is_user_defined": True,
|
|
"is_favorite": False,
|
|
"created_at": "2026-07-28T00:00:00Z"
|
|
}
|
|
res = client.post("/api/v1/ai/presets", json=preset, headers=_auth_header())
|
|
assert res.status_code == 200, res.text
|
|
data = res.json()
|
|
assert data["success"] is True
|
|
assert data["preset"]["id"] == "preset_test_my_beat"
|
|
|
|
# Verify it appears in listing
|
|
res2 = client.get("/api/v1/ai/presets", headers=_auth_header())
|
|
assert res2.status_code == 200
|
|
names = [p["name"] for p in res2.json()["presets"]]
|
|
assert "My Beat (4 Bars)" in names
|
|
assert len(res2.json()["presets"]) == 8 # 7 defaults + 1 user
|
|
|
|
|
|
def test_update_user_preset():
|
|
preset = {
|
|
"id": "preset_test_my_beat",
|
|
"name": "My Beat Updated (8 Bars)",
|
|
"keywords": ["my beat", "custom beat"],
|
|
"category": "Hip Hop / Beat",
|
|
"default_bars": 8,
|
|
"default_bpm": 110,
|
|
"default_scale": "C Minor",
|
|
"system_instruction_template": "Generate an 8-bar hip hop beat.",
|
|
"is_user_defined": True,
|
|
"is_favorite": True,
|
|
"created_at": "2026-07-28T00:00:00Z"
|
|
}
|
|
res = client.post("/api/v1/ai/presets", json=preset, headers=_auth_header())
|
|
assert res.status_code == 200
|
|
data = res.json()
|
|
assert data["preset"]["name"] == "My Beat Updated (8 Bars)"
|
|
assert data["preset"]["is_favorite"] is True
|
|
assert data["preset"]["default_bars"] == 8
|
|
|
|
|
|
def test_delete_user_preset():
|
|
res = client.delete("/api/v1/ai/presets/preset_test_my_beat", headers=_auth_header())
|
|
assert res.status_code == 200
|
|
data = res.json()
|
|
assert data["success"] is True
|
|
|
|
res2 = client.get("/api/v1/ai/presets", headers=_auth_header())
|
|
assert res2.status_code == 200
|
|
names = [p["name"] for p in res2.json()["presets"]]
|
|
assert "My Beat Updated (8 Bars)" not in names
|
|
|
|
|
|
def test_delete_nonexistent_preset():
|
|
res = client.delete("/api/v1/ai/presets/nonexistent_id", headers=_auth_header())
|
|
assert res.status_code == 404
|
|
|
|
|
|
def test_presets_anonymous_cannot_save():
|
|
preset = {
|
|
"id": "preset_anon",
|
|
"name": "Anon Preset",
|
|
"keywords": ["anon"],
|
|
"category": "General",
|
|
"default_bars": 4,
|
|
"default_bpm": 120,
|
|
"default_scale": "C Major",
|
|
"system_instruction_template": "Test",
|
|
"is_user_defined": True,
|
|
"is_favorite": False
|
|
}
|
|
res = client.post("/api/v1/ai/presets", json=preset)
|
|
assert res.status_code == 401
|
|
|
|
res2 = client.delete("/api/v1/ai/presets/preset_anon")
|
|
assert res2.status_code == 401
|
|
|
|
|
|
def test_presets_isolation_between_users():
|
|
token_a = create_token("user_a", "usera", "standard", False)
|
|
token_b = create_token("user_b", "userb", "standard", False)
|
|
|
|
preset_a = {
|
|
"id": "preset_user_a_only",
|
|
"name": "User A Preset",
|
|
"keywords": ["user a"],
|
|
"category": "General",
|
|
"default_bars": 4,
|
|
"default_bpm": 120,
|
|
"default_scale": "C Major",
|
|
"system_instruction_template": "User A specific",
|
|
"is_user_defined": True,
|
|
"is_favorite": False
|
|
}
|
|
res = client.post("/api/v1/ai/presets", json=preset_a, headers={"Authorization": f"Bearer {token_a}"})
|
|
assert res.status_code == 200
|
|
|
|
# User B shouldn't see User A's presets
|
|
res_b = client.get("/api/v1/ai/presets", headers={"Authorization": f"Bearer {token_b}"})
|
|
assert res_b.status_code == 200
|
|
names_b = [p["name"] for p in res_b.json()["presets"]]
|
|
assert "User A Preset" not in names_b
|
|
assert "Epic Orchestra Intro (8 Bars)" in names_b
|