feat: add announced checkbox to track rows

Add a persistent "announced" checkbox after each track's Announce button.
The state is stored in a new `announced` column on `show_tracks` and is
auto-set when the Announce button is pressed. The checkbox is also freely
togglable, and announced tracks have their Announce button disabled.

Also fixes .env leakage in test_config.py (pass _env_file=None) and adds
tests for the new DB method, API endpoint, and announce side-effect.

Made-with: Cursor
This commit is contained in:
cottongin
2026-04-01 22:56:43 -04:00
parent a5f77187b3
commit 11f13c86b5
7 changed files with 218 additions and 17 deletions

View File

@@ -2,7 +2,7 @@ from ntr_fetcher.config import Settings
def test_settings_defaults():
settings = Settings(admin_token="test-secret")
settings = Settings(admin_token="test-secret", _env_file=None)
assert settings.port == 8000
assert settings.host == "127.0.0.1"
assert settings.db_path == "./ntr_fetcher.db"
@@ -17,7 +17,7 @@ def test_settings_from_env(monkeypatch):
monkeypatch.setenv("NTR_HOST", "0.0.0.0")
monkeypatch.setenv("NTR_ADMIN_TOKEN", "my-secret")
monkeypatch.setenv("NTR_SOUNDCLOUD_USER", "someoneelse")
settings = Settings()
settings = Settings(_env_file=None)
assert settings.port == 9090
assert settings.host == "0.0.0.0"
assert settings.admin_token == "my-secret"
@@ -27,7 +27,7 @@ def test_settings_from_env(monkeypatch):
def test_settings_admin_token_required():
import pytest
with pytest.raises(Exception):
Settings()
Settings(_env_file=None)
def test_dashboard_config_absent(monkeypatch):
@@ -35,7 +35,7 @@ def test_dashboard_config_absent(monkeypatch):
monkeypatch.delenv("NTR_WEB_USER", raising=False)
monkeypatch.delenv("NTR_WEB_PASSWORD", raising=False)
monkeypatch.delenv("NTR_SECRET_KEY", raising=False)
s = Settings()
s = Settings(_env_file=None)
assert s.web_user is None
assert s.web_password is None
assert s.secret_key is None
@@ -47,7 +47,7 @@ def test_dashboard_config_present(monkeypatch):
monkeypatch.setenv("NTR_WEB_USER", "nick")
monkeypatch.setenv("NTR_WEB_PASSWORD", "secret")
monkeypatch.setenv("NTR_SECRET_KEY", "signme")
s = Settings()
s = Settings(_env_file=None)
assert s.web_user == "nick"
assert s.web_password == "secret"
assert s.secret_key == "signme"

View File

@@ -187,3 +187,66 @@ def test_ws_subscribe_with_invalid_token(app):
ws.send_json({"type": "subscribe", "token": "wrong"})
with pytest.raises(Exception):
ws.receive_json()
# --- Announced endpoint tests ---
def test_announce_sets_announced_flag(client, db):
show = _seed_show(db)
resp = client.post(
"/admin/announce",
json={"show_id": show.id, "position": 1},
headers={"Authorization": "Bearer test-token"},
)
assert resp.status_code == 200
tracks = db.get_show_tracks(show.id)
assert tracks[0]["announced"] == 1
def test_set_announced_with_bearer(client, db):
show = _seed_show(db)
resp = client.post(
"/admin/announced",
json={"show_id": show.id, "position": 1, "announced": True},
headers={"Authorization": "Bearer test-token"},
)
assert resp.status_code == 200
assert resp.json()["status"] == "ok"
tracks = db.get_show_tracks(show.id)
assert tracks[0]["announced"] == 1
def test_set_announced_toggle_off(client, db):
show = _seed_show(db)
client.post(
"/admin/announced",
json={"show_id": show.id, "position": 1, "announced": True},
headers={"Authorization": "Bearer test-token"},
)
resp = client.post(
"/admin/announced",
json={"show_id": show.id, "position": 1, "announced": False},
headers={"Authorization": "Bearer test-token"},
)
assert resp.status_code == 200
tracks = db.get_show_tracks(show.id)
assert tracks[0]["announced"] == 0
def test_set_announced_without_auth(client, db):
_seed_show(db)
resp = client.post(
"/admin/announced",
json={"show_id": 1, "position": 1, "announced": True},
)
assert resp.status_code == 401
def test_set_announced_invalid_position(client, db):
_seed_show(db)
resp = client.post(
"/admin/announced",
json={"show_id": 1, "position": 99, "announced": True},
headers={"Authorization": "Bearer test-token"},
)
assert resp.status_code == 404

View File

@@ -295,3 +295,51 @@ def test_has_track_in_show(db):
db.set_show_tracks(show.id, [t1.id])
assert db.has_track_in_show(show.id, 1) is True
assert db.has_track_in_show(show.id, 999) is False
def test_announced_defaults_to_zero(db):
week_start = datetime(2026, 3, 13, 2, 0, 0, tzinfo=timezone.utc)
week_end = datetime(2026, 3, 20, 2, 0, 0, tzinfo=timezone.utc)
show = db.get_or_create_show(week_start, week_end)
t1 = _make_track(1, "2026-03-14T01:00:00+00:00")
db.upsert_track(t1)
db.set_show_tracks(show.id, [t1.id])
tracks = db.get_show_tracks(show.id)
assert tracks[0]["announced"] == 0
def test_set_track_announced(db):
week_start = datetime(2026, 3, 13, 2, 0, 0, tzinfo=timezone.utc)
week_end = datetime(2026, 3, 20, 2, 0, 0, tzinfo=timezone.utc)
show = db.get_or_create_show(week_start, week_end)
t1 = _make_track(1, "2026-03-14T01:00:00+00:00")
db.upsert_track(t1)
db.set_show_tracks(show.id, [t1.id])
db.set_track_announced(show.id, 1, True)
tracks = db.get_show_tracks(show.id)
assert tracks[0]["announced"] == 1
def test_set_track_announced_toggle_off(db):
week_start = datetime(2026, 3, 13, 2, 0, 0, tzinfo=timezone.utc)
week_end = datetime(2026, 3, 20, 2, 0, 0, tzinfo=timezone.utc)
show = db.get_or_create_show(week_start, week_end)
t1 = _make_track(1, "2026-03-14T01:00:00+00:00")
db.upsert_track(t1)
db.set_show_tracks(show.id, [t1.id])
db.set_track_announced(show.id, 1, True)
db.set_track_announced(show.id, 1, False)
tracks = db.get_show_tracks(show.id)
assert tracks[0]["announced"] == 0
def test_get_show_track_by_position_includes_announced(db):
week_start = datetime(2026, 3, 13, 2, 0, 0, tzinfo=timezone.utc)
week_end = datetime(2026, 3, 20, 2, 0, 0, tzinfo=timezone.utc)
show = db.get_or_create_show(week_start, week_end)
t1 = _make_track(1, "2026-03-14T01:00:00+00:00")
db.upsert_track(t1)
db.set_show_tracks(show.id, [t1.id])
db.set_track_announced(show.id, 1, True)
result = db.get_show_track_by_position(show.id, 1)
assert result["announced"] == 1