"""End-to-end through Flask's test client: the gate, the SSO callback, replay, health. Run with the venv python: `cd app && ../.venv/bin/python -m unittest`.""" import os import unittest os.environ["OTM_SSO_SECRET"] = "sso-secret" os.environ["AUTH_SECRET"] = "auth-secret" import portable # noqa: E402 (env must be set before import? no — read per request; set anyway) import otm # noqa: E402 class Gate(unittest.TestCase): def setUp(self): self.c = portable.app.test_client() def test_health_is_public(self): r = self.c.get("/api/health") self.assertEqual(r.status_code, 200) self.assertEqual(r.get_json()["version"], otm.APP_VERSION) def test_anonymous_is_bounced_to_login(self): r = self.c.get("/") self.assertEqual(r.status_code, 302) self.assertTrue(r.headers["Location"].startswith("/login?next=%2F")) r = self.c.get("/https://example.com/article") self.assertEqual(r.status_code, 302) self.assertIn("/login", r.headers["Location"]) r = self.c.post("/article", data={"link": "https://example.com"}) self.assertEqual(r.status_code, 401) def test_login_page_offers_otm(self): r = self.c.get("/login?next=/x") self.assertEqual(r.status_code, 200) self.assertIn(b"sso/authorize?aud=app%3A13ft&next=%2Fx", r.data) def test_ticket_signs_in_once(self): t = otm.mint_sso_ticket("sso-secret", {"aud": "app:13ft", "sub": "u1", "email": "tony@example.com", "role": "admin", "next": "/"}) r = self.c.get(f"/api/auth/otm-sso?token={t}") self.assertEqual(r.status_code, 302) self.assertEqual(r.headers["Location"], "/") self.assertIn(otm.SESSION_COOKIE, r.headers.get("Set-Cookie", "")) # Signed in: the home page renders, with the OTM footer. r = self.c.get("/") self.assertEqual(r.status_code, 200) self.assertIn(b"Powered by OTM", r.data) self.assertIn(b"tony@example.com", r.data) # Replay of the same ticket is refused. fresh = portable.app.test_client() r = fresh.get(f"/api/auth/otm-sso?token={t}") self.assertEqual(r.headers["Location"], "/login?reason=link_already_used") def test_bad_ticket(self): r = self.c.get("/api/auth/otm-sso?token=nope") self.assertEqual(r.headers["Location"], "/login?reason=invalid_token") wrong = otm.mint_sso_ticket("sso-secret", {"aud": "app:qr", "sub": "u1", "email": "e@x", "role": "admin"}) r = self.c.get(f"/api/auth/otm-sso?token={wrong}") self.assertEqual(r.headers["Location"], "/login?reason=invalid_token") def test_logout_clears(self): t = otm.mint_sso_ticket("sso-secret", {"aud": "app:13ft", "sub": "u1", "email": "e@x.io", "role": "admin"}) self.c.get(f"/api/auth/otm-sso?token={t}") self.assertEqual(self.c.get("/").status_code, 200) self.c.get("/logout") self.assertEqual(self.c.get("/").status_code, 302) class FailClosed(unittest.TestCase): def test_no_secret_means_503_everywhere(self): saved = {k: os.environ.pop(k, None) for k in ("OTM_SSO_SECRET", "AUTH_SECRET")} try: c = portable.app.test_client() self.assertEqual(c.get("/").status_code, 503) self.assertEqual(c.get("/https://example.com").status_code, 503) self.assertEqual(c.get("/api/health").status_code, 200) self.assertIn(b"isn't wired to OTM", c.get("/login").data) finally: for k, v in saved.items(): if v is not None: os.environ[k] = v if __name__ == "__main__": unittest.main()