Coverage for tests/login/test_login.py: 100%
74 statements
« prev ^ index » next coverage.py v7.16.1, created at 2026-09-18 22:11 +0000
« prev ^ index » next coverage.py v7.16.1, created at 2026-09-18 22:11 +0000
1import unittest
2from types import SimpleNamespace
3from unittest.mock import patch
4from urllib.parse import urlparse
6import responses
7from flask import session
8from pymacaroons import Macaroon
10from webapp.app import app
11from webapp.login import views as login_views
14def make_root_macaroon():
15 root = Macaroon(location="api.staging.snapcraft.io", identifier="store-usso")
16 login_host = urlparse(login_views.LOGIN_URL).hostname or login_views.LOGIN_URL
17 root.add_third_party_caveat(login_host, "secret-key", "caveat-id")
18 return root.serialize()
21class TestLoginViews(unittest.TestCase):
22 def setUp(self):
23 self.app = app
24 app.config["TESTING"] = True
25 self.client = self.app.test_client()
26 self.issue_url = login_views.publisher_gateway.get_endpoint_url(
27 "tokens/usso"
28 )
29 self.root_macaroon = make_root_macaroon()
31 @responses.activate
32 @patch("webapp.login.views.open_id.try_login")
33 def test_login(self, mock_try_login):
34 mock_try_login.return_value = "ok"
35 responses.add(
36 responses.POST,
37 self.issue_url,
38 json={"macaroon": self.root_macaroon},
39 status=200,
40 )
42 with self.client as client:
43 res = client.get("/login")
44 self.assertEqual(res.data, b"ok")
45 self.assertIn("account-macaroon", session)
47 @responses.activate
48 @patch("webapp.login.views.open_id.try_login")
49 def test_login_next(self, mock_try_login):
50 mock_try_login.return_value = "ok"
51 responses.add(
52 responses.POST,
53 self.issue_url,
54 json={"macaroon": self.root_macaroon},
55 status=200,
56 )
58 with self.client as client:
59 client.get("/login?next=/test-page")
60 self.assertIn("next_url", session)
61 self.assertEqual(session["next_url"], "/test-page")
63 @responses.activate
64 @patch("webapp.login.views.open_id.try_login")
65 def test_login_api_500(self, mock_try_login):
66 mock_try_login.return_value = "ok"
67 responses.add(
68 responses.Response(method="POST", url=self.issue_url, status=500)
69 )
71 response = self.client.get("/login")
73 assert len(responses.calls) == 1
74 assert response.status_code == 502
76 def test_logout(self):
77 response = self.client.get("/logout")
78 self.assertEqual(response.status_code, 302)
79 with self.client.session_transaction() as s:
80 self.assertEqual(response.location, "/")
81 self.assertEqual(s.get("account-auth"), None)
82 self.assertEqual(s.get("account-macaroon"), None)
84 @patch("webapp.login.views.publisher_gateway.exchange_usso_macaroons")
85 @patch("webapp.login.views.publisher_gateway.macaroon_info")
86 def test_login_callback(self, mock_macaroon_info, mock_exchange):
87 discharge = Macaroon(
88 location=urlparse(login_views.LOGIN_URL).hostname, identifier="discharge"
89 ).serialize()
90 mock_exchange.return_value = "account-auth-token"
91 mock_macaroon_info.return_value = {"account": {"id": "test-account"}}
93 with self.app.test_request_context("/login", headers={"User-Agent": "UA"}):
94 session["account-macaroon"] = self.root_macaroon
95 response = login_views.login_callback(
96 SimpleNamespace(
97 extensions={"macaroon": SimpleNamespace(discharge=discharge)}
98 )
99 )
101 self.assertEqual(response.status_code, 302)
102 self.assertEqual(response.location, "/charms")
103 self.assertEqual(session["account-auth"], "account-auth-token")
104 self.assertIn("account", session)
106 def test_login_callback_missing_root_macaroon(self):
107 discharge = Macaroon(
108 location=urlparse(login_views.LOGIN_URL).hostname, identifier="discharge"
109 ).serialize()
111 with self.app.test_request_context("/login", headers={"User-Agent": "UA"}):
112 session["next_url"] = "/integrations"
113 response = login_views.login_callback(
114 SimpleNamespace(
115 extensions={"macaroon": SimpleNamespace(discharge=discharge)}
116 )
117 )
119 self.assertEqual(response.status_code, 302)
120 self.assertEqual(response.location, "/login?next=/integrations")