/ tests / ApplicationOAuth.py
ApplicationOAuth.py
  1  ############################ Copyrights and license ############################
  2  #                                                                              #
  3  # Copyright 2019 Rigas Papathanasopoulos <rigaspapas@gmail.com>                #
  4  #                                                                              #
  5  # This file is part of PyGithub.                                               #
  6  # http://pygithub.readthedocs.io/                                              #
  7  #                                                                              #
  8  # PyGithub is free software: you can redistribute it and/or modify it under    #
  9  # the terms of the GNU Lesser General Public License as published by the Free  #
 10  # Software Foundation, either version 3 of the License, or (at your option)    #
 11  # any later version.                                                           #
 12  #                                                                              #
 13  # PyGithub is distributed in the hope that it will be useful, but WITHOUT ANY  #
 14  # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS    #
 15  # FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more #
 16  # details.                                                                     #
 17  #                                                                              #
 18  # You should have received a copy of the GNU Lesser General Public License     #
 19  # along with PyGithub. If not, see <http://www.gnu.org/licenses/>.             #
 20  #                                                                              #
 21  ################################################################################
 22  
 23  from datetime import datetime, timezone
 24  from unittest import mock
 25  
 26  import github
 27  from github.ApplicationOAuth import ApplicationOAuth as aoa
 28  
 29  from . import Framework
 30  
 31  
 32  class ApplicationOAuth(Framework.TestCase):
 33      def setUp(self):
 34          super().setUp()
 35          self.CLIENT_ID = "client_id_removed"
 36          self.CLIENT_SECRET = "client_secret_removed"
 37          self.app = self.g.get_oauth_application(self.CLIENT_ID, self.CLIENT_SECRET)
 38  
 39      def testLoginURL(self):
 40          BASE_URL = "https://github.com/login/oauth/authorize"
 41          sample_uri = "https://myapp.com/some/path"
 42          sample_uri_encoded = "https%3A%2F%2Fmyapp.com%2Fsome%2Fpath"
 43          self.assertEqual(self.app.get_login_url(), f"{BASE_URL}?client_id={self.CLIENT_ID}")
 44          self.assertTrue(f"redirect_uri={sample_uri_encoded}" in self.app.get_login_url(redirect_uri=sample_uri))
 45          self.assertTrue(f"client_id={self.CLIENT_ID}" in self.app.get_login_url(redirect_uri=sample_uri))
 46          self.assertTrue("state=123abc" in self.app.get_login_url(state="123abc", login="user"))
 47          self.assertTrue("login=user" in self.app.get_login_url(state="123abc", login="user"))
 48          self.assertTrue(f"client_id={self.CLIENT_ID}" in self.app.get_login_url(state="123abc", login="user"))
 49  
 50      def testGetAccessToken(self):
 51          access_token = self.app.get_access_token("oauth_code_removed", state="state_removed")
 52          # Test string representation
 53          self.assertEqual(
 54              str(access_token),
 55              'AccessToken(type="bearer", token="acces...", scope="", '
 56              "refresh_token_expires_in=None, refresh_token=None, expires_in=None)",
 57          )
 58          self.assertEqual(access_token.token, "access_token_removed")
 59          self.assertEqual(access_token.type, "bearer")
 60          self.assertEqual(access_token.scope, "")
 61          self.assertIsNone(access_token.expires_in)
 62          self.assertIsNone(access_token.expires_at)
 63          self.assertIsNone(access_token.refresh_token)
 64          self.assertIsNone(access_token.refresh_expires_in)
 65          self.assertIsNone(access_token.refresh_expires_at)
 66  
 67      def testGetAccessTokenWithExpiry(self):
 68          with mock.patch("github.AccessToken.datetime") as dt:
 69              dt.now = mock.Mock(return_value=datetime(2023, 6, 7, 12, 0, 0, 123, tzinfo=timezone.utc))
 70              access_token = self.app.get_access_token("oauth_code_removed", state="state_removed")
 71          # Test string representation
 72          self.assertEqual(
 73              str(access_token),
 74              'AccessToken(type="bearer", token="acces...", scope="", '
 75              'refresh_token_expires_in=15811200, refresh_token="refre...", expires_in=28800)',
 76          )
 77          self.assertEqual(access_token.token, "access_token_removed")
 78          self.assertEqual(access_token.type, "bearer")
 79          self.assertEqual(access_token.scope, "")
 80          self.assertEqual(access_token.expires_in, 28800)
 81          self.assertEqual(
 82              access_token.expires_at,
 83              datetime(2023, 6, 7, 20, 0, 0, 123, tzinfo=timezone.utc),
 84          )
 85          self.assertEqual(access_token.refresh_token, "refresh_token_removed")
 86          self.assertEqual(access_token.refresh_expires_in, 15811200)
 87          self.assertEqual(
 88              access_token.refresh_expires_at,
 89              datetime(2023, 12, 7, 12, 0, 0, 123, tzinfo=timezone.utc),
 90          )
 91  
 92      def testRefreshAccessToken(self):
 93          access_token = self.app.get_access_token("oauth_code_removed", state="state_removed")
 94  
 95          with mock.patch("github.AccessToken.datetime") as dt:
 96              dt.now = mock.Mock(return_value=datetime(2023, 6, 7, 12, 0, 0, 123, tzinfo=timezone.utc))
 97              refreshed = self.app.refresh_access_token(access_token.refresh_token)
 98  
 99          self.assertNotEqual(refreshed.token, access_token.token)
100          self.assertNotEqual(refreshed.refresh_token, access_token.refresh_token)
101          self.assertNotEqual(refreshed.created, access_token.created)
102          # Test string representation
103          self.assertEqual(
104              str(refreshed),
105              'AccessToken(type="bearer", token="anoth...", scope="", '
106              'refresh_token_expires_in=15811200, refresh_token="anoth...", expires_in=28800)',
107          )
108          self.assertEqual(refreshed.token, "another_access_token_removed")
109          self.assertEqual(refreshed.type, "bearer")
110          self.assertEqual(refreshed.scope, "")
111          self.assertEqual(
112              refreshed.created,
113              datetime(2023, 6, 7, 12, 0, 0, 123, tzinfo=timezone.utc),
114          )
115          self.assertEqual(refreshed.expires_in, 28800)
116          self.assertEqual(
117              refreshed.expires_at,
118              datetime(2023, 6, 7, 20, 0, 0, 123, tzinfo=timezone.utc),
119          )
120          self.assertEqual(refreshed.refresh_token, "another_refresh_token_removed")
121          self.assertEqual(refreshed.refresh_expires_in, 15811200)
122          self.assertEqual(
123              refreshed.refresh_expires_at,
124              datetime(2023, 12, 7, 12, 0, 0, 123, tzinfo=timezone.utc),
125          )
126  
127      def testGetAccessTokenBadCode(self):
128          with self.assertRaises(github.BadCredentialsException) as exc:
129              self.app.get_access_token("oauth_code_removed", state="state_removed")
130          self.assertEqual(exc.exception.status, 200)
131          self.assertIn("error", exc.exception.data)
132          self.assertEqual(exc.exception.data["error"], "bad_verification_code")
133  
134      def testGetAccessTokenUnknownError(self):
135          with self.assertRaises(github.GithubException) as exc:
136              self.app.get_access_token("oauth_code_removed", state="state_removed")
137          self.assertEqual(exc.exception.status, 200)
138          self.assertIn("error", exc.exception.data)
139          self.assertEqual(exc.exception.data["error"], "some_unknown_error")
140  
141      def testRefreshAccessTokenBadCode(self):
142          with self.assertRaises(github.BadCredentialsException) as exc:
143              self.app.refresh_access_token("oauth_code_removed")
144          self.assertEqual(exc.exception.status, 200)
145          self.assertIn("error", exc.exception.data)
146          self.assertEqual(exc.exception.data["error"], "bad_verification_code")
147  
148      def testRefreshAccessTokenUnknownError(self):
149          with self.assertRaises(github.GithubException) as exc:
150              self.app.refresh_access_token("oauth_code_removed")
151          self.assertEqual(exc.exception.status, 200)
152          self.assertIn("error", exc.exception.data)
153          self.assertEqual(exc.exception.data["error"], "some_unknown_error")
154  
155      def testCheckError(self):
156          expected_header = {"header": True}
157          expected_data = {"data": True}
158  
159          header, data = aoa._checkError(expected_header, None)
160          self.assertEqual(header, expected_header)
161          self.assertIsNone(data)
162  
163          header, data = aoa._checkError(expected_header, expected_data)
164          self.assertEqual(header, expected_header)
165          self.assertEqual(data, expected_data)
166  
167          with self.assertRaises(github.BadCredentialsException) as exc:
168              aoa._checkError({}, {"error": "bad_verification_code"})
169          self.assertEqual(exc.exception.status, 200)
170          self.assertIn("error", exc.exception.data)
171          self.assertEqual(exc.exception.data["error"], "bad_verification_code")
172  
173          with self.assertRaises(github.GithubException) as exc:
174              aoa._checkError({}, {"error": "other"})
175          self.assertEqual(exc.exception.status, 200)
176          self.assertIn("error", exc.exception.data)
177          self.assertEqual(exc.exception.data["error"], "other")