From b9d71fa787a2ffb99b6631e4bd6df932a4d4adbb Mon Sep 17 00:00:00 2001 From: Vincent Jacques Date: Thu, 28 Mar 2013 21:05:19 +0100 Subject: [PATCH 1/6] Slightly improve coverage :-D --- github/GithubObject.py | 2 +- github/tests/Github_.py | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/github/GithubObject.py b/github/GithubObject.py index 99884297..968926dc 100644 --- a/github/GithubObject.py +++ b/github/GithubObject.py @@ -20,7 +20,7 @@ import GithubException class _NotSetType: def __repr__(self): - return "NotSet" # pragma no cover + return "NotSet" NotSet = _NotSetType() diff --git a/github/tests/Github_.py b/github/tests/Github_.py index 149af2b8..e401a257 100644 --- a/github/tests/Github_.py +++ b/github/tests/Github_.py @@ -17,6 +17,7 @@ import datetime import Framework +import github class Github(Framework.TestCase): def testGetGists(self): @@ -107,3 +108,6 @@ class Github(Framework.TestCase): t = self.g.get_gitignore_template("C++") self.assertEqual(t.name, "C++") self.assertEqual(t.source, "# Compiled Object files\n*.slo\n*.lo\n*.o\n\n# Compiled Dynamic libraries\n*.so\n*.dylib\n\n# Compiled Static libraries\n*.lai\n*.la\n*.a\n") + + def testStringOfNotSet(self): + self.assertEqual(str(github.GithubObject.NotSet), "NotSet") From 1d18bd66f3a4a4225435bd38df04b8a227b5e821 Mon Sep 17 00:00:00 2001 From: Vincent Jacques Date: Thu, 28 Mar 2013 21:23:03 +0100 Subject: [PATCH 2/6] Add a test that would have caught #153 --- github/tests/Authentication.py | 16 ++++++++++++++++ github/tests/Framework.py | 8 +++++++- ...tication.testAuthorizationHeaderWithLogin.txt | 11 +++++++++++ ...tication.testAuthorizationHeaderWithToken.txt | 11 +++++++++++ 4 files changed, 45 insertions(+), 1 deletion(-) create mode 100644 github/tests/ReplayData/Authentication.testAuthorizationHeaderWithLogin.txt create mode 100644 github/tests/ReplayData/Authentication.testAuthorizationHeaderWithToken.txt diff --git a/github/tests/Authentication.py b/github/tests/Authentication.py index 42a0a782..8db4238f 100644 --- a/github/tests/Authentication.py +++ b/github/tests/Authentication.py @@ -38,3 +38,19 @@ class Authentication(Framework.BasicTestCase): def testUserAgent(self): g = github.Github(user_agent="PyGithubTester") self.assertEqual(g.get_user("jacquev6").name, "Vincent Jacques") + + def testAuthorizationHeaderWithLogin(self): + # See special case in Framework.fixAuthorizationHeader + g = github.Github("fake_login", "fake_password") + try: + g.get_user().name + except github.GithubException: + pass + + def testAuthorizationHeaderWithToken(self): + # See special case in Framework.fixAuthorizationHeader + g = github.Github("ZmFrZV9sb2dpbjpmYWtlX3Bhc3N3b3Jk") + try: + g.get_user().name + except github.GithubException: + pass diff --git a/github/tests/Framework.py b/github/tests/Framework.py index 35176e3b..254900ce 100644 --- a/github/tests/Framework.py +++ b/github/tests/Framework.py @@ -53,7 +53,13 @@ class FakeHttpResponse: def fixAuthorizationHeader(headers): if "Authorization" in headers: - if headers["Authorization"].startswith("token "): + if headers["Authorization"].endswith("ZmFrZV9sb2dpbjpmYWtlX3Bhc3N3b3Jk"): + # This special case is here to test the real Authorization header + # sent by PyGithub. It would have avoided issue https://github.com/jacquev6/PyGithub/issues/153 + # because we would have seen that Python 3 was not generating the same + # header as Python 2 + pass + elif headers["Authorization"].startswith("token "): headers["Authorization"] = "token private_token_removed" elif headers["Authorization"].startswith("Basic "): headers["Authorization"] = "Basic login_and_password_removed" diff --git a/github/tests/ReplayData/Authentication.testAuthorizationHeaderWithLogin.txt b/github/tests/ReplayData/Authentication.testAuthorizationHeaderWithLogin.txt new file mode 100644 index 00000000..726ee89b --- /dev/null +++ b/github/tests/ReplayData/Authentication.testAuthorizationHeaderWithLogin.txt @@ -0,0 +1,11 @@ +https +GET +api.github.com +None +/user +{'Authorization': 'Basic ZmFrZV9sb2dpbjpmYWtlX3Bhc3N3b3Jk'} +null +401 +[('status', '401 Unauthorized'), ('content-length', '29'), ('x-github-media-type', 'github.beta; format=json'), ('server', 'GitHub.com'), ('connection', 'keep-alive'), ('date', 'Thu, 28 Mar 2013 20:14:22 GMT'), ('content-type', 'application/json; charset=utf-8')] +{"message":"Bad credentials"} + diff --git a/github/tests/ReplayData/Authentication.testAuthorizationHeaderWithToken.txt b/github/tests/ReplayData/Authentication.testAuthorizationHeaderWithToken.txt new file mode 100644 index 00000000..a088639f --- /dev/null +++ b/github/tests/ReplayData/Authentication.testAuthorizationHeaderWithToken.txt @@ -0,0 +1,11 @@ +https +GET +api.github.com +None +/user +{'Authorization': 'token ZmFrZV9sb2dpbjpmYWtlX3Bhc3N3b3Jk'} +null +403 +[('status', '403 Forbidden'), ('content-length', '50'), ('x-github-media-type', 'github.beta; format=json'), ('server', 'GitHub.com'), ('connection', 'keep-alive'), ('date', 'Thu, 28 Mar 2013 20:15:00 GMT'), ('content-type', 'application/json; charset=utf-8')] +{"message":"Max number of login attempt exceeded"} + From 1c67359a318f05e50bf457818e1983ce95aa5946 Mon Sep 17 00:00:00 2001 From: Vincent Jacques Date: Thu, 28 Mar 2013 21:28:01 +0100 Subject: [PATCH 3/6] Improve test coverage a bit --- github/Requester.py | 2 +- github/tests/Enterprise.py | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/github/Requester.py b/github/Requester.py index 7598e3f2..eb6a0e49 100644 --- a/github/Requester.py +++ b/github/Requester.py @@ -70,7 +70,7 @@ class Requester: elif o.scheme == "http": self.__connectionClass = self.__httpConnectionClass else: - assert False, "Unknown URL scheme" # pragma no cover + assert False, "Unknown URL scheme" self.rate_limiting = (5000, 5000) self.FIX_REPO_GET_GIT_REF = True self.per_page = per_page diff --git a/github/tests/Enterprise.py b/github/tests/Enterprise.py index f072b2a1..d08a3464 100644 --- a/github/tests/Enterprise.py +++ b/github/tests/Enterprise.py @@ -28,6 +28,11 @@ class Enterprise(Framework.BasicTestCase): g = github.Github(self.login, self.password, base_url="http://my.enterprise.com") self.assertListKeyEqual(g.get_user().get_repos(), lambda r: r.name, ["TestPyGithub", "django", "PyGithub", "developer.github.com", "acme-public-website", "C4Planner", "Hacking", "vincent-jacques.net", "Contests", "Candidates", "Tests", "DrawTurksHead", "DrawSyntax", "QuadProgMm", "Boost.HierarchicalEnum", "ViDE"]) + def testUnknownUrlScheme(self): + with self.assertRaises(AssertionError) as cm: + github.Github(self.login, self.password, base_url="foobar://my.enterprise.com") + self.assertEqual(cm.exception.args[0], "Unknown URL scheme") + def testLongUrl(self): g = github.Github(self.login, self.password, base_url="http://my.enterprise.com/path/to/github") repos = g.get_user().get_repos() From 8a9afbb1aa36c6ba04142c6e6c1cfbd7de982a6a Mon Sep 17 00:00:00 2001 From: Vincent Jacques Date: Thu, 28 Mar 2013 21:49:39 +0100 Subject: [PATCH 4/6] Remove branches for hypothetical unknown Authorization headers --- github/Requester.py | 2 -- github/tests/Framework.py | 2 -- 2 files changed, 4 deletions(-) diff --git a/github/Requester.py b/github/Requester.py index eb6a0e49..81de4c3c 100644 --- a/github/Requester.py +++ b/github/Requester.py @@ -215,6 +215,4 @@ class Requester: requestHeaders["Authorization"] = "Basic (login and password removed)" elif requestHeaders["Authorization"].startswith("token"): requestHeaders["Authorization"] = "token (oauth token removed)" - else: # pragma no cover - requestHeaders["Authorization"] = "Unknown authorization removed" logger.debug("%s %s://%s%s %s %s ==> %i %s %s", str(verb), self.__scheme, self.__hostname, str(url), str(requestHeaders), str(input), status, str(responseHeaders), str(output)) diff --git a/github/tests/Framework.py b/github/tests/Framework.py index 254900ce..931088e6 100644 --- a/github/tests/Framework.py +++ b/github/tests/Framework.py @@ -63,8 +63,6 @@ def fixAuthorizationHeader(headers): headers["Authorization"] = "token private_token_removed" elif headers["Authorization"].startswith("Basic "): headers["Authorization"] = "Basic login_and_password_removed" - else: # pragma no cover - assert False class RecordingConnection: # pragma no cover From 1c47be4e895b823baf907b25c647e43ab63c16dd Mon Sep 17 00:00:00 2001 From: Vincent Jacques Date: Thu, 28 Mar 2013 21:50:17 +0100 Subject: [PATCH 5/6] Document `#pragma no cover`s (Issue #154) --- github/Requester.py | 14 +++++++------- github/__init__.py | 2 +- github/tests/ContentFile.py | 2 +- github/tests/Exceptions.py | 6 +++--- github/tests/Framework.py | 16 ++++++++-------- 5 files changed, 20 insertions(+), 20 deletions(-) diff --git a/github/Requester.py b/github/Requester.py index 81de4c3c..eacda7ab 100644 --- a/github/Requester.py +++ b/github/Requester.py @@ -25,8 +25,8 @@ atLeastPython3 = sys.hexversion >= 0x03000000 if atLeastPython26: import json -else: # pragma no cover - import simplejson as json # pragma no cover +else: # pragma no cover (Covered by all tests with Python 2.5) + import simplejson as json # pragma no cover (Covered by all tests with Python 2.5) import GithubException @@ -49,7 +49,7 @@ class Requester: if password is not None: login = login_or_token if atLeastPython3: - self.__authorizationHeader = "Basic " + base64.b64encode((login + ":" + password).encode("utf-8")).decode("utf-8").replace('\n', '') # pragma no cover + self.__authorizationHeader = "Basic " + base64.b64encode((login + ":" + password).encode("utf-8")).decode("utf-8").replace('\n', '') # pragma no cover (Covered by Authentication.testAuthorizationHeaderWithXxx with Python 3) else: self.__authorizationHeader = "Basic " + base64.b64encode(login + ":" + password).replace('\n', '') elif login_or_token is not None: @@ -97,8 +97,8 @@ class Requester: if len(data) == 0: return None else: - if atLeastPython3 and isinstance(data, bytes): # pragma no branch - data = data.decode("utf-8") # pragma no cover + if atLeastPython3 and isinstance(data, bytes): # pragma no branch (Covered by Issue142.testDecodeJson with Python 3) + data = data.decode("utf-8") # pragma no cover (Covered by Issue142.testDecodeJson with Python 3) return json.loads(data) def requestJson(self, verb, url, parameters, input): @@ -201,9 +201,9 @@ class Requester: def __createConnection(self): kwds = {} - if not atLeastPython3: # pragma no branch + if not atLeastPython3: # pragma no branch (Branch useful only with Python 3) kwds["strict"] = True # Useless in Python3, would generate a deprecation warning - if atLeastPython26: # pragma no branch + if atLeastPython26: # pragma no branch (Branch useful only with Python 2.5) kwds["timeout"] = self.__timeout # Did not exist before Python2.6 return self.__connectionClass(host=self.__hostname, port=self.__port, **kwds) diff --git a/github/__init__.py b/github/__init__.py index 3f20d650..3e8deb3f 100644 --- a/github/__init__.py +++ b/github/__init__.py @@ -30,7 +30,7 @@ from InputGitAuthor import InputGitAuthor from InputGitTreeElement import InputGitTreeElement -def enable_console_debug_logging(): # pragma no cover +def enable_console_debug_logging(): # pragma no cover (Function useful only outside test environment) """ This function sets up a very simple logging configuration (log everything on standard output) that is useful for troubleshooting. """ diff --git a/github/tests/ContentFile.py b/github/tests/ContentFile.py index ba88872f..3323c4a6 100644 --- a/github/tests/ContentFile.py +++ b/github/tests/ContentFile.py @@ -36,7 +36,7 @@ class ContentFile(Framework.TestCase): self.assertEqual(self.file.name, "ReadMe.md") self.assertEqual(self.file.path, "ReadMe.md") if atLeastPython3: - self.assertEqual(len(base64.b64decode(bytearray(self.file.content, "utf-8"))), 7531) # pragma no cover + self.assertEqual(len(base64.b64decode(bytearray(self.file.content, "utf-8"))), 7531) # pragma no cover (Covered with Python 3) else: self.assertEqual(len(base64.b64decode(self.file.content)), 7531) self.assertEqual(self.file.sha, "5628799a7d517a4aaa0c1a7004d07569cd154df0") diff --git a/github/tests/Exceptions.py b/github/tests/Exceptions.py index c068122f..eded7944 100644 --- a/github/tests/Exceptions.py +++ b/github/tests/Exceptions.py @@ -57,7 +57,7 @@ class Exceptions(Framework.TestCase): # To stay compatible with Python 2.6, we if atLeastPython26 and atMostPython2: self.assertEqual(str(exception), "404 {u'message': u'Not Found'}") else: - self.assertEqual(str(exception), "404 {'message': 'Not Found'}") # pragma no cover + self.assertEqual(str(exception), "404 {'message': 'Not Found'}") # pragma no cover (Covered with Python 3) self.assertTrue(raised) def testUnknownUser(self): @@ -71,7 +71,7 @@ class Exceptions(Framework.TestCase): # To stay compatible with Python 2.6, we if atLeastPython26 and atMostPython2: self.assertEqual(str(exception), "404 {u'message': u'Not Found'}") else: - self.assertEqual(str(exception), "404 {'message': 'Not Found'}") # pragma no cover + self.assertEqual(str(exception), "404 {'message': 'Not Found'}") # pragma no cover (Covered with Python 3) self.assertTrue(raised) def testBadAuthentication(self): @@ -85,5 +85,5 @@ class Exceptions(Framework.TestCase): # To stay compatible with Python 2.6, we if atLeastPython26 and atMostPython2: self.assertEqual(str(exception), "401 {u'message': u'Bad credentials'}") else: - self.assertEqual(str(exception), "401 {'message': 'Bad credentials'}") # pragma no cover + self.assertEqual(str(exception), "401 {'message': 'Bad credentials'}") # pragma no cover (Covered with Python 3) self.assertTrue(raised) diff --git a/github/tests/Framework.py b/github/tests/Framework.py index 931088e6..a766fa3b 100644 --- a/github/tests/Framework.py +++ b/github/tests/Framework.py @@ -27,8 +27,8 @@ atMostPython32 = sys.hexversion < 0x03030000 if atLeastPython26: import json -else: # pragma no cover - import simplejson as json # pragma no cover +else: # pragma no cover (Covered by all tests with Python 2.5) + import simplejson as json # pragma no cover (Covered by all tests with Python 2.5) def readLine(file): @@ -65,7 +65,7 @@ def fixAuthorizationHeader(headers): headers["Authorization"] = "Basic login_and_password_removed" -class RecordingConnection: # pragma no cover +class RecordingConnection: # pragma no cover (Class useful only when recording new tests, not used during automated tests) def __init__(self, file, protocol, host, port, *args, **kwds): self.__file = file self.__protocol = protocol @@ -107,14 +107,14 @@ class RecordingConnection: # pragma no cover self.__file.write(line + "\n") -class RecordingHttpConnection(RecordingConnection): # pragma no cover +class RecordingHttpConnection(RecordingConnection): # pragma no cover (Class useful only when recording new tests, not used during automated tests) _realConnection = httplib.HTTPConnection def __init__(self, file, *args, **kwds): RecordingConnection.__init__(self, file, "http", *args, **kwds) -class RecordingHttpsConnection(RecordingConnection): # pragma no cover +class RecordingHttpsConnection(RecordingConnection): # pragma no cover (Class useful only when recording new tests, not used during automated tests) _realConnection = httplib.HTTPSConnection def __init__(self, file, *args, **kwds): @@ -179,7 +179,7 @@ class BasicTestCase(unittest.TestCase): unittest.TestCase.setUp(self) self.__fileName = "" self.__file = None - if self.recordMode: # pragma no cover + if self.recordMode: # pragma no cover (Branch useful only when recording new tests, not used during automated tests) github.Requester.Requester.injectConnectionClasses( lambda ignored, *args, **kwds: RecordingHttpConnection(self.__openFile("wb"), *args, **kwds), lambda ignored, *args, **kwds: RecordingHttpsConnection(self.__openFile("wb"), *args, **kwds) @@ -220,7 +220,7 @@ class BasicTestCase(unittest.TestCase): def __closeReplayFileIfNeeded(self): if self.__file is not None: - if not self.recordMode: # pragma no branch + if not self.recordMode: # pragma no branch (Branch useful only when recording new tests, not used during automated tests) self.assertEqual(readLine(self.__file), "") self.__file.close() @@ -239,5 +239,5 @@ class TestCase(BasicTestCase): self.g = github.Github(self.login, self.password) -def activateRecordMode(): # pragma no cover +def activateRecordMode(): # pragma no cover (Function useful only when recording new tests, not used during automated tests) BasicTestCase.recordMode = True From e0db8cad4ec01c65e5e0eb50e11765e425e88ef9 Mon Sep 17 00:00:00 2001 From: Vincent Jacques Date: Thu, 28 Mar 2013 21:51:02 +0100 Subject: [PATCH 6/6] pep8 --- github/tests/Github_.py | 1 + 1 file changed, 1 insertion(+) diff --git a/github/tests/Github_.py b/github/tests/Github_.py index e401a257..63de20e5 100644 --- a/github/tests/Github_.py +++ b/github/tests/Github_.py @@ -19,6 +19,7 @@ import Framework import github + class Github(Framework.TestCase): def testGetGists(self): self.assertListKeyBegin(self.g.get_gists(), lambda g: g.id, ["2729695", "2729656", "2729597", "2729584", "2729569", "2729554", "2729543", "2729537", "2729536", "2729533", "2729525", "2729522", "2729519", "2729515", "2729506", "2729487", "2729484", "2729482", "2729441", "2729432", "2729420", "2729398", "2729372", "2729371", "2729351", "2729346", "2729316", "2729304", "2729296", "2729276", "2729272", "2729265", "2729195", "2729160", "2729143", "2729127", "2729119", "2729113", "2729103", "2729069", "2729059", "2729051", "2729029", "2729027", "2729026", "2729022", "2729002", "2728985", "2728979", "2728964", "2728937", "2728933", "2728884", "2728869", "2728866", "2728855", "2728854", "2728853", "2728846", "2728825", "2728814", "2728813", "2728812", "2728805", "2728802", "2728800", "2728798", "2728797", "2728796", "2728793", "2728758", "2728754", "2728751", "2728748", "2728721", "2728716", "2728715", "2728705", "2728701", "2728699", "2728697", "2728688", "2728683", "2728677", "2728649", "2728640", "2728625", "2728620", "2728615", "2728614", "2728565", "2728564", "2728554", "2728523", "2728519", "2728511", "2728497", "2728496", "2728495", "2728487"])