From eb6b5e45f461c4967e25c64c904f0d3b2051a854 Mon Sep 17 00:00:00 2001 From: David Medina Date: Thu, 10 Nov 2011 23:20:30 +0100 Subject: Init test enviroment Also rename user handler module to 'users' --- github3/tests/base_test.py | 29 ++++++++++++++ github3/tests/gist_tests.py | 98 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 127 insertions(+) create mode 100644 github3/tests/base_test.py create mode 100644 github3/tests/gist_tests.py (limited to 'github3/tests') diff --git a/github3/tests/base_test.py b/github3/tests/base_test.py new file mode 100644 index 0000000..59d474a --- /dev/null +++ b/github3/tests/base_test.py @@ -0,0 +1,29 @@ +#!/usr/bin/env python +# -*- encoding: utf-8 -*- + +from unittest import TestCase +import github3 +from github3 import api +from github3 import handlers + + +class TestGetHandlers(TestCase): + + def setUp(self): + self.anom_gh = api.Github() + self.auth_gh = api.Github('test', 'password') + + def test_get_user(self): + anom_user = self.anom_gh.users + auth_user = self.auth_gh.users + + self.assertEquals(isinstance(anom_user, handlers.users.User), True) + self.assertEquals(isinstance(auth_user, handlers.users.AuthUser), True) + + def test_get_gists(self): + anom_gists = self.anom_gh.gists + auth_gists = self.auth_gh.gists + + self.assertEquals(isinstance(anom_gists, handlers.gists.Gist), True) + self.assertEquals( + isinstance(auth_gists, handlers.gists.AuthGist), True) diff --git a/github3/tests/gist_tests.py b/github3/tests/gist_tests.py new file mode 100644 index 0000000..1218772 --- /dev/null +++ b/github3/tests/gist_tests.py @@ -0,0 +1,98 @@ +#!/usr/bin/env python +# -*- encoding: utf-8 -*- +# +# author: Antti Kaihola + +from datetime import datetime +import json +from mock import MagicMock, Mock, patch +import unittest + +import github3.api +import github3.handlers.gists +import github3.handlers.user +import github3.models + + +GIST_RESPONSE = '{"user":{"gravatar_id":"123","url":"https://api.github.com/users/testuser","avatar_url":"https://secure.gravatar.com/avatar/123?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-1.png","login":"testuser","id":12345},"url":"https://api.github.com/gists/791920","history":[{"version":"deadbeefdeadbeefdeadbeefdeadbeefdeadbeef","url":"https://api.github.com/gists/791920/deadbeefdeadbeefdeadbeefdeadbeefdeadbeef","user":{"gravatar_id":"123","url":"https://api.github.com/users/testuser","avatar_url":"https://secure.gravatar.com/avatar/123?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-1.png","login":"testuser","id":12345},"committed_at":"2011-11-09T08:50:53Z","change_status":{"deletions":0,"additions":1,"total":1}}],"description":"description","created_at":"2011-11-09T08:50:53Z","public":true,"comments":0,"updated_at":"2011-11-09T08:50:53Z","git_pull_url":"git://gist.github.com/791920.git","forks":[],"git_push_url":"git@gist.github.com:791920.git","html_url":"https://gist.github.com/791920","id":"791920","files":{"filename.ext":{"raw_url":"https://gist.github.com/raw/791920/badafadacadafadabadacadafadabadabadacada/filename.ext","type":"text/plain","content":"content","size":7,"language":null,"filename":"filename.ext"}}}' + + +class GistsTestCase(unittest.TestCase): + def test_create_gist(self): + """The HTTP request for creating a gist is correct""" + g = github3.api.Github() + g.session.auth = ('testuser', 'password') + u = github3.handlers.user.AuthUser(g) + gists = github3.handlers.gists.AuthGist(g) + OpenerDirector = MagicMock(name='OpenerDirector') + opener = OpenerDirector.return_value + response = opener.open.return_value + response.read.return_value = GIST_RESPONSE + response.code = 201 + + with patch('urllib2.OpenerDirector', OpenerDirector): + + gist = gists.create_gist( + 'description', + files={'filename.ext': {'content': 'content'}}) + + request = opener.open.call_args[0][0] + self.assertEqual(request.method, 'POST') + self.assertEqual(request.get_full_url(), + 'https://api.github.com/gists?per_page=100') + self.assertEqual(request.headers['Authorization'], + 'Basic dGVzdHVzZXI6cGFzc3dvcmQ=') + self.assertEqual(json.loads(request.data), + {u'description': u'description', + u'files': {u'filename.ext': {u'content': u'content'}}, + u'public': True}) + + +class GistHandlerTestCase(unittest.TestCase): + def test_response_conversion(self): + """A gist response is decoded correctly to a Gist object""" + g = github3.api.Github() + handler = github3.handlers.gists.Gist(g) + converter = handler._get_converter() + converter.inject(github3.models.Gist) + + gist = converter.loads(json.loads(GIST_RESPONSE)) + + self.assertEqual( + {filename: value.__dict__ + for filename, value in gist.files.iteritems()}, + {u'filename.ext': { + 'content': u'content', + 'filename': u'filename.ext', + 'raw_url': (u'https://gist.github.com/' + u'raw/791920/' + u'badafadacadafadabadacadafadabadabadacada/' + u'filename.ext'), + 'size': 7, + 'type': u'text/plain'}}) + self.assertEqual(gist.description, u'description') + self.assertEqual(gist.url, u'https://api.github.com/gists/791920') + self.assertEqual(gist.created_at, datetime(2011, 11, 9, 8, 50, 53)) + self.assertEqual(gist.html_url, u'https://gist.github.com/791920') + self.assertEqual(gist.public, True) + self.assertEqual( + gist.user.__dict__, + {'avatar_url': (u'https://secure.gravatar.com/avatar/123' + u'?d=https://a248.e.akamai.net/' + u'assets.github.com%2Fimages%2Fgravatars' + u'%2Fgravatar-1.png'), + 'id': 12345, + 'login': u'testuser', + 'url': u'https://api.github.com/users/testuser'}) + self.assertEqual(gist.git_pull_url, u'git://gist.github.com/791920.git') + self.assertEqual(gist.git_push_url, u'git@gist.github.com:791920.git') + self.assertEqual(gist.id, u'791920') + self.assertEqual(len(gist.history), 1) + h = gist.history[0] + self.assertEqual(h.change_status.__dict__, {'additions': 1, 'total': 1}) + self.assertEqual(h.committed_at, datetime(2011, 11, 9, 8, 50, 53)) + self.assertEqual(h.url, + u'https://api.github.com/gists/791920/' + u'deadbeefdeadbeefdeadbeefdeadbeefdeadbeef') + self.assertEqual(h.user.__dict__, gist.user.__dict__) + self.assertEqual(h.version, u'deadbeefdeadbeefdeadbeefdeadbeefdeadbeef') -- cgit v1.3-6-gb490 From 2ed172774e179dbf7020e1898cb1bcc4e485e9e9 Mon Sep 17 00:00:00 2001 From: David Medina Date: Sat, 12 Nov 2011 02:55:52 +0100 Subject: Added core_test and fix some bugs / pep8 Readme to Markdown --- README.md | 63 +++++++++++++++++++++++++ README.rst | 78 ------------------------------ github3/api.py | 29 ++++++------ github3/errors.py | 3 +- github3/handlers/users.py | 9 +++- github3/tests/test_core.py | 115 +++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 202 insertions(+), 95 deletions(-) create mode 100644 README.md delete mode 100644 README.rst create mode 100644 github3/tests/test_core.py (limited to 'github3/tests') diff --git a/README.md b/README.md new file mode 100644 index 0000000..334b975 --- /dev/null +++ b/README.md @@ -0,0 +1,63 @@ +Fork +==== +Refactor and complete api wrapper. Intensive work in progress + +Use with auth user +------------------ + + from github3.api import Github + + gh = Github('user', 'password') + + users_handler = gh.users + for repo in users_handler.get_repos(): + print repo + + gists_handler = gh.gists + gists_handler.create_gist( + u'Description', + files={'file1.txt': {'content': u'Content of first file'}}) + +Installation +------------ + +To install Github3, simply: + + $ pip -e git+https://copitux@github.com/copitux/python-github3#egg=python-github3 + +License +------- + +ISC License. + + Copyright (c) 2011, Kenneth Reitz + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + +Contribute +---------- + +If you'd like to contribute, simply fork `the repository`, commit your changes +to the **develop** branch (or branch off of it), and send a pull request. Make +sure you add yourself to `AUTHORS`. + + +Roadmap +------- + +- Unittests +- Handlers +- Sphinx Documentation +- Examples +- OAuth Last (how?) diff --git a/README.rst b/README.rst deleted file mode 100644 index c07cbc9..0000000 --- a/README.rst +++ /dev/null @@ -1,78 +0,0 @@ -Fork -====================================== -Refactor and complete api wrapper. Intensive work in progress - -Use with auth user ---------------------- - -```python -from github3.api import Github - -gh = Github('user', 'password') - -users_handler = gh.users -for repo in users_handler.get_repos(): - print repo - -gists_handler = gh.gists -gists_handler.create_gist( - u'Description', - files={'file1.txt': {'content': u'Content of first file'}}) -``` - - -Installation ------------- - -To install Github3, simply: :: - - $ pip install github3 - -Or, if you absolutely must: :: - - $ easy_install github3 - -But, you really shouldn't do that. - - - -License -------- - -ISC License. :: - - Copyright (c) 2011, Kenneth Reitz - - Permission to use, copy, modify, and/or distribute this software for any - purpose with or without fee is hereby granted, provided that the above - copyright notice and this permission notice appear in all copies. - - THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - - -Contribute ----------- - -If you'd like to contribute, simply fork `the repository`_, commit your changes -to the **develop** branch (or branch off of it), and send a pull request. Make -sure you add yourself to AUTHORS_. - - - -Roadmap -------- - -- Unittests -- Handlers -- Get it Started -- HTTP BASIC -- Get it working -- Sphinx Documetnation -- Examples -- OAuth Last (how?) diff --git a/github3/api.py b/github3/api.py index e9eebb5..9dbe52f 100644 --- a/github3/api.py +++ b/github3/api.py @@ -10,6 +10,7 @@ from handlers import users, gists RESOURCES_PER_PAGE = 100 + class GithubCore(object): """ Wrapper to github api requests @@ -88,22 +89,21 @@ class GithubCore(object): """ Arg's parser to `_request` method - It check keyword args to parse extra request args to params - Sample: - _parse_args(arg1=1, arg2=2) => params = {'arg1': 1, 'arg2': 2} + Put extra request_args in params """ request_core = ( - 'params','data','headers','cookies','files','auth','tiemout', - 'allow_redirects','proxies','return_response','config') + 'params', 'data', 'headers', 'cookies', 'files', 'auth', 'tiemout', + 'allow_redirects', 'proxies', 'return_response', 'config') request_params = request_args.get('params') extra_params = {} for k, v in request_args.items(): - if k in request_core: continue + if k in request_core: + continue extra_params.update({k: v}) del request_args[k] - if request_params: + if request_params and getattr(request_params, 'update'): request_args['params'].update(extra_params) - else: + elif extra_params: request_args['params'] = extra_params return request_args @@ -117,15 +117,16 @@ class GithubCore(object): :param kwargs: Keyword args to request """ request = self.base_url + request - parsed_args = self._parse_args(kwargs) - response = self.session.request(verb, request, **parsed_args) + self._parse_args(kwargs) + response = self.session.request(verb, request, **kwargs) self.requests_remaining = response.headers.get( - 'x-ratelimit-remaining',-1) + 'x-ratelimit-remaining', -1) error = GithubError(response) error.process() return response + class Github(GithubCore): """ Library enter """ @@ -133,10 +134,10 @@ class Github(GithubCore): super(Github, self).__init__() self.authenticated = False auth = len(args) - if auth == 2: # Basic auth - self.session.auth = tuple(map(str,args)) + if auth == 2: # Basic auth + self.session.auth = tuple(map(str, args)) self.authenticated = True - elif auth == 1: # Token oauth + elif auth == 1: # Token oauth raise NotImplementedError elif auth > 2: raise TypeError("user, password or token") diff --git a/github3/errors.py b/github3/errors.py index 09e616b..bd85483 100644 --- a/github3/errors.py +++ b/github3/errors.py @@ -6,6 +6,7 @@ import json import github3.exceptions as exceptions + class GithubError(object): """ Handler for API errors """ @@ -14,7 +15,7 @@ class GithubError(object): self.status_code = response.status_code try: self.debug = self._parser.loads(response.content) - except ValueError: + except (ValueError, TypeError): self.debug = {'message': response.content} def error_400(self): diff --git a/github3/handlers/users.py b/github3/handlers/users.py index fb893b4..b990c65 100644 --- a/github3/handlers/users.py +++ b/github3/handlers/users.py @@ -7,6 +7,7 @@ from .base import Handler import github3.models as models from github3.converters import Rawlizer + class User(Handler): """ User handler with public access """ @@ -25,6 +26,7 @@ class User(Handler): parse_user = str(getattr(user, 'login', user)) self.username = parse_user self.prefix = '/'.join((self.prefix, parse_user)) + return self def get(self): """ Return user """ @@ -61,6 +63,7 @@ class User(Handler): return self._get_resources('gists', model=models.Gist) + class AuthUser(User): """ User handler with public and private access """ @@ -116,6 +119,8 @@ class AuthUser(User): Follow user :param `user`: User model or username string + + NOTE: Maybe bug in API, return text/html. Waitingf for answer """ parse_user = str(getattr(user, 'login', user)) @@ -151,8 +156,8 @@ class AuthUser(User): #TODO: render key.pub file key = { - 'title': kwargs.get('title',''), - 'key': kwargs.get('key','') + 'title': kwargs.get('title', ''), + 'key': kwargs.get('key', '') } return self._post_resource('keys', data=key, model=models.Key) diff --git a/github3/tests/test_core.py b/github3/tests/test_core.py new file mode 100644 index 0000000..258a737 --- /dev/null +++ b/github3/tests/test_core.py @@ -0,0 +1,115 @@ +#!/usr/bin/env python +# -*- encoding: utf-8 -*- + +from mock import Mock, patch +from unittest import TestCase +from github3 import api +from github3.exceptions import * +import json +import requests + + +@patch.object(requests.sessions.Session, 'request') +class TestGithubCore(TestCase): + + def setUp(self): + self.gh = api.GithubCore() + self.assertEquals(self.gh.base_url, 'https://api.github.com/') + self.assertEquals(self.gh._parser, json) + self.base_url = self.gh.base_url + self.parser = self.gh._parser + + def test_parse_args(self, request_method): + args = { + 'data': {'some': 'data'}, + 'params': {'arg0': 'some'}, + 'headers': 'out', + 'auth': 'out', + 'arg1': 'some', + 'arg2': 'some', + 'arg3': {'some': 'data', 'are': {'nested': 'true'}}, + } + self.gh._parse_args(args) + self.assertEquals(args, { + 'data': {'some': 'data'}, + 'params': {'arg0': 'some', 'arg1': 'some', 'arg2': 'some', + 'arg3': {'some': 'data', 'are': {'nested': 'true'}}}, + 'headers': 'out', + 'auth': 'out', + }) + + def test_raise_errors(self, request_method): + real_request = (self.gh._request, 'GET', 'test') + request_method.return_value.status_code = 404 + self.assertRaises(NotFound, *real_request) + + request_method.return_value.status_code = 400 + self.assertRaises(BadRequest, *real_request) + + request_method.return_value.status_code = 422 + self.assertRaises(UnprocessableEntity, *real_request) + + request_method.return_value.status_code = 401 + self.assertRaises(Unauthorized, *real_request) + + def test_get(self, request_method): + response = request_method.return_value + response.content = self.parser.dumps({'test': 'test'}) + content = self.gh.get('core') + request_method.assert_called_with('GET', self.base_url + 'core') + self.assertEquals(content, {'test': 'test'}) + + response = request_method.return_value + response.headers = {'link': 'url_with_links'} + response.content = self.parser.dumps({'test': 'test'}) + header, content = self.gh.get('core', paginate=True) + request_method.assert_called_with('GET', self.base_url + 'core') + self.assertEquals(header, 'url_with_links') + self.assertEquals(content, {'test': 'test'}) + + def test_head(self, request_method): + pass # It has no sense using mocks + + def test_post_and_patch(self, request_method): + data = {'login': 'test', 'bio': 'test'} + response = request_method.return_value + response.status_code = 201 + response.content = self.parser.dumps({'post': 'done'}) + + content = self.gh.post('core', data=data) + request_method.assert_called_with( + 'POST', self.base_url + 'core', + data=self.parser.dumps(data)) + self.assertEquals(content, {'post': 'done'}) + + content = self.gh.post('core') + request_method.assert_called_with( + 'POST', self.base_url + 'core', + data=self.parser.dumps(None)) + self.assertEquals(content, {'post': 'done'}) + + response.status_code = 200 + content = self.gh.patch('core', data=data) + request_method.assert_called_with( + 'PATCH', self.base_url + 'core', + data=self.parser.dumps(data)) + self.assertEquals(content, {'post': 'done'}) + + content = self.gh.patch('core') + request_method.assert_called_with( + 'PATCH', self.base_url + 'core', + data=self.parser.dumps(None)) + self.assertEquals(content, {'post': 'done'}) + + def test_delete(self, request_method): + data = {'test': 'test'} + response = request_method.return_value + response.status_code = 204 + response.content = self.parser.dumps({'delete': 'done'}) + delete = self.gh.delete('core', data=data) + request_method.assert_called_with( + 'DELETE', self.base_url + 'core', + data=self.parser.dumps(data)) + delete = self.gh.delete('core') + request_method.assert_called_with( + 'DELETE', self.base_url + 'core') -- cgit v1.3-6-gb490 From b2890225a70d26067f997f8b5a6345eaecfac84b Mon Sep 17 00:00:00 2001 From: David Medina Date: Sat, 12 Nov 2011 13:13:28 +0100 Subject: Added converters test --- github3/core.py | 1 - github3/handlers/base.py | 3 +- github3/tests/gist_tests.py | 98 ---------------------------------------- github3/tests/test_converters.py | 91 +++++++++++++++++++++++++++++++++++++ 4 files changed, 92 insertions(+), 101 deletions(-) delete mode 100644 github3/tests/gist_tests.py create mode 100644 github3/tests/test_converters.py (limited to 'github3/tests') diff --git a/github3/core.py b/github3/core.py index d7237af..42f8a07 100644 --- a/github3/core.py +++ b/github3/core.py @@ -29,7 +29,6 @@ class Paginate: return self.last - # TODO: reset iterators... multiple? def __iter__(self): return self diff --git a/github3/handlers/base.py b/github3/handlers/base.py index 50e2df8..d0af418 100644 --- a/github3/handlers/base.py +++ b/github3/handlers/base.py @@ -53,9 +53,8 @@ class Handler(object): """ Hander request to multiple resources """ resource = self._prefix_resource(resource) - page_resources = Paginate(resource, self._gh.get, **kwargs) counter = 1 - for page in page_resources: + for page in Paginate(resource, self._gh.get, **kwargs): for raw_resource in page: if limit and counter > limit: break counter += 1 diff --git a/github3/tests/gist_tests.py b/github3/tests/gist_tests.py deleted file mode 100644 index 1218772..0000000 --- a/github3/tests/gist_tests.py +++ /dev/null @@ -1,98 +0,0 @@ -#!/usr/bin/env python -# -*- encoding: utf-8 -*- -# -# author: Antti Kaihola - -from datetime import datetime -import json -from mock import MagicMock, Mock, patch -import unittest - -import github3.api -import github3.handlers.gists -import github3.handlers.user -import github3.models - - -GIST_RESPONSE = '{"user":{"gravatar_id":"123","url":"https://api.github.com/users/testuser","avatar_url":"https://secure.gravatar.com/avatar/123?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-1.png","login":"testuser","id":12345},"url":"https://api.github.com/gists/791920","history":[{"version":"deadbeefdeadbeefdeadbeefdeadbeefdeadbeef","url":"https://api.github.com/gists/791920/deadbeefdeadbeefdeadbeefdeadbeefdeadbeef","user":{"gravatar_id":"123","url":"https://api.github.com/users/testuser","avatar_url":"https://secure.gravatar.com/avatar/123?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-1.png","login":"testuser","id":12345},"committed_at":"2011-11-09T08:50:53Z","change_status":{"deletions":0,"additions":1,"total":1}}],"description":"description","created_at":"2011-11-09T08:50:53Z","public":true,"comments":0,"updated_at":"2011-11-09T08:50:53Z","git_pull_url":"git://gist.github.com/791920.git","forks":[],"git_push_url":"git@gist.github.com:791920.git","html_url":"https://gist.github.com/791920","id":"791920","files":{"filename.ext":{"raw_url":"https://gist.github.com/raw/791920/badafadacadafadabadacadafadabadabadacada/filename.ext","type":"text/plain","content":"content","size":7,"language":null,"filename":"filename.ext"}}}' - - -class GistsTestCase(unittest.TestCase): - def test_create_gist(self): - """The HTTP request for creating a gist is correct""" - g = github3.api.Github() - g.session.auth = ('testuser', 'password') - u = github3.handlers.user.AuthUser(g) - gists = github3.handlers.gists.AuthGist(g) - OpenerDirector = MagicMock(name='OpenerDirector') - opener = OpenerDirector.return_value - response = opener.open.return_value - response.read.return_value = GIST_RESPONSE - response.code = 201 - - with patch('urllib2.OpenerDirector', OpenerDirector): - - gist = gists.create_gist( - 'description', - files={'filename.ext': {'content': 'content'}}) - - request = opener.open.call_args[0][0] - self.assertEqual(request.method, 'POST') - self.assertEqual(request.get_full_url(), - 'https://api.github.com/gists?per_page=100') - self.assertEqual(request.headers['Authorization'], - 'Basic dGVzdHVzZXI6cGFzc3dvcmQ=') - self.assertEqual(json.loads(request.data), - {u'description': u'description', - u'files': {u'filename.ext': {u'content': u'content'}}, - u'public': True}) - - -class GistHandlerTestCase(unittest.TestCase): - def test_response_conversion(self): - """A gist response is decoded correctly to a Gist object""" - g = github3.api.Github() - handler = github3.handlers.gists.Gist(g) - converter = handler._get_converter() - converter.inject(github3.models.Gist) - - gist = converter.loads(json.loads(GIST_RESPONSE)) - - self.assertEqual( - {filename: value.__dict__ - for filename, value in gist.files.iteritems()}, - {u'filename.ext': { - 'content': u'content', - 'filename': u'filename.ext', - 'raw_url': (u'https://gist.github.com/' - u'raw/791920/' - u'badafadacadafadabadacadafadabadabadacada/' - u'filename.ext'), - 'size': 7, - 'type': u'text/plain'}}) - self.assertEqual(gist.description, u'description') - self.assertEqual(gist.url, u'https://api.github.com/gists/791920') - self.assertEqual(gist.created_at, datetime(2011, 11, 9, 8, 50, 53)) - self.assertEqual(gist.html_url, u'https://gist.github.com/791920') - self.assertEqual(gist.public, True) - self.assertEqual( - gist.user.__dict__, - {'avatar_url': (u'https://secure.gravatar.com/avatar/123' - u'?d=https://a248.e.akamai.net/' - u'assets.github.com%2Fimages%2Fgravatars' - u'%2Fgravatar-1.png'), - 'id': 12345, - 'login': u'testuser', - 'url': u'https://api.github.com/users/testuser'}) - self.assertEqual(gist.git_pull_url, u'git://gist.github.com/791920.git') - self.assertEqual(gist.git_push_url, u'git@gist.github.com:791920.git') - self.assertEqual(gist.id, u'791920') - self.assertEqual(len(gist.history), 1) - h = gist.history[0] - self.assertEqual(h.change_status.__dict__, {'additions': 1, 'total': 1}) - self.assertEqual(h.committed_at, datetime(2011, 11, 9, 8, 50, 53)) - self.assertEqual(h.url, - u'https://api.github.com/gists/791920/' - u'deadbeefdeadbeefdeadbeefdeadbeefdeadbeef') - self.assertEqual(h.user.__dict__, gist.user.__dict__) - self.assertEqual(h.version, u'deadbeefdeadbeefdeadbeefdeadbeefdeadbeef') diff --git a/github3/tests/test_converters.py b/github3/tests/test_converters.py new file mode 100644 index 0000000..ca1c715 --- /dev/null +++ b/github3/tests/test_converters.py @@ -0,0 +1,91 @@ +#!/usr/bin/env python +# -*- encoding: utf-8 -*- + +from github3.converters import * +from github3.models.base import BaseResource +from unittest import TestCase +from datetime import datetime + +API_STUB = { + 'test_str': 'string', 'test_int': 1, + 'test_date': '2008-01-14T04:33:35Z', 'test_bool': True, + 'map': {'test_str': 'string'}, + 'dict_map': { + 'map1': { + 'test_str': 'string', + 'test_int': 1 + }, + 'map2': { + 'test_str': 'string', + 'test_int': 2 + }, + }, + 'list_map': [ + {'test_str': 'string', 'test_int': 1}, + {'test_str': 'string', 'test_int': 2}, + ] +} + + +class Model(BaseResource): + + @classmethod + def idl(self): + return { + 'strs': ['test_str'], + 'ints': ['test_int'], + 'dates': ['test_date'], + 'bools': ['test_bool'], + 'maps': {'map': Model}, + 'collection_maps': { + 'dict_map': Model, + 'list_map': Model, + }, + } + + +class TestModelizer(TestCase): + + def setUp(self): + model = Model + self.modelizer = Modelizer() + self.modelizer.inject(model) + + def test_loads(self): + parsed_model = self.modelizer.loads(API_STUB) + self.assertEquals(parsed_model.test_str, 'string') + self.assertEquals(parsed_model.test_int, 1) + self.assertEquals( + parsed_model.test_date, + datetime(2008, 1, 14, 4, 33, 35)) + self.assertTrue(parsed_model.test_bool) + self.assertTrue(isinstance(parsed_model.map, Model)) + self.assertEquals(parsed_model.map.test_str, 'string') + self.assertTrue(isinstance(parsed_model.dict_map, dict)) + map1 = parsed_model.dict_map['map1'] + map2 = parsed_model.dict_map['map2'] + self.assertTrue(isinstance(map1, Model)) + self.assertTrue(isinstance(map2, Model)) + self.assertEquals(map1.test_str, 'string') + self.assertEquals(map1.test_int, 1) + self.assertEquals(map2.test_str, 'string') + self.assertEquals(map2.test_int, 2) + + list_map = parsed_model.list_map + self.assertTrue(isinstance(list_map, list)) + self.assertEquals(list_map[0].test_str, 'string') + self.assertEquals(list_map[0].test_int, 1) + self.assertEquals(list_map[1].test_str, 'string') + self.assertEquals(list_map[1].test_int, 2) + + +class TestRawlizer(TestCase): + + def setUp(self): + model = Model + self.rawlizer = Rawlizer() + + # Trivial, I know it + def test_loads(self): + raw = self.rawlizer.loads(API_STUB) + self.assertEquals(raw, API_STUB) -- cgit v1.3-6-gb490 From 1836ee60f0a3772188c9341efe737aa03ca6f2ff Mon Sep 17 00:00:00 2001 From: David Medina Date: Sat, 12 Nov 2011 19:06:55 +0100 Subject: PEP8 and renaming --- github3/core.py | 7 ++- github3/tests/api_test.py | 115 +++++++++++++++++++++++++++++++++++++ github3/tests/base_test.py | 29 ---------- github3/tests/converters_test.py | 91 +++++++++++++++++++++++++++++ github3/tests/get_handlers_test.py | 28 +++++++++ github3/tests/test_converters.py | 91 ----------------------------- github3/tests/test_core.py | 115 ------------------------------------- 7 files changed, 240 insertions(+), 236 deletions(-) create mode 100644 github3/tests/api_test.py delete mode 100644 github3/tests/base_test.py create mode 100644 github3/tests/converters_test.py create mode 100644 github3/tests/get_handlers_test.py delete mode 100644 github3/tests/test_converters.py delete mode 100644 github3/tests/test_core.py (limited to 'github3/tests') diff --git a/github3/core.py b/github3/core.py index 42f8a07..b838bf1 100644 --- a/github3/core.py +++ b/github3/core.py @@ -3,6 +3,7 @@ # # author: David Medina + class Paginate: """ Paginate resource iterator @@ -33,7 +34,10 @@ class Paginate: return self def initial(self): - """ First request. Force requester to paginate returning link header """ + """ + First request + Force requester to paginate returning link header + """ link, content = self.requester(self.resource, paginate=True, page=1, **self.kwargs) self.last = self._last_page(link) if link else 1 @@ -53,6 +57,7 @@ class Paginate: self.page += 1 return content + class Converter(object): """ Abstract converter class """ diff --git a/github3/tests/api_test.py b/github3/tests/api_test.py new file mode 100644 index 0000000..258a737 --- /dev/null +++ b/github3/tests/api_test.py @@ -0,0 +1,115 @@ +#!/usr/bin/env python +# -*- encoding: utf-8 -*- + +from mock import Mock, patch +from unittest import TestCase +from github3 import api +from github3.exceptions import * +import json +import requests + + +@patch.object(requests.sessions.Session, 'request') +class TestGithubCore(TestCase): + + def setUp(self): + self.gh = api.GithubCore() + self.assertEquals(self.gh.base_url, 'https://api.github.com/') + self.assertEquals(self.gh._parser, json) + self.base_url = self.gh.base_url + self.parser = self.gh._parser + + def test_parse_args(self, request_method): + args = { + 'data': {'some': 'data'}, + 'params': {'arg0': 'some'}, + 'headers': 'out', + 'auth': 'out', + 'arg1': 'some', + 'arg2': 'some', + 'arg3': {'some': 'data', 'are': {'nested': 'true'}}, + } + self.gh._parse_args(args) + self.assertEquals(args, { + 'data': {'some': 'data'}, + 'params': {'arg0': 'some', 'arg1': 'some', 'arg2': 'some', + 'arg3': {'some': 'data', 'are': {'nested': 'true'}}}, + 'headers': 'out', + 'auth': 'out', + }) + + def test_raise_errors(self, request_method): + real_request = (self.gh._request, 'GET', 'test') + request_method.return_value.status_code = 404 + self.assertRaises(NotFound, *real_request) + + request_method.return_value.status_code = 400 + self.assertRaises(BadRequest, *real_request) + + request_method.return_value.status_code = 422 + self.assertRaises(UnprocessableEntity, *real_request) + + request_method.return_value.status_code = 401 + self.assertRaises(Unauthorized, *real_request) + + def test_get(self, request_method): + response = request_method.return_value + response.content = self.parser.dumps({'test': 'test'}) + content = self.gh.get('core') + request_method.assert_called_with('GET', self.base_url + 'core') + self.assertEquals(content, {'test': 'test'}) + + response = request_method.return_value + response.headers = {'link': 'url_with_links'} + response.content = self.parser.dumps({'test': 'test'}) + header, content = self.gh.get('core', paginate=True) + request_method.assert_called_with('GET', self.base_url + 'core') + self.assertEquals(header, 'url_with_links') + self.assertEquals(content, {'test': 'test'}) + + def test_head(self, request_method): + pass # It has no sense using mocks + + def test_post_and_patch(self, request_method): + data = {'login': 'test', 'bio': 'test'} + response = request_method.return_value + response.status_code = 201 + response.content = self.parser.dumps({'post': 'done'}) + + content = self.gh.post('core', data=data) + request_method.assert_called_with( + 'POST', self.base_url + 'core', + data=self.parser.dumps(data)) + self.assertEquals(content, {'post': 'done'}) + + content = self.gh.post('core') + request_method.assert_called_with( + 'POST', self.base_url + 'core', + data=self.parser.dumps(None)) + self.assertEquals(content, {'post': 'done'}) + + response.status_code = 200 + content = self.gh.patch('core', data=data) + request_method.assert_called_with( + 'PATCH', self.base_url + 'core', + data=self.parser.dumps(data)) + self.assertEquals(content, {'post': 'done'}) + + content = self.gh.patch('core') + request_method.assert_called_with( + 'PATCH', self.base_url + 'core', + data=self.parser.dumps(None)) + self.assertEquals(content, {'post': 'done'}) + + def test_delete(self, request_method): + data = {'test': 'test'} + response = request_method.return_value + response.status_code = 204 + response.content = self.parser.dumps({'delete': 'done'}) + delete = self.gh.delete('core', data=data) + request_method.assert_called_with( + 'DELETE', self.base_url + 'core', + data=self.parser.dumps(data)) + delete = self.gh.delete('core') + request_method.assert_called_with( + 'DELETE', self.base_url + 'core') diff --git a/github3/tests/base_test.py b/github3/tests/base_test.py deleted file mode 100644 index 59d474a..0000000 --- a/github3/tests/base_test.py +++ /dev/null @@ -1,29 +0,0 @@ -#!/usr/bin/env python -# -*- encoding: utf-8 -*- - -from unittest import TestCase -import github3 -from github3 import api -from github3 import handlers - - -class TestGetHandlers(TestCase): - - def setUp(self): - self.anom_gh = api.Github() - self.auth_gh = api.Github('test', 'password') - - def test_get_user(self): - anom_user = self.anom_gh.users - auth_user = self.auth_gh.users - - self.assertEquals(isinstance(anom_user, handlers.users.User), True) - self.assertEquals(isinstance(auth_user, handlers.users.AuthUser), True) - - def test_get_gists(self): - anom_gists = self.anom_gh.gists - auth_gists = self.auth_gh.gists - - self.assertEquals(isinstance(anom_gists, handlers.gists.Gist), True) - self.assertEquals( - isinstance(auth_gists, handlers.gists.AuthGist), True) diff --git a/github3/tests/converters_test.py b/github3/tests/converters_test.py new file mode 100644 index 0000000..ca1c715 --- /dev/null +++ b/github3/tests/converters_test.py @@ -0,0 +1,91 @@ +#!/usr/bin/env python +# -*- encoding: utf-8 -*- + +from github3.converters import * +from github3.models.base import BaseResource +from unittest import TestCase +from datetime import datetime + +API_STUB = { + 'test_str': 'string', 'test_int': 1, + 'test_date': '2008-01-14T04:33:35Z', 'test_bool': True, + 'map': {'test_str': 'string'}, + 'dict_map': { + 'map1': { + 'test_str': 'string', + 'test_int': 1 + }, + 'map2': { + 'test_str': 'string', + 'test_int': 2 + }, + }, + 'list_map': [ + {'test_str': 'string', 'test_int': 1}, + {'test_str': 'string', 'test_int': 2}, + ] +} + + +class Model(BaseResource): + + @classmethod + def idl(self): + return { + 'strs': ['test_str'], + 'ints': ['test_int'], + 'dates': ['test_date'], + 'bools': ['test_bool'], + 'maps': {'map': Model}, + 'collection_maps': { + 'dict_map': Model, + 'list_map': Model, + }, + } + + +class TestModelizer(TestCase): + + def setUp(self): + model = Model + self.modelizer = Modelizer() + self.modelizer.inject(model) + + def test_loads(self): + parsed_model = self.modelizer.loads(API_STUB) + self.assertEquals(parsed_model.test_str, 'string') + self.assertEquals(parsed_model.test_int, 1) + self.assertEquals( + parsed_model.test_date, + datetime(2008, 1, 14, 4, 33, 35)) + self.assertTrue(parsed_model.test_bool) + self.assertTrue(isinstance(parsed_model.map, Model)) + self.assertEquals(parsed_model.map.test_str, 'string') + self.assertTrue(isinstance(parsed_model.dict_map, dict)) + map1 = parsed_model.dict_map['map1'] + map2 = parsed_model.dict_map['map2'] + self.assertTrue(isinstance(map1, Model)) + self.assertTrue(isinstance(map2, Model)) + self.assertEquals(map1.test_str, 'string') + self.assertEquals(map1.test_int, 1) + self.assertEquals(map2.test_str, 'string') + self.assertEquals(map2.test_int, 2) + + list_map = parsed_model.list_map + self.assertTrue(isinstance(list_map, list)) + self.assertEquals(list_map[0].test_str, 'string') + self.assertEquals(list_map[0].test_int, 1) + self.assertEquals(list_map[1].test_str, 'string') + self.assertEquals(list_map[1].test_int, 2) + + +class TestRawlizer(TestCase): + + def setUp(self): + model = Model + self.rawlizer = Rawlizer() + + # Trivial, I know it + def test_loads(self): + raw = self.rawlizer.loads(API_STUB) + self.assertEquals(raw, API_STUB) diff --git a/github3/tests/get_handlers_test.py b/github3/tests/get_handlers_test.py new file mode 100644 index 0000000..0ae1302 --- /dev/null +++ b/github3/tests/get_handlers_test.py @@ -0,0 +1,28 @@ +#!/usr/bin/env python +# -*- encoding: utf-8 -*- + +from unittest import TestCase +from github3 import api +from github3 import handlers + + +class TestGetHandlers(TestCase): + + def setUp(self): + self.anom_gh = api.Github() + self.auth_gh = api.Github('test', 'password') + + def test_get_user(self): + anom_user = self.anom_gh.users + auth_user = self.auth_gh.users + + self.assertEquals(isinstance(anom_user, handlers.users.User), True) + self.assertEquals(isinstance(auth_user, handlers.users.AuthUser), True) + + def test_get_gists(self): + anom_gists = self.anom_gh.gists + auth_gists = self.auth_gh.gists + + self.assertEquals(isinstance(anom_gists, handlers.gists.Gist), True) + self.assertEquals( + isinstance(auth_gists, handlers.gists.AuthGist), True) diff --git a/github3/tests/test_converters.py b/github3/tests/test_converters.py deleted file mode 100644 index ca1c715..0000000 --- a/github3/tests/test_converters.py +++ /dev/null @@ -1,91 +0,0 @@ -#!/usr/bin/env python -# -*- encoding: utf-8 -*- - -from github3.converters import * -from github3.models.base import BaseResource -from unittest import TestCase -from datetime import datetime - -API_STUB = { - 'test_str': 'string', 'test_int': 1, - 'test_date': '2008-01-14T04:33:35Z', 'test_bool': True, - 'map': {'test_str': 'string'}, - 'dict_map': { - 'map1': { - 'test_str': 'string', - 'test_int': 1 - }, - 'map2': { - 'test_str': 'string', - 'test_int': 2 - }, - }, - 'list_map': [ - {'test_str': 'string', 'test_int': 1}, - {'test_str': 'string', 'test_int': 2}, - ] -} - - -class Model(BaseResource): - - @classmethod - def idl(self): - return { - 'strs': ['test_str'], - 'ints': ['test_int'], - 'dates': ['test_date'], - 'bools': ['test_bool'], - 'maps': {'map': Model}, - 'collection_maps': { - 'dict_map': Model, - 'list_map': Model, - }, - } - - -class TestModelizer(TestCase): - - def setUp(self): - model = Model - self.modelizer = Modelizer() - self.modelizer.inject(model) - - def test_loads(self): - parsed_model = self.modelizer.loads(API_STUB) - self.assertEquals(parsed_model.test_str, 'string') - self.assertEquals(parsed_model.test_int, 1) - self.assertEquals( - parsed_model.test_date, - datetime(2008, 1, 14, 4, 33, 35)) - self.assertTrue(parsed_model.test_bool) - self.assertTrue(isinstance(parsed_model.map, Model)) - self.assertEquals(parsed_model.map.test_str, 'string') - self.assertTrue(isinstance(parsed_model.dict_map, dict)) - map1 = parsed_model.dict_map['map1'] - map2 = parsed_model.dict_map['map2'] - self.assertTrue(isinstance(map1, Model)) - self.assertTrue(isinstance(map2, Model)) - self.assertEquals(map1.test_str, 'string') - self.assertEquals(map1.test_int, 1) - self.assertEquals(map2.test_str, 'string') - self.assertEquals(map2.test_int, 2) - - list_map = parsed_model.list_map - self.assertTrue(isinstance(list_map, list)) - self.assertEquals(list_map[0].test_str, 'string') - self.assertEquals(list_map[0].test_int, 1) - self.assertEquals(list_map[1].test_str, 'string') - self.assertEquals(list_map[1].test_int, 2) - - -class TestRawlizer(TestCase): - - def setUp(self): - model = Model - self.rawlizer = Rawlizer() - - # Trivial, I know it - def test_loads(self): - raw = self.rawlizer.loads(API_STUB) - self.assertEquals(raw, API_STUB) diff --git a/github3/tests/test_core.py b/github3/tests/test_core.py deleted file mode 100644 index 258a737..0000000 --- a/github3/tests/test_core.py +++ /dev/null @@ -1,115 +0,0 @@ -#!/usr/bin/env python -# -*- encoding: utf-8 -*- - -from mock import Mock, patch -from unittest import TestCase -from github3 import api -from github3.exceptions import * -import json -import requests - - -@patch.object(requests.sessions.Session, 'request') -class TestGithubCore(TestCase): - - def setUp(self): - self.gh = api.GithubCore() - self.assertEquals(self.gh.base_url, 'https://api.github.com/') - self.assertEquals(self.gh._parser, json) - self.base_url = self.gh.base_url - self.parser = self.gh._parser - - def test_parse_args(self, request_method): - args = { - 'data': {'some': 'data'}, - 'params': {'arg0': 'some'}, - 'headers': 'out', - 'auth': 'out', - 'arg1': 'some', - 'arg2': 'some', - 'arg3': {'some': 'data', 'are': {'nested': 'true'}}, - } - self.gh._parse_args(args) - self.assertEquals(args, { - 'data': {'some': 'data'}, - 'params': {'arg0': 'some', 'arg1': 'some', 'arg2': 'some', - 'arg3': {'some': 'data', 'are': {'nested': 'true'}}}, - 'headers': 'out', - 'auth': 'out', - }) - - def test_raise_errors(self, request_method): - real_request = (self.gh._request, 'GET', 'test') - request_method.return_value.status_code = 404 - self.assertRaises(NotFound, *real_request) - - request_method.return_value.status_code = 400 - self.assertRaises(BadRequest, *real_request) - - request_method.return_value.status_code = 422 - self.assertRaises(UnprocessableEntity, *real_request) - - request_method.return_value.status_code = 401 - self.assertRaises(Unauthorized, *real_request) - - def test_get(self, request_method): - response = request_method.return_value - response.content = self.parser.dumps({'test': 'test'}) - content = self.gh.get('core') - request_method.assert_called_with('GET', self.base_url + 'core') - self.assertEquals(content, {'test': 'test'}) - - response = request_method.return_value - response.headers = {'link': 'url_with_links'} - response.content = self.parser.dumps({'test': 'test'}) - header, content = self.gh.get('core', paginate=True) - request_method.assert_called_with('GET', self.base_url + 'core') - self.assertEquals(header, 'url_with_links') - self.assertEquals(content, {'test': 'test'}) - - def test_head(self, request_method): - pass # It has no sense using mocks - - def test_post_and_patch(self, request_method): - data = {'login': 'test', 'bio': 'test'} - response = request_method.return_value - response.status_code = 201 - response.content = self.parser.dumps({'post': 'done'}) - - content = self.gh.post('core', data=data) - request_method.assert_called_with( - 'POST', self.base_url + 'core', - data=self.parser.dumps(data)) - self.assertEquals(content, {'post': 'done'}) - - content = self.gh.post('core') - request_method.assert_called_with( - 'POST', self.base_url + 'core', - data=self.parser.dumps(None)) - self.assertEquals(content, {'post': 'done'}) - - response.status_code = 200 - content = self.gh.patch('core', data=data) - request_method.assert_called_with( - 'PATCH', self.base_url + 'core', - data=self.parser.dumps(data)) - self.assertEquals(content, {'post': 'done'}) - - content = self.gh.patch('core') - request_method.assert_called_with( - 'PATCH', self.base_url + 'core', - data=self.parser.dumps(None)) - self.assertEquals(content, {'post': 'done'}) - - def test_delete(self, request_method): - data = {'test': 'test'} - response = request_method.return_value - response.status_code = 204 - response.content = self.parser.dumps({'delete': 'done'}) - delete = self.gh.delete('core', data=data) - request_method.assert_called_with( - 'DELETE', self.base_url + 'core', - data=self.parser.dumps(data)) - delete = self.gh.delete('core') - request_method.assert_called_with( - 'DELETE', self.base_url + 'core') -- cgit v1.3-6-gb490 From 78752f56adb57bb1d5672a6ac819944477b7b529 Mon Sep 17 00:00:00 2001 From: David Medina Date: Sun, 13 Nov 2011 19:29:12 +0100 Subject: Testing on fire (handlers, user_handler) Also fix bugs, pep8 --- github3/api.py | 1 + github3/exceptions.py | 2 ++ github3/handlers/base.py | 6 ++-- github3/tests/fixtures.py | 40 ++++++++++++++++++++++++++ github3/tests/get_handlers_test.py | 11 ++++---- github3/tests/handler_test.py | 53 ++++++++++++++++++++++++++++++++++ github3/tests/user_handler_test.py | 58 ++++++++++++++++++++++++++++++++++++++ 7 files changed, 163 insertions(+), 8 deletions(-) create mode 100644 github3/tests/fixtures.py create mode 100644 github3/tests/handler_test.py create mode 100644 github3/tests/user_handler_test.py (limited to 'github3/tests') diff --git a/github3/api.py b/github3/api.py index 9dbe52f..bf0ebe2 100644 --- a/github3/api.py +++ b/github3/api.py @@ -71,6 +71,7 @@ class GithubCore(object): def put(self, request, **kwargs): """ PUT request """ + # Content-length: 0 to headers ? response = self._request('PUT', request, **kwargs) assert response.status_code == 204 return response diff --git a/github3/exceptions.py b/github3/exceptions.py index b033001..dbc4d2c 100644 --- a/github3/exceptions.py +++ b/github3/exceptions.py @@ -11,3 +11,5 @@ class NotFound(Exception): pass class Unauthorized(Exception): pass +class UserIsAnonymous(Exception): + pass diff --git a/github3/handlers/base.py b/github3/handlers/base.py index d0af418..68eb6b4 100644 --- a/github3/handlers/base.py +++ b/github3/handlers/base.py @@ -15,15 +15,15 @@ class Handler(object): def _prefix_resource(self, resource): prefix = getattr(self, 'prefix', '') - return '/'.join((prefix, resource)).rstrip('/') + return '/'.join((prefix, resource)).strip('/') def _get_converter(self, **kwargs): converter = kwargs.get( 'converter', # 1. in kwargs getattr(self, 'converter', # 2. in handler - Modelizer())) # 3. Default + Modelizer)) # 3. Default - return converter + return converter() def _put(self, resource, **kwargs): """ Put proxy request""" diff --git a/github3/tests/fixtures.py b/github3/tests/fixtures.py new file mode 100644 index 0000000..aef4425 --- /dev/null +++ b/github3/tests/fixtures.py @@ -0,0 +1,40 @@ +#!/usr/bin/env python +# -*- encoding: utf-8 -*- + +GET_USER = { + "login": "octocat", + "id": 1, + "avatar_url": "https://github.com/images/error/octocat_happy.gif", + "url": "https://api.github.com/users/octocat", + "name": "monalisa octocat", + "company": "GitHub", + "blog": "https://github.com/blog", + "location": "San Francisco", + "email": "octocat@github.com", + "hireable": False, + "bio": "There once was...", + "public_repos": 2, + "public_gists": 1, + "followers": 20, + "following": 0, + "html_url": "https://github.com/octocat", + "created_at": "2008-01-14T04:33:35Z", + "type": "User" +} + +GET_LINK = '; rel="next", ; rel="last"' + +GET_FOLLOWERS = [ + { + "login": "octocat", + "id": 1, + "avatar_url": "https://github.com/images/error/octocat_happy.gif", + "url": "https://api.github.com/users/octocat" + }, + { + "login": "octocat", + "id": 1, + "avatar_url": "https://github.com/images/error/octocat_happy.gif", + "url": "https://api.github.com/users/octocat" + }, +] diff --git a/github3/tests/get_handlers_test.py b/github3/tests/get_handlers_test.py index 0ae1302..f07a7cf 100644 --- a/github3/tests/get_handlers_test.py +++ b/github3/tests/get_handlers_test.py @@ -16,13 +16,14 @@ class TestGetHandlers(TestCase): anom_user = self.anom_gh.users auth_user = self.auth_gh.users - self.assertEquals(isinstance(anom_user, handlers.users.User), True) - self.assertEquals(isinstance(auth_user, handlers.users.AuthUser), True) + self.assertIsInstance(anom_user, handlers.users.User) + self.assertEquals(anom_user.prefix, 'users') + self.assertIsInstance(auth_user, handlers.users.AuthUser) + self.assertEquals(auth_user.prefix, 'user') def test_get_gists(self): anom_gists = self.anom_gh.gists auth_gists = self.auth_gh.gists - self.assertEquals(isinstance(anom_gists, handlers.gists.Gist), True) - self.assertEquals( - isinstance(auth_gists, handlers.gists.AuthGist), True) + self.assertIsInstance(anom_gists, handlers.gists.Gist) + self.assertIsInstance(auth_gists, handlers.gists.AuthGist) diff --git a/github3/tests/handler_test.py b/github3/tests/handler_test.py new file mode 100644 index 0000000..51cd111 --- /dev/null +++ b/github3/tests/handler_test.py @@ -0,0 +1,53 @@ +#!/usr/bin/env python +# -*- encoding: utf-8 -*- + +from mock import Mock, patch +from unittest import TestCase +from github3 import api +from github3.handlers.base import Handler +from github3.exceptions import * +from github3.converters import * +import json +import requests + + +class TestHandler(TestCase): + + def setUp(self): + self.gh = api.Github() + self.handler = Handler(self.gh) + + def test_get_converter(self): + self.assertIsInstance(self.handler._get_converter(), Modelizer) + self.assertIsInstance(self.handler._get_converter(converter=Rawlizer), + Rawlizer) + self.handler.converter = Modelizer + self.assertIsInstance(self.handler._get_converter(), Modelizer) + + def test_bool(self): + with patch.object(api.Github, 'head') as head: + response = head.return_value + response.status_code = 204 + bool1 = self.handler._bool('test') + head.side_effect = NotFound() + bool2 = self.handler._bool('test') + head.assert_called_with('test') + self.assertTrue(bool1) + self.assertFalse(bool2) + + with patch.object(api.Github, 'put') as put: + response = put.return_value + response.status_code = 204 + booll = self.handler._put('test') + put.assert_called_with('test', method='put') + self.assertTrue(booll) + + with patch.object(api.Github, 'delete') as delete: + response = delete.return_value + response.content = self.gh._parser.dumps({'data': 'test'}) + response.status_code = 204 + bool1 = self.handler._bool('test', method='delete') + bool2 = self.handler._bool('test', method='delete', + data={'some': 'data'}) + self.assertTrue(bool1) + self.assertTrue(bool2) diff --git a/github3/tests/user_handler_test.py b/github3/tests/user_handler_test.py new file mode 100644 index 0000000..9eb3708 --- /dev/null +++ b/github3/tests/user_handler_test.py @@ -0,0 +1,58 @@ +#!/usr/bin/env python +# -*- encoding: utf-8 -*- + +from unittest import TestCase +from mock import Mock, patch +from github3 import api +from fixtures import * +from github3.models.user import User, AuthUser +from github3.exceptions import * + + +class TestUserHandler(TestCase): + """ Test public api about users """ + + def setUp(self): + self.gh = api.Github() + self.handler = self.gh.users + + def test_set_username(self): + handler = self.handler.set_username('test') + self.assertEquals(id(handler), id(self.handler)) + self.assertEquals(handler.username, 'test') + model_user = Mock() + model_user.login = 'test' + handler = self.handler.set_username(model_user) + self.assertEquals(handler.username, 'test') + + def test_parse_user(self): + model_user = Mock() + model_user.login = 'test' + self.assertRaises(UserIsAnonymous, self.handler._parse_user, None) + user = self.handler._parse_user(model_user) + self.assertEquals(user, 'test') + user = self.handler._parse_user('test') + self.assertEquals(user, 'test') + self.assertRaises(UserIsAnonymous, self.handler._parse_user, Mock()) + self.handler.set_username('octocat') + self.assertEquals('octocat', self.handler._parse_user(None)) + self.assertEquals('octocat', self.handler._parse_user(Mock())) + self.assertEquals('test', self.handler._parse_user('test')) + self.assertEquals('test', self.handler._parse_user(model_user)) + + @patch.object(api.Github, 'get') + def test_get(self, get): + get.return_value = GET_USER + self.assertRaises(UserIsAnonymous, self.handler.get) + user = self.handler.get('octocat') + self.assertIsInstance(user, User) + get.assert_called_with('users/octocat') + + @patch.object(api.Github, '_request') + def test_get_followers(self, request): + response = request.return_value + response.headers = {'link': GET_LINK} # 5 pages + response.content = self.gh._parser.dumps(GET_FOLLOWERS) + followers = list(self.handler.get_followers('test')) + self.assertIsInstance(followers[0], User) + pass -- cgit v1.3-6-gb490 From 6323be600d0c265e40df9f8950602e85ee94955b Mon Sep 17 00:00:00 2001 From: David Medina Date: Sun, 13 Nov 2011 23:43:35 +0100 Subject: Added handler base test --- github3/tests/fixtures.py | 5 +++- github3/tests/handler_test.py | 53 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+), 1 deletion(-) (limited to 'github3/tests') diff --git a/github3/tests/fixtures.py b/github3/tests/fixtures.py index aef4425..fdbc3fb 100644 --- a/github3/tests/fixtures.py +++ b/github3/tests/fixtures.py @@ -23,7 +23,10 @@ GET_USER = { } GET_LINK = '; rel="next", ; rel="last"' - +GET_RESOURCES = [ + {'login': 'octocat'}, + {'login': 'octocat'} +] GET_FOLLOWERS = [ { "login": "octocat", diff --git a/github3/tests/handler_test.py b/github3/tests/handler_test.py index 51cd111..e691182 100644 --- a/github3/tests/handler_test.py +++ b/github3/tests/handler_test.py @@ -7,6 +7,8 @@ from github3 import api from github3.handlers.base import Handler from github3.exceptions import * from github3.converters import * +from github3.models.user import User +from fixtures import * import json import requests @@ -51,3 +53,54 @@ class TestHandler(TestCase): data={'some': 'data'}) self.assertTrue(bool1) self.assertTrue(bool2) + + @patch.object(api.Github, '_request') + def test_get_resources(self, request): + # Simulating per_page=2 with STUB (it returns two resources) + response = request.return_value + response.status_code = 200 + response.headers = {'link': GET_LINK} + response.content = self.gh._parser.dumps(GET_RESOURCES) + resources = self.handler._get_resources('users', model=User) + self.assertFalse(request.called) + resources = list(resources) + self.assertTrue(request.call_count, 5) + request_args = ('GET', 'users') + self.assertEquals(request.call_args_list, [ + (request_args, {'page': 1}), + (request_args, {'page': 2}), + (request_args, {'page': 3}), + (request_args, {'page': 4}), + (request_args, {'page': 5})]) + self.assertEquals(len(resources), 10) + self.assertEquals(resources[0].login, 'octocat') + + request.reset_mock() + resources = self.handler._get_resources('users', model=User, limit=5) + resources = list(resources) + self.assertEquals(request.call_count, 3) + self.assertEquals(len(resources), 5) + request.reset_mock() + resources = self.handler._get_resources('users', model=User, limit=4) + resources = list(resources) + self.assertEquals(request.call_count, 2) + self.assertEquals(len(resources), 4) + request.reset_mock() + resources = self.handler._get_resources('users', model=User, limit=-5) + resources = list(resources) + self.assertEquals(request.call_count, 3) + self.assertEquals(len(resources), 5) + + @patch.object(api.Github, 'get') + def test_get_resource(self, get): + # Converter test + api(get) test. Half trivial + get.return_value = {'login': 'octocat'} + model = self.handler._get_resource('test', model=User) + self.assertEquals(model.login, 'octocat') + + @patch.object(api.Github, 'post') + def test_post_resource(self, post): + post.return_value = {'data': 'posted'} + data = {'data': 'to_post'} + user_new = self.handler._post_resource('test', data=data, model=User) + post.assert_called_with('test', data=data) -- cgit v1.3-6-gb490 From 61dabfbe621a5a9a0d098ea4b4001833dd7573e2 Mon Sep 17 00:00:00 2001 From: David Medina Date: Mon, 14 Nov 2011 00:29:46 +0100 Subject: Fix converter bug --- github3/converters.py | 5 +++-- github3/tests/converters_test.py | 6 ++++-- 2 files changed, 7 insertions(+), 4 deletions(-) (limited to 'github3/tests') diff --git a/github3/converters.py b/github3/converters.py index 1df61a6..05e3435 100644 --- a/github3/converters.py +++ b/github3/converters.py @@ -52,7 +52,8 @@ class Modelizer(Converter): self.model = model def _parse_map(self, model, raw_resource): - return Modelizer(model).loads(raw_resource) + if getattr(raw_resource, 'items', False): + return Modelizer(model).loads(raw_resource) def _parse_collection_map(self, model, raw_resources): # Dict of resources (Ex: Gist file) @@ -62,7 +63,7 @@ class Modelizer(Converter): dict_map[key] = Modelizer(model).loads(raw_resource) return dict_map # list of resources - else: + elif getattr(raw_resources, '__iter__', False): return [Modelizer(model).loads(raw_resource) for raw_resource in raw_resources] diff --git a/github3/tests/converters_test.py b/github3/tests/converters_test.py index ca1c715..315477c 100644 --- a/github3/tests/converters_test.py +++ b/github3/tests/converters_test.py @@ -23,7 +23,8 @@ API_STUB = { 'list_map': [ {'test_str': 'string', 'test_int': 1}, {'test_str': 'string', 'test_int': 2}, - ] + ], + 'fake_map': 9, } @@ -36,10 +37,11 @@ class Model(BaseResource): 'ints': ['test_int'], 'dates': ['test_date'], 'bools': ['test_bool'], - 'maps': {'map': Model}, + 'maps': {'map': Model, 'fake_map': Model}, 'collection_maps': { 'dict_map': Model, 'list_map': Model, + 'fake_map': Model, }, } -- cgit v1.3-6-gb490 From 0664d60525f03ba5cbeee080b3d69a4f79bd249a Mon Sep 17 00:00:00 2001 From: David Medina Date: Mon, 14 Nov 2011 00:46:46 +0100 Subject: Complete anonymous User handler test --- github3/tests/fixtures.py | 71 +++++++++++++++++++++++++++++++- github3/tests/user_handler_test.py | 84 ++++++++++++++++++++++++++++++++++++-- 2 files changed, 149 insertions(+), 6 deletions(-) (limited to 'github3/tests') diff --git a/github3/tests/fixtures.py b/github3/tests/fixtures.py index fdbc3fb..469b00a 100644 --- a/github3/tests/fixtures.py +++ b/github3/tests/fixtures.py @@ -22,12 +22,13 @@ GET_USER = { "type": "User" } -GET_LINK = '; rel="next", ; rel="last"' +GET_LINK = '; rel="next", \ +; rel="last"' GET_RESOURCES = [ {'login': 'octocat'}, {'login': 'octocat'} ] -GET_FOLLOWERS = [ +GET_SHORT_USERS = [ { "login": "octocat", "id": 1, @@ -41,3 +42,69 @@ GET_FOLLOWERS = [ "url": "https://api.github.com/users/octocat" }, ] +GET_SHORT_ORGS = [ + { + "login": "github", + "id": 1, + "url": "https://api.github.com/orgs/1", + "avatar_url": "https://github.com/images/error/octocat_happy.gif" + } +] + +GET_SHORT_REPOS = [ + { + "url": "https://api.github.com/repos/octocat/Hello-World", + "html_url": "https://github.com/octocat/Hello-World", + "clone_url": "https://github.com/octocat/Hello-World.git", + "git_url": "git://github.com/octocat/Hello-World.git", + "ssh_url": "git@github.com:octocat/Hello-World.git", + "svn_url": "https://svn.github.com/octocat/Hello-World", + "owner": { + "login": "octocat", + "id": 1, + "avatar_url": "https://github.com/images/error/octocat_happy.gif", + "url": "https://api.github.com/users/octocat" + }, + "name": "Hello-World", + "description": "This your first repo!", + "homepage": "https://github.com", + "language": None, + "private": False, + "fork": False, + "forks": 9, + "watchers": 80, + "size": 108, + "master_branch": "master", + "open_issues": 0, + "pushed_at": "2011-01-26T19:06:43Z", + "created_at": "2011-01-26T19:01:12Z" + } +] +GET_SHORT_GISTS = [ + { + "url": "https://api.github.com/gists/1", + "id": "1", + "description": "description of gist", + "public": True, + "user": { + "login": "octocat", + "id": 1, + "avatar_url": "https://github.com/images/error/octocat_happy.gif", + "url": "https://api.github.com/users/octocat" + }, + "files": { + "ring.erl": { + "size": 932, + "filename": "ring.erl", + "raw_url": "https://gist.github.com/raw/365370/8c4d2d43d178df\ + 44f4c03a7f2ac0ff512853564e/ring.erl", + "content": "contents of gist" + } + }, + "comments": 0, + "html_url": "https://gist.github.com/1", + "git_pull_url": "git://gist.github.com/1.git", + "git_push_url": "git@gist.github.com:1.git", + "created_at": "2010-04-14T02:15:15Z" + } +] diff --git a/github3/tests/user_handler_test.py b/github3/tests/user_handler_test.py index 9eb3708..ef57925 100644 --- a/github3/tests/user_handler_test.py +++ b/github3/tests/user_handler_test.py @@ -5,10 +5,17 @@ from unittest import TestCase from mock import Mock, patch from github3 import api from fixtures import * -from github3.models.user import User, AuthUser +from github3.models import User, AuthUser, Repo, Gist, Org from github3.exceptions import * +class TestAuthUserHandler(TestCase): + """ Test private api about user logged """ + + def setUp(self): + pass + + class TestUserHandler(TestCase): """ Test public api about users """ @@ -51,8 +58,77 @@ class TestUserHandler(TestCase): @patch.object(api.Github, '_request') def test_get_followers(self, request): response = request.return_value - response.headers = {'link': GET_LINK} # 5 pages - response.content = self.gh._parser.dumps(GET_FOLLOWERS) + response.headers = {'link': GET_LINK} + response.content = self.gh._parser.dumps(GET_SHORT_USERS) # 2 users followers = list(self.handler.get_followers('test')) + request.assert_called_with('GET', 'users/test/followers', page=5) self.assertIsInstance(followers[0], User) - pass + self.assertEquals(len(followers), 10) + followers = list(self.handler.get_followers('test', limit=2)) + self.assertEquals(len(followers), 2) + self.assertEquals(followers[0].login, 'octocat') + + @patch.object(api.Github, '_request') + def test_get_following(self, request): + response = request.return_value + response.headers = {'link': GET_LINK} + response.content = self.gh._parser.dumps(GET_SHORT_USERS) # 2 users + following = list(self.handler.get_following('test')) + request.assert_called_with('GET', 'users/test/following', page=5) + self.assertIsInstance(following[0], User) + self.assertEquals(len(following), 10) + following = list(self.handler.get_following('test', limit=2)) + self.assertEquals(len(following), 2) + + @patch.object(api.Github, '_request') + def test_get_repos(self, request): + response = request.return_value + response.headers = {'link': GET_LINK} + response.content = self.gh._parser.dumps(GET_SHORT_REPOS) # 1 repo + repos = list(self.handler.get_repos('test')) + request.assert_called_with('GET', 'users/test/repos', page=5) + self.assertIsInstance(repos[0], Repo) + self.assertEquals(len(repos), 5) + repos = list(self.handler.get_repos('test', limit=2)) + self.assertEquals(len(repos), 2) + self.assertIsInstance(repos[0].owner, User) + + @patch.object(api.Github, '_request') + def test_get_watched(self, request): + response = request.return_value + response.headers = {'link': GET_LINK} + response.content = self.gh._parser.dumps(GET_SHORT_REPOS) # 1 repo + watched = list(self.handler.get_watched('test')) + request.assert_called_with('GET', 'users/test/watched', page=5) + self.assertIsInstance(watched[0], Repo) + self.assertEquals(len(watched), 5) + watched = list(self.handler.get_watched('test', limit=2)) + self.assertEquals(len(watched), 2) + + @patch.object(api.Github, '_request') + def test_get_orgs(self, request): + response = request.return_value + response.headers = {'link': GET_LINK} + response.content = self.gh._parser.dumps(GET_SHORT_ORGS) # 1 repo + orgs = list(self.handler.get_orgs('test')) + request.assert_called_with('GET', 'users/test/orgs', page=5) + self.assertIsInstance(orgs[0], Org) + self.assertEquals(len(orgs), 5) + orgs = list(self.handler.get_orgs('test', limit=2)) + self.assertEquals(len(orgs), 2) + self.assertEquals(orgs[0].login, 'github') + + @patch.object(api.Github, '_request') + def test_get_gists(self, request): + response = request.return_value + response.headers = {'link': GET_LINK} + response.content = self.gh._parser.dumps(GET_SHORT_GISTS) # 1 repo + gists = list(self.handler.get_gists('test')) + request.assert_called_with('GET', 'users/test/gists', page=5) + self.assertIsInstance(gists[0], Gist) + self.assertEquals(len(gists), 5) + gists = list(self.handler.get_gists('test', limit=2)) + self.assertEquals(len(gists), 2) + self.assertIsInstance(gists[0].files, dict) + from github3.models.gists import File + self.assertIsInstance(gists[0].files['ring.erl'], File) -- cgit v1.3-6-gb490 From 9e7d4e655d966b325cc9db190273f99220b6041b Mon Sep 17 00:00:00 2001 From: David Medina Date: Tue, 15 Nov 2011 23:26:31 +0100 Subject: API change: Added gravatar_id in User model Also fix some bugs, update test and PEP8 --- github3/converters.py | 12 ++++++------ github3/models/base.py | 3 +++ github3/models/user.py | 13 +++++++++---- github3/tests/converters_test.py | 7 +++++-- github3/tests/fixtures.py | 9 +++++++++ 5 files changed, 32 insertions(+), 12 deletions(-) (limited to 'github3/tests') diff --git a/github3/converters.py b/github3/converters.py index 05e3435..58eb6b5 100644 --- a/github3/converters.py +++ b/github3/converters.py @@ -75,24 +75,24 @@ class Modelizer(Converter): idl = self.model.idl() attrs.update( {attr: raw_resource[attr] for attr in idl.get('strs',()) - if raw_resource.get(attr)}) + if attr in raw_resource}) attrs.update( {attr: raw_resource[attr] for attr in idl.get('ints',()) - if raw_resource.get(attr)}) + if attr in raw_resource}) attrs.update( {attr: self._parse_date(raw_resource[attr]) - for attr in idl.get('dates',()) if raw_resource.get(attr)}) + for attr in idl.get('dates',()) if attr in raw_resource}) attrs.update( {attr: raw_resource[attr] for attr in idl.get('bools',()) - if raw_resource.get(attr)}) + if attr in raw_resource}) attrs.update( {attr: self._parse_map(model, raw_resource[attr]) for attr, model in idl.get('maps',{}).items() - if raw_resource.get(attr)}) + if attr in raw_resource}) attrs.update( {attr: self._parse_collection_map(model, raw_resource[attr]) for attr, model in idl.get('collection_maps',{}).items() - if raw_resource.get(attr)}) + if attr in raw_resource}) return self.model(attrs) diff --git a/github3/models/base.py b/github3/models/base.py index df0c82b..bd07650 100644 --- a/github3/models/base.py +++ b/github3/models/base.py @@ -14,6 +14,9 @@ class BaseResource(object): setattr(self, attr, value) super(BaseResource, self).__init__() + def __len__(self): + return len(self.__dict__) + @classmethod def idl(self): raise NotImplementedError('Each model need subcass that method') diff --git a/github3/models/user.py b/github3/models/user.py index 7ec7999..e2d82b6 100644 --- a/github3/models/user.py +++ b/github3/models/user.py @@ -5,6 +5,7 @@ from .base import BaseResource + class Plan(BaseResource): """Github Plan object model.""" @@ -18,6 +19,7 @@ class Plan(BaseResource): def __repr__(self): return '' % self.name + class Key(BaseResource): """Github Key object model.""" @@ -31,20 +33,23 @@ class Key(BaseResource): def __repr__(self): return '' % self.title + class User(BaseResource): """Github User object model.""" @classmethod def idl(self): return { - 'strs': ['login','avatar_url', 'url', 'name', 'company', 'blog', - 'location', 'email', 'bio', 'html_url', 'type'], + 'strs': [ + 'login', 'avatar_url', 'gravatar_id', 'url', 'name', + 'company', 'blog', 'location', 'email', 'bio', 'html_url', + 'type'], 'ints': [ 'id', 'public_repos', 'public_gists', 'followers', 'following', 'total_private_repos', 'owned_private_repos', 'private_gists', 'disk_usage', 'collaborators'], 'maps': {'plan': Plan}, - 'dates': ['created_at',], + 'dates': ['created_at', ], 'bools': ['hireable', ], } @@ -54,6 +59,7 @@ class User(BaseResource): #def handler(self): # return self._gh.user_handler(self.login, force=True) + class AuthUser(User): """Github Authenticated User object model.""" @@ -62,4 +68,3 @@ class AuthUser(User): def __repr__(self): return '' % self.login - diff --git a/github3/tests/converters_test.py b/github3/tests/converters_test.py index 315477c..66eedc3 100644 --- a/github3/tests/converters_test.py +++ b/github3/tests/converters_test.py @@ -7,8 +7,10 @@ from unittest import TestCase from datetime import datetime API_STUB = { - 'test_str': 'string', 'test_int': 1, - 'test_date': '2008-01-14T04:33:35Z', 'test_bool': True, + 'test_str': 'string', + 'test_int': 1, + 'test_date': '2008-01-14T04:33:35Z', + 'test_bool': True, 'map': {'test_str': 'string'}, 'dict_map': { 'map1': { @@ -55,6 +57,7 @@ class TestModelizer(TestCase): def test_loads(self): parsed_model = self.modelizer.loads(API_STUB) + self.assertEquals(len(parsed_model), len(API_STUB)) self.assertEquals(parsed_model.test_str, 'string') self.assertEquals(parsed_model.test_int, 1) self.assertEquals( diff --git a/github3/tests/fixtures.py b/github3/tests/fixtures.py index 469b00a..02086f8 100644 --- a/github3/tests/fixtures.py +++ b/github3/tests/fixtures.py @@ -5,6 +5,7 @@ GET_USER = { "login": "octocat", "id": 1, "avatar_url": "https://github.com/images/error/octocat_happy.gif", + "gravatar_id": "somehexcode", "url": "https://api.github.com/users/octocat", "name": "monalisa octocat", "company": "GitHub", @@ -24,24 +25,29 @@ GET_USER = { GET_LINK = '; rel="next", \ ; rel="last"' + GET_RESOURCES = [ {'login': 'octocat'}, {'login': 'octocat'} ] + GET_SHORT_USERS = [ { "login": "octocat", "id": 1, "avatar_url": "https://github.com/images/error/octocat_happy.gif", + "gravatar_id": "somehexcode", "url": "https://api.github.com/users/octocat" }, { "login": "octocat", "id": 1, "avatar_url": "https://github.com/images/error/octocat_happy.gif", + "gravatar_id": "somehexcode", "url": "https://api.github.com/users/octocat" }, ] + GET_SHORT_ORGS = [ { "login": "github", @@ -63,6 +69,7 @@ GET_SHORT_REPOS = [ "login": "octocat", "id": 1, "avatar_url": "https://github.com/images/error/octocat_happy.gif", + "gravatar_id": "somehexcode", "url": "https://api.github.com/users/octocat" }, "name": "Hello-World", @@ -80,6 +87,7 @@ GET_SHORT_REPOS = [ "created_at": "2011-01-26T19:01:12Z" } ] + GET_SHORT_GISTS = [ { "url": "https://api.github.com/gists/1", @@ -90,6 +98,7 @@ GET_SHORT_GISTS = [ "login": "octocat", "id": 1, "avatar_url": "https://github.com/images/error/octocat_happy.gif", + "gravatar_id": "somehexcode", "url": "https://api.github.com/users/octocat" }, "files": { -- cgit v1.3-6-gb490 From 84a085138f69401bb5787916a530b59935c8dc19 Mon Sep 17 00:00:00 2001 From: David Medina Date: Wed, 16 Nov 2011 01:52:33 +0100 Subject: Fix 411 with PUT request with data as None, requests module didn't add specific header --- github3/api.py | 5 +++-- github3/tests/api_test.py | 9 +++++++++ 2 files changed, 12 insertions(+), 2 deletions(-) (limited to 'github3/tests') diff --git a/github3/api.py b/github3/api.py index 1c467ff..a4ee4a9 100644 --- a/github3/api.py +++ b/github3/api.py @@ -72,8 +72,9 @@ class GithubCore(object): def put(self, request, **kwargs): """ PUT request """ - # Content-length: 0 to headers ? - response = self._request('PUT', request, **kwargs) + + response = self._request('PUT', request, + headers={'Content-length': '0'}, **kwargs) assert response.status_code == 204 return response diff --git a/github3/tests/api_test.py b/github3/tests/api_test.py index 258a737..3ae75ee 100644 --- a/github3/tests/api_test.py +++ b/github3/tests/api_test.py @@ -113,3 +113,12 @@ class TestGithubCore(TestCase): delete = self.gh.delete('core') request_method.assert_called_with( 'DELETE', self.base_url + 'core') + + def test_put(self, request_method): + response = request_method.return_value + response.status_code = 204 + response.content = '' + put = self.gh.put('core') + request_method.assert_called_with( + 'PUT', self.base_url + 'core', + headers={'Content-length': '0'}) -- cgit v1.3-6-gb490 From 437b7a6b9623bc4670de3c2e370d671ced0bc5c8 Mon Sep 17 00:00:00 2001 From: David Medina Date: Wed, 16 Nov 2011 01:55:57 +0100 Subject: Wip on AuthUser handler tests Also "python way" with Design by contract --- github3/handlers/base.py | 2 +- github3/handlers/users.py | 5 ++--- github3/tests/fixtures.py | 36 ++++++++++++++++++++++++++++++++++++ github3/tests/user_handler_test.py | 34 +++++++++++++++++++++++++++++++++- 4 files changed, 72 insertions(+), 5 deletions(-) (limited to 'github3/tests') diff --git a/github3/handlers/base.py b/github3/handlers/base.py index c27b80c..0b3fa8a 100644 --- a/github3/handlers/base.py +++ b/github3/handlers/base.py @@ -15,7 +15,7 @@ class Handler(object): def _prefix_resource(self, resource): prefix = getattr(self, 'prefix', '') - return '/'.join((prefix, resource)).strip('/') + return '/'.join((prefix, str(resource))).strip('/') def _get_converter(self, **kwargs): converter = kwargs.get( diff --git a/github3/handlers/users.py b/github3/handlers/users.py index 0b84107..84180b5 100644 --- a/github3/handlers/users.py +++ b/github3/handlers/users.py @@ -166,10 +166,9 @@ class AuthUser(User): :param `user`: User model or username string - NOTE: Maybe bug in API, return text/html. Waitingf for answer """ - parse_user = str(getattr(user, 'login', user)) + parse_user = getattr(user, 'login', user) return self._put('following/%s' % parse_user) def unfollow(self, user): @@ -179,7 +178,7 @@ class AuthUser(User): :param `user`: User model or username string """ - parse_user = str(getattr(user, 'login', user)) + parse_user = getattr(user, 'login', user) return self._delete('following/%s' % parse_user) def get_keys(self): diff --git a/github3/tests/fixtures.py b/github3/tests/fixtures.py index 02086f8..5f56753 100644 --- a/github3/tests/fixtures.py +++ b/github3/tests/fixtures.py @@ -117,3 +117,39 @@ GET_SHORT_GISTS = [ "created_at": "2010-04-14T02:15:15Z" } ] +GET_FULL_USER = { + "login": "octocat", + "id": 1, + "avatar_url": "https://github.com/images/error/octocat_happy.gif", + "gravatar_id": "somehexcode", + "url": "https://api.github.com/users/octocat", + "name": "monalisa octocat", + "company": "GitHub", + "blog": "https://github.com/blog", + "location": "San Francisco", + "email": "octocat@github.com", + "hireable": False, + "bio": "There once was...", + "public_repos": 2, + "public_gists": 1, + "followers": 20, + "following": 0, + "html_url": "https://github.com/octocat", + "created_at": "2008-01-14T04:33:35Z", + "type": "User", + "total_private_repos": 100, + "owned_private_repos": 100, + "private_gists": 81, + "disk_usage": 10000, + "collaborators": 8, + "plan": { + "name": "Medium", + "space": 400, + "collaborators": 10, + "private_repos": 20 + } +} +GET_USER_EMAILS = [ + "octocat@github.com", + "support@github.com" +] diff --git a/github3/tests/user_handler_test.py b/github3/tests/user_handler_test.py index ef57925..b1fd0d0 100644 --- a/github3/tests/user_handler_test.py +++ b/github3/tests/user_handler_test.py @@ -13,8 +13,40 @@ class TestAuthUserHandler(TestCase): """ Test private api about user logged """ def setUp(self): - pass + self.gh = api.Github('test', 'pass') + self.handler = self.gh.users + @patch.object(api.Github, 'get') + def test_get(self, get): + get.return_value = GET_FULL_USER + user = self.handler.get() + self.assertIsInstance(user, AuthUser) + get.assert_called_with('user') + self.assertEquals(len(user), len(GET_FULL_USER)) + + @patch.object(api.Github, 'get') + def test_get_emails(self, get): + get.return_value = GET_USER_EMAILS + emails = self.handler.get_emails() + get.assert_called_with('user/emails') + self.assertEquals(emails, GET_USER_EMAILS) + + @patch.object(api.Github, 'post') + def test_create_emails(self, post): + post.return_value = GET_USER_EMAILS + emails = self.handler.create_emails(*GET_USER_EMAILS) + post.assert_called_with('user/emails', data=GET_USER_EMAILS) + self.assertEquals(emails, GET_USER_EMAILS) + + @patch.object(api.Github, 'delete') + def test_delete_emails(self, delete): + response = delete.return_value + response.return_value = '' + response.status_code = 204 + emails = self.handler.delete_emails(*GET_USER_EMAILS) + delete.assert_called_with('user/emails', data=GET_USER_EMAILS, + method='delete') + self.assertTrue(emails) class TestUserHandler(TestCase): """ Test public api about users """ -- cgit v1.3-6-gb490 From 357985782664eba287e73e5bbbb28c1884adccea Mon Sep 17 00:00:00 2001 From: David Medina Date: Thu, 17 Nov 2011 00:17:48 +0100 Subject: Complete AuthUser handler test Update code putting tests in green --- github3/handlers/users.py | 46 +++++++++----- github3/models/user.py | 11 ++-- github3/tests/fixtures.py | 13 +++- github3/tests/user_handler_test.py | 121 ++++++++++++++++++++++++++++++++++++- 4 files changed, 167 insertions(+), 24 deletions(-) (limited to 'github3/tests') diff --git a/github3/handlers/users.py b/github3/handlers/users.py index 34ed013..08c6171 100644 --- a/github3/handlers/users.py +++ b/github3/handlers/users.py @@ -155,7 +155,7 @@ class AuthUser(User): :param `user`: User model or username string """ - parse_user = str(getattr(user, 'login', user)) + parse_user = getattr(user, 'login', user) return self._bool('following/%s' % parse_user) def follow(self, user): @@ -179,23 +179,28 @@ class AuthUser(User): parse_user = getattr(user, 'login', user) return self._delete('following/%s' % parse_user) - def get_keys(self): + def get_keys(self, limit=None): """ Get public keys """ return self._get_resources('keys', model=models.Key, limit=limit) - def get_key(self, key_id): - """ Get public key by id """ + def get_key(self, key): + """ Get public key + + :param `key`: Key model or key id + + """ - return self._get_resource('keys/%s' % key_id, model=models.Key) + parse_key_id = getattr(key, 'id', key) + return self._get_resource('keys/%s' % parse_key_id, model=models.Key) def create_key(self, **kwargs): """ Create public key :param title - :param key: Key string + :param key: Key string (It must starts with 'ssh-rsa') """ #TODO: render key.pub file @@ -205,12 +210,17 @@ class AuthUser(User): } return self._post_resource('keys', data=key, model=models.Key) - def delete_key(self, key_id): - """ Delete public key """ + def delete_key(self, key): + """ Delete public key - return self._delete('keys/%s' % key_id) + :param `key`: Key model or key id - def get_repos(self, filter='all'): + """ + + parse_key_id = getattr(key, 'id', key) + return self._delete('keys/%s' % parse_key_id) + + def get_repos(self, filter='all', limit=None): """ Return user's public repositories @@ -224,8 +234,8 @@ class AuthUser(User): """ Return true if you are watching the user repository - :param owner: username - :param repo: repository name + :param owner: Model user or username string + :param repo: Model repo or repo name string is_watching_repo('copitux', 'python-github3') """ @@ -237,18 +247,22 @@ class AuthUser(User): """ Watch the repository - :param owner: username - :param repo: repository name + :param owner: Model user or username string + :param repo: Model repo or repo name string """ + owner = getattr(owner, 'login', owner) + repo = getattr(repo, 'name', repo) return self._put('watched/%s/%s' % (owner, repo)) def unwatch_repo(self, owner, repo): """ Unwatch the repository - :param owner: username - :param repo: repository name + :param owner: Model user or username string + :param repo: Model repo or repo name string """ + owner = getattr(owner, 'login', owner) + repo = getattr(repo, 'name', repo) return self._delete('watched/%s/%s' % (owner, repo)) diff --git a/github3/models/user.py b/github3/models/user.py index 93201bd..aed6f09 100644 --- a/github3/models/user.py +++ b/github3/models/user.py @@ -52,17 +52,18 @@ class User(BaseResource): } def __repr__(self): - return '' % self.login + return '' % getattr(self, 'login', 'without user') #def handler(self): - # return self._gh.user_handler(self.login, force=True) + # return self._gh.users class AuthUser(User): """Github Authenticated User object model.""" - #def handler(self): - # return self._gh.user_handler(self.login, force=True, private=True) - def __repr__(self): return '' % self.login + + #def handler(self): + # return self._gh.users + diff --git a/github3/tests/fixtures.py b/github3/tests/fixtures.py index 5f56753..ff5179c 100644 --- a/github3/tests/fixtures.py +++ b/github3/tests/fixtures.py @@ -23,8 +23,8 @@ GET_USER = { "type": "User" } -GET_LINK = '; rel="next", \ -; rel="last"' +GET_LINK = '; rel="next", \ +; rel="last"' GET_RESOURCES = [ {'login': 'octocat'}, @@ -153,3 +153,12 @@ GET_USER_EMAILS = [ "octocat@github.com", "support@github.com" ] + +GET_USER_KEYS = [ + { + "url": "https://api.github.com/user/keys/1", + "id": 1, + "title": "octocat@octomac", + "key": "ssh-rsa AAA..." + } +] diff --git a/github3/tests/user_handler_test.py b/github3/tests/user_handler_test.py index b1fd0d0..28fda3b 100644 --- a/github3/tests/user_handler_test.py +++ b/github3/tests/user_handler_test.py @@ -5,7 +5,7 @@ from unittest import TestCase from mock import Mock, patch from github3 import api from fixtures import * -from github3.models import User, AuthUser, Repo, Gist, Org +from github3.models import User, AuthUser, Repo, Gist, Org, Key from github3.exceptions import * @@ -15,6 +15,8 @@ class TestAuthUserHandler(TestCase): def setUp(self): self.gh = api.Github('test', 'pass') self.handler = self.gh.users + self.user_mock = Mock() + self.user_mock.login = 'user_model' @patch.object(api.Github, 'get') def test_get(self, get): @@ -48,6 +50,123 @@ class TestAuthUserHandler(TestCase): method='delete') self.assertTrue(emails) + @patch.object(api.Github, 'head') + def test_is_following(self, head): + response = head.return_value + response.status_code = 204 + self.assertTrue(self.handler.is_following('test')) + head.assert_called_with('user/following/test') + self.handler.is_following(self.user_mock) + head.assert_called_with('user/following/user_model') + + @patch.object(api.Github, 'put') + def test_follow(self, put): + response = put.return_value + response.status_code = 204 + self.assertTrue(self.handler.follow('test')) + put.assert_called_with('user/following/test', method='put') + + @patch.object(api.Github, 'delete') + def test_unfollow(self, delete): + response = delete.return_value + response.status_code = 204 + self.assertTrue(self.handler.unfollow('test')) + delete.assert_called_with('user/following/test', method='delete') + + @patch.object(api.Github, '_request') + def test_get_keys(self, request): + response = request.return_value + response.status_code = 200 + response.content = self.gh._parser.dumps(GET_USER_KEYS) + response.headers = {'link': GET_LINK} # 1 per page + keys = list(self.handler.get_keys()) + self.assertEquals(len(keys), 5) + self.assertIsInstance(keys[0], Key) + request.assert_called_with('GET', 'user/keys', page=5) + keys = list(self.handler.get_keys(limit=2)) + self.assertEquals(len(keys), 2) + + @patch.object(api.Github, 'get') + def test_get_key(self, get): + get.return_value = GET_USER_KEYS[0] + key = self.handler.get_key(1) + self.assertIsInstance(key, Key) + get.assert_called_with('user/keys/1') + model_key = Mock() + model_key.id = 1 + key = self.handler.get_key(model_key) + get.assert_called_with('user/keys/1') + + @patch.object(api.Github, 'post') + def test_create_key(self, post): + post.return_value = GET_USER_KEYS[0] + key_data = {'title': 'some', 'key': 'ssh-rsa AAA'} + created_key = self.handler.create_key(**key_data) + self.assertIsInstance(created_key, Key) + post.assert_called_with('user/keys', data=key_data) + + @patch.object(api.Github, 'delete') + def test_delete_key(self, delete): + response = delete.return_value + response.status_code = 204 + self.assertTrue(self.handler.delete_key(1)) + delete.assert_called_with('user/keys/1', method='delete') + model_key = Mock() + model_key.id = 1 + key = self.handler.delete_key(model_key) + delete.assert_called_with('user/keys/1', method='delete') + + @patch.object(api.Github, '_request') + def test_get_repos(self, request): + response = request.return_value + response.status_code = 200 + response.content = self.gh._parser.dumps(GET_SHORT_REPOS) + response.headers = {'link': GET_LINK} # 1 per page + repos = list(self.handler.get_repos(filter='public')) + self.assertEquals(len(repos), 5) + self.assertIsInstance(repos[0], Repo) + request.assert_called_with('GET', 'user/repos', + page=5, type='public') + repos = list(self.handler.get_repos(limit=2)) + self.assertEquals(len(repos), 2) + + @patch.object(api.Github, 'head') + def test_is_watching_repo(self, head): + response = head.return_value + response.status_code = 204 + self.assertTrue(self.handler.is_watching_repo('user', 'repo')) + head.assert_called_with('user/watched/user/repo') + model_user, model_repo = Mock(), Mock() + model_user.login = 'user' + model_repo.name = 'repo' + self.assertTrue(self.handler.is_watching_repo('user', 'repo')) + head.assert_called_with('user/watched/user/repo') + + @patch.object(api.Github, 'put') + def test_watch_repo(self, put): + response = put.return_value + response.status_code = 204 + self.assertTrue(self.handler.watch_repo('user', 'repo')) + put.assert_called_with('user/watched/user/repo', method='put') + model_user, model_repo = Mock(), Mock() + model_user.login = 'user' + model_repo.name = 'repo' + self.assertTrue(self.handler.watch_repo('user', 'repo')) + put.assert_called_with('user/watched/user/repo', method='put') + + @patch.object(api.Github, 'delete') + def test_unwatch_repo(self, delete): + response = delete.return_value + response.status_code = 204 + self.assertTrue(self.handler.unwatch_repo('user', 'repo')) + delete.assert_called_with('user/watched/user/repo', method='delete') + model_user, model_repo = Mock(), Mock() + model_user.login = 'user' + model_repo.name = 'repo' + self.assertTrue(self.handler.unwatch_repo('user', 'repo')) + delete.assert_called_with('user/watched/user/repo', method='delete') + + class TestUserHandler(TestCase): """ Test public api about users """ -- cgit v1.3-6-gb490 From ba0ce2dc98c2d72852a10ece61d5611fdfdbf66d Mon Sep 17 00:00:00 2001 From: David Medina Date: Sun, 27 Nov 2011 13:20:06 +0100 Subject: Diff between 'my' and 'get' --- github3/handlers/users.py | 37 ++++++++++++++++++++++++--- github3/tests/user_handler_test.py | 51 ++++++++++++++++++++++++++++++++++---- 2 files changed, 80 insertions(+), 8 deletions(-) (limited to 'github3/tests') diff --git a/github3/handlers/users.py b/github3/handlers/users.py index 4a181c8..ec40f02 100644 --- a/github3/handlers/users.py +++ b/github3/handlers/users.py @@ -108,17 +108,35 @@ class User(Handler): limit=limit) -class AuthUser(User): +class AuthUser(Handler): """ User handler with public and private access """ prefix = 'user' + def __init__(self, gh): + super(AuthUser, self).__init__(gh) + self._inject_handler(User(gh), prefix='get') + def __repr__(self): return ' %s>' % self._gh.session.auth[0] - def get(self): + def me(self): + """ Return authenticated user """ + return self._get_resource('', model=models.AuthUser) + def my_followers(self, limit=None): + """ Return authenticated user followers """ + + return self._get_resources('followers', model=models.User, + limit=limit) + + def my_following(self, limit=None): + """ Return authenticated user following """ + + return self._get_resources('following', model=models.User, + limit=limit) + def get_emails(self): """ Return list of emails """ @@ -220,7 +238,7 @@ class AuthUser(User): parse_key_id = getattr(key, 'id', key) return self._delete('keys/%s' % parse_key_id) - def get_repos(self, filter='all', limit=None): + def my_repos(self, filter='all', limit=None): """ Return user's public repositories @@ -230,6 +248,12 @@ class AuthUser(User): return self._get_resources('repos', model=models.Repo, limit=limit, type=str(filter)) + def my_watched(self, limit=None): + """ Return authenticated user repos that he watch """ + + return self._get_resources('watched', model=models.Repo, + limit=limit) + def is_watching_repo(self, owner, repo): """ Return true if you are watching the user repository @@ -266,3 +290,10 @@ class AuthUser(User): owner = getattr(owner, 'login', owner) repo = getattr(repo, 'name', repo) return self._delete('watched/%s/%s' % (owner, repo)) + + def my_orgs(self, limit=None): + """ List public and private organizations + for the authenticated user + """ + + return self._get_resources('orgs', model=models.Org, limit=limit) diff --git a/github3/tests/user_handler_test.py b/github3/tests/user_handler_test.py index 28fda3b..33658d2 100644 --- a/github3/tests/user_handler_test.py +++ b/github3/tests/user_handler_test.py @@ -7,6 +7,7 @@ from github3 import api from fixtures import * from github3.models import User, AuthUser, Repo, Gist, Org, Key from github3.exceptions import * +from github3 import handlers class TestAuthUserHandler(TestCase): @@ -18,14 +19,54 @@ class TestAuthUserHandler(TestCase): self.user_mock = Mock() self.user_mock.login = 'user_model' + def test_inject_user_handler(self): + self.assertEquals(self.handler.get.im_class, handlers.users.User) + self.assertEquals(self.handler.get_followers.im_class, + handlers.users.User) + self.assertEquals(self.handler.get_following.im_class, + handlers.users.User) + self.assertEquals(self.handler.get_repos.im_class, + handlers.users.User) + self.assertEquals(self.handler.get_watched.im_class, + handlers.users.User) + self.assertEquals(self.handler.get_orgs.im_class, + handlers.users.User) + self.assertEquals(self.handler.get_gists.im_class, + handlers.users.User) + @patch.object(api.Github, 'get') - def test_get(self, get): + def test_me(self, get): get.return_value = GET_FULL_USER - user = self.handler.get() + user = self.handler.me() self.assertIsInstance(user, AuthUser) get.assert_called_with('user') self.assertEquals(len(user), len(GET_FULL_USER)) + @patch.object(handlers.base.Handler, '_get_resource') + def test_get(self, get): + user = self.handler.get('test') + get.assert_called_with('test', model=User) + + @patch.object(handlers.base.Handler, '_get_resources') + def test_get_my_followers(self, get): + followers = self.handler.my_followers() + get.assert_called_with('followers', model=User, limit=None) + + @patch.object(handlers.base.Handler, '_get_resources') + def test_get_my_following(self, get): + following = self.handler.my_following() + get.assert_called_with('following', model=User, limit=None) + + @patch.object(handlers.base.Handler, '_get_resources') + def test_get_my_watched(self, get): + following = self.handler.my_watched() + get.assert_called_with('watched', model=Repo, limit=None) + + @patch.object(handlers.base.Handler, '_get_resources') + def test_get_my_orgs(self, get): + following = self.handler.my_orgs() + get.assert_called_with('orgs', model=Org, limit=None) + @patch.object(api.Github, 'get') def test_get_emails(self, get): get.return_value = GET_USER_EMAILS @@ -117,17 +158,17 @@ class TestAuthUserHandler(TestCase): delete.assert_called_with('user/keys/1', method='delete') @patch.object(api.Github, '_request') - def test_get_repos(self, request): + def test_my_repos(self, request): response = request.return_value response.status_code = 200 response.content = self.gh._parser.dumps(GET_SHORT_REPOS) response.headers = {'link': GET_LINK} # 1 per page - repos = list(self.handler.get_repos(filter='public')) + repos = list(self.handler.my_repos(filter='public')) self.assertEquals(len(repos), 5) self.assertIsInstance(repos[0], Repo) request.assert_called_with('GET', 'user/repos', page=5, type='public') - repos = list(self.handler.get_repos(limit=2)) + repos = list(self.handler.my_repos(limit=2)) self.assertEquals(len(repos), 2) @patch.object(api.Github, 'head') -- cgit v1.3-6-gb490 From 8060db65c51d551107eff76bf4e94ef82d9f869b Mon Sep 17 00:00:00 2001 From: David Medina Date: Mon, 28 Nov 2011 01:07:42 +0100 Subject: MimeType mixin --- github3/handlers/base.py | 32 ++++++++++++++++++++++++++++++++ github3/handlers/users.py | 2 +- github3/tests/handler_test.py | 24 +++++++++++++++++++++++- 3 files changed, 56 insertions(+), 2 deletions(-) (limited to 'github3/tests') diff --git a/github3/handlers/base.py b/github3/handlers/base.py index a85ce20..7bc55d2 100644 --- a/github3/handlers/base.py +++ b/github3/handlers/base.py @@ -4,6 +4,38 @@ from github3.core import Paginate from github3.converters import Modelizer + +class MimeTypeMixin(object): + + VERSION = 'beta' + + def __init__(self): + self.mimetypes = set() + + def _parse_mime_type(self, type): + return 'application/vnd.github.%s.%s+json' % ( + self.VERSION, type) + + def add_raw(self): + self.mimetypes.add(self._parse_mime_type('raw')) + return self + + def add_text(self): + self.mimetypes.add(self._parse_mime_type('text')) + return self + + def add_html(self): + self.mimetypes.add(self._parse_mime_type('html')) + return self + + def add_full(self): + self.mimetypes.add(self._parse_mime_type('full')) + return self + + def mime_header(self): + return {'Accept': ', '.join(self.mimetypes)} + + class Handler(object): """ Handler base. Requests to API and modelize responses """ diff --git a/github3/handlers/users.py b/github3/handlers/users.py index ec40f02..92eea6f 100644 --- a/github3/handlers/users.py +++ b/github3/handlers/users.py @@ -2,7 +2,7 @@ # -*- encoding: utf-8 -*- from .base import Handler -import github3.models as models +from github3 import models from github3.converters import Rawlizer from github3.exceptions import UserIsAnonymous diff --git a/github3/tests/handler_test.py b/github3/tests/handler_test.py index e691182..e95374b 100644 --- a/github3/tests/handler_test.py +++ b/github3/tests/handler_test.py @@ -4,7 +4,7 @@ from mock import Mock, patch from unittest import TestCase from github3 import api -from github3.handlers.base import Handler +from github3.handlers.base import Handler, MimeTypeMixin from github3.exceptions import * from github3.converters import * from github3.models.user import User @@ -13,6 +13,28 @@ import json import requests +class TestMimeTypeMixin(TestCase): + + def setUp(self): + self.mixin = MimeTypeMixin() + + def _parse_mime_type(self, type): + return 'application/vnd.github.%s.%s+json' % ( + MimeTypeMixin.VERSION, type) + + def test_add_mimetypes(self): + self.mixin.add_raw() + self.mixin.add_text() + self.mixin.add_html() + self.mixin.add_full() + self.assertEquals(sorted(self.mixin.mime_header()), sorted({ + 'Accept': '%s, %s, %s, %s' % ( + self._parse_mime_type('raw'), + self._parse_mime_type('text'), + self._parse_mime_type('html'), + self._parse_mime_type('full'))})) + + class TestHandler(TestCase): def setUp(self): -- cgit v1.3-6-gb490 From f5ab0a9c6f35950e09e3c4fa0f908c1fc477cf33 Mon Sep 17 00:00:00 2001 From: David Medina Date: Wed, 30 Nov 2011 00:07:05 +0100 Subject: Fix get_converter to delete converter in kwargs It doesn't matter if converter is populate in kwargs to requests but it's more elegant and simplify tests --- github3/handlers/base.py | 12 ++++++------ github3/tests/handler_test.py | 4 +++- 2 files changed, 9 insertions(+), 7 deletions(-) (limited to 'github3/tests') diff --git a/github3/handlers/base.py b/github3/handlers/base.py index b20bb7d..e36a08a 100644 --- a/github3/handlers/base.py +++ b/github3/handlers/base.py @@ -53,8 +53,8 @@ class Handler(object): prefix = getattr(self, 'prefix', '') return '/'.join((prefix, str(resource))).strip('/') - def _get_converter(self, **kwargs): - converter = kwargs.get( + def _get_converter(self, kwargs={}): + converter = kwargs.pop( 'converter', # 1. in kwargs getattr(self, 'converter', # 2. in handler Modelizer)) # 3. Default @@ -91,11 +91,11 @@ class Handler(object): if limit: limit = abs(limit) resource = self._prefix_resource(resource) + converter = self._get_converter(kwargs) counter = 1 for page in Paginate(resource, self._gh.get, **kwargs): for raw_resource in page: counter += 1 - converter = self._get_converter(**kwargs) converter.inject(model) yield converter.loads(raw_resource) if limit and counter > limit: @@ -108,8 +108,8 @@ class Handler(object): """ Handler request to single resource """ resource = self._prefix_resource(resource) - raw_resource = self._gh.get(resource) - converter = self._get_converter(**kwargs) + converter = self._get_converter(kwargs) + raw_resource = self._gh.get(resource, **kwargs) converter.inject(model) return converter.loads(raw_resource) @@ -118,6 +118,6 @@ class Handler(object): resource = self._prefix_resource(resource) raw_resource = self._gh.post(resource, data=data) - converter = self._get_converter(**kwargs) + converter = self._get_converter(kwargs) converter.inject(model) return converter.loads(raw_resource) diff --git a/github3/tests/handler_test.py b/github3/tests/handler_test.py index e95374b..4dbe77a 100644 --- a/github3/tests/handler_test.py +++ b/github3/tests/handler_test.py @@ -43,8 +43,10 @@ class TestHandler(TestCase): def test_get_converter(self): self.assertIsInstance(self.handler._get_converter(), Modelizer) - self.assertIsInstance(self.handler._get_converter(converter=Rawlizer), + kwargs = {'converter': Rawlizer} + self.assertIsInstance(self.handler._get_converter(kwargs), Rawlizer) + self.assertEquals(kwargs, {}) self.handler.converter = Modelizer self.assertIsInstance(self.handler._get_converter(), Modelizer) -- cgit v1.3-6-gb490 From 3547813022e7f5da658e74317ca62849609b5d24 Mon Sep 17 00:00:00 2001 From: David Medina Date: Wed, 30 Nov 2011 00:15:25 +0100 Subject: Fix mime_header to return None if needed --- github3/handlers/base.py | 4 +++- github3/tests/handler_test.py | 3 +++ 2 files changed, 6 insertions(+), 1 deletion(-) (limited to 'github3/tests') diff --git a/github3/handlers/base.py b/github3/handlers/base.py index e36a08a..565978f 100644 --- a/github3/handlers/base.py +++ b/github3/handlers/base.py @@ -33,7 +33,9 @@ class MimeTypeMixin(object): return self def mime_header(self): - return {'Accept': ', '.join(self.mimetypes)} + if self.mimetypes: + return {'Accept': ', '.join(self.mimetypes)} + return None class Handler(object): diff --git a/github3/tests/handler_test.py b/github3/tests/handler_test.py index 4dbe77a..83c89ef 100644 --- a/github3/tests/handler_test.py +++ b/github3/tests/handler_test.py @@ -22,6 +22,9 @@ class TestMimeTypeMixin(TestCase): return 'application/vnd.github.%s.%s+json' % ( MimeTypeMixin.VERSION, type) + def test_header(self): + self.assertEquals(self.mixin.mime_header(), None) + def test_add_mimetypes(self): self.mixin.add_raw() self.mixin.add_text() -- cgit v1.3-6-gb490 From fbd04697750cefc087611943e0b70be6bed5df3d Mon Sep 17 00:00:00 2001 From: David Medina Date: Wed, 30 Nov 2011 00:19:50 +0100 Subject: Gist public handler completed --- github3/handlers/gists.py | 42 ++++++++++++++++++++++++++++---- github3/tests/get_handlers_test.py | 2 ++ github3/tests/gists_handler_test.py | 48 +++++++++++++++++++++++++++++++++++++ 3 files changed, 87 insertions(+), 5 deletions(-) create mode 100644 github3/tests/gists_handler_test.py (limited to 'github3/tests') diff --git a/github3/handlers/gists.py b/github3/handlers/gists.py index 38418ca..37b1c3d 100644 --- a/github3/handlers/gists.py +++ b/github3/handlers/gists.py @@ -1,23 +1,55 @@ #!/usr/bin/env python # -*- encoding: utf-8 -*- -from .base import Handler -from .. import models +from .base import Handler, MimeTypeMixin +from github3 import models -class Gist(Handler): - """ Gist handler """ +class Gist(Handler, MimeTypeMixin): + """ Gist handler with public access """ prefix = 'gists' def __repr__(self): return '' + def all_gists(self, limit=None): + """ Return all public gists + + NOTE: It returns all gists in github environment. Maybe you + want to use `limit` parameter + """ + + return self._get_resources('', model=models.Gist, limit=limit) + def get(self, gist_id): - """ Return gist """ + """ Return gist + + param `gist_id`: Gist id + """ return self._get_resource(gist_id, model=models.Gist) + def get_comments(self, gist_id, limit=None): + """ Return gist's comments + + param `gist_id`: Gist id + param `limit`: Number of comments + """ + + return self._get_resources('%s/comments' % gist_id, + model=models.GistComment, limit=limit, + headers=self.mime_header()) + + def get_comment(self, comment_id): + """ Return gist's comment + + param `comment_id`: Comment id + """ + + return self._get_resource('comments/%s' % comment_id, + model=models.GistComment, headers=self.mime_header()) + class AuthGist(Gist): diff --git a/github3/tests/get_handlers_test.py b/github3/tests/get_handlers_test.py index f07a7cf..0d88670 100644 --- a/github3/tests/get_handlers_test.py +++ b/github3/tests/get_handlers_test.py @@ -26,4 +26,6 @@ class TestGetHandlers(TestCase): auth_gists = self.auth_gh.gists self.assertIsInstance(anom_gists, handlers.gists.Gist) + self.assertEquals(anom_gists.prefix, 'gists') self.assertIsInstance(auth_gists, handlers.gists.AuthGist) + self.assertEquals(anom_gists.prefix, 'gists') diff --git a/github3/tests/gists_handler_test.py b/github3/tests/gists_handler_test.py new file mode 100644 index 0000000..df67659 --- /dev/null +++ b/github3/tests/gists_handler_test.py @@ -0,0 +1,48 @@ +#!/usr/bin/env python +# -*- encoding: utf-8 -*- + +from unittest import TestCase +from mock import Mock, patch +from github3 import api +from github3.models import Gist, GistComment +from github3.handlers.base import Handler + + +class TestGistHandler(TestCase): + + def setUp(self): + self.gh = api.Github() + self.handler = self.gh.gists + + @patch.object(Handler, '_get_resources') + def test_get_gists(self, get): + gists = self.handler.all_gists() + get.assert_called_with('', model=Gist, limit=None) + + @patch.object(Handler, '_get_resource') + def test_get(self, get): + gist = self.handler.get(1) + get.assert_called_with(1, model=Gist) + + @patch.object(Handler, '_get_resources') + def test_get_comments(self, get): + comments = self.handler.get_comments(1) + get.assert_called_with('1/comments', model=GistComment, limit=None, + headers=None) + + @patch.object(Handler, '_get_resource') + def test_get_comment(self, get): + comment = self.handler.get_comment(1) + get.assert_called_with('comments/1', model=GistComment, headers=None) + +class TestAuthGistHandler(TestCase): + + def setUp(self): + self.gh = api.Github('test', 'pass') + self.handler = self.gh.gists + + def test_inherit(self): + self.assertTrue(hasattr(self.handler, 'get')) + self.assertTrue(hasattr(self.handler, 'get_comments')) + self.assertTrue(hasattr(self.handler, 'get_comment')) + -- cgit v1.3-6-gb490 From bb52626bda2a1d61833fe9c4cf9276b1feeb4e2e Mon Sep 17 00:00:00 2001 From: David Medina Date: Sat, 3 Dec 2011 13:22:12 +0100 Subject: Complete AuthGist handler --- github3/handlers/gists.py | 94 +++++++++++++++++++++++++++++++++++-- github3/tests/gists_handler_test.py | 62 ++++++++++++++++++++++++ 2 files changed, 151 insertions(+), 5 deletions(-) (limited to 'github3/tests') diff --git a/github3/handlers/gists.py b/github3/handlers/gists.py index 37b1c3d..ed03c31 100644 --- a/github3/handlers/gists.py +++ b/github3/handlers/gists.py @@ -53,9 +53,93 @@ class Gist(Handler, MimeTypeMixin): class AuthGist(Gist): - def create_gist(self, description, public=True, files={}): - """ Create a gist """ - data = {'description': description, - 'public': public, - 'files': files} + def all_gists(self, limit=None): + """ Return all public gists + + NOTE: It returns all gists in github environment. Maybe you + want to use `limit` parameter + """ + + return self._get_resources('public', model=models.Gist, limit=limit) + + def my_gists(self, limit=None): + """ Return authenticated user's gists + + param `limit`: Number of gists + """ + + return self._get_resources('', model=models.Gist, limit=limit) + + def my_starred_gists(self, limit=None): + """ Return authenticated user's starred gists + + param `limit`: Number of gists + """ + + return self._get_resources('starred', model=models.Gist, limit=limit) + + def create_gist(self, is_public, files, desc=None): + """ Create and return a gist """ + + data = { + 'public': bool(is_public), + 'files': files, # TODO: Issue #1 + 'desc': desc or '', + } return self._post_resource('', data=data, model=models.Gist) + + def star_gist(self, gist_id): + """ Star a gist + + param `gist_id`: Gist id to star + """ + + return self._put('%s/star' % gist_id) + + def unstar_gist(self, gist_id): + """ Unstar a gist + + param `gist_id`: Gist id to unstar + """ + + return self._delete('%s/star' % gist_id) + + def is_starred(self, gist_id): + """ True if gist is starred + + param `gist_id`: Gist id + """ + + return self._bool('%s/star' % gist_id) + + def fork_gist(self, gist_id): + """ Return forked gist from id + + param `gist_id`: Gist id to be forked... + """ + + return self._post_resource('%s/fork' % gist_id, data=None, + model=models.Gist) + + def delete_gist(self, gist_id): + """ Delete the gist + + param `gist_id`: Gist id + """ + + return self._delete(str(gist_id)) + + def create_comment(self, gist_id, comment): + """ Create comment into gist """ + + data = {'body': comment} + return self._post_resource('%s/comments' % gist_id, data=data, + model=models.GistComment) + + def delete_comment(self, comment_id): + """ Delete comment + + param `comment_id`: Comment id + """ + + return self._delete('comments/%s' % comment_id) diff --git a/github3/tests/gists_handler_test.py b/github3/tests/gists_handler_test.py index df67659..ecadcfb 100644 --- a/github3/tests/gists_handler_test.py +++ b/github3/tests/gists_handler_test.py @@ -35,6 +35,7 @@ class TestGistHandler(TestCase): comment = self.handler.get_comment(1) get.assert_called_with('comments/1', model=GistComment, headers=None) + class TestAuthGistHandler(TestCase): def setUp(self): @@ -46,3 +47,64 @@ class TestAuthGistHandler(TestCase): self.assertTrue(hasattr(self.handler, 'get_comments')) self.assertTrue(hasattr(self.handler, 'get_comment')) + @patch.object(Handler, '_get_resources') + def test_all_gists(self, get): + gists = self.handler.all_gists() + get.assert_called_with('public', model=Gist, limit=None) + + @patch.object(Handler, '_get_resources') + def test_my_gists(self, get): + gists = self.handler.my_gists() + get.assert_called_with('', model=Gist, limit=None) + + @patch.object(Handler, '_get_resources') + def test_my_starred_gists(self, get): + gists = self.handler.my_starred_gists() + get.assert_called_with('starred', model=Gist, limit=None) + + @patch.object(Handler, '_post_resource') + def test_create_gist(self, post): + data = { + 'public': False, + 'files': {'file': {'contents': 'file_data'}}, + 'desc': 'some' + } + gist = self.handler.create_gist(data['public'], data['files'], + data['desc']) + post.assert_called_with('', data=data, model=Gist) + + @patch.object(Handler, '_put') + def test_star_gist(self, put): + boolean = self.handler.star_gist(1) + put.assert_called_with('1/star') + + @patch.object(Handler, '_delete') + def test_unstar_gist(self, delete): + boolean = self.handler.unstar_gist(1) + delete.assert_callted_with('1/star') + + @patch.object(Handler, '_bool') + def test_is_starred(self, bool): + boolean = self.handler.is_starred(1) + bool.assert_called_with('1/star') + + @patch.object(Handler, '_post_resource') + def test_fork_gist(self, post): + gist = self.handler.fork_gist(1) + post.assert_called_with('1/fork', data=None, model=Gist) + + @patch.object(Handler, '_delete') + def test_delete_gist(self, delete): + boolean = self.handler.delete_gist(1) + delete.assert_called_with('1') + + @patch.object(Handler, '_post_resource') + def test_create_comment(self, post): + gist_comment = self.handler.create_comment(1, 'comment') + post.assert_called_with('1/comments', data={'body': 'comment'}, + model=GistComment) + + @patch.object(Handler, '_delete') + def test_delete_comment(self, delete): + boolean = self.handler.delete_comment(1) + delete.assert_called_with('comments/1') -- cgit v1.3-6-gb490