Coverage for tests/login/tests_login_handler.py: 100%
111 statements
« prev ^ index » next coverage.py v7.8.0, created at 2025-04-28 22:05 +0000
« prev ^ index » next coverage.py v7.8.0, created at 2025-04-28 22:05 +0000
1import requests
3import responses
4from flask_testing import TestCase
5from pymacaroons import Macaroon
6from webapp.app import create_app
8from unittest.mock import patch, MagicMock
11class LoginHandlerTest(TestCase):
12 def setUp(self):
13 self.api_url = "https://dashboard.snapcraft.io/dev/api/acl/"
14 self.endpoint_url = "/login"
16 def create_app(self):
17 app = create_app(testing=True)
18 app.secret_key = "secret_key"
19 app.config["WTF_CSRF_METHODS"] = []
21 return app
23 def test_redirect_user_logged_in(self):
24 with self.client.session_transaction() as s:
25 s["publisher"] = "openid"
26 s["macaroon_root"] = "macaroon_root"
27 s["macaroon_discharge"] = "macaroon_discharge"
29 response = self.client.get(self.endpoint_url)
30 assert response.status_code == 302
31 self.assertEqual("http://localhost/", response.location)
33 def test_redirect_user_logged_in_next_url(self):
34 with self.client.session_transaction() as s:
35 s["publisher"] = "openid"
36 s["macaroon_root"] = "macaroon_root"
37 s["macaroon_discharge"] = "macaroon_discharge"
39 response = self.client.get(self.endpoint_url + "?next=/test")
40 assert response.status_code == 302
41 self.assertEqual("/test", response.location)
43 @responses.activate
44 def test_login_handler_redirect(self):
45 m = Macaroon()
46 m.add_third_party_caveat("login.ubuntu.com", "key", "id")
48 serialized_macaroon = m.serialize()
50 responses.add(
51 responses.Response(
52 method="POST",
53 url=self.api_url,
54 json={"macaroon": serialized_macaroon},
55 status=200,
56 )
57 )
59 response = self.client.get(self.endpoint_url)
61 assert len(responses.calls) == 1
62 assert response.status_code == 302
64 @responses.activate
65 def test_login_api_500(self):
66 responses.add(
67 responses.Response(method="POST", url=self.api_url, status=500)
68 )
70 response = self.client.get(self.endpoint_url)
72 assert len(responses.calls) == 1
73 assert response.status_code == 502
75 @responses.activate
76 def test_login_api_401(self):
77 responses.add(
78 responses.Response(method="POST", url=self.api_url, status=401)
79 )
81 response = self.client.get(self.endpoint_url)
83 assert len(responses.calls) == 1
84 assert response.status_code == 302
85 self.assertEqual("/logout", response.location)
87 @responses.activate
88 def test_login_connection_error(self):
89 responses.add(
90 responses.Response(
91 method="POST",
92 url=self.api_url,
93 body=requests.exceptions.ConnectionError(),
94 status=500,
95 )
96 )
98 response = self.client.get(self.endpoint_url)
100 assert response.status_code == 502
103class AfterLoginHandlerTest(TestCase):
104 def create_app(self):
105 app = create_app(testing=True)
107 # set up a fake route for testing the after_login function
108 # since it is decorated with @open_id.after_login
109 @app.route("/_test_after_login")
110 def _test_after_login():
111 from webapp.login.views import after_login
113 return after_login(self.mock_resp)
115 return app
117 # creates a mocked responses for the get_account function
118 # and the login response passed to after_login
119 def prepare_mock_response(
120 self, mock_get_account, email="test@test.com", groups=[]
121 ):
122 self.mock_resp = MagicMock()
123 self.mock_resp.nickname = "test"
124 self.mock_resp.identity_url = "https://login.ubuntu.com/test"
125 self.mock_resp.fullname = "Test"
126 self.mock_resp.image = "test.png"
127 self.mock_resp.email = email
128 self.mock_resp.extensions = {
129 "macaroon": MagicMock(discharge="some-discharge"),
130 "lp": MagicMock(is_member=groups),
131 }
133 mock_get_account.return_value = {
134 "username": self.mock_resp.nickname,
135 "displayname": self.mock_resp.fullname,
136 "email": email,
137 "stores": [],
138 }
140 @patch("webapp.login.views.ENVIRONMENT", "staging")
141 @patch("webapp.login.views.dashboard.get_stores", return_value=[])
142 @patch("webapp.login.views.logic.get_stores", return_value=[])
143 @patch(
144 "webapp.login.views.dashboard.get_validation_sets", return_value=None
145 )
146 @patch("webapp.login.views.dashboard.get_account")
147 def test_is_canonical_true_if_email_ends_with_canonical_on_staging(
148 self,
149 mock_get_account,
150 *_,
151 ):
152 # on test environments, we treat publisher's account as "canonical"
153 # if their email is (at)canonical email
154 self.prepare_mock_response(
155 mock_get_account, email="test@canonical.com", groups=[]
156 )
158 self.client.get("/_test_after_login")
160 with self.client.session_transaction() as s:
161 publisher = s.get("publisher")
162 assert publisher is not None
163 assert publisher["is_canonical"] is True
165 @patch("webapp.login.views.ENVIRONMENT", "production")
166 @patch("webapp.login.views.dashboard.get_stores", return_value=[])
167 @patch("webapp.login.views.logic.get_stores", return_value=[])
168 @patch(
169 "webapp.login.views.dashboard.get_validation_sets", return_value=None
170 )
171 @patch("webapp.login.views.dashboard.get_account")
172 def test_is_canonical_true_if_member_of_team_on_production(
173 self,
174 mock_get_account,
175 *_,
176 ):
177 # on production, we treat publisher's account as "canonical"
178 # if they are a member of the canonical team
179 self.prepare_mock_response(mock_get_account, groups=["canonical"])
181 self.client.get("/_test_after_login")
183 with self.client.session_transaction() as s:
184 publisher = s.get("publisher")
185 assert publisher is not None
186 assert publisher["is_canonical"] is True
188 @patch("webapp.login.views.ENVIRONMENT", "production")
189 @patch("webapp.login.views.dashboard.get_stores", return_value=[])
190 @patch("webapp.login.views.logic.get_stores", return_value=[])
191 @patch(
192 "webapp.login.views.dashboard.get_validation_sets", return_value=None
193 )
194 @patch("webapp.login.views.dashboard.get_account")
195 def test_is_canonical_false_if_not_member_of_team_on_production(
196 self,
197 mock_get_account,
198 *_,
199 ):
200 # on production, we treat publisher's account as "canonical"
201 # only if they are a member of the canonical team, not based on email
202 self.prepare_mock_response(
203 mock_get_account, email="test@canonical.com", groups=[]
204 )
206 self.client.get("/_test_after_login")
208 with self.client.session_transaction() as s:
209 publisher = s.get("publisher")
210 assert publisher is not None
211 assert publisher["is_canonical"] is False