Files
3dtours cff3950670 feat: AI Copilot suggestion chips + typeahead dropdown
- 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)
2026-07-28 11:20:26 +07:00

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