Initial Commit
slip slop
This commit is contained in:
commit
064e672ab7
61 changed files with 3504 additions and 0 deletions
53
tests/conftest.py
Normal file
53
tests/conftest.py
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
"""
|
||||
Shared pytest fixtures.
|
||||
Tests run against SQLite in-memory — no Docker required.
|
||||
"""
|
||||
import os
|
||||
import pytest
|
||||
|
||||
os.environ.setdefault("DATABASE_URL", "sqlite:///:memory:")
|
||||
os.environ.setdefault("API_KEY", "test-key")
|
||||
|
||||
from app import create_app
|
||||
from app.extensions import db as _db
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def app():
|
||||
application = create_app()
|
||||
application.config.update(
|
||||
TESTING=True,
|
||||
SQLALCHEMY_DATABASE_URI="sqlite:///:memory:",
|
||||
)
|
||||
with application.app_context():
|
||||
_db.create_all()
|
||||
# Seed instruments
|
||||
from app.models import Instrument
|
||||
for name in ("banjo", "fiddle"):
|
||||
if not _db.session.query(Instrument).filter_by(name=name).first():
|
||||
_db.session.add(Instrument(name=name))
|
||||
_db.session.commit()
|
||||
yield application
|
||||
_db.drop_all()
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def client(app):
|
||||
return app.test_client()
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def clean_db(app):
|
||||
with app.app_context():
|
||||
yield
|
||||
_db.session.rollback()
|
||||
from app.models import (TuneByInstrumentNote, TuneByInstrument,
|
||||
Reference, TuneNote, Tune, Source, Musician, Tuning)
|
||||
for model in (TuneByInstrumentNote, Reference, TuneNote,
|
||||
TuneByInstrument, Tune, Source, Musician, Tuning):
|
||||
_db.session.query(model).delete()
|
||||
_db.session.commit()
|
||||
|
||||
|
||||
AUTH = {"X-API-Key": "test-key"}
|
||||
BAD_AUTH = {"X-API-Key": "wrong-key"}
|
||||
130
tests/test_instruments.py
Normal file
130
tests/test_instruments.py
Normal file
|
|
@ -0,0 +1,130 @@
|
|||
"""Tests for tune_by_instrument endpoints."""
|
||||
from tests.conftest import AUTH
|
||||
|
||||
|
||||
def make_tune(client):
|
||||
r = client.post("/tunes/", json={"name": "Test Tune", "key": "D"}, headers=AUTH)
|
||||
assert r.status_code == 201
|
||||
return r.get_json()
|
||||
|
||||
|
||||
def make_musician(client, name="Earl Scruggs"):
|
||||
r = client.post("/musicians/", json={"name": name}, headers=AUTH)
|
||||
assert r.status_code == 201
|
||||
return r.get_json()
|
||||
|
||||
|
||||
def get_instrument_id(client, name):
|
||||
return next(i["id"] for i in client.get("/instruments").get_json() if i["name"] == name)
|
||||
|
||||
|
||||
def make_tuning(client, name="double C"):
|
||||
r = client.post("/tunings/", json={"name": name}, headers=AUTH)
|
||||
assert r.status_code == 201
|
||||
return r.get_json()
|
||||
|
||||
|
||||
class TestTuneByInstrument:
|
||||
def test_create_entry(self, client):
|
||||
tune = make_tune(client)
|
||||
banjo_id = get_instrument_id(client, "banjo")
|
||||
tuning = make_tuning(client, "double C")
|
||||
r = client.post(f'/tunes/{tune["id"]}/instruments',
|
||||
json={"instrument_id": banjo_id, "tuning_id": tuning["id"], "callable": True},
|
||||
headers=AUTH)
|
||||
assert r.status_code == 201
|
||||
body = r.get_json()
|
||||
assert body["instrument_name"] == "banjo"
|
||||
assert body["tuning"] == "double C"
|
||||
assert body["callable"] is True
|
||||
|
||||
def test_create_with_musician(self, client):
|
||||
tune = make_tune(client)
|
||||
banjo_id = get_instrument_id(client, "banjo")
|
||||
m = make_musician(client)
|
||||
r = client.post(f'/tunes/{tune["id"]}/instruments',
|
||||
json={"instrument_id": banjo_id, "learned_from_id": m["id"]},
|
||||
headers=AUTH)
|
||||
assert r.status_code == 201
|
||||
assert r.get_json()["learned_from"] == "Earl Scruggs"
|
||||
|
||||
def test_update_entry(self, client):
|
||||
tune = make_tune(client)
|
||||
banjo_id = get_instrument_id(client, "banjo")
|
||||
tuning = make_tuning(client, "sawmill")
|
||||
entry = client.post(f'/tunes/{tune["id"]}/instruments',
|
||||
json={"instrument_id": banjo_id, "callable": False},
|
||||
headers=AUTH).get_json()
|
||||
r = client.patch(f'/tunes/{tune["id"]}/instruments/{entry["id"]}',
|
||||
json={"callable": True, "tuning_id": tuning["id"]}, headers=AUTH)
|
||||
assert r.status_code == 200
|
||||
assert r.get_json()["callable"] is True
|
||||
assert r.get_json()["tuning"] == "sawmill"
|
||||
|
||||
def test_delete_entry(self, client):
|
||||
tune = make_tune(client)
|
||||
banjo_id = get_instrument_id(client, "banjo")
|
||||
entry = client.post(f'/tunes/{tune["id"]}/instruments',
|
||||
json={"instrument_id": banjo_id}, headers=AUTH).get_json()
|
||||
r = client.delete(f'/tunes/{tune["id"]}/instruments/{entry["id"]}', headers=AUTH)
|
||||
assert r.status_code == 200
|
||||
remaining = client.get(f'/tunes/{tune["id"]}/instruments').get_json()
|
||||
assert all(e["id"] != entry["id"] for e in remaining)
|
||||
|
||||
def test_unique_constraint(self, client):
|
||||
tune = make_tune(client)
|
||||
banjo_id = get_instrument_id(client, "banjo")
|
||||
client.post(f'/tunes/{tune["id"]}/instruments',
|
||||
json={"instrument_id": banjo_id}, headers=AUTH)
|
||||
r = client.post(f'/tunes/{tune["id"]}/instruments',
|
||||
json={"instrument_id": banjo_id}, headers=AUTH)
|
||||
assert r.status_code == 409
|
||||
|
||||
|
||||
class TestInstrumentNotes:
|
||||
def test_add_and_list_notes(self, client):
|
||||
tune = make_tune(client)
|
||||
banjo_id = get_instrument_id(client, "banjo")
|
||||
entry = client.post(f'/tunes/{tune["id"]}/instruments',
|
||||
json={"instrument_id": banjo_id}, headers=AUTH).get_json()
|
||||
client.post(f'/tunes/{tune["id"]}/instruments/{entry["id"]}/notes',
|
||||
json={"note": "tricky B part"}, headers=AUTH)
|
||||
client.post(f'/tunes/{tune["id"]}/instruments/{entry["id"]}/notes',
|
||||
json={"note": "watch the timing"}, headers=AUTH)
|
||||
notes = client.get(f'/tunes/{tune["id"]}/instruments/{entry["id"]}/notes').get_json()
|
||||
assert len(notes) == 2
|
||||
|
||||
def test_update_note(self, client):
|
||||
tune = make_tune(client)
|
||||
banjo_id = get_instrument_id(client, "banjo")
|
||||
entry = client.post(f'/tunes/{tune["id"]}/instruments',
|
||||
json={"instrument_id": banjo_id}, headers=AUTH).get_json()
|
||||
note = client.post(f'/tunes/{tune["id"]}/instruments/{entry["id"]}/notes',
|
||||
json={"note": "old"}, headers=AUTH).get_json()
|
||||
r = client.patch(f'/tunes/{tune["id"]}/instruments/{entry["id"]}/notes/{note["id"]}',
|
||||
json={"note": "new"}, headers=AUTH)
|
||||
assert r.get_json()["note"] == "new"
|
||||
|
||||
def test_delete_note(self, client):
|
||||
tune = make_tune(client)
|
||||
banjo_id = get_instrument_id(client, "banjo")
|
||||
entry = client.post(f'/tunes/{tune["id"]}/instruments',
|
||||
json={"instrument_id": banjo_id}, headers=AUTH).get_json()
|
||||
note = client.post(f'/tunes/{tune["id"]}/instruments/{entry["id"]}/notes',
|
||||
json={"note": "to delete"}, headers=AUTH).get_json()
|
||||
r = client.delete(f'/tunes/{tune["id"]}/instruments/{entry["id"]}/notes/{note["id"]}',
|
||||
headers=AUTH)
|
||||
assert r.status_code == 200
|
||||
|
||||
def test_notes_included_in_entry_to_dict(self, client):
|
||||
tune = make_tune(client)
|
||||
banjo_id = get_instrument_id(client, "banjo")
|
||||
entry = client.post(f'/tunes/{tune["id"]}/instruments',
|
||||
json={"instrument_id": banjo_id}, headers=AUTH).get_json()
|
||||
client.post(f'/tunes/{tune["id"]}/instruments/{entry["id"]}/notes',
|
||||
json={"note": "my note"}, headers=AUTH)
|
||||
# Notes appear in the instrument entry and in the parent tune
|
||||
tune_data = client.get(f'/tunes/{tune["id"]}').get_json()
|
||||
inst_entry = tune_data["instruments"][0]
|
||||
assert len(inst_entry["notes"]) == 1
|
||||
assert inst_entry["notes"][0]["note"] == "my note"
|
||||
70
tests/test_sources.py
Normal file
70
tests/test_sources.py
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
"""Tests for /sources endpoints."""
|
||||
from tests.conftest import AUTH, BAD_AUTH
|
||||
|
||||
|
||||
def create_source(client, name="Doc Watson"):
|
||||
r = client.post("/sources/", json={"name": name}, headers=AUTH)
|
||||
assert r.status_code == 201
|
||||
return r.get_json()
|
||||
|
||||
|
||||
class TestSources:
|
||||
def test_create(self, client):
|
||||
s = create_source(client)
|
||||
assert s["name"] == "Doc Watson"
|
||||
assert "id" in s
|
||||
|
||||
def test_list(self, client):
|
||||
create_source(client, "Doc Watson")
|
||||
create_source(client, "Tony Rice")
|
||||
r = client.get("/sources/")
|
||||
assert r.status_code == 200
|
||||
names = [s["name"] for s in r.get_json()]
|
||||
assert "Doc Watson" in names
|
||||
assert "Tony Rice" in names
|
||||
|
||||
def test_list_is_public(self, client):
|
||||
r = client.get("/sources/")
|
||||
assert r.status_code == 200
|
||||
|
||||
def test_create_requires_auth(self, client):
|
||||
r = client.post("/sources/", json={"name": "test"})
|
||||
assert r.status_code == 401
|
||||
|
||||
def test_update(self, client):
|
||||
s = create_source(client)
|
||||
r = client.patch(f'/sources/{s["id"]}', json={"name": "Doc Watson Jr."}, headers=AUTH)
|
||||
assert r.status_code == 200
|
||||
assert r.get_json()["name"] == "Doc Watson Jr."
|
||||
|
||||
def test_delete(self, client):
|
||||
s = create_source(client)
|
||||
r = client.delete(f'/sources/{s["id"]}', headers=AUTH)
|
||||
assert r.status_code == 200
|
||||
remaining = [x["id"] for x in client.get("/sources/").get_json()]
|
||||
assert s["id"] not in remaining
|
||||
|
||||
def test_tune_with_source(self, client):
|
||||
s = create_source(client, "Clarence Ashley")
|
||||
r = client.post("/tunes/", json={"name": "Coo Coo Bird", "key": "D", "source_id": s["id"]}, headers=AUTH)
|
||||
assert r.status_code == 201
|
||||
tune = r.get_json()
|
||||
assert tune["source"]["name"] == "Clarence Ashley"
|
||||
assert tune["source_id"] == s["id"]
|
||||
|
||||
def test_filter_by_source(self, client):
|
||||
s = create_source(client, "Clarence Ashley")
|
||||
client.post("/tunes/", json={"name": "Coo Coo Bird", "source_id": s["id"]}, headers=AUTH)
|
||||
client.post("/tunes/", json={"name": "Salt Creek"}, headers=AUTH)
|
||||
r = client.get(f'/tunes/?source_id={s["id"]}')
|
||||
results = r.get_json()
|
||||
assert len(results) == 1
|
||||
assert results[0]["name"] == "Coo Coo Bird"
|
||||
|
||||
def test_filter_by_modal(self, client):
|
||||
client.post("/tunes/", json={"name": "Modal Tune", "modal": True}, headers=AUTH)
|
||||
client.post("/tunes/", json={"name": "Regular Tune", "modal": False}, headers=AUTH)
|
||||
r = client.get("/tunes/?modal=true")
|
||||
names = [t["name"] for t in r.get_json()]
|
||||
assert "Modal Tune" in names
|
||||
assert "Regular Tune" not in names
|
||||
133
tests/test_sub.py
Normal file
133
tests/test_sub.py
Normal file
|
|
@ -0,0 +1,133 @@
|
|||
"""Tests for notes, references, and musicians sub-resource endpoints."""
|
||||
from tests.conftest import AUTH
|
||||
|
||||
|
||||
def make_tune(client):
|
||||
r = client.post("/tunes/", json={"name": "Sub Test Tune", "key": "G"}, headers=AUTH)
|
||||
assert r.status_code == 201
|
||||
return r.get_json()
|
||||
|
||||
|
||||
def make_musician(client, name="Doc Watson"):
|
||||
r = client.post("/musicians/", json={"name": name}, headers=AUTH)
|
||||
assert r.status_code == 201
|
||||
return r.get_json()
|
||||
|
||||
|
||||
# ── Notes ──────────────────────────────────────────────────────
|
||||
|
||||
class TestNotes:
|
||||
def test_add_note(self, client):
|
||||
tune = make_tune(client)
|
||||
r = client.post(f'/tunes/{tune["id"]}/notes',
|
||||
json={"note": "remember the B part"}, headers=AUTH)
|
||||
assert r.status_code == 201
|
||||
assert r.get_json()["note"] == "remember the B part"
|
||||
|
||||
def test_list_notes(self, client):
|
||||
tune = make_tune(client)
|
||||
client.post(f'/tunes/{tune["id"]}/notes', json={"note": "note 1"}, headers=AUTH)
|
||||
client.post(f'/tunes/{tune["id"]}/notes', json={"note": "note 2"}, headers=AUTH)
|
||||
r = client.get(f'/tunes/{tune["id"]}/notes', headers=AUTH)
|
||||
assert len(r.get_json()) == 2
|
||||
|
||||
def test_update_note(self, client):
|
||||
tune = make_tune(client)
|
||||
note = client.post(f'/tunes/{tune["id"]}/notes',
|
||||
json={"note": "old"}, headers=AUTH).get_json()
|
||||
r = client.patch(f'/tunes/{tune["id"]}/notes/{note["id"]}',
|
||||
json={"note": "new"}, headers=AUTH)
|
||||
assert r.get_json()["note"] == "new"
|
||||
|
||||
def test_delete_note(self, client):
|
||||
tune = make_tune(client)
|
||||
note = client.post(f'/tunes/{tune["id"]}/notes',
|
||||
json={"note": "to delete"}, headers=AUTH).get_json()
|
||||
r = client.delete(f'/tunes/{tune["id"]}/notes/{note["id"]}', headers=AUTH)
|
||||
assert r.status_code == 200
|
||||
|
||||
def test_note_on_missing_tune_returns_404(self, client):
|
||||
r = client.post("/tunes/99999/notes", json={"note": "x"}, headers=AUTH)
|
||||
assert r.status_code == 404
|
||||
|
||||
|
||||
# ── References ────────────────────────────────────────────────
|
||||
|
||||
class TestReferences:
|
||||
def test_add_reference(self, client):
|
||||
tune = make_tune(client)
|
||||
r = client.post(f'/tunes/{tune["id"]}/references',
|
||||
json={"link": "https://youtube.com/abc", "site": "YouTube"},
|
||||
headers=AUTH)
|
||||
assert r.status_code == 201
|
||||
assert r.get_json()["musicians"] == []
|
||||
|
||||
def test_add_reference_with_musicians(self, client):
|
||||
tune = make_tune(client)
|
||||
m1 = make_musician(client, "Doc Watson")
|
||||
m2 = make_musician(client, "Merle Watson")
|
||||
r = client.post(f'/tunes/{tune["id"]}/references',
|
||||
json={"link": "https://example.com", "site": "YouTube",
|
||||
"musicians": [{"id": m1["id"]}, {"id": m2["id"]}]},
|
||||
headers=AUTH)
|
||||
assert r.status_code == 201
|
||||
names = [m["name"] for m in r.get_json()["musicians"]]
|
||||
assert "Doc Watson" in names
|
||||
assert "Merle Watson" in names
|
||||
|
||||
def test_list_references(self, client):
|
||||
tune = make_tune(client)
|
||||
client.post(f'/tunes/{tune["id"]}/references',
|
||||
json={"link": "https://a.com"}, headers=AUTH)
|
||||
client.post(f'/tunes/{tune["id"]}/references',
|
||||
json={"link": "https://b.com"}, headers=AUTH)
|
||||
r = client.get(f'/tunes/{tune["id"]}/references', headers=AUTH)
|
||||
assert len(r.get_json()) == 2
|
||||
|
||||
def test_update_reference(self, client):
|
||||
tune = make_tune(client)
|
||||
ref = client.post(f'/tunes/{tune["id"]}/references',
|
||||
json={"link": "https://old.com", "site": "Old"},
|
||||
headers=AUTH).get_json()
|
||||
r = client.patch(f'/tunes/{tune["id"]}/references/{ref["id"]}',
|
||||
json={"site": "New"}, headers=AUTH)
|
||||
assert r.get_json()["site"] == "New"
|
||||
|
||||
def test_delete_reference(self, client):
|
||||
tune = make_tune(client)
|
||||
ref = client.post(f'/tunes/{tune["id"]}/references',
|
||||
json={"link": "https://del.com"}, headers=AUTH).get_json()
|
||||
r = client.delete(f'/tunes/{tune["id"]}/references/{ref["id"]}', headers=AUTH)
|
||||
assert r.status_code == 200
|
||||
|
||||
|
||||
# ── Musicians on references ───────────────────────────────────
|
||||
|
||||
class TestReferenceMusicians:
|
||||
def _make_ref(self, client, tune_id):
|
||||
r = client.post(f'/tunes/{tune_id}/references',
|
||||
json={"link": "https://example.com"}, headers=AUTH)
|
||||
return r.get_json()
|
||||
|
||||
def test_add_musician_to_reference(self, client):
|
||||
tune = make_tune(client)
|
||||
ref = self._make_ref(client, tune["id"])
|
||||
m = make_musician(client, "Tony Rice")
|
||||
r = client.post(f'/tunes/{tune["id"]}/references/{ref["id"]}/musicians',
|
||||
json={"musician_id": m["id"]}, headers=AUTH)
|
||||
assert r.status_code == 200
|
||||
names = [x["name"] for x in r.get_json()["musicians"]]
|
||||
assert "Tony Rice" in names
|
||||
|
||||
def test_remove_musician_from_reference(self, client):
|
||||
tune = make_tune(client)
|
||||
m = make_musician(client, "Tony Rice")
|
||||
ref = self._make_ref(client, tune["id"])
|
||||
# add musician via the join endpoint
|
||||
client.post(f'/tunes/{tune["id"]}/references/{ref["id"]}/musicians',
|
||||
json={"musician_id": m["id"]}, headers=AUTH)
|
||||
r = client.delete(
|
||||
f'/tunes/{tune["id"]}/references/{ref["id"]}/musicians/{m["id"]}',
|
||||
headers=AUTH)
|
||||
assert r.status_code == 200
|
||||
assert r.get_json()["musicians"] == []
|
||||
188
tests/test_tunes.py
Normal file
188
tests/test_tunes.py
Normal file
|
|
@ -0,0 +1,188 @@
|
|||
"""Tests for /tunes endpoints."""
|
||||
from tests.conftest import AUTH, BAD_AUTH
|
||||
|
||||
|
||||
def create_tune(client, **kwargs):
|
||||
data = {"name": "Cluck Old Hen", "key": "A", **kwargs}
|
||||
r = client.post("/tunes/", json=data, headers=AUTH)
|
||||
assert r.status_code == 201, r.get_json()
|
||||
return r.get_json()
|
||||
|
||||
|
||||
def get_instrument_id(client, name):
|
||||
instruments = client.get("/instruments").get_json()
|
||||
return next(i["id"] for i in instruments if i["name"] == name)
|
||||
|
||||
|
||||
# ── Auth ──────────────────────────────────────────────────────
|
||||
|
||||
class TestAuth:
|
||||
def test_missing_key_rejected_on_write(self, client):
|
||||
r = client.post("/tunes/", json={"name": "Test"})
|
||||
assert r.status_code == 401
|
||||
|
||||
def test_wrong_key_rejected_on_write(self, client):
|
||||
r = client.post("/tunes/", json={"name": "Test"}, headers=BAD_AUTH)
|
||||
assert r.status_code == 401
|
||||
|
||||
def test_reads_are_public(self, client):
|
||||
assert client.get("/tunes/").status_code == 200
|
||||
|
||||
def test_correct_key_accepted_on_write(self, client):
|
||||
r = client.post("/tunes/", json={"name": "Test"}, headers=AUTH)
|
||||
assert r.status_code == 201
|
||||
|
||||
|
||||
# ── Health ────────────────────────────────────────────────────
|
||||
|
||||
class TestHealth:
|
||||
def test_health(self, client):
|
||||
assert client.get("/health").get_json()["status"] == "ok"
|
||||
|
||||
|
||||
# ── Instruments lookup ────────────────────────────────────────
|
||||
|
||||
class TestInstrumentsLookup:
|
||||
def test_list_instruments(self, client):
|
||||
names = [i["name"] for i in client.get("/instruments").get_json()]
|
||||
assert "banjo" in names
|
||||
assert "fiddle" in names
|
||||
|
||||
|
||||
# ── Tune CRUD ─────────────────────────────────────────────────
|
||||
|
||||
class TestTuneCRUD:
|
||||
def test_create_minimal(self, client):
|
||||
tune = create_tune(client)
|
||||
assert tune["name"] == "Cluck Old Hen"
|
||||
assert tune["key"] == "A"
|
||||
assert tune["modal"] is None
|
||||
assert tune["source"] is None
|
||||
assert tune["instruments"] == []
|
||||
assert tune["notes"] == []
|
||||
assert tune["references"] == []
|
||||
|
||||
def test_create_with_instrument(self, client):
|
||||
banjo_id = get_instrument_id(client, "banjo")
|
||||
tuning = client.post("/tunings/", json={"name": "double C"}, headers=AUTH).get_json()
|
||||
tune = create_tune(client, instruments=[{
|
||||
"instrument_id": banjo_id,
|
||||
"tuning_id": tuning["id"],
|
||||
"callable": True,
|
||||
}])
|
||||
assert len(tune["instruments"]) == 1
|
||||
assert tune["instruments"][0]["instrument_name"] == "banjo"
|
||||
assert tune["instruments"][0]["tuning"] == "double C"
|
||||
|
||||
def test_create_with_two_instruments(self, client):
|
||||
banjo_id = get_instrument_id(client, "banjo")
|
||||
fiddle_id = get_instrument_id(client, "fiddle")
|
||||
tune = create_tune(client, instruments=[
|
||||
{"instrument_id": banjo_id, "tuning": "double C"},
|
||||
{"instrument_id": fiddle_id, "tuning": "cross"},
|
||||
])
|
||||
assert len(tune["instruments"]) == 2
|
||||
|
||||
def test_create_with_notes_and_references(self, client):
|
||||
tune = create_tune(client,
|
||||
notes=[{"note": "tricky B part"}],
|
||||
references=[{"link": "https://youtube.com/x", "site": "YouTube"}],
|
||||
)
|
||||
assert tune["notes"][0]["note"] == "tricky B part"
|
||||
assert tune["references"][0]["site"] == "YouTube"
|
||||
|
||||
def test_get_single(self, client):
|
||||
created = create_tune(client)
|
||||
r = client.get(f'/tunes/{created["id"]}', headers=AUTH)
|
||||
assert r.status_code == 200
|
||||
assert r.get_json()["id"] == created["id"]
|
||||
|
||||
def test_get_nonexistent_returns_404(self, client):
|
||||
assert client.get("/tunes/99999", headers=AUTH).status_code == 404
|
||||
|
||||
def test_list_returns_all(self, client):
|
||||
create_tune(client, name="Tune A")
|
||||
create_tune(client, name="Tune B")
|
||||
names = [t["name"] for t in client.get("/tunes/", headers=AUTH).get_json()]
|
||||
assert "Tune A" in names and "Tune B" in names
|
||||
|
||||
def test_patch_core_fields(self, client):
|
||||
tune = create_tune(client)
|
||||
r = client.patch(f'/tunes/{tune["id"]}',
|
||||
json={"key": "G", "name": "New Name", "modal": True},
|
||||
headers=AUTH)
|
||||
assert r.status_code == 200
|
||||
u = r.get_json()
|
||||
assert u["key"] == "G" and u["name"] == "New Name" and u["modal"] is True
|
||||
|
||||
def test_delete(self, client):
|
||||
tune = create_tune(client)
|
||||
r = client.delete(f'/tunes/{tune["id"]}', headers=AUTH)
|
||||
assert r.status_code == 200
|
||||
assert client.get(f'/tunes/{tune["id"]}', headers=AUTH).status_code == 404
|
||||
|
||||
def test_delete_cascades_to_instrument_entry(self, client):
|
||||
from app.models import TuneByInstrument
|
||||
from app.extensions import db
|
||||
banjo_id = get_instrument_id(client, "banjo")
|
||||
tune = create_tune(client, instruments=[{"instrument_id": banjo_id}])
|
||||
client.delete(f'/tunes/{tune["id"]}', headers=AUTH)
|
||||
assert db.session.query(TuneByInstrument).filter_by(tune_id=tune["id"]).count() == 0
|
||||
|
||||
|
||||
# ── Filtering ─────────────────────────────────────────────────
|
||||
|
||||
class TestFiltering:
|
||||
def test_filter_by_key(self, client):
|
||||
create_tune(client, name="D tune", key="D")
|
||||
create_tune(client, name="G tune", key="G")
|
||||
results = client.get("/tunes/?key=D", headers=AUTH).get_json()
|
||||
assert all(t["key"] == "D" for t in results)
|
||||
|
||||
def test_search_by_name(self, client):
|
||||
create_tune(client, name="Whiskey Before Breakfast")
|
||||
create_tune(client, name="Salt Creek")
|
||||
results = client.get("/tunes/?search=whiskey", headers=AUTH).get_json()
|
||||
assert len(results) == 1
|
||||
assert results[0]["name"] == "Whiskey Before Breakfast"
|
||||
|
||||
def test_filter_by_instrument(self, client):
|
||||
banjo_id = get_instrument_id(client, "banjo")
|
||||
create_tune(client, name="Has Banjo",
|
||||
instruments=[{"instrument_id": banjo_id, "tuning": "standard"}])
|
||||
create_tune(client, name="No Banjo")
|
||||
results = client.get(f"/tunes/?instrument_id={banjo_id}", headers=AUTH).get_json()
|
||||
names = [t["name"] for t in results]
|
||||
assert "Has Banjo" in names and "No Banjo" not in names
|
||||
|
||||
def test_filter_by_tuning(self, client):
|
||||
fiddle_id = get_instrument_id(client, "fiddle")
|
||||
cross_tuning = client.post("/tunings/", json={"name": "cross"}, headers=AUTH).get_json()
|
||||
std_tuning = client.post("/tunings/", json={"name": "standard"}, headers=AUTH).get_json()
|
||||
create_tune(client, name="Cross", instruments=[{"instrument_id": fiddle_id, "tuning_id": cross_tuning["id"]}])
|
||||
create_tune(client, name="Std", instruments=[{"instrument_id": fiddle_id, "tuning_id": std_tuning["id"]}])
|
||||
results = client.get(f'/tunes/?instrument_id={fiddle_id}&tuning={cross_tuning["id"]}', headers=AUTH).get_json()
|
||||
assert len(results) == 1 and results[0]["name"] == "Cross"
|
||||
|
||||
def test_filter_callable(self, client):
|
||||
banjo_id = get_instrument_id(client, "banjo")
|
||||
create_tune(client, name="Can call", instruments=[{"instrument_id": banjo_id, "callable": True}])
|
||||
create_tune(client, name="Cannot call", instruments=[{"instrument_id": banjo_id, "callable": False}])
|
||||
results = client.get(f"/tunes/?instrument_id={banjo_id}&callable=true", headers=AUTH).get_json()
|
||||
names = [t["name"] for t in results]
|
||||
assert "Can call" in names and "Cannot call" not in names
|
||||
|
||||
def test_filter_to_learn(self, client):
|
||||
fiddle_id = get_instrument_id(client, "fiddle")
|
||||
create_tune(client, name="To learn", instruments=[{"instrument_id": fiddle_id, "to_learn": True}])
|
||||
create_tune(client, name="Learned", instruments=[{"instrument_id": fiddle_id, "to_learn": False}])
|
||||
results = client.get(f"/tunes/?instrument_id={fiddle_id}&to_learn=true", headers=AUTH).get_json()
|
||||
names = [t["name"] for t in results]
|
||||
assert "To learn" in names and "Learned" not in names
|
||||
|
||||
def test_filter_modal(self, client):
|
||||
create_tune(client, name="Modal", modal=True)
|
||||
create_tune(client, name="Regular", modal=False)
|
||||
results = client.get("/tunes/?modal=true", headers=AUTH).get_json()
|
||||
names = [t["name"] for t in results]
|
||||
assert "Modal" in names and "Regular" not in names
|
||||
Loading…
Add table
Add a link
Reference in a new issue