"""routers/api_widgets.py's POST .../lock endpoint (models.PhotoWidgetConfig. locked) -- same permission shape as test_widget_border.py's own endpoint, just for a photos-only field instead of a shared Widget-level one.""" from __future__ import annotations import time from app.models import Frame, PhotoWidgetConfig, StaticWidgetConfig, Widget from .conftest import csrf_headers, link_user, login, make_user def _widget_id(db_session, widget_type="photos") -> int: return db_session.query(Widget).filter_by(frame_id=1, widget_type=widget_type).one().id def test_new_widget_defaults_to_unlocked(db_session): widget_id = _widget_id(db_session) assert db_session.get(PhotoWidgetConfig, widget_id).locked is False def test_set_lock_persists(client, db_session): client.post("/setup", data={"username": "alice", "password": "hunter22"}) widget_id = _widget_id(db_session) resp = client.post(f"/api/frames/1/widgets/{widget_id}/lock", json={"locked": True}, headers=csrf_headers(client)) assert resp.status_code == 200, resp.text assert resp.json() == {"status": "saved", "locked": True} assert db_session.get(PhotoWidgetConfig, widget_id).locked is True resp = client.post(f"/api/frames/1/widgets/{widget_id}/lock", json={"locked": False}, headers=csrf_headers(client)) assert resp.status_code == 200, resp.text assert db_session.get(PhotoWidgetConfig, widget_id).locked is False def test_set_lock_404s_for_unknown_widget(client, db_session): client.post("/setup", data={"username": "alice", "password": "hunter22"}) resp = client.post("/api/frames/1/widgets/999999/lock", json={"locked": True}, headers=csrf_headers(client)) assert resp.status_code == 404 def test_set_lock_rejects_non_photos_widget(client, db_session): client.post("/setup", data={"username": "alice", "password": "hunter22"}) # Built directly rather than through the placement-validated create # endpoint -- the default frame's photos widget already covers the # whole grid, and overlap isn't the thing under test here. static_widget = Widget(frame_id=1, widget_type="static", x=0, y=0, w=1, h=1, sort_order=99, created_at=time.time()) db_session.add(static_widget) db_session.flush() db_session.add(StaticWidgetConfig(widget_id=static_widget.id)) db_session.commit() resp = client.post(f"/api/frames/1/widgets/{static_widget.id}/lock", json={"locked": True}, headers=csrf_headers(client)) assert resp.status_code == 400 def test_set_lock_unrelated_user_404s(client, db_session): client.post("/setup", data={"username": "alice", "password": "hunter22"}) make_user(db_session, "mallory") widget_id = _widget_id(db_session) client.cookies.clear() login(client, "mallory") resp = client.post(f"/api/frames/1/widgets/{widget_id}/lock", json={"locked": True}, headers=csrf_headers(client)) assert resp.status_code == 404 assert db_session.get(PhotoWidgetConfig, widget_id).locked is False def test_set_lock_linked_but_not_controlling_user_409s(client, db_session): client.post("/setup", data={"username": "alice", "password": "hunter22"}) bob = make_user(db_session, "bob") frame = db_session.get(Frame, 1) link_user(db_session, bob, frame) widget_id = _widget_id(db_session) client.cookies.clear() login(client, "bob") resp = client.post(f"/api/frames/1/widgets/{widget_id}/lock", json={"locked": True}, headers=csrf_headers(client)) assert resp.status_code == 409 assert resp.json()["detail"]["error"] == "not_controller" def test_widgets_list_reports_locked_state(client, db_session): client.post("/setup", data={"username": "alice", "password": "hunter22"}) widget_id = _widget_id(db_session) resp = client.get("/api/frames/1/widgets") assert resp.status_code == 200, resp.text assert next(w for w in resp.json()["widgets"] if w["id"] == widget_id)["locked"] is False client.post(f"/api/frames/1/widgets/{widget_id}/lock", json={"locked": True}, headers=csrf_headers(client)) resp = client.get("/api/frames/1/widgets") assert next(w for w in resp.json()["widgets"] if w["id"] == widget_id)["locked"] is True