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

1import unittest 

2from types import SimpleNamespace 

3from unittest.mock import patch 

4from urllib.parse import urlparse 

5 

6import responses 

7from flask import session 

8from pymacaroons import Macaroon 

9 

10from webapp.app import app 

11from webapp.login import views as login_views 

12 

13 

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() 

19 

20 

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() 

30 

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 ) 

41 

42 with self.client as client: 

43 res = client.get("/login") 

44 self.assertEqual(res.data, b"ok") 

45 self.assertIn("account-macaroon", session) 

46 

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 ) 

57 

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") 

62 

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 ) 

70 

71 response = self.client.get("/login") 

72 

73 assert len(responses.calls) == 1 

74 assert response.status_code == 502 

75 

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) 

83 

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"}} 

92 

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 ) 

100 

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) 

105 

106 def test_login_callback_missing_root_macaroon(self): 

107 discharge = Macaroon( 

108 location=urlparse(login_views.LOGIN_URL).hostname, identifier="discharge" 

109 ).serialize() 

110 

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 ) 

118 

119 self.assertEqual(response.status_code, 302) 

120 self.assertEqual(response.location, "/login?next=/integrations")