"""Tests for per-owner cookie-scoped session — first visit, isolation, restore.""" from fastapi.testclient import TestClient from imptune.services.session import COOKIE_NAME def test_first_visit_sets_cookie_and_new_owner_flag(client): resp = client.get("/") assert resp.status_code == 200 assert COOKIE_NAME in client.cookies assert "session-choice-modal" in resp.text # Second visit — cookie already present, modal must not reappear resp2 = client.get("/") assert "session-choice-modal" not in resp2.text def test_printers_are_isolated_between_owners(tmp_data_dir): from imptune.main import app with TestClient(app) as client_a, TestClient(app) as client_b: client_a.post( "/printers", data={"name": "Owner A Printer", "ip_address": "10.0.0.1", "port_name": "IP_A"}, follow_redirects=False, ) page_a = client_a.get("/printers") page_b = client_b.get("/printers") assert "Owner A Printer" in page_a.text assert "Owner A Printer" not in page_b.text def test_owner_b_cannot_reach_owner_a_printer_by_id(tmp_data_dir): from imptune.main import app with TestClient(app) as client_a, TestClient(app) as client_b: client_a.post( "/printers", data={"name": "Private Printer", "ip_address": "10.0.0.2", "port_name": "IP_B"}, follow_redirects=False, ) from imptune.db.models import Printer printer_id = Printer.get(Printer.name == "Private Printer").id assert client_b.get(f"/printers/{printer_id}").status_code == 404 assert client_b.patch(f"/printers/{printer_id}", data={"name": "Hijacked"}).status_code == 404 assert client_b.delete(f"/printers/{printer_id}").status_code == 404 assert client_b.get(f"/printers/{printer_id}/scripts/install").status_code == 404 assert client_b.get(f"/printers/{printer_id}/packages/ninja").status_code == 404 def test_download_key_marks_permanent_and_returns_key(client): from imptune.db.models import Owner client.get("/") resp = client.get("/session/key/download") assert resp.status_code == 200 assert "attachment" in resp.headers["content-disposition"] assert "imptune-backup-key.txt" in resp.headers["content-disposition"] key = resp.text assert len(key) > 20 owner = Owner.get(Owner.key == key) assert owner.is_permanent is True def test_restore_with_valid_key_reattaches_owner_data(tmp_data_dir): from imptune.main import app with TestClient(app) as client_a: client_a.get("/") client_a.post( "/printers", data={"name": "Backed Up Printer", "ip_address": "10.0.0.3", "port_name": "IP_C"}, follow_redirects=False, ) key = client_a.get("/session/key/download").text with TestClient(app) as client_new: restore_resp = client_new.post( "/session/restore", data={"key": key}, headers={"origin": "http://testserver"}, follow_redirects=False, ) assert restore_resp.status_code == 303 page = client_new.get("/printers") assert "Backed Up Printer" in page.text def test_restore_with_invalid_key_shows_error(client): resp = client.post( "/session/restore", data={"key": "not-a-real-key"}, headers={"origin": "http://testserver"}, ) assert resp.status_code == 404 assert "Key not found" in resp.text def test_restore_rejects_cross_origin_post(client): """CSRF guard: a forged cross-site form POST must not be able to re-point the victim's cookie at an attacker-known key (login-CSRF / session fixation).""" resp = client.post( "/session/restore", data={"key": "irrelevant"}, headers={"origin": "https://attacker.example"}, ) assert resp.status_code == 403 resp_no_header = client.post("/session/restore", data={"key": "irrelevant"}) assert resp_no_header.status_code == 403 def test_health_endpoint_does_not_create_owner_rows(client): from imptune.db.models import Owner before = Owner.select().count() for _ in range(5): client.get("/health") after = Owner.select().count() assert after == before