Drop the last legacy widget-system and shared-token auth scaffolding
Firmware build check / build-check (push) Successful in 5m37s
Build and release firmware / build-and-release (push) Successful in 5m36s
Build and push server image / test (push) Successful in 1m37s
Build and push server image / build-and-push (push) Successful in 4m18s
Build and push server image / deploy (push) Failing after 1m20s
Firmware build check / build-check (push) Successful in 5m37s
Build and release firmware / build-and-release (push) Successful in 5m36s
Build and push server image / test (push) Successful in 1m37s
Build and push server image / build-and-push (push) Successful in 4m18s
Build and push server image / deploy (push) Failing after 1m20s
Server: migration 41 drops the pre-widget-system Frame columns (mode/album_id/current_asset_id/queue/calendar_*/whiteboard_*, etc) docs/widgets.md flagged as the deliberately-deferred Phase 6 cleanup, with a raw-SQL backfill safety net for any frame that still somehow lacks a Widget. Also drops legacy_token_enabled and the shared MANAGEMENT_TOKEN fallback it gated in require_device/require_browser -- the per-frame manage_token/device_token flow (and the /m/ page) fully supersede it now; MANAGEMENT_TOKEN's only remaining role is the optional pre-setup claim gate. Confirmed with the maintainer that the deployed frame is already off the shared token before removing the server-side fallback. Firmware: the captive portal's "Access Token" field and its NVS/ build_url plumbing only ever mattered for pointing new firmware at an old pre-multi-frame server -- gone along with the server-side fallback it fed. Version bump to publish the change.
This commit is contained in:
@@ -112,6 +112,19 @@ def link_user(db: Session, user: User, frame: Frame) -> None:
|
||||
db.flush()
|
||||
|
||||
|
||||
def claim_device(db: Session, frame: Frame, device_id: str = "001122334455",
|
||||
token: str = "devtok-1") -> str:
|
||||
"""Gives `frame` device credentials and returns the "id=...&token=..."
|
||||
query string real firmware always sends -- require_device has no
|
||||
fallback for a bare /frame/* request without ?id= (the old shared-
|
||||
MANAGEMENT_TOKEN/no-id path this project used to resolve to a single
|
||||
legacy frame is gone), so any device-facing test needs this."""
|
||||
frame.device_id = device_id
|
||||
frame.device_token = token
|
||||
db.commit()
|
||||
return f"id={device_id}&token={token}"
|
||||
|
||||
|
||||
def login(client: TestClient, username: str, password: str = "testpass123") -> None:
|
||||
resp = client.post("/login", data={"username": username, "password": password})
|
||||
assert resp.status_code == 303, resp.text
|
||||
|
||||
@@ -26,6 +26,8 @@ from app.models import (
|
||||
Widget,
|
||||
)
|
||||
|
||||
from .conftest import claim_device
|
||||
|
||||
EXPECTED_BYTES = 800 * 480 // 2
|
||||
_ASSETS = [{"id": "asset-1"}, {"id": "asset-2"}, {"id": "asset-3"}]
|
||||
|
||||
@@ -41,16 +43,17 @@ def _mock_immich(monkeypatch):
|
||||
def test_unclaimed_frame_shows_placeholder(client, db_session):
|
||||
frame = db_session.get(Frame, 1)
|
||||
frame.owner_user_id = None
|
||||
db_session.commit()
|
||||
creds = claim_device(db_session, frame)
|
||||
|
||||
resp = client.get("/frame/image")
|
||||
resp = client.get(f"/frame/image?{creds}")
|
||||
assert resp.status_code == 200
|
||||
assert len(resp.content) == EXPECTED_BYTES
|
||||
|
||||
|
||||
def test_claimed_frame_with_unconfigured_photo_widget_still_renders(client, db_session):
|
||||
client.post("/setup", data={"username": "alice", "password": "hunter22"})
|
||||
resp = client.get("/frame/image")
|
||||
creds = claim_device(db_session, db_session.get(Frame, 1))
|
||||
resp = client.get(f"/frame/image?{creds}")
|
||||
assert resp.status_code == 200
|
||||
assert len(resp.content) == EXPECTED_BYTES
|
||||
|
||||
@@ -66,24 +69,24 @@ def test_configured_photo_widget_renders_and_advances_via_button(client, db_sess
|
||||
widget = db_session.query(Widget).filter_by(frame_id=frame.id, widget_type="photos").one()
|
||||
cfg = db_session.get(PhotoWidgetConfig, widget.id)
|
||||
cfg.album_id = "album-1"
|
||||
db_session.commit()
|
||||
creds = claim_device(db_session, frame)
|
||||
|
||||
_mock_immich(monkeypatch)
|
||||
|
||||
resp = client.get("/frame/image")
|
||||
resp = client.get(f"/frame/image?{creds}")
|
||||
assert resp.status_code == 200
|
||||
assert len(resp.content) == EXPECTED_BYTES
|
||||
|
||||
db_session.refresh(cfg)
|
||||
assert cfg.current_asset_id == "asset-1"
|
||||
|
||||
resp = client.post("/frame/advance")
|
||||
resp = client.post(f"/frame/advance?{creds}")
|
||||
assert resp.status_code == 200
|
||||
assert len(resp.content) == EXPECTED_BYTES
|
||||
db_session.refresh(cfg)
|
||||
assert cfg.current_asset_id != "asset-1" # the default next->advance binding fired
|
||||
|
||||
resp = client.post("/frame/back")
|
||||
resp = client.post(f"/frame/back?{creds}")
|
||||
assert resp.status_code == 200
|
||||
db_session.refresh(cfg)
|
||||
assert cfg.current_asset_id == "asset-1" # back undid it
|
||||
@@ -94,11 +97,11 @@ def test_manage_flag_still_returns_a_valid_image(client, db_session, monkeypatch
|
||||
frame = db_session.get(Frame, 1)
|
||||
widget = db_session.query(Widget).filter_by(frame_id=frame.id, widget_type="photos").one()
|
||||
db_session.get(PhotoWidgetConfig, widget.id).album_id = "album-1"
|
||||
db_session.commit()
|
||||
creds = claim_device(db_session, frame)
|
||||
_mock_immich(monkeypatch)
|
||||
|
||||
plain = client.get("/frame/image").content
|
||||
with_manage = client.get("/frame/image?manage=1").content
|
||||
plain = client.get(f"/frame/image?{creds}").content
|
||||
with_manage = client.get(f"/frame/image?{creds}&manage=1").content
|
||||
assert len(with_manage) == EXPECTED_BYTES
|
||||
assert with_manage != plain # the manage-QR overlay actually got composited in
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ from app.models import (
|
||||
WhiteboardWidgetConfig,
|
||||
)
|
||||
|
||||
from .conftest import csrf_headers, link_user, login, make_user
|
||||
from .conftest import claim_device, csrf_headers, link_user, login, make_user
|
||||
|
||||
EXPECTED_BYTES = 800 * 480 // 2
|
||||
|
||||
@@ -113,7 +113,8 @@ def test_save_logged_out_401s(client, db_session):
|
||||
|
||||
def test_global_next_is_a_noop_when_unset(client, db_session):
|
||||
client.post("/setup", data={"username": "alice", "password": "hunter22"})
|
||||
resp = client.post("/frame/global-next")
|
||||
creds = claim_device(db_session, db_session.get(Frame, 1))
|
||||
resp = client.post(f"/frame/global-next?{creds}")
|
||||
assert resp.status_code == 200
|
||||
assert len(resp.content) == EXPECTED_BYTES
|
||||
|
||||
@@ -123,9 +124,9 @@ def test_global_next_runs_the_configured_action(client, db_session):
|
||||
frame = db_session.get(Frame, 1)
|
||||
photo_widget_id = db_session.query(Widget).filter_by(frame_id=frame.id, widget_type="photos").one().id
|
||||
frame.next_hold_action = "toggle_all_photo_locks"
|
||||
db_session.commit()
|
||||
creds = claim_device(db_session, frame)
|
||||
|
||||
resp = client.post("/frame/global-next")
|
||||
resp = client.post(f"/frame/global-next?{creds}")
|
||||
assert resp.status_code == 200
|
||||
assert len(resp.content) == EXPECTED_BYTES
|
||||
assert db_session.get(PhotoWidgetConfig, photo_widget_id).locked is True
|
||||
@@ -136,9 +137,9 @@ def test_global_back_runs_the_configured_action(client, db_session):
|
||||
frame = db_session.get(Frame, 1)
|
||||
photo_widget_id = db_session.query(Widget).filter_by(frame_id=frame.id, widget_type="photos").one().id
|
||||
frame.back_hold_action = "toggle_all_photo_locks"
|
||||
db_session.commit()
|
||||
creds = claim_device(db_session, frame)
|
||||
|
||||
resp = client.post("/frame/global-back")
|
||||
resp = client.post(f"/frame/global-back?{creds}")
|
||||
assert resp.status_code == 200
|
||||
assert db_session.get(PhotoWidgetConfig, photo_widget_id).locked is True
|
||||
|
||||
@@ -149,9 +150,9 @@ def test_global_next_with_an_unrecognized_stored_action_is_a_noop(client, db_ses
|
||||
client.post("/setup", data={"username": "alice", "password": "hunter22"})
|
||||
frame = db_session.get(Frame, 1)
|
||||
frame.next_hold_action = "no_longer_exists"
|
||||
db_session.commit()
|
||||
creds = claim_device(db_session, frame)
|
||||
|
||||
resp = client.post("/frame/global-next")
|
||||
resp = client.post(f"/frame/global-next?{creds}")
|
||||
assert resp.status_code == 200
|
||||
assert len(resp.content) == EXPECTED_BYTES
|
||||
|
||||
|
||||
+105
-29
@@ -28,6 +28,60 @@ from app.models import (
|
||||
|
||||
from .conftest import make_user
|
||||
|
||||
# Columns migration 41 drops from `frames` -- a fresh-install create_all()
|
||||
# copy (what every db_session fixture starts from) already reflects
|
||||
# today's models.py, i.e. the post-41 shape without these, so a test that
|
||||
# wants to simulate a pre-41 database has to add them back itself before
|
||||
# setting schema_version below 41 and calling run_migrations() -- same
|
||||
# "frames isn't dropped/recreated by these replay tests" situation
|
||||
# test_migration_29/30's own comments describe, just for columns being
|
||||
# removed instead of added.
|
||||
_LEGACY_FRAME_COLUMNS = [
|
||||
"mode TEXT NOT NULL DEFAULT 'photos'",
|
||||
"album_id TEXT NOT NULL DEFAULT ''",
|
||||
"photo_order TEXT NOT NULL DEFAULT 'sequential'",
|
||||
"display_mode TEXT NOT NULL DEFAULT 'crop_faces'",
|
||||
"queue_target_len INTEGER NOT NULL DEFAULT 20",
|
||||
"current_asset_id TEXT NOT NULL DEFAULT ''",
|
||||
"current_asset_set_at REAL NOT NULL DEFAULT 0.0",
|
||||
"queue TEXT NOT NULL DEFAULT '[]'",
|
||||
"queue_cursor INTEGER NOT NULL DEFAULT 0",
|
||||
"history TEXT NOT NULL DEFAULT '[]'",
|
||||
"excluded_asset_ids TEXT NOT NULL DEFAULT '[]'",
|
||||
"calendar_view TEXT NOT NULL DEFAULT 'agenda'",
|
||||
"calendar_week_start INTEGER NOT NULL DEFAULT 0",
|
||||
"calendar_photo_inlay INTEGER NOT NULL DEFAULT 0",
|
||||
"calendar_browse_offset INTEGER NOT NULL DEFAULT 0",
|
||||
"calendar_checked_at REAL NOT NULL DEFAULT 0.0",
|
||||
"calendar_cached_events TEXT",
|
||||
"calendar_fetch_summary TEXT NOT NULL DEFAULT ''",
|
||||
"calendar_weather_enabled INTEGER NOT NULL DEFAULT 0",
|
||||
"calendar_weather_units TEXT NOT NULL DEFAULT 'fahrenheit'",
|
||||
"calendar_weather_cities TEXT",
|
||||
"calendar_weather_checked_at REAL NOT NULL DEFAULT 0.0",
|
||||
"calendar_weather_cached TEXT",
|
||||
"calendar_week_days INTEGER NOT NULL DEFAULT 7",
|
||||
"calendar_week_layout TEXT NOT NULL DEFAULT 'horizontal'",
|
||||
"calendar_week_start_offset INTEGER NOT NULL DEFAULT 0",
|
||||
"calendar_tasks_enabled INTEGER NOT NULL DEFAULT 0",
|
||||
"calendar_tasks_user_id INTEGER REFERENCES users(id) ON DELETE SET NULL",
|
||||
"calendar_tasks_calendar_key TEXT",
|
||||
"calendar_tasks_checked_at REAL NOT NULL DEFAULT 0.0",
|
||||
"calendar_tasks_cached TEXT",
|
||||
"whiteboard_user_id INTEGER REFERENCES users(id) ON DELETE SET NULL",
|
||||
"whiteboard_url TEXT NOT NULL DEFAULT ''",
|
||||
"whiteboard_checked_at REAL NOT NULL DEFAULT 0.0",
|
||||
"whiteboard_cached_image BLOB",
|
||||
"legacy_token_enabled INTEGER NOT NULL DEFAULT 0",
|
||||
]
|
||||
|
||||
|
||||
def _add_legacy_frame_columns(conn) -> None:
|
||||
existing = {c["name"] for c in inspect(db_module.engine).get_columns("frames")}
|
||||
for col_def in _LEGACY_FRAME_COLUMNS:
|
||||
if col_def.split()[0] not in existing:
|
||||
conn.execute(text(f"ALTER TABLE frames ADD COLUMN {col_def}"))
|
||||
|
||||
|
||||
def test_migrations_list_is_sequential_and_unique():
|
||||
versions = [v for v, _ in MIGRATIONS]
|
||||
@@ -72,8 +126,6 @@ def test_expected_columns_exist_on_current_schema():
|
||||
assert "webdav_base_url" in user_columns # migration 15
|
||||
assert "webdav_username" in user_columns # migration 14
|
||||
assert "calendar_caldav_url" in user_columns
|
||||
assert "whiteboard_cached_image" in frame_columns # migration 14
|
||||
assert "calendar_week_start_offset" in frame_columns
|
||||
assert "name" in task_widget_columns # migration 19
|
||||
assert "static_widget_configs" in inspector.get_table_names() # migration 20
|
||||
assert "text_widget_configs" in inspector.get_table_names() # migration 21
|
||||
@@ -106,14 +158,16 @@ def test_expected_columns_exist_on_current_schema():
|
||||
assert "render_style" in calendar_widget_columns # migration 38
|
||||
assert "theme" in frame_columns # migration 39
|
||||
assert "font_scale" in widget_columns # migration 40
|
||||
assert not {"mode", "album_id", "current_asset_id", "calendar_view", "whiteboard_url",
|
||||
"legacy_token_enabled"} & frame_columns # migration 41
|
||||
|
||||
|
||||
# --- widget system backfill (migration 16 + _ensure_widgets_backfilled) ---
|
||||
# --- widget system: fresh-install default widget, and migration 41's
|
||||
# raw-SQL backfill safety net for a pre-widget-system database ---
|
||||
|
||||
|
||||
def test_fresh_install_backfills_one_photos_widget_with_default_buttons(db_session):
|
||||
def test_fresh_install_creates_a_default_photos_widget_with_default_buttons(db_session):
|
||||
frame = db_session.get(Frame, 1)
|
||||
assert frame.mode == "photos"
|
||||
|
||||
widgets = db_session.scalars(select(Widget).where(Widget.frame_id == frame.id)).all()
|
||||
assert len(widgets) == 1
|
||||
@@ -123,7 +177,7 @@ def test_fresh_install_backfills_one_photos_widget_with_default_buttons(db_sessi
|
||||
|
||||
config = db_session.get(PhotoWidgetConfig, widget.id)
|
||||
assert config is not None
|
||||
assert config.album_id == frame.album_id
|
||||
assert config.album_id == ""
|
||||
|
||||
actions = db_session.scalars(select(FrameButtonAction).where(FrameButtonAction.frame_id == frame.id)).all()
|
||||
assert {a.button: a.action for a in actions} == {"next": "advance", "back": "back"}
|
||||
@@ -140,17 +194,26 @@ def test_rerunning_migrations_does_not_duplicate_widgets(db_session):
|
||||
def test_calendar_photo_inlay_frame_backfills_into_two_widgets(db_session):
|
||||
"""Reproduces the old fixed 50/50 inlay split as two independent,
|
||||
non-overlapping widgets instead of silently dropping the photo half
|
||||
on upgrade -- see models.py's CalendarWidgetConfig docstring."""
|
||||
frame = Frame(
|
||||
name="Inlay Frame", device_token="tok-inlay", manage_token="mtok-inlay",
|
||||
mode="calendar", orientation="landscape", calendar_view="week",
|
||||
calendar_photo_inlay=True, album_id="album-123",
|
||||
current_asset_id="asset-1", queue=["asset-1", "asset-2"],
|
||||
created_at=time.time(),
|
||||
)
|
||||
on upgrade -- see models.py's CalendarWidgetConfig docstring. Exercises
|
||||
_migration_41's raw-SQL backfill safety net: a frame whose legacy
|
||||
Frame columns (pre-widget-system) still carry real data but which
|
||||
somehow has no Widget yet."""
|
||||
frame = Frame(name="Inlay Frame", device_token="tok-inlay", manage_token="mtok-inlay",
|
||||
orientation="landscape", created_at=time.time())
|
||||
db_session.add(frame)
|
||||
db_session.flush()
|
||||
frame_id = frame.id
|
||||
db_session.commit()
|
||||
|
||||
with db_module.engine.begin() as conn:
|
||||
_add_legacy_frame_columns(conn)
|
||||
conn.execute(text(
|
||||
"UPDATE frames SET mode='calendar', calendar_view='week', calendar_photo_inlay=1, "
|
||||
"album_id='album-123', current_asset_id='asset-1', queue='[\"asset-1\", \"asset-2\"]' "
|
||||
"WHERE id = :id"
|
||||
), {"id": frame_id})
|
||||
conn.execute(text("UPDATE schema_version SET version = 40"))
|
||||
|
||||
run_migrations()
|
||||
|
||||
widgets = db_session.scalars(
|
||||
@@ -180,15 +243,24 @@ def test_calendar_photo_inlay_frame_backfills_into_two_widgets(db_session):
|
||||
|
||||
|
||||
def test_whiteboard_frame_backfills_check_now_on_both_buttons(db_session):
|
||||
frame = Frame(
|
||||
name="WB Frame", device_token="tok-wb", manage_token="mtok-wb",
|
||||
mode="whiteboard", orientation="portrait",
|
||||
whiteboard_url="https://example.com/board.whiteboard",
|
||||
created_at=time.time(),
|
||||
)
|
||||
"""Exercises _migration_41's raw-SQL backfill safety net for a
|
||||
whiteboard-mode legacy frame -- same shape as the calendar-inlay case
|
||||
above, just the simpler single-widget mode dispatch branch."""
|
||||
frame = Frame(name="WB Frame", device_token="tok-wb", manage_token="mtok-wb",
|
||||
orientation="portrait", created_at=time.time())
|
||||
db_session.add(frame)
|
||||
db_session.flush()
|
||||
frame_id = frame.id
|
||||
db_session.commit()
|
||||
|
||||
with db_module.engine.begin() as conn:
|
||||
_add_legacy_frame_columns(conn)
|
||||
conn.execute(text(
|
||||
"UPDATE frames SET mode='whiteboard', "
|
||||
"whiteboard_url='https://example.com/board.whiteboard' WHERE id = :id"
|
||||
), {"id": frame_id})
|
||||
conn.execute(text("UPDATE schema_version SET version = 40"))
|
||||
|
||||
run_migrations()
|
||||
|
||||
widgets = db_session.scalars(select(Widget).where(Widget.frame_id == frame.id)).all()
|
||||
@@ -249,19 +321,23 @@ def test_migration_16_raw_sql_path_applies_to_an_existing_pre_widget_database(db
|
||||
conn.execute(text(
|
||||
"CREATE UNIQUE INDEX ix_frame_calendars_unique ON frame_calendars (frame_id, user_id, calendar_key)"
|
||||
))
|
||||
_add_legacy_frame_columns(conn)
|
||||
conn.execute(text("UPDATE schema_version SET version = 15"))
|
||||
|
||||
frame = Frame(
|
||||
name="Upgrading Frame", device_token="tok-up", manage_token="mtok-up",
|
||||
mode="photos", album_id="legacy-album", current_asset_id="legacy-asset",
|
||||
queue=["legacy-asset", "next-asset"], created_at=time.time(),
|
||||
)
|
||||
frame = Frame(name="Upgrading Frame", device_token="tok-up", manage_token="mtok-up",
|
||||
created_at=time.time())
|
||||
db_session.add(frame)
|
||||
db_session.flush()
|
||||
user = make_user(db_session, "legacy-owner")
|
||||
db_session.commit()
|
||||
frame_id, user_id = frame.id, user.id
|
||||
|
||||
with db_module.engine.begin() as conn:
|
||||
conn.execute(text(
|
||||
"UPDATE frames SET mode='photos', album_id='legacy-album', current_asset_id='legacy-asset', "
|
||||
"queue='[\"legacy-asset\", \"next-asset\"]' WHERE id = :id"
|
||||
), {"id": frame_id})
|
||||
|
||||
# An orphaned frame_calendars row (this frame's mode was never
|
||||
# "calendar", so it has no calendar widget for _ensure_frame_
|
||||
# calendars_rekeyed to attach it to) -- exercises that it's dropped
|
||||
@@ -533,12 +609,11 @@ def test_frame_calendars_rekey_attaches_existing_rows_to_their_calendar_widget(d
|
||||
conn.execute(text(
|
||||
"CREATE UNIQUE INDEX ix_frame_calendars_unique ON frame_calendars (frame_id, user_id, calendar_key)"
|
||||
))
|
||||
_add_legacy_frame_columns(conn)
|
||||
conn.execute(text("UPDATE schema_version SET version = 15"))
|
||||
|
||||
frame = Frame(
|
||||
name="Calendar Frame", device_token="tok-cal", manage_token="mtok-cal",
|
||||
mode="calendar", created_at=time.time(),
|
||||
)
|
||||
frame = Frame(name="Calendar Frame", device_token="tok-cal", manage_token="mtok-cal",
|
||||
created_at=time.time())
|
||||
db_session.add(frame)
|
||||
db_session.flush()
|
||||
user = make_user(db_session, "cal-owner")
|
||||
@@ -546,6 +621,7 @@ def test_frame_calendars_rekey_attaches_existing_rows_to_their_calendar_widget(d
|
||||
frame_id, user_id = frame.id, user.id
|
||||
|
||||
with db_module.engine.begin() as conn:
|
||||
conn.execute(text("UPDATE frames SET mode='calendar' WHERE id = :id"), {"id": frame_id})
|
||||
conn.execute(text(
|
||||
"INSERT INTO frame_calendars (frame_id, user_id, calendar_key, calendar_label, included, color_index) "
|
||||
"VALUES (:frame_id, :user_id, 'ics', 'Legacy Cal', 1, 3)"
|
||||
|
||||
@@ -14,7 +14,7 @@ from PIL import Image
|
||||
from app.image_pipeline import logical_render_size
|
||||
from app.models import Frame
|
||||
|
||||
from .conftest import link_user, login, make_user
|
||||
from .conftest import claim_device, link_user, login, make_user
|
||||
|
||||
|
||||
def test_now_displaying_404s_before_any_device_fetch(client, db_session):
|
||||
@@ -27,8 +27,9 @@ def test_now_displaying_404s_before_any_device_fetch(client, db_session):
|
||||
def test_frame_image_records_now_displaying(client, db_session):
|
||||
client.post("/setup", data={"username": "alice", "password": "hunter22"})
|
||||
frame = db_session.get(Frame, 1)
|
||||
creds = claim_device(db_session, frame)
|
||||
|
||||
resp = client.get("/frame/image")
|
||||
resp = client.get(f"/frame/image?{creds}")
|
||||
assert resp.status_code == 200
|
||||
|
||||
resp = client.get("/api/frames/1/now-displaying")
|
||||
@@ -43,18 +44,18 @@ def test_frame_image_records_now_displaying(client, db_session):
|
||||
|
||||
def test_advance_and_back_also_update_now_displaying(client, db_session):
|
||||
client.post("/setup", data={"username": "alice", "password": "hunter22"})
|
||||
db_session.get(Frame, 1)
|
||||
creds = claim_device(db_session, db_session.get(Frame, 1))
|
||||
|
||||
client.get("/frame/image")
|
||||
client.get(f"/frame/image?{creds}")
|
||||
first = client.get("/api/frames/1/now-displaying")
|
||||
assert first.status_code == 200
|
||||
|
||||
resp = client.post("/frame/advance")
|
||||
resp = client.post(f"/frame/advance?{creds}")
|
||||
assert resp.status_code == 200
|
||||
after_advance = client.get("/api/frames/1/now-displaying")
|
||||
assert after_advance.status_code == 200
|
||||
|
||||
resp = client.post("/frame/back")
|
||||
resp = client.post(f"/frame/back?{creds}")
|
||||
assert resp.status_code == 200
|
||||
after_back = client.get("/api/frames/1/now-displaying")
|
||||
assert after_back.status_code == 200
|
||||
@@ -66,7 +67,8 @@ def test_now_displaying_visible_to_linked_user(client, db_session):
|
||||
bob = make_user(db_session, "bob")
|
||||
frame = db_session.get(Frame, 1)
|
||||
link_user(db_session, bob, frame)
|
||||
client.get("/frame/image")
|
||||
creds = claim_device(db_session, frame)
|
||||
client.get(f"/frame/image?{creds}")
|
||||
|
||||
client.cookies.clear()
|
||||
login(client, "bob")
|
||||
|
||||
Reference in New Issue
Block a user