From 402566a665dfdf0862f15a7e59e4d804d1301c77 Mon Sep 17 00:00:00 2001 From: Nejc Habjan Date: Wed, 8 Apr 2020 23:57:32 +0200 Subject: [PATCH 1/4] chore: remove remnants of python2 imports --- gitlab/cli.py | 1 - gitlab/v4/cli.py | 1 - gitlab/v4/objects.py | 2 -- 3 files changed, 4 deletions(-) diff --git a/gitlab/cli.py b/gitlab/cli.py index 8fc30bc36..d356d162a 100644 --- a/gitlab/cli.py +++ b/gitlab/cli.py @@ -16,7 +16,6 @@ # You should have received a copy of the GNU Lesser General Public License # along with this program. If not, see . -from __future__ import print_function import argparse import functools diff --git a/gitlab/v4/cli.py b/gitlab/v4/cli.py index a8752612e..51416f142 100644 --- a/gitlab/v4/cli.py +++ b/gitlab/v4/cli.py @@ -16,7 +16,6 @@ # You should have received a copy of the GNU Lesser General Public License # along with this program. If not, see . -from __future__ import print_function import inspect import operator import sys diff --git a/gitlab/v4/objects.py b/gitlab/v4/objects.py index 2f3e8a591..2a3615fa3 100644 --- a/gitlab/v4/objects.py +++ b/gitlab/v4/objects.py @@ -15,8 +15,6 @@ # You should have received a copy of the GNU Lesser General Public License # along with this program. If not, see . -from __future__ import print_function -from __future__ import absolute_import import base64 from gitlab.base import * # noqa From 11383e70f74c70e6fe8a56f18b5b170db982f402 Mon Sep 17 00:00:00 2001 From: Nejc Habjan Date: Thu, 9 Apr 2020 00:13:36 +0200 Subject: [PATCH 2/4] chore: run unittest2pytest on all unit tests --- gitlab/tests/objects/test_application.py | 8 +- gitlab/tests/objects/test_commits.py | 16 +- gitlab/tests/objects/test_groups.py | 26 +- gitlab/tests/objects/test_projects.py | 70 ++--- gitlab/tests/test_base.py | 58 ++-- gitlab/tests/test_cli.py | 70 ++--- gitlab/tests/test_config.py | 81 +++--- gitlab/tests/test_exceptions.py | 5 +- gitlab/tests/test_gitlab.py | 335 ++++++++++++----------- gitlab/tests/test_mixins.py | 184 +++++++------ gitlab/tests/test_types.py | 24 +- gitlab/tests/test_utils.py | 8 +- 12 files changed, 446 insertions(+), 439 deletions(-) diff --git a/gitlab/tests/objects/test_application.py b/gitlab/tests/objects/test_application.py index 50ca1ad50..a10691bbb 100644 --- a/gitlab/tests/objects/test_application.py +++ b/gitlab/tests/objects/test_application.py @@ -80,13 +80,13 @@ def resp_update_appearance(url, request): with HTTMock(resp_get_appearance), HTTMock(resp_update_appearance): appearance = self.gl.appearance.get() - self.assertEqual(appearance.title, self.title) - self.assertEqual(appearance.description, self.description) + assert appearance.title == self.title + assert appearance.description == self.description appearance.title = self.new_title appearance.description = self.new_description appearance.save() - self.assertEqual(appearance.title, self.new_title) - self.assertEqual(appearance.description, self.new_description) + assert appearance.title == self.new_title + assert appearance.description == self.new_description def test_update_appearance(self): @urlmatch( diff --git a/gitlab/tests/objects/test_commits.py b/gitlab/tests/objects/test_commits.py index 7e7c3b484..bf7d5a8ad 100644 --- a/gitlab/tests/objects/test_commits.py +++ b/gitlab/tests/objects/test_commits.py @@ -78,8 +78,8 @@ class TestCommit(TestProject): @with_httmock(resp_get_commit) def test_get_commit(self): commit = self.project.commits.get("6b2257ea") - self.assertEqual(commit.short_id, "6b2257ea") - self.assertEqual(commit.title, "Initial commit") + assert commit.short_id == "6b2257ea" + assert commit.title == "Initial commit" @with_httmock(resp_create_commit) def test_create_commit(self): @@ -89,19 +89,19 @@ def test_create_commit(self): "actions": [{"action": "create", "file_path": "README", "content": "",}], } commit = self.project.commits.create(data) - self.assertEqual(commit.short_id, "ed899a2f") - self.assertEqual(commit.title, data["commit_message"]) + assert commit.short_id == "ed899a2f" + assert commit.title == data["commit_message"] @with_httmock(resp_revert_commit) def test_revert_commit(self): commit = self.project.commits.get("6b2257ea", lazy=True) revert_commit = commit.revert(branch="master") - self.assertEqual(revert_commit["short_id"], "8b090c1b") - self.assertEqual(revert_commit["title"], 'Revert "Initial commit"') + assert revert_commit["short_id"] == "8b090c1b" + assert revert_commit["title"] == 'Revert "Initial commit"' @with_httmock(resp_get_commit_gpg_signature) def test_get_commit_gpg_signature(self): commit = self.project.commits.get("6b2257ea", lazy=True) signature = commit.signature() - self.assertEqual(signature["gpg_key_primary_keyid"], "8254AAB3FBD54AC9") - self.assertEqual(signature["verification_status"], "verified") + assert signature["gpg_key_primary_keyid"] == "8254AAB3FBD54AC9" + assert signature["verification_status"] == "verified" diff --git a/gitlab/tests/objects/test_groups.py b/gitlab/tests/objects/test_groups.py index 075d91567..12ebdb297 100644 --- a/gitlab/tests/objects/test_groups.py +++ b/gitlab/tests/objects/test_groups.py @@ -48,18 +48,18 @@ def setUp(self): @with_httmock(resp_get_group) def test_get_group(self): data = self.gl.groups.get(1) - self.assertIsInstance(data, gitlab.v4.objects.Group) - self.assertEqual(data.name, "name") - self.assertEqual(data.path, "path") - self.assertEqual(data.id, 1) + assert isinstance(data, gitlab.v4.objects.Group) + assert data.name == "name" + assert data.path == "path" + assert data.id == 1 @with_httmock(resp_create_group) def test_create_group(self): name, path = "name", "path" data = self.gl.groups.create({"name": name, "path": path}) - self.assertIsInstance(data, gitlab.v4.objects.Group) - self.assertEqual(data.name, name) - self.assertEqual(data.path, path) + assert isinstance(data, gitlab.v4.objects.Group) + assert data.name == name + assert data.path == path class TestGroupExport(TestGroup): @@ -70,32 +70,32 @@ def setUp(self): @with_httmock(resp_create_export) def test_create_group_export(self): export = self.group.exports.create() - self.assertEqual(export.message, "202 Accepted") + assert export.message == "202 Accepted" @unittest.skip("GitLab API endpoint not implemented") @with_httmock(resp_create_export) def test_refresh_group_export_status(self): export = self.group.exports.create() export.refresh() - self.assertEqual(export.export_status, "finished") + assert export.export_status == "finished" @with_httmock(resp_create_export, resp_download_export) def test_download_group_export(self): export = self.group.exports.create() download = export.download() - self.assertIsInstance(download, bytes) - self.assertEqual(download, binary_content) + assert isinstance(download, bytes) + assert download == binary_content class TestGroupImport(TestGroup): @with_httmock(resp_create_import) def test_import_group(self): group_import = self.gl.groups.import_group("file", "api-group", "API Group") - self.assertEqual(group_import["message"], "202 Accepted") + assert group_import["message"] == "202 Accepted" @unittest.skip("GitLab API endpoint not implemented") @with_httmock(resp_create_import) def test_refresh_group_import_status(self): group_import = self.group.imports.get() group_import.refresh() - self.assertEqual(group_import.import_status, "finished") + assert group_import.import_status == "finished" diff --git a/gitlab/tests/objects/test_projects.py b/gitlab/tests/objects/test_projects.py index ca7e0c8f4..fa105aea3 100644 --- a/gitlab/tests/objects/test_projects.py +++ b/gitlab/tests/objects/test_projects.py @@ -341,9 +341,9 @@ def resp_list_snippet(url, request): with HTTMock(resp_list_snippet): snippets = self.project.snippets.list() - self.assertEqual(len(snippets), 1) - self.assertEqual(snippets[0].title, title) - self.assertEqual(snippets[0].visibility, visibility) + assert len(snippets) == 1 + assert snippets[0].title == title + assert snippets[0].visibility == visibility def test_get_project_snippets(self): title = "Example Snippet Title" @@ -370,8 +370,8 @@ def resp_get_snippet(url, request): with HTTMock(resp_get_snippet): snippet = self.project.snippets.get(1) - self.assertEqual(snippet.title, title) - self.assertEqual(snippet.visibility, visibility) + assert snippet.title == title + assert snippet.visibility == visibility def test_create_update_project_snippets(self): title = "Example Snippet Title" @@ -424,107 +424,107 @@ def resp_create_snippet(url, request): "visibility": visibility, } ) - self.assertEqual(snippet.title, title) - self.assertEqual(snippet.visibility, visibility) + assert snippet.title == title + assert snippet.visibility == visibility title = "new-title" snippet.title = title snippet.save() - self.assertEqual(snippet.title, title) - self.assertEqual(snippet.visibility, visibility) + assert snippet.title == title + assert snippet.visibility == visibility class TestProjectExport(TestProject): @with_httmock(resp_create_export) def test_create_project_export(self): export = self.project.exports.create() - self.assertEqual(export.message, "202 Accepted") + assert export.message == "202 Accepted" @with_httmock(resp_create_export, resp_export_status) def test_refresh_project_export_status(self): export = self.project.exports.create() export.refresh() - self.assertEqual(export.export_status, "finished") + assert export.export_status == "finished" @with_httmock(resp_create_export, resp_download_export) def test_download_project_export(self): export = self.project.exports.create() download = export.download() - self.assertIsInstance(download, bytes) - self.assertEqual(download, binary_content) + assert isinstance(download, bytes) + assert download == binary_content class TestProjectImport(TestProject): @with_httmock(resp_import_project) def test_import_project(self): project_import = self.gl.projects.import_project("file", "api-project") - self.assertEqual(project_import["import_status"], "scheduled") + assert project_import["import_status"] == "scheduled" @with_httmock(resp_import_status) def test_refresh_project_import_status(self): project_import = self.project.imports.get() project_import.refresh() - self.assertEqual(project_import.import_status, "finished") + assert project_import.import_status == "finished" @with_httmock(resp_import_github) def test_import_github(self): base_path = "/root" name = "my-repo" ret = self.gl.projects.import_github("githubkey", 1234, base_path, name) - self.assertIsInstance(ret, dict) - self.assertEqual(ret["name"], name) - self.assertEqual(ret["full_path"], "/".join((base_path, name))) - self.assertTrue(ret["full_name"].endswith(name)) + assert isinstance(ret, dict) + assert ret["name"] == name + assert ret["full_path"] == "/".join((base_path, name)) + assert ret["full_name"].endswith(name) class TestProjectRemoteMirrors(TestProject): @with_httmock(resp_get_remote_mirrors) def test_list_project_remote_mirrors(self): mirrors = self.project.remote_mirrors.list() - self.assertIsInstance(mirrors, list) - self.assertIsInstance(mirrors[0], ProjectRemoteMirror) - self.assertTrue(mirrors[0].enabled) + assert isinstance(mirrors, list) + assert isinstance(mirrors[0], ProjectRemoteMirror) + assert mirrors[0].enabled @with_httmock(resp_create_remote_mirror) def test_create_project_remote_mirror(self): mirror = self.project.remote_mirrors.create({"url": "https://example.com"}) - self.assertIsInstance(mirror, ProjectRemoteMirror) - self.assertEqual(mirror.update_status, "none") + assert isinstance(mirror, ProjectRemoteMirror) + assert mirror.update_status == "none" @with_httmock(resp_create_remote_mirror, resp_update_remote_mirror) def test_update_project_remote_mirror(self): mirror = self.project.remote_mirrors.create({"url": "https://example.com"}) mirror.only_protected_branches = True mirror.save() - self.assertEqual(mirror.update_status, "finished") - self.assertTrue(mirror.only_protected_branches) + assert mirror.update_status == "finished" + assert mirror.only_protected_branches class TestProjectServices(TestProject): @with_httmock(resp_get_active_services) def test_list_active_services(self): services = self.project.services.list() - self.assertIsInstance(services, list) - self.assertIsInstance(services[0], ProjectService) - self.assertTrue(services[0].active) - self.assertTrue(services[0].push_events) + assert isinstance(services, list) + assert isinstance(services[0], ProjectService) + assert services[0].active + assert services[0].push_events def test_list_available_services(self): services = self.project.services.available() - self.assertIsInstance(services, list) - self.assertIsInstance(services[0], str) + assert isinstance(services, list) + assert isinstance(services[0], str) @with_httmock(resp_get_service) def test_get_service(self): service = self.project.services.get("pipelines-email") - self.assertIsInstance(service, ProjectService) - self.assertEqual(service.push_events, True) + assert isinstance(service, ProjectService) + assert service.push_events == True @with_httmock(resp_get_service, resp_update_service) def test_update_service(self): service = self.project.services.get("pipelines-email") service.issues_events = True service.save() - self.assertEqual(service.issues_events, True) + assert service.issues_events == True class TestProjectPipelineSchedule(TestProject): diff --git a/gitlab/tests/test_base.py b/gitlab/tests/test_base.py index 5a43b1d9d..666060c4f 100644 --- a/gitlab/tests/test_base.py +++ b/gitlab/tests/test_base.py @@ -19,6 +19,7 @@ import unittest from gitlab import base +import pytest class FakeGitlab(object): @@ -41,7 +42,7 @@ class MGR(base.RESTManager): _obj_cls = object mgr = MGR(FakeGitlab()) - self.assertEqual(mgr._computed_path, "/tests") + assert mgr._computed_path == "/tests" def test_computed_path_with_parent(self): class MGR(base.RESTManager): @@ -53,7 +54,7 @@ class Parent(object): id = 42 mgr = MGR(FakeGitlab(), parent=Parent()) - self.assertEqual(mgr._computed_path, "/tests/42/cases") + assert mgr._computed_path == "/tests/42/cases" def test_path_property(self): class MGR(base.RESTManager): @@ -61,7 +62,7 @@ class MGR(base.RESTManager): _obj_cls = object mgr = MGR(FakeGitlab()) - self.assertEqual(mgr.path, "/tests") + assert mgr.path == "/tests" class TestRESTObject(unittest.TestCase): @@ -72,54 +73,55 @@ def setUp(self): def test_instanciate(self): obj = FakeObject(self.manager, {"foo": "bar"}) - self.assertDictEqual({"foo": "bar"}, obj._attrs) - self.assertDictEqual({}, obj._updated_attrs) - self.assertEqual(None, obj._create_managers()) - self.assertEqual(self.manager, obj.manager) - self.assertEqual(self.gitlab, obj.manager.gitlab) + assert {"foo": "bar"} == obj._attrs + assert {} == obj._updated_attrs + assert None == obj._create_managers() + assert self.manager == obj.manager + assert self.gitlab == obj.manager.gitlab def test_pickability(self): obj = FakeObject(self.manager, {"foo": "bar"}) original_obj_module = obj._module pickled = pickle.dumps(obj) unpickled = pickle.loads(pickled) - self.assertIsInstance(unpickled, FakeObject) - self.assertTrue(hasattr(unpickled, "_module")) - self.assertEqual(unpickled._module, original_obj_module) + assert isinstance(unpickled, FakeObject) + assert hasattr(unpickled, "_module") + assert unpickled._module == original_obj_module pickled2 = pickle.dumps(unpickled) def test_attrs(self): obj = FakeObject(self.manager, {"foo": "bar"}) - self.assertEqual("bar", obj.foo) - self.assertRaises(AttributeError, getattr, obj, "bar") + assert "bar" == obj.foo + with pytest.raises(AttributeError): + getattr(obj, "bar") obj.bar = "baz" - self.assertEqual("baz", obj.bar) - self.assertDictEqual({"foo": "bar"}, obj._attrs) - self.assertDictEqual({"bar": "baz"}, obj._updated_attrs) + assert "baz" == obj.bar + assert {"foo": "bar"} == obj._attrs + assert {"bar": "baz"} == obj._updated_attrs def test_get_id(self): obj = FakeObject(self.manager, {"foo": "bar"}) obj.id = 42 - self.assertEqual(42, obj.get_id()) + assert 42 == obj.get_id() obj.id = None - self.assertEqual(None, obj.get_id()) + assert None == obj.get_id() def test_custom_id_attr(self): class OtherFakeObject(FakeObject): _id_attr = "foo" obj = OtherFakeObject(self.manager, {"foo": "bar"}) - self.assertEqual("bar", obj.get_id()) + assert "bar" == obj.get_id() def test_update_attrs(self): obj = FakeObject(self.manager, {"foo": "bar"}) obj.bar = "baz" obj._update_attrs({"foo": "foo", "bar": "bar"}) - self.assertDictEqual({"foo": "foo", "bar": "bar"}, obj._attrs) - self.assertDictEqual({}, obj._updated_attrs) + assert {"foo": "foo", "bar": "bar"} == obj._attrs + assert {} == obj._updated_attrs def test_create_managers(self): class ObjectWithManager(FakeObject): @@ -127,14 +129,14 @@ class ObjectWithManager(FakeObject): obj = ObjectWithManager(self.manager, {"foo": "bar"}) obj.id = 42 - self.assertIsInstance(obj.fakes, FakeManager) - self.assertEqual(obj.fakes.gitlab, self.gitlab) - self.assertEqual(obj.fakes._parent, obj) + assert isinstance(obj.fakes, FakeManager) + assert obj.fakes.gitlab == self.gitlab + assert obj.fakes._parent == obj def test_equality(self): obj1 = FakeObject(self.manager, {"id": "foo"}) obj2 = FakeObject(self.manager, {"id": "foo", "other_attr": "bar"}) - self.assertEqual(obj1, obj2) + assert obj1 == obj2 def test_equality_custom_id(self): class OtherFakeObject(FakeObject): @@ -142,14 +144,14 @@ class OtherFakeObject(FakeObject): obj1 = OtherFakeObject(self.manager, {"foo": "bar"}) obj2 = OtherFakeObject(self.manager, {"foo": "bar", "other_attr": "baz"}) - self.assertEqual(obj1, obj2) + assert obj1 == obj2 def test_inequality(self): obj1 = FakeObject(self.manager, {"id": "foo"}) obj2 = FakeObject(self.manager, {"id": "bar"}) - self.assertNotEqual(obj1, obj2) + assert obj1 != obj2 def test_inequality_no_id(self): obj1 = FakeObject(self.manager, {"attr1": "foo"}) obj2 = FakeObject(self.manager, {"attr1": "bar"}) - self.assertNotEqual(obj1, obj2) + assert obj1 != obj2 diff --git a/gitlab/tests/test_cli.py b/gitlab/tests/test_cli.py index 48201036f..63a57937a 100644 --- a/gitlab/tests/test_cli.py +++ b/gitlab/tests/test_cli.py @@ -37,12 +37,13 @@ def redirect_stderr(new_target): from gitlab import cli import gitlab.v4.cli +import pytest class TestCLI(unittest.TestCase): def test_what_to_cls(self): - self.assertEqual("Foo", cli.what_to_cls("foo")) - self.assertEqual("FooBar", cli.what_to_cls("foo-bar")) + assert "Foo" == cli.what_to_cls("foo") + assert "FooBar" == cli.what_to_cls("foo-bar") def test_cls_to_what(self): class Class(object): @@ -51,64 +52,63 @@ class Class(object): class TestClass(object): pass - self.assertEqual("test-class", cli.cls_to_what(TestClass)) - self.assertEqual("class", cli.cls_to_what(Class)) + assert "test-class" == cli.cls_to_what(TestClass) + assert "class" == cli.cls_to_what(Class) def test_die(self): fl = io.StringIO() with redirect_stderr(fl): - with self.assertRaises(SystemExit) as test: + with pytest.raises(SystemExit) as test: cli.die("foobar") - self.assertEqual(fl.getvalue(), "foobar\n") - self.assertEqual(test.exception.code, 1) + assert fl.getvalue() == "foobar\n" + assert test.value.code == 1 def test_parse_value(self): ret = cli._parse_value("foobar") - self.assertEqual(ret, "foobar") + assert ret == "foobar" ret = cli._parse_value(True) - self.assertEqual(ret, True) + assert ret == True ret = cli._parse_value(1) - self.assertEqual(ret, 1) + assert ret == 1 ret = cli._parse_value(None) - self.assertEqual(ret, None) + assert ret == None fd, temp_path = tempfile.mkstemp() os.write(fd, b"content") os.close(fd) ret = cli._parse_value("@%s" % temp_path) - self.assertEqual(ret, "content") + assert ret == "content" os.unlink(temp_path) fl = io.StringIO() with redirect_stderr(fl): - with self.assertRaises(SystemExit) as exc: + with pytest.raises(SystemExit) as exc: cli._parse_value("@/thisfileprobablydoesntexist") - self.assertEqual( - fl.getvalue(), - "[Errno 2] No such file or directory:" - " '/thisfileprobablydoesntexist'\n", + assert ( + fl.getvalue() == "[Errno 2] No such file or directory:" + " '/thisfileprobablydoesntexist'\n" ) - self.assertEqual(exc.exception.code, 1) + assert exc.value.code == 1 def test_base_parser(self): parser = cli._get_base_parser() args = parser.parse_args( ["-v", "-g", "gl_id", "-c", "foo.cfg", "-c", "bar.cfg"] ) - self.assertTrue(args.verbose) - self.assertEqual(args.gitlab, "gl_id") - self.assertEqual(args.config_file, ["foo.cfg", "bar.cfg"]) + assert args.verbose + assert args.gitlab == "gl_id" + assert args.config_file == ["foo.cfg", "bar.cfg"] class TestV4CLI(unittest.TestCase): def test_parse_args(self): parser = cli._get_parser(gitlab.v4.cli) args = parser.parse_args(["project", "list"]) - self.assertEqual(args.what, "project") - self.assertEqual(args.whaction, "list") + assert args.what == "project" + assert args.whaction == "list" def test_parser(self): parser = cli._get_parser(gitlab.v4.cli) @@ -117,25 +117,25 @@ def test_parser(self): for action in parser._actions if isinstance(action, argparse._SubParsersAction) ) - self.assertIsNotNone(subparsers) - self.assertIn("project", subparsers.choices) + assert subparsers is not None + assert "project" in subparsers.choices user_subparsers = next( action for action in subparsers.choices["project"]._actions if isinstance(action, argparse._SubParsersAction) ) - self.assertIsNotNone(user_subparsers) - self.assertIn("list", user_subparsers.choices) - self.assertIn("get", user_subparsers.choices) - self.assertIn("delete", user_subparsers.choices) - self.assertIn("update", user_subparsers.choices) - self.assertIn("create", user_subparsers.choices) - self.assertIn("archive", user_subparsers.choices) - self.assertIn("unarchive", user_subparsers.choices) + assert user_subparsers is not None + assert "list" in user_subparsers.choices + assert "get" in user_subparsers.choices + assert "delete" in user_subparsers.choices + assert "update" in user_subparsers.choices + assert "create" in user_subparsers.choices + assert "archive" in user_subparsers.choices + assert "unarchive" in user_subparsers.choices actions = user_subparsers.choices["create"]._option_string_actions - self.assertFalse(actions["--description"].required) + assert not actions["--description"].required user_subparsers = next( action @@ -143,4 +143,4 @@ def test_parser(self): if isinstance(action, argparse._SubParsersAction) ) actions = user_subparsers.choices["create"]._option_string_actions - self.assertTrue(actions["--name"].required) + assert actions["--name"].required diff --git a/gitlab/tests/test_config.py b/gitlab/tests/test_config.py index 681b3d174..abdeed040 100644 --- a/gitlab/tests/test_config.py +++ b/gitlab/tests/test_config.py @@ -22,6 +22,7 @@ import io from gitlab import config +import pytest valid_config = u"""[global] @@ -76,18 +77,18 @@ class TestEnvConfig(unittest.TestCase): def test_env_present(self): with mock.patch.dict(os.environ, {"PYTHON_GITLAB_CFG": "/some/path"}): - self.assertEqual(["/some/path"], config._env_config()) + assert ["/some/path"] == config._env_config() def test_env_missing(self): with mock.patch.dict(os.environ, {}, clear=True): - self.assertEqual([], config._env_config()) + assert [] == config._env_config() class TestConfigParser(unittest.TestCase): @mock.patch("os.path.exists") def test_missing_config(self, path_exists): path_exists.return_value = False - with self.assertRaises(config.GitlabConfigMissingError): + with pytest.raises(config.GitlabConfigMissingError): config.GitlabConfigParser("test") @mock.patch("os.path.exists") @@ -98,14 +99,14 @@ def test_invalid_id(self, m_open, path_exists): m_open.return_value = fd path_exists.return_value = True config.GitlabConfigParser("there") - self.assertRaises(config.GitlabIDError, config.GitlabConfigParser) + with pytest.raises(config.GitlabIDError): + config.GitlabConfigParser() fd = io.StringIO(valid_config) fd.close = mock.Mock(return_value=None) m_open.return_value = fd - self.assertRaises( - config.GitlabDataError, config.GitlabConfigParser, gitlab_id="not_there" - ) + with pytest.raises(config.GitlabDataError): + config.GitlabConfigParser(gitlab_id="not_there") @mock.patch("os.path.exists") @mock.patch("builtins.open") @@ -117,15 +118,13 @@ def test_invalid_data(self, m_open, path_exists): config.GitlabConfigParser("one") config.GitlabConfigParser("one") - self.assertRaises( - config.GitlabDataError, config.GitlabConfigParser, gitlab_id="two" - ) - self.assertRaises( - config.GitlabDataError, config.GitlabConfigParser, gitlab_id="three" - ) - with self.assertRaises(config.GitlabDataError) as emgr: + with pytest.raises(config.GitlabDataError): + config.GitlabConfigParser(gitlab_id="two") + with pytest.raises(config.GitlabDataError): + config.GitlabConfigParser(gitlab_id="three") + with pytest.raises(config.GitlabDataError) as emgr: config.GitlabConfigParser("four") - self.assertEqual("Unsupported per_page number: 200", emgr.exception.args[0]) + assert "Unsupported per_page number: 200" == emgr.value.args[0] @mock.patch("os.path.exists") @mock.patch("builtins.open") @@ -136,44 +135,44 @@ def test_valid_data(self, m_open, path_exists): path_exists.return_value = True cp = config.GitlabConfigParser() - self.assertEqual("one", cp.gitlab_id) - self.assertEqual("http://one.url", cp.url) - self.assertEqual("ABCDEF", cp.private_token) - self.assertEqual(None, cp.oauth_token) - self.assertEqual(2, cp.timeout) - self.assertEqual(True, cp.ssl_verify) - self.assertIsNone(cp.per_page) + assert "one" == cp.gitlab_id + assert "http://one.url" == cp.url + assert "ABCDEF" == cp.private_token + assert None == cp.oauth_token + assert 2 == cp.timeout + assert True == cp.ssl_verify + assert cp.per_page is None fd = io.StringIO(valid_config) fd.close = mock.Mock(return_value=None) m_open.return_value = fd cp = config.GitlabConfigParser(gitlab_id="two") - self.assertEqual("two", cp.gitlab_id) - self.assertEqual("https://two.url", cp.url) - self.assertEqual("GHIJKL", cp.private_token) - self.assertEqual(None, cp.oauth_token) - self.assertEqual(10, cp.timeout) - self.assertEqual(False, cp.ssl_verify) + assert "two" == cp.gitlab_id + assert "https://two.url" == cp.url + assert "GHIJKL" == cp.private_token + assert None == cp.oauth_token + assert 10 == cp.timeout + assert False == cp.ssl_verify fd = io.StringIO(valid_config) fd.close = mock.Mock(return_value=None) m_open.return_value = fd cp = config.GitlabConfigParser(gitlab_id="three") - self.assertEqual("three", cp.gitlab_id) - self.assertEqual("https://three.url", cp.url) - self.assertEqual("MNOPQR", cp.private_token) - self.assertEqual(None, cp.oauth_token) - self.assertEqual(2, cp.timeout) - self.assertEqual("/path/to/CA/bundle.crt", cp.ssl_verify) - self.assertEqual(50, cp.per_page) + assert "three" == cp.gitlab_id + assert "https://three.url" == cp.url + assert "MNOPQR" == cp.private_token + assert None == cp.oauth_token + assert 2 == cp.timeout + assert "/path/to/CA/bundle.crt" == cp.ssl_verify + assert 50 == cp.per_page fd = io.StringIO(valid_config) fd.close = mock.Mock(return_value=None) m_open.return_value = fd cp = config.GitlabConfigParser(gitlab_id="four") - self.assertEqual("four", cp.gitlab_id) - self.assertEqual("https://four.url", cp.url) - self.assertEqual(None, cp.private_token) - self.assertEqual("STUV", cp.oauth_token) - self.assertEqual(2, cp.timeout) - self.assertEqual(True, cp.ssl_verify) + assert "four" == cp.gitlab_id + assert "https://four.url" == cp.url + assert None == cp.private_token + assert "STUV" == cp.oauth_token + assert 2 == cp.timeout + assert True == cp.ssl_verify diff --git a/gitlab/tests/test_exceptions.py b/gitlab/tests/test_exceptions.py index 1f00af067..57622c09f 100644 --- a/gitlab/tests/test_exceptions.py +++ b/gitlab/tests/test_exceptions.py @@ -1,6 +1,7 @@ import unittest from gitlab import exceptions +import pytest class TestExceptions(unittest.TestCase): @@ -14,6 +15,6 @@ class TestError(Exception): def raise_error_from_http_error(): raise exceptions.GitlabHttpError - with self.assertRaises(TestError) as context: + with pytest.raises(TestError) as context: raise_error_from_http_error() - self.assertIsInstance(context.exception.__cause__, exceptions.GitlabHttpError) + assert isinstance(context.value.__cause__, exceptions.GitlabHttpError) diff --git a/gitlab/tests/test_gitlab.py b/gitlab/tests/test_gitlab.py index 6fc551cf5..59139e4af 100644 --- a/gitlab/tests/test_gitlab.py +++ b/gitlab/tests/test_gitlab.py @@ -30,6 +30,7 @@ import gitlab from gitlab import * # noqa from gitlab.v4.objects import * # noqa +import pytest valid_config = b"""[global] @@ -45,17 +46,17 @@ class TestSanitize(unittest.TestCase): def test_do_nothing(self): - self.assertEqual(1, gitlab._sanitize(1)) - self.assertEqual(1.5, gitlab._sanitize(1.5)) - self.assertEqual("foo", gitlab._sanitize("foo")) + assert 1 == gitlab._sanitize(1) + assert 1.5 == gitlab._sanitize(1.5) + assert "foo" == gitlab._sanitize("foo") def test_slash(self): - self.assertEqual("foo%2Fbar", gitlab._sanitize("foo/bar")) + assert "foo%2Fbar" == gitlab._sanitize("foo/bar") def test_dict(self): source = {"url": "foo/bar", "id": 1} expected = {"url": "foo%2Fbar", "id": 1} - self.assertEqual(expected, gitlab._sanitize(source)) + assert expected == gitlab._sanitize(source) class TestGitlabList(unittest.TestCase): @@ -102,22 +103,20 @@ def resp_2(url, request): with HTTMock(resp_1): obj = self.gl.http_list("/tests", as_list=False) - self.assertEqual(len(obj), 2) - self.assertEqual( - obj._next_url, "http://localhost/api/v4/tests?per_page=1&page=2" - ) - self.assertEqual(obj.current_page, 1) - self.assertEqual(obj.prev_page, None) - self.assertEqual(obj.next_page, 2) - self.assertEqual(obj.per_page, 1) - self.assertEqual(obj.total_pages, 2) - self.assertEqual(obj.total, 2) + assert len(obj) == 2 + assert obj._next_url == "http://localhost/api/v4/tests?per_page=1&page=2" + assert obj.current_page == 1 + assert obj.prev_page == None + assert obj.next_page == 2 + assert obj.per_page == 1 + assert obj.total_pages == 2 + assert obj.total == 2 with HTTMock(resp_2): l = list(obj) - self.assertEqual(len(l), 2) - self.assertEqual(l[0]["a"], "b") - self.assertEqual(l[1]["c"], "d") + assert len(l) == 2 + assert l[0]["a"] == "b" + assert l[1]["c"] == "d" def test_all_omitted_when_as_list(self): @urlmatch(scheme="http", netloc="localhost", path="/api/v4/tests", method="get") @@ -135,7 +134,7 @@ def resp(url, request): with HTTMock(resp): result = self.gl.http_list("/tests", as_list=False, all=True) - self.assertIsInstance(result, GitlabList) + assert isinstance(result, GitlabList) class TestGitlabHttpMethods(unittest.TestCase): @@ -146,11 +145,11 @@ def setUp(self): def test_build_url(self): r = self.gl._build_url("http://localhost/api/v4") - self.assertEqual(r, "http://localhost/api/v4") + assert r == "http://localhost/api/v4" r = self.gl._build_url("https://localhost/api/v4") - self.assertEqual(r, "https://localhost/api/v4") + assert r == "https://localhost/api/v4" r = self.gl._build_url("/projects") - self.assertEqual(r, "http://localhost/api/v4/projects") + assert r == "http://localhost/api/v4/projects" def test_http_request(self): @urlmatch( @@ -164,7 +163,7 @@ def resp_cont(url, request): with HTTMock(resp_cont): http_r = self.gl.http_request("get", "/projects") http_r.json() - self.assertEqual(http_r.status_code, 200) + assert http_r.status_code == 200 def test_http_request_404(self): @urlmatch( @@ -175,9 +174,8 @@ def resp_cont(url, request): return response(404, content, {}, None, 5, request) with HTTMock(resp_cont): - self.assertRaises( - GitlabHttpError, self.gl.http_request, "get", "/not_there" - ) + with pytest.raises(GitlabHttpError): + self.gl.http_request("get", "/not_there") def test_get_request(self): @urlmatch( @@ -190,8 +188,8 @@ def resp_cont(url, request): with HTTMock(resp_cont): result = self.gl.http_get("/projects") - self.assertIsInstance(result, dict) - self.assertEqual(result["name"], "project1") + assert isinstance(result, dict) + assert result["name"] == "project1" def test_get_request_raw(self): @urlmatch( @@ -204,7 +202,7 @@ def resp_cont(url, request): with HTTMock(resp_cont): result = self.gl.http_get("/projects") - self.assertEqual(result.content.decode("utf-8"), "content") + assert result.content.decode("utf-8") == "content" def test_get_request_404(self): @urlmatch( @@ -215,7 +213,8 @@ def resp_cont(url, request): return response(404, content, {}, None, 5, request) with HTTMock(resp_cont): - self.assertRaises(GitlabHttpError, self.gl.http_get, "/not_there") + with pytest.raises(GitlabHttpError): + self.gl.http_get("/not_there") def test_get_request_invalid_data(self): @urlmatch( @@ -227,7 +226,8 @@ def resp_cont(url, request): return response(200, content, headers, None, 5, request) with HTTMock(resp_cont): - self.assertRaises(GitlabParsingError, self.gl.http_get, "/projects") + with pytest.raises(GitlabParsingError): + self.gl.http_get("/projects") def test_list_request(self): @urlmatch( @@ -240,18 +240,18 @@ def resp_cont(url, request): with HTTMock(resp_cont): result = self.gl.http_list("/projects", as_list=True) - self.assertIsInstance(result, list) - self.assertEqual(len(result), 1) + assert isinstance(result, list) + assert len(result) == 1 with HTTMock(resp_cont): result = self.gl.http_list("/projects", as_list=False) - self.assertIsInstance(result, GitlabList) - self.assertEqual(len(result), 1) + assert isinstance(result, GitlabList) + assert len(result) == 1 with HTTMock(resp_cont): result = self.gl.http_list("/projects", all=True) - self.assertIsInstance(result, list) - self.assertEqual(len(result), 1) + assert isinstance(result, list) + assert len(result) == 1 def test_list_request_404(self): @urlmatch( @@ -262,7 +262,8 @@ def resp_cont(url, request): return response(404, content, {}, None, 5, request) with HTTMock(resp_cont): - self.assertRaises(GitlabHttpError, self.gl.http_list, "/not_there") + with pytest.raises(GitlabHttpError): + self.gl.http_list("/not_there") def test_list_request_invalid_data(self): @urlmatch( @@ -274,7 +275,8 @@ def resp_cont(url, request): return response(200, content, headers, None, 5, request) with HTTMock(resp_cont): - self.assertRaises(GitlabParsingError, self.gl.http_list, "/projects") + with pytest.raises(GitlabParsingError): + self.gl.http_list("/projects") def test_post_request(self): @urlmatch( @@ -287,8 +289,8 @@ def resp_cont(url, request): with HTTMock(resp_cont): result = self.gl.http_post("/projects") - self.assertIsInstance(result, dict) - self.assertEqual(result["name"], "project1") + assert isinstance(result, dict) + assert result["name"] == "project1" def test_post_request_404(self): @urlmatch( @@ -299,7 +301,8 @@ def resp_cont(url, request): return response(404, content, {}, None, 5, request) with HTTMock(resp_cont): - self.assertRaises(GitlabHttpError, self.gl.http_post, "/not_there") + with pytest.raises(GitlabHttpError): + self.gl.http_post("/not_there") def test_post_request_invalid_data(self): @urlmatch( @@ -311,7 +314,8 @@ def resp_cont(url, request): return response(200, content, headers, None, 5, request) with HTTMock(resp_cont): - self.assertRaises(GitlabParsingError, self.gl.http_post, "/projects") + with pytest.raises(GitlabParsingError): + self.gl.http_post("/projects") def test_put_request(self): @urlmatch( @@ -324,8 +328,8 @@ def resp_cont(url, request): with HTTMock(resp_cont): result = self.gl.http_put("/projects") - self.assertIsInstance(result, dict) - self.assertEqual(result["name"], "project1") + assert isinstance(result, dict) + assert result["name"] == "project1" def test_put_request_404(self): @urlmatch( @@ -336,7 +340,8 @@ def resp_cont(url, request): return response(404, content, {}, None, 5, request) with HTTMock(resp_cont): - self.assertRaises(GitlabHttpError, self.gl.http_put, "/not_there") + with pytest.raises(GitlabHttpError): + self.gl.http_put("/not_there") def test_put_request_invalid_data(self): @urlmatch( @@ -348,7 +353,8 @@ def resp_cont(url, request): return response(200, content, headers, None, 5, request) with HTTMock(resp_cont): - self.assertRaises(GitlabParsingError, self.gl.http_put, "/projects") + with pytest.raises(GitlabParsingError): + self.gl.http_put("/projects") def test_delete_request(self): @urlmatch( @@ -361,8 +367,8 @@ def resp_cont(url, request): with HTTMock(resp_cont): result = self.gl.http_delete("/projects") - self.assertIsInstance(result, requests.Response) - self.assertEqual(result.json(), True) + assert isinstance(result, requests.Response) + assert result.json() == True def test_delete_request_404(self): @urlmatch( @@ -373,7 +379,8 @@ def resp_cont(url, request): return response(404, content, {}, None, 5, request) with HTTMock(resp_cont): - self.assertRaises(GitlabHttpError, self.gl.http_delete, "/not_there") + with pytest.raises(GitlabHttpError): + self.gl.http_delete("/not_there") class TestGitlabStripBaseUrl(unittest.TestCase): @@ -383,81 +390,77 @@ def setUp(self): ) def test_strip_base_url(self): - self.assertEqual(self.gl.url, "http://localhost") + assert self.gl.url == "http://localhost" def test_strip_api_url(self): - self.assertEqual(self.gl.api_url, "http://localhost/api/v4") + assert self.gl.api_url == "http://localhost/api/v4" def test_build_url(self): r = self.gl._build_url("/projects") - self.assertEqual(r, "http://localhost/api/v4/projects") + assert r == "http://localhost/api/v4/projects" class TestGitlabAuth(unittest.TestCase): def test_invalid_auth_args(self): - self.assertRaises( - ValueError, - Gitlab, - "http://localhost", - api_version="4", - private_token="private_token", - oauth_token="bearer", - ) - self.assertRaises( - ValueError, - Gitlab, - "http://localhost", - api_version="4", - oauth_token="bearer", - http_username="foo", - http_password="bar", - ) - self.assertRaises( - ValueError, - Gitlab, - "http://localhost", - api_version="4", - private_token="private_token", - http_password="bar", - ) - self.assertRaises( - ValueError, - Gitlab, - "http://localhost", - api_version="4", - private_token="private_token", - http_username="foo", - ) + with pytest.raises(ValueError): + Gitlab( + "http://localhost", + api_version="4", + private_token="private_token", + oauth_token="bearer", + ) + with pytest.raises(ValueError): + Gitlab( + "http://localhost", + api_version="4", + oauth_token="bearer", + http_username="foo", + http_password="bar", + ) + with pytest.raises(ValueError): + Gitlab( + "http://localhost", + api_version="4", + private_token="private_token", + http_password="bar", + ) + with pytest.raises(ValueError): + Gitlab( + "http://localhost", + api_version="4", + private_token="private_token", + http_username="foo", + ) def test_private_token_auth(self): gl = Gitlab("http://localhost", private_token="private_token", api_version="4") - self.assertEqual(gl.private_token, "private_token") - self.assertEqual(gl.oauth_token, None) - self.assertEqual(gl.job_token, None) - self.assertEqual(gl._http_auth, None) - self.assertNotIn("Authorization", gl.headers) - self.assertEqual(gl.headers["PRIVATE-TOKEN"], "private_token") - self.assertNotIn("JOB-TOKEN", gl.headers) + assert gl.private_token == "private_token" + assert gl.oauth_token == None + assert gl.job_token == None + assert gl._http_auth == None + assert "Authorization" not in gl.headers + assert gl.headers["PRIVATE-TOKEN"] == "private_token" + assert "JOB-TOKEN" not in gl.headers def test_oauth_token_auth(self): gl = Gitlab("http://localhost", oauth_token="oauth_token", api_version="4") - self.assertEqual(gl.private_token, None) - self.assertEqual(gl.oauth_token, "oauth_token") - self.assertEqual(gl.job_token, None) - self.assertEqual(gl._http_auth, None) - self.assertEqual(gl.headers["Authorization"], "Bearer oauth_token") - self.assertNotIn("PRIVATE-TOKEN", gl.headers) - self.assertNotIn("JOB-TOKEN", gl.headers) + assert gl.private_token == None + assert gl.oauth_token == "oauth_token" + assert gl.job_token == None + assert gl._http_auth == None + assert gl.headers["Authorization"] == "Bearer oauth_token" + assert "PRIVATE-TOKEN" not in gl.headers + assert "JOB-TOKEN" not in gl.headers def test_job_token_auth(self): gl = Gitlab("http://localhost", job_token="CI_JOB_TOKEN", api_version="4") - self.assertEqual(gl.private_token, None) - self.assertEqual(gl.oauth_token, None) - self.assertEqual(gl.job_token, "CI_JOB_TOKEN") - self.assertEqual(gl._http_auth, None) - self.assertNotIn("Authorization", gl.headers) - self.assertNotIn("PRIVATE-TOKEN", gl.headers) - self.assertEqual(gl.headers["JOB-TOKEN"], "CI_JOB_TOKEN") + assert gl.private_token == None + assert gl.oauth_token == None + assert gl.job_token == "CI_JOB_TOKEN" + assert gl._http_auth == None + assert "Authorization" not in gl.headers + assert "PRIVATE-TOKEN" not in gl.headers + assert gl.headers["JOB-TOKEN"] == "CI_JOB_TOKEN" def test_http_auth(self): gl = Gitlab( @@ -467,12 +470,12 @@ def test_http_auth(self): http_password="bar", api_version="4", ) - self.assertEqual(gl.private_token, "private_token") - self.assertEqual(gl.oauth_token, None) - self.assertEqual(gl.job_token, None) - self.assertIsInstance(gl._http_auth, requests.auth.HTTPBasicAuth) - self.assertEqual(gl.headers["PRIVATE-TOKEN"], "private_token") - self.assertNotIn("Authorization", gl.headers) + assert gl.private_token == "private_token" + assert gl.oauth_token == None + assert gl.job_token == None + assert isinstance(gl._http_auth, requests.auth.HTTPBasicAuth) + assert gl.headers["PRIVATE-TOKEN"] == "private_token" + assert "Authorization" not in gl.headers class TestGitlab(unittest.TestCase): @@ -488,9 +491,9 @@ def test_pickability(self): original_gl_objects = self.gl._objects pickled = pickle.dumps(self.gl) unpickled = pickle.loads(pickled) - self.assertIsInstance(unpickled, Gitlab) - self.assertTrue(hasattr(unpickled, "_objects")) - self.assertEqual(unpickled._objects, original_gl_objects) + assert isinstance(unpickled, Gitlab) + assert hasattr(unpickled, "_objects") + assert unpickled._objects == original_gl_objects def test_token_auth(self, callback=None): name = "username" @@ -506,9 +509,9 @@ def resp_cont(url, request): with HTTMock(resp_cont): self.gl.auth() - self.assertEqual(self.gl.user.username, name) - self.assertEqual(self.gl.user.id, id_) - self.assertIsInstance(self.gl.user, CurrentUser) + assert self.gl.user.username == name + assert self.gl.user.id == id_ + assert isinstance(self.gl.user, CurrentUser) def test_hooks(self): @urlmatch( @@ -521,9 +524,9 @@ def resp_get_hook(url, request): with HTTMock(resp_get_hook): data = self.gl.hooks.get(1) - self.assertIsInstance(data, Hook) - self.assertEqual(data.url, "testurl") - self.assertEqual(data.id, 1) + assert isinstance(data, Hook) + assert data.url == "testurl" + assert data.id == 1 def test_projects(self): @urlmatch( @@ -536,9 +539,9 @@ def resp_get_project(url, request): with HTTMock(resp_get_project): data = self.gl.projects.get(1) - self.assertIsInstance(data, Project) - self.assertEqual(data.name, "name") - self.assertEqual(data.id, 1) + assert isinstance(data, Project) + assert data.name == "name" + assert data.id == 1 def test_project_environments(self): @urlmatch( @@ -565,10 +568,10 @@ def resp_get_environment(url, request): with HTTMock(resp_get_project, resp_get_environment): project = self.gl.projects.get(1) environment = project.environments.get(1) - self.assertIsInstance(environment, ProjectEnvironment) - self.assertEqual(environment.id, 1) - self.assertEqual(environment.last_deployment, "sometime") - self.assertEqual(environment.name, "environment_name") + assert isinstance(environment, ProjectEnvironment) + assert environment.id == 1 + assert environment.last_deployment == "sometime" + assert environment.name == "environment_name" def test_project_additional_statistics(self): @urlmatch( @@ -595,8 +598,8 @@ def resp_get_environment(url, request): with HTTMock(resp_get_project, resp_get_environment): project = self.gl.projects.get(1) statistics = project.additionalstatistics.get() - self.assertIsInstance(statistics, ProjectAdditionalStatistics) - self.assertEqual(statistics.fetches["total"], 50) + assert isinstance(statistics, ProjectAdditionalStatistics) + assert statistics.fetches["total"] == 50 def test_project_issues_statistics(self): @urlmatch( @@ -623,8 +626,8 @@ def resp_get_environment(url, request): with HTTMock(resp_get_project, resp_get_environment): project = self.gl.projects.get(1) statistics = project.issuesstatistics.get() - self.assertIsInstance(statistics, ProjectIssuesStatistics) - self.assertEqual(statistics.statistics["counts"]["all"], 20) + assert isinstance(statistics, ProjectIssuesStatistics) + assert statistics.statistics["counts"]["all"] == 20 def test_issues(self): @urlmatch( @@ -638,8 +641,8 @@ def resp_get_issue(url, request): with HTTMock(resp_get_issue): data = self.gl.issues.list() - self.assertEqual(data[1].id, 2) - self.assertEqual(data[1].name, "other_name") + assert data[1].id == 2 + assert data[1].name == "other_name" @urlmatch(scheme="http", netloc="localhost", path="/api/v4/users/1", method="get") def resp_get_user(self, url, request): @@ -654,9 +657,9 @@ def resp_get_user(self, url, request): def test_users(self): with HTTMock(self.resp_get_user): user = self.gl.users.get(1) - self.assertIsInstance(user, User) - self.assertEqual(user.name, "name") - self.assertEqual(user.id, 1) + assert isinstance(user, User) + assert user.name == "name" + assert user.id == 1 def test_user_memberships(self): @urlmatch( @@ -687,8 +690,8 @@ def resp_get_user_memberships(url, request): with HTTMock(resp_get_user_memberships): user = self.gl.users.get(1, lazy=True) memberships = user.memberships.list() - self.assertIsInstance(memberships[0], UserMembership) - self.assertEqual(memberships[0].source_type, "Project") + assert isinstance(memberships[0], UserMembership) + assert memberships[0].source_type == "Project" def test_user_status(self): @urlmatch( @@ -707,9 +710,9 @@ def resp_get_user_status(url, request): user = self.gl.users.get(1) with HTTMock(resp_get_user_status): status = user.status.get() - self.assertIsInstance(status, UserStatus) - self.assertEqual(status.message, "test") - self.assertEqual(status.emoji, "thumbsup") + assert isinstance(status, UserStatus) + assert status.message == "test" + assert status.emoji == "thumbsup" def test_todo(self): with open(os.path.dirname(__file__) + "/data/todo.json", "r") as json_file: @@ -736,10 +739,10 @@ def resp_mark_as_done(url, request): with HTTMock(resp_get_todo): todo = self.gl.todos.list()[0] - self.assertIsInstance(todo, Todo) - self.assertEqual(todo.id, 102) - self.assertEqual(todo.target_type, "MergeRequest") - self.assertEqual(todo.target["assignee"]["username"], "root") + assert isinstance(todo, Todo) + assert todo.id == 102 + assert todo.target_type == "MergeRequest" + assert todo.target["assignee"]["username"] == "root" with HTTMock(resp_mark_as_done): todo.mark_as_done() @@ -791,15 +794,15 @@ def resp_deployment_update(url, request): "status": "created", } ) - self.assertEqual(deployment.id, 42) - self.assertEqual(deployment.status, "success") - self.assertEqual(deployment.ref, "master") + assert deployment.id == 42 + assert deployment.status == "success" + assert deployment.ref == "master" with HTTMock(resp_deployment_update): json_content["status"] = "failed" deployment.status = "failed" deployment.save() - self.assertEqual(deployment.status, "failed") + assert deployment.status == "failed" def test_user_activate_deactivate(self): @urlmatch( @@ -862,9 +865,9 @@ def resp_update_submodule(url, request): with HTTMock(resp_get_project): project = self.gl.projects.get(1) - self.assertIsInstance(project, Project) - self.assertEqual(project.name, "name") - self.assertEqual(project.id, 1) + assert isinstance(project, Project) + assert project.name == "name" + assert project.id == 1 with HTTMock(resp_update_submodule): ret = project.update_submodule( submodule="foo/bar", @@ -872,9 +875,9 @@ def resp_update_submodule(url, request): commit_sha="4c3674f66071e30b3311dac9b9ccc90502a72664", commit_message="Message", ) - self.assertIsInstance(ret, dict) - self.assertEqual(ret["message"], "Message") - self.assertEqual(ret["id"], "ed899a2f4b50b4370feeea94676502b42383c746") + assert isinstance(ret, dict) + assert ret["message"] == "Message" + assert ret["id"] == "ed899a2f4b50b4370feeea94676502b42383c746" def test_applications(self): content = '{"name": "test_app", "redirect_uri": "http://localhost:8080", "scopes": ["api", "email"]}' @@ -899,9 +902,9 @@ def resp_application_create(url, request): "confidential": False, } ) - self.assertEqual(application.name, "test_app") - self.assertEqual(application.redirect_uri, "http://localhost:8080") - self.assertEqual(application.scopes, ["api", "email"]) + assert application.name == "test_app" + assert application.redirect_uri == "http://localhost:8080" + assert application.scopes == ["api", "email"] def test_deploy_tokens(self): @urlmatch( @@ -931,11 +934,11 @@ def resp_deploy_token_create(url, request): "scopes": ["read_repository"], } ) - self.assertIsInstance(deploy_token, ProjectDeployToken) - self.assertEqual(deploy_token.id, 1), - self.assertEqual(deploy_token.expires_at, "2022-01-01T00:00:00.000Z"), - self.assertEqual(deploy_token.username, "custom-user") - self.assertEqual(deploy_token.scopes, ["read_repository"]) + assert isinstance(deploy_token, ProjectDeployToken) + assert deploy_token.id == 1 + assert deploy_token.expires_at == "2022-01-01T00:00:00.000Z" + assert deploy_token.username == "custom-user" + assert deploy_token.scopes == ["read_repository"] def _default_config(self): fd, temp_path = tempfile.mkstemp() @@ -954,5 +957,5 @@ class MyGitlab(gitlab.Gitlab): config_path = self._default_config() gl = MyGitlab.from_config("one", [config_path]) - self.assertIsInstance(gl, MyGitlab) + assert isinstance(gl, MyGitlab) os.unlink(config_path) diff --git a/gitlab/tests/test_mixins.py b/gitlab/tests/test_mixins.py index 749c0d260..e8613f2da 100644 --- a/gitlab/tests/test_mixins.py +++ b/gitlab/tests/test_mixins.py @@ -25,6 +25,7 @@ from gitlab import * # noqa from gitlab.base import * # noqa from gitlab.mixins import * # noqa +import pytest class TestObjectMixinsAttributes(unittest.TestCase): @@ -33,47 +34,47 @@ class O(AccessRequestMixin): pass obj = O() - self.assertTrue(hasattr(obj, "approve")) + assert hasattr(obj, "approve") def test_subscribable_mixin(self): class O(SubscribableMixin): pass obj = O() - self.assertTrue(hasattr(obj, "subscribe")) - self.assertTrue(hasattr(obj, "unsubscribe")) + assert hasattr(obj, "subscribe") + assert hasattr(obj, "unsubscribe") def test_todo_mixin(self): class O(TodoMixin): pass obj = O() - self.assertTrue(hasattr(obj, "todo")) + assert hasattr(obj, "todo") def test_time_tracking_mixin(self): class O(TimeTrackingMixin): pass obj = O() - self.assertTrue(hasattr(obj, "time_stats")) - self.assertTrue(hasattr(obj, "time_estimate")) - self.assertTrue(hasattr(obj, "reset_time_estimate")) - self.assertTrue(hasattr(obj, "add_spent_time")) - self.assertTrue(hasattr(obj, "reset_spent_time")) + assert hasattr(obj, "time_stats") + assert hasattr(obj, "time_estimate") + assert hasattr(obj, "reset_time_estimate") + assert hasattr(obj, "add_spent_time") + assert hasattr(obj, "reset_spent_time") def test_set_mixin(self): class O(SetMixin): pass obj = O() - self.assertTrue(hasattr(obj, "set")) + assert hasattr(obj, "set") def test_user_agent_detail_mixin(self): class O(UserAgentDetailMixin): pass obj = O() - self.assertTrue(hasattr(obj, "user_agent_detail")) + assert hasattr(obj, "user_agent_detail") class TestMetaMixins(unittest.TestCase): @@ -82,45 +83,45 @@ class M(RetrieveMixin): pass obj = M() - self.assertTrue(hasattr(obj, "list")) - self.assertTrue(hasattr(obj, "get")) - self.assertFalse(hasattr(obj, "create")) - self.assertFalse(hasattr(obj, "update")) - self.assertFalse(hasattr(obj, "delete")) - self.assertIsInstance(obj, ListMixin) - self.assertIsInstance(obj, GetMixin) + assert hasattr(obj, "list") + assert hasattr(obj, "get") + assert not hasattr(obj, "create") + assert not hasattr(obj, "update") + assert not hasattr(obj, "delete") + assert isinstance(obj, ListMixin) + assert isinstance(obj, GetMixin) def test_crud_mixin(self): class M(CRUDMixin): pass obj = M() - self.assertTrue(hasattr(obj, "get")) - self.assertTrue(hasattr(obj, "list")) - self.assertTrue(hasattr(obj, "create")) - self.assertTrue(hasattr(obj, "update")) - self.assertTrue(hasattr(obj, "delete")) - self.assertIsInstance(obj, ListMixin) - self.assertIsInstance(obj, GetMixin) - self.assertIsInstance(obj, CreateMixin) - self.assertIsInstance(obj, UpdateMixin) - self.assertIsInstance(obj, DeleteMixin) + assert hasattr(obj, "get") + assert hasattr(obj, "list") + assert hasattr(obj, "create") + assert hasattr(obj, "update") + assert hasattr(obj, "delete") + assert isinstance(obj, ListMixin) + assert isinstance(obj, GetMixin) + assert isinstance(obj, CreateMixin) + assert isinstance(obj, UpdateMixin) + assert isinstance(obj, DeleteMixin) def test_no_update_mixin(self): class M(NoUpdateMixin): pass obj = M() - self.assertTrue(hasattr(obj, "get")) - self.assertTrue(hasattr(obj, "list")) - self.assertTrue(hasattr(obj, "create")) - self.assertFalse(hasattr(obj, "update")) - self.assertTrue(hasattr(obj, "delete")) - self.assertIsInstance(obj, ListMixin) - self.assertIsInstance(obj, GetMixin) - self.assertIsInstance(obj, CreateMixin) - self.assertNotIsInstance(obj, UpdateMixin) - self.assertIsInstance(obj, DeleteMixin) + assert hasattr(obj, "get") + assert hasattr(obj, "list") + assert hasattr(obj, "create") + assert not hasattr(obj, "update") + assert hasattr(obj, "delete") + assert isinstance(obj, ListMixin) + assert isinstance(obj, GetMixin) + assert isinstance(obj, CreateMixin) + assert not isinstance(obj, UpdateMixin) + assert isinstance(obj, DeleteMixin) class FakeObject(base.RESTObject): @@ -153,9 +154,9 @@ def resp_cont(url, request): with HTTMock(resp_cont): mgr = M(self.gl) obj = mgr.get(42) - self.assertIsInstance(obj, FakeObject) - self.assertEqual(obj.foo, "bar") - self.assertEqual(obj.id, 42) + assert isinstance(obj, FakeObject) + assert obj.foo == "bar" + assert obj.id == 42 def test_refresh_mixin(self): class O(RefreshMixin, FakeObject): @@ -173,9 +174,9 @@ def resp_cont(url, request): mgr = FakeManager(self.gl) obj = O(mgr, {"id": 42}) res = obj.refresh() - self.assertIsNone(res) - self.assertEqual(obj.foo, "bar") - self.assertEqual(obj.id, 42) + assert res is None + assert obj.foo == "bar" + assert obj.id == 42 def test_get_without_id_mixin(self): class M(GetWithoutIdMixin, FakeManager): @@ -190,9 +191,9 @@ def resp_cont(url, request): with HTTMock(resp_cont): mgr = M(self.gl) obj = mgr.get() - self.assertIsInstance(obj, FakeObject) - self.assertEqual(obj.foo, "bar") - self.assertFalse(hasattr(obj, "id")) + assert isinstance(obj, FakeObject) + assert obj.foo == "bar" + assert not hasattr(obj, "id") def test_list_mixin(self): class M(ListMixin, FakeManager): @@ -208,18 +209,18 @@ def resp_cont(url, request): # test RESTObjectList mgr = M(self.gl) obj_list = mgr.list(as_list=False) - self.assertIsInstance(obj_list, base.RESTObjectList) + assert isinstance(obj_list, base.RESTObjectList) for obj in obj_list: - self.assertIsInstance(obj, FakeObject) - self.assertIn(obj.id, (42, 43)) + assert isinstance(obj, FakeObject) + assert obj.id in (42, 43) # test list() obj_list = mgr.list(all=True) - self.assertIsInstance(obj_list, list) - self.assertEqual(obj_list[0].id, 42) - self.assertEqual(obj_list[1].id, 43) - self.assertIsInstance(obj_list[0], FakeObject) - self.assertEqual(len(obj_list), 2) + assert isinstance(obj_list, list) + assert obj_list[0].id == 42 + assert obj_list[1].id == 43 + assert isinstance(obj_list[0], FakeObject) + assert len(obj_list) == 2 def test_list_other_url(self): class M(ListMixin, FakeManager): @@ -236,11 +237,12 @@ def resp_cont(url, request): with HTTMock(resp_cont): mgr = M(self.gl) obj_list = mgr.list(path="/others", as_list=False) - self.assertIsInstance(obj_list, base.RESTObjectList) + assert isinstance(obj_list, base.RESTObjectList) obj = obj_list.next() - self.assertEqual(obj.id, 42) - self.assertEqual(obj.foo, "bar") - self.assertRaises(StopIteration, obj_list.next) + assert obj.id == 42 + assert obj.foo == "bar" + with pytest.raises(StopIteration): + obj_list.next() def test_create_mixin_get_attrs(self): class M1(CreateMixin, FakeManager): @@ -252,15 +254,15 @@ class M2(CreateMixin, FakeManager): mgr = M1(self.gl) required, optional = mgr.get_create_attrs() - self.assertEqual(len(required), 0) - self.assertEqual(len(optional), 0) + assert len(required) == 0 + assert len(optional) == 0 mgr = M2(self.gl) required, optional = mgr.get_create_attrs() - self.assertIn("foo", required) - self.assertIn("bar", optional) - self.assertIn("baz", optional) - self.assertNotIn("bam", optional) + assert "foo" in required + assert "bar" in optional + assert "baz" in optional + assert "bam" not in optional def test_create_mixin_missing_attrs(self): class M(CreateMixin, FakeManager): @@ -271,9 +273,9 @@ class M(CreateMixin, FakeManager): mgr._check_missing_create_attrs(data) data = {"baz": "blah"} - with self.assertRaises(AttributeError) as error: + with pytest.raises(AttributeError) as error: mgr._check_missing_create_attrs(data) - self.assertIn("foo", str(error.exception)) + assert "foo" in str(error.value) def test_create_mixin(self): class M(CreateMixin, FakeManager): @@ -291,9 +293,9 @@ def resp_cont(url, request): with HTTMock(resp_cont): mgr = M(self.gl) obj = mgr.create({"foo": "bar"}) - self.assertIsInstance(obj, FakeObject) - self.assertEqual(obj.id, 42) - self.assertEqual(obj.foo, "bar") + assert isinstance(obj, FakeObject) + assert obj.id == 42 + assert obj.foo == "bar" def test_create_mixin_custom_path(self): class M(CreateMixin, FakeManager): @@ -311,9 +313,9 @@ def resp_cont(url, request): with HTTMock(resp_cont): mgr = M(self.gl) obj = mgr.create({"foo": "bar"}, path="/others") - self.assertIsInstance(obj, FakeObject) - self.assertEqual(obj.id, 42) - self.assertEqual(obj.foo, "bar") + assert isinstance(obj, FakeObject) + assert obj.id == 42 + assert obj.foo == "bar" def test_update_mixin_get_attrs(self): class M1(UpdateMixin, FakeManager): @@ -325,15 +327,15 @@ class M2(UpdateMixin, FakeManager): mgr = M1(self.gl) required, optional = mgr.get_update_attrs() - self.assertEqual(len(required), 0) - self.assertEqual(len(optional), 0) + assert len(required) == 0 + assert len(optional) == 0 mgr = M2(self.gl) required, optional = mgr.get_update_attrs() - self.assertIn("foo", required) - self.assertIn("bam", optional) - self.assertNotIn("bar", optional) - self.assertNotIn("baz", optional) + assert "foo" in required + assert "bam" in optional + assert "bar" not in optional + assert "baz" not in optional def test_update_mixin_missing_attrs(self): class M(UpdateMixin, FakeManager): @@ -344,9 +346,9 @@ class M(UpdateMixin, FakeManager): mgr._check_missing_update_attrs(data) data = {"baz": "blah"} - with self.assertRaises(AttributeError) as error: + with pytest.raises(AttributeError) as error: mgr._check_missing_update_attrs(data) - self.assertIn("foo", str(error.exception)) + assert "foo" in str(error.value) def test_update_mixin(self): class M(UpdateMixin, FakeManager): @@ -364,9 +366,9 @@ def resp_cont(url, request): with HTTMock(resp_cont): mgr = M(self.gl) server_data = mgr.update(42, {"foo": "baz"}) - self.assertIsInstance(server_data, dict) - self.assertEqual(server_data["id"], 42) - self.assertEqual(server_data["foo"], "baz") + assert isinstance(server_data, dict) + assert server_data["id"] == 42 + assert server_data["foo"] == "baz" def test_update_mixin_no_id(self): class M(UpdateMixin, FakeManager): @@ -382,8 +384,8 @@ def resp_cont(url, request): with HTTMock(resp_cont): mgr = M(self.gl) server_data = mgr.update(new_data={"foo": "baz"}) - self.assertIsInstance(server_data, dict) - self.assertEqual(server_data["foo"], "baz") + assert isinstance(server_data, dict) + assert server_data["foo"] == "baz" def test_delete_mixin(self): class M(DeleteMixin, FakeManager): @@ -421,8 +423,8 @@ def resp_cont(url, request): obj = O(mgr, {"id": 42, "foo": "bar"}) obj.foo = "baz" obj.save() - self.assertEqual(obj._attrs["foo"], "baz") - self.assertDictEqual(obj._updated_attrs, {}) + assert obj._attrs["foo"] == "baz" + assert obj._updated_attrs == {} def test_set_mixin(self): class M(SetMixin, FakeManager): @@ -439,6 +441,6 @@ def resp_cont(url, request): with HTTMock(resp_cont): mgr = M(self.gl) obj = mgr.set("foo", "bar") - self.assertIsInstance(obj, FakeObject) - self.assertEqual(obj.key, "foo") - self.assertEqual(obj.value, "bar") + assert isinstance(obj, FakeObject) + assert obj.key == "foo" + assert obj.value == "bar" diff --git a/gitlab/tests/test_types.py b/gitlab/tests/test_types.py index 3613383de..8471bdff6 100644 --- a/gitlab/tests/test_types.py +++ b/gitlab/tests/test_types.py @@ -23,49 +23,49 @@ class TestGitlabAttribute(unittest.TestCase): def test_all(self): o = types.GitlabAttribute("whatever") - self.assertEqual("whatever", o.get()) + assert "whatever" == o.get() o.set_from_cli("whatever2") - self.assertEqual("whatever2", o.get()) + assert "whatever2" == o.get() - self.assertEqual("whatever2", o.get_for_api()) + assert "whatever2" == o.get_for_api() o = types.GitlabAttribute() - self.assertEqual(None, o._value) + assert None == o._value class TestListAttribute(unittest.TestCase): def test_list_input(self): o = types.ListAttribute() o.set_from_cli("foo,bar,baz") - self.assertEqual(["foo", "bar", "baz"], o.get()) + assert ["foo", "bar", "baz"] == o.get() o.set_from_cli("foo") - self.assertEqual(["foo"], o.get()) + assert ["foo"] == o.get() def test_empty_input(self): o = types.ListAttribute() o.set_from_cli("") - self.assertEqual([], o.get()) + assert [] == o.get() o.set_from_cli(" ") - self.assertEqual([], o.get()) + assert [] == o.get() def test_get_for_api_from_cli(self): o = types.ListAttribute() o.set_from_cli("foo,bar,baz") - self.assertEqual("foo,bar,baz", o.get_for_api()) + assert "foo,bar,baz" == o.get_for_api() def test_get_for_api_from_list(self): o = types.ListAttribute(["foo", "bar", "baz"]) - self.assertEqual("foo,bar,baz", o.get_for_api()) + assert "foo,bar,baz" == o.get_for_api() def test_get_for_api_does_not_split_string(self): o = types.ListAttribute("foo") - self.assertEqual("foo", o.get_for_api()) + assert "foo" == o.get_for_api() class TestLowercaseStringAttribute(unittest.TestCase): def test_get_for_api(self): o = types.LowercaseStringAttribute("FOO") - self.assertEqual("foo", o.get_for_api()) + assert "foo" == o.get_for_api() diff --git a/gitlab/tests/test_utils.py b/gitlab/tests/test_utils.py index b57dedadb..7ebd006a7 100644 --- a/gitlab/tests/test_utils.py +++ b/gitlab/tests/test_utils.py @@ -24,17 +24,17 @@ class TestUtils(unittest.TestCase): def test_clean_str_id(self): src = "nothing_special" dest = "nothing_special" - self.assertEqual(dest, utils.clean_str_id(src)) + assert dest == utils.clean_str_id(src) src = "foo#bar/baz/" dest = "foo%23bar%2Fbaz%2F" - self.assertEqual(dest, utils.clean_str_id(src)) + assert dest == utils.clean_str_id(src) def test_sanitized_url(self): src = "http://localhost/foo/bar" dest = "http://localhost/foo/bar" - self.assertEqual(dest, utils.sanitized_url(src)) + assert dest == utils.sanitized_url(src) src = "http://localhost/foo.bar.baz" dest = "http://localhost/foo%2Ebar%2Ebaz" - self.assertEqual(dest, utils.sanitized_url(src)) + assert dest == utils.sanitized_url(src) From 76b2cadf1418e4ea2ac420ebba5a4b4f16fbd4c7 Mon Sep 17 00:00:00 2001 From: Nejc Habjan Date: Fri, 17 Apr 2020 02:26:28 +0200 Subject: [PATCH 3/4] refactor: split unit tests by GitLab API resources --- gitlab/__init__.py | 10 +- gitlab/tests/conftest.py | 41 + gitlab/tests/mixins/test_meta_mixins.py | 58 + gitlab/tests/mixins/test_mixin_methods.py | 331 ++++++ .../mixins/test_object_mixins_attributes.py | 79 ++ gitlab/tests/objects/test_application.py | 226 ++-- gitlab/tests/objects/test_commits.py | 74 +- gitlab/tests/objects/test_deploy_tokens.py | 44 + gitlab/tests/objects/test_deployments.py | 53 + gitlab/tests/objects/test_environments.py | 31 + gitlab/tests/objects/test_groups.py | 121 +- gitlab/tests/objects/test_hooks.py | 23 + gitlab/tests/objects/test_issues.py | 42 + .../tests/objects/test_pipeline_schedules.py | 71 ++ .../objects/test_project_import_export.py | 129 +++ .../tests/objects/test_project_statistics.py | 29 + gitlab/tests/objects/test_projects.py | 718 +++--------- gitlab/tests/objects/test_remote_mirrors.py | 103 ++ gitlab/tests/objects/test_services.py | 134 +++ gitlab/tests/objects/test_snippets.py | 121 ++ gitlab/tests/objects/test_submodules.py | 58 + gitlab/tests/objects/test_todos.py | 58 + gitlab/tests/objects/test_users.py | 94 ++ gitlab/tests/test_base.py | 77 +- gitlab/tests/test_cli.py | 220 ++-- gitlab/tests/test_config.py | 206 ++-- gitlab/tests/test_exceptions.py | 24 +- gitlab/tests/test_gitlab.py | 1022 ++--------------- gitlab/tests/test_gitlab_auth.py | 85 ++ gitlab/tests/test_gitlab_http_methods.py | 234 ++++ gitlab/tests/test_mixins.py | 446 ------- gitlab/tests/test_types.py | 74 +- gitlab/tests/test_utils.py | 48 +- gitlab/utils.py | 8 + 34 files changed, 2645 insertions(+), 2447 deletions(-) create mode 100644 gitlab/tests/mixins/test_meta_mixins.py create mode 100644 gitlab/tests/mixins/test_mixin_methods.py create mode 100644 gitlab/tests/mixins/test_object_mixins_attributes.py create mode 100644 gitlab/tests/objects/test_deploy_tokens.py create mode 100644 gitlab/tests/objects/test_deployments.py create mode 100644 gitlab/tests/objects/test_environments.py create mode 100644 gitlab/tests/objects/test_hooks.py create mode 100644 gitlab/tests/objects/test_issues.py create mode 100644 gitlab/tests/objects/test_pipeline_schedules.py create mode 100644 gitlab/tests/objects/test_project_import_export.py create mode 100644 gitlab/tests/objects/test_project_statistics.py create mode 100644 gitlab/tests/objects/test_remote_mirrors.py create mode 100644 gitlab/tests/objects/test_services.py create mode 100644 gitlab/tests/objects/test_snippets.py create mode 100644 gitlab/tests/objects/test_submodules.py create mode 100644 gitlab/tests/objects/test_todos.py create mode 100644 gitlab/tests/objects/test_users.py create mode 100644 gitlab/tests/test_gitlab_auth.py create mode 100644 gitlab/tests/test_gitlab_http_methods.py delete mode 100644 gitlab/tests/test_mixins.py diff --git a/gitlab/__init__.py b/gitlab/__init__.py index f5db45502..1959adcee 100644 --- a/gitlab/__init__.py +++ b/gitlab/__init__.py @@ -45,14 +45,6 @@ ALLOWED_KEYSET_ENDPOINTS = ["/projects"] -def _sanitize(value): - if isinstance(value, dict): - return dict((k, _sanitize(v)) for k, v in value.items()) - if isinstance(value, str): - return value.replace("/", "%2F") - return value - - class Gitlab(object): """Represents a GitLab server connection. @@ -322,7 +314,7 @@ def set_license(self, license, **kwargs): def _construct_url(self, id_, obj, parameters, action=None): if "next_url" in parameters: return parameters["next_url"] - args = _sanitize(parameters) + args = utils.sanitize_parameters(parameters) url_attr = "_url" if action is not None: diff --git a/gitlab/tests/conftest.py b/gitlab/tests/conftest.py index 91752c671..2d4cb3a9d 100644 --- a/gitlab/tests/conftest.py +++ b/gitlab/tests/conftest.py @@ -10,3 +10,44 @@ def gl(): ssl_verify=True, api_version=4, ) + + +# Todo: parametrize, but check what tests it's really useful for +@pytest.fixture +def gl_trailing(): + return gitlab.Gitlab( + "http://localhost/", + private_token="private_token", + api_version=4 + ) + + +@pytest.fixture +def default_config(tmpdir): + valid_config = """[global] + default = one + ssl_verify = true + timeout = 2 + + [one] + url = http://one.url + private_token = ABCDEF + """ + + config_path = tmpdir.join("python-gitlab.cfg") + config_path.write(valid_config) + return str(config_path) + +@pytest.fixture +def group(gl): + return gl.groups.get(1, lazy=True) + + +@pytest.fixture +def project(gl): + return gl.projects.get(1, lazy=True) + + +@pytest.fixture +def user(gl): + return gl.users.get(1, lazy=True) diff --git a/gitlab/tests/mixins/test_meta_mixins.py b/gitlab/tests/mixins/test_meta_mixins.py new file mode 100644 index 000000000..025e9f419 --- /dev/null +++ b/gitlab/tests/mixins/test_meta_mixins.py @@ -0,0 +1,58 @@ +from gitlab.mixins import ( + CreateMixin, + CRUDMixin, + DeleteMixin, + GetMixin, + ListMixin, + NoUpdateMixin, + UpdateMixin, + RetrieveMixin, +) + + +def test_retrieve_mixin(): + class M(RetrieveMixin): + pass + + obj = M() + assert hasattr(obj, "list") + assert hasattr(obj, "get") + assert not hasattr(obj, "create") + assert not hasattr(obj, "update") + assert not hasattr(obj, "delete") + assert isinstance(obj, ListMixin) + assert isinstance(obj, GetMixin) + + +def test_crud_mixin(): + class M(CRUDMixin): + pass + + obj = M() + assert hasattr(obj, "get") + assert hasattr(obj, "list") + assert hasattr(obj, "create") + assert hasattr(obj, "update") + assert hasattr(obj, "delete") + assert isinstance(obj, ListMixin) + assert isinstance(obj, GetMixin) + assert isinstance(obj, CreateMixin) + assert isinstance(obj, UpdateMixin) + assert isinstance(obj, DeleteMixin) + + +def test_no_update_mixin(): + class M(NoUpdateMixin): + pass + + obj = M() + assert hasattr(obj, "get") + assert hasattr(obj, "list") + assert hasattr(obj, "create") + assert not hasattr(obj, "update") + assert hasattr(obj, "delete") + assert isinstance(obj, ListMixin) + assert isinstance(obj, GetMixin) + assert isinstance(obj, CreateMixin) + assert not isinstance(obj, UpdateMixin) + assert isinstance(obj, DeleteMixin) diff --git a/gitlab/tests/mixins/test_mixin_methods.py b/gitlab/tests/mixins/test_mixin_methods.py new file mode 100644 index 000000000..171e90cf1 --- /dev/null +++ b/gitlab/tests/mixins/test_mixin_methods.py @@ -0,0 +1,331 @@ +import pytest + +from httmock import HTTMock, response, urlmatch # noqa + +from gitlab import base +from gitlab.mixins import ( + CreateMixin, + DeleteMixin, + GetMixin, + GetWithoutIdMixin, + ListMixin, + RefreshMixin, + SaveMixin, + SetMixin, + UpdateMixin, +) + + +class FakeObject(base.RESTObject): + pass + + +class FakeManager(base.RESTManager): + _path = "/tests" + _obj_cls = FakeObject + + +def test_get_mixin(gl): + class M(GetMixin, FakeManager): + pass + + @urlmatch(scheme="http", netloc="localhost", path="/api/v4/tests/42", method="get") + def resp_cont(url, request): + headers = {"Content-Type": "application/json"} + content = '{"id": 42, "foo": "bar"}' + return response(200, content, headers, None, 5, request) + + with HTTMock(resp_cont): + mgr = M(gl) + obj = mgr.get(42) + assert isinstance(obj, FakeObject) + assert obj.foo == "bar" + assert obj.id == 42 + + +def test_refresh_mixin(gl): + class O(RefreshMixin, FakeObject): + pass + + @urlmatch(scheme="http", netloc="localhost", path="/api/v4/tests/42", method="get") + def resp_cont(url, request): + headers = {"Content-Type": "application/json"} + content = '{"id": 42, "foo": "bar"}' + return response(200, content, headers, None, 5, request) + + with HTTMock(resp_cont): + mgr = FakeManager(gl) + obj = O(mgr, {"id": 42}) + res = obj.refresh() + assert res is None + assert obj.foo == "bar" + assert obj.id == 42 + + +def test_get_without_id_mixin(gl): + class M(GetWithoutIdMixin, FakeManager): + pass + + @urlmatch(scheme="http", netloc="localhost", path="/api/v4/tests", method="get") + def resp_cont(url, request): + headers = {"Content-Type": "application/json"} + content = '{"foo": "bar"}' + return response(200, content, headers, None, 5, request) + + with HTTMock(resp_cont): + mgr = M(gl) + obj = mgr.get() + assert isinstance(obj, FakeObject) + assert obj.foo == "bar" + assert not hasattr(obj, "id") + + +def test_list_mixin(gl): + class M(ListMixin, FakeManager): + pass + + @urlmatch(scheme="http", netloc="localhost", path="/api/v4/tests", method="get") + def resp_cont(url, request): + headers = {"Content-Type": "application/json"} + content = '[{"id": 42, "foo": "bar"},{"id": 43, "foo": "baz"}]' + return response(200, content, headers, None, 5, request) + + with HTTMock(resp_cont): + # test RESTObjectList + mgr = M(gl) + obj_list = mgr.list(as_list=False) + assert isinstance(obj_list, base.RESTObjectList) + for obj in obj_list: + assert isinstance(obj, FakeObject) + assert obj.id in (42, 43) + + # test list() + obj_list = mgr.list(all=True) + assert isinstance(obj_list, list) + assert obj_list[0].id == 42 + assert obj_list[1].id == 43 + assert isinstance(obj_list[0], FakeObject) + assert len(obj_list) == 2 + + +def test_list_other_url(gl): + class M(ListMixin, FakeManager): + pass + + @urlmatch(scheme="http", netloc="localhost", path="/api/v4/others", method="get") + def resp_cont(url, request): + headers = {"Content-Type": "application/json"} + content = '[{"id": 42, "foo": "bar"}]' + return response(200, content, headers, None, 5, request) + + with HTTMock(resp_cont): + mgr = M(gl) + obj_list = mgr.list(path="/others", as_list=False) + assert isinstance(obj_list, base.RESTObjectList) + obj = obj_list.next() + assert obj.id == 42 + assert obj.foo == "bar" + with pytest.raises(StopIteration): + obj_list.next() + + +def test_create_mixin_get_attrs(gl): + class M1(CreateMixin, FakeManager): + pass + + class M2(CreateMixin, FakeManager): + _create_attrs = (("foo",), ("bar", "baz")) + _update_attrs = (("foo",), ("bam",)) + + mgr = M1(gl) + required, optional = mgr.get_create_attrs() + assert len(required) == 0 + assert len(optional) == 0 + + mgr = M2(gl) + required, optional = mgr.get_create_attrs() + assert "foo" in required + assert "bar" in optional + assert "baz" in optional + assert "bam" not in optional + + +def test_create_mixin_missing_attrs(gl): + class M(CreateMixin, FakeManager): + _create_attrs = (("foo",), ("bar", "baz")) + + mgr = M(gl) + data = {"foo": "bar", "baz": "blah"} + mgr._check_missing_create_attrs(data) + + data = {"baz": "blah"} + with pytest.raises(AttributeError) as error: + mgr._check_missing_create_attrs(data) + assert "foo" in str(error.value) + + +def test_create_mixin(gl): + class M(CreateMixin, FakeManager): + _create_attrs = (("foo",), ("bar", "baz")) + _update_attrs = (("foo",), ("bam",)) + + @urlmatch(scheme="http", netloc="localhost", path="/api/v4/tests", method="post") + def resp_cont(url, request): + headers = {"Content-Type": "application/json"} + content = '{"id": 42, "foo": "bar"}' + return response(200, content, headers, None, 5, request) + + with HTTMock(resp_cont): + mgr = M(gl) + obj = mgr.create({"foo": "bar"}) + assert isinstance(obj, FakeObject) + assert obj.id == 42 + assert obj.foo == "bar" + + +def test_create_mixin_custom_path(gl): + class M(CreateMixin, FakeManager): + _create_attrs = (("foo",), ("bar", "baz")) + _update_attrs = (("foo",), ("bam",)) + + @urlmatch(scheme="http", netloc="localhost", path="/api/v4/others", method="post") + def resp_cont(url, request): + headers = {"Content-Type": "application/json"} + content = '{"id": 42, "foo": "bar"}' + return response(200, content, headers, None, 5, request) + + with HTTMock(resp_cont): + mgr = M(gl) + obj = mgr.create({"foo": "bar"}, path="/others") + assert isinstance(obj, FakeObject) + assert obj.id == 42 + assert obj.foo == "bar" + + +def test_update_mixin_get_attrs(gl): + class M1(UpdateMixin, FakeManager): + pass + + class M2(UpdateMixin, FakeManager): + _create_attrs = (("foo",), ("bar", "baz")) + _update_attrs = (("foo",), ("bam",)) + + mgr = M1(gl) + required, optional = mgr.get_update_attrs() + assert len(required) == 0 + assert len(optional) == 0 + + mgr = M2(gl) + required, optional = mgr.get_update_attrs() + assert "foo" in required + assert "bam" in optional + assert "bar" not in optional + assert "baz" not in optional + + +def test_update_mixin_missing_attrs(gl): + class M(UpdateMixin, FakeManager): + _update_attrs = (("foo",), ("bar", "baz")) + + mgr = M(gl) + data = {"foo": "bar", "baz": "blah"} + mgr._check_missing_update_attrs(data) + + data = {"baz": "blah"} + with pytest.raises(AttributeError) as error: + mgr._check_missing_update_attrs(data) + assert "foo" in str(error.value) + + +def test_update_mixin(gl): + class M(UpdateMixin, FakeManager): + _create_attrs = (("foo",), ("bar", "baz")) + _update_attrs = (("foo",), ("bam",)) + + @urlmatch(scheme="http", netloc="localhost", path="/api/v4/tests/42", method="put") + def resp_cont(url, request): + headers = {"Content-Type": "application/json"} + content = '{"id": 42, "foo": "baz"}' + return response(200, content, headers, None, 5, request) + + with HTTMock(resp_cont): + mgr = M(gl) + server_data = mgr.update(42, {"foo": "baz"}) + assert isinstance(server_data, dict) + assert server_data["id"] == 42 + assert server_data["foo"] == "baz" + + +def test_update_mixin_no_id(gl): + class M(UpdateMixin, FakeManager): + _create_attrs = (("foo",), ("bar", "baz")) + _update_attrs = (("foo",), ("bam",)) + + @urlmatch(scheme="http", netloc="localhost", path="/api/v4/tests", method="put") + def resp_cont(url, request): + headers = {"Content-Type": "application/json"} + content = '{"foo": "baz"}' + return response(200, content, headers, None, 5, request) + + with HTTMock(resp_cont): + mgr = M(gl) + server_data = mgr.update(new_data={"foo": "baz"}) + assert isinstance(server_data, dict) + assert server_data["foo"] == "baz" + + +def test_delete_mixin(gl): + class M(DeleteMixin, FakeManager): + pass + + @urlmatch( + scheme="http", netloc="localhost", path="/api/v4/tests/42", method="delete" + ) + def resp_cont(url, request): + headers = {"Content-Type": "application/json"} + content = "" + return response(200, content, headers, None, 5, request) + + with HTTMock(resp_cont): + mgr = M(gl) + mgr.delete(42) + + +def test_save_mixin(gl): + class M(UpdateMixin, FakeManager): + pass + + class O(SaveMixin, base.RESTObject): + pass + + @urlmatch(scheme="http", netloc="localhost", path="/api/v4/tests/42", method="put") + def resp_cont(url, request): + headers = {"Content-Type": "application/json"} + content = '{"id": 42, "foo": "baz"}' + return response(200, content, headers, None, 5, request) + + with HTTMock(resp_cont): + mgr = M(gl) + obj = O(mgr, {"id": 42, "foo": "bar"}) + obj.foo = "baz" + obj.save() + assert obj._attrs["foo"] == "baz" + assert obj._updated_attrs == {} + + +def test_set_mixin(gl): + class M(SetMixin, FakeManager): + pass + + @urlmatch(scheme="http", netloc="localhost", path="/api/v4/tests/foo", method="put") + def resp_cont(url, request): + headers = {"Content-Type": "application/json"} + content = '{"key": "foo", "value": "bar"}' + return response(200, content, headers, None, 5, request) + + with HTTMock(resp_cont): + mgr = M(gl) + obj = mgr.set("foo", "bar") + assert isinstance(obj, FakeObject) + assert obj.key == "foo" + assert obj.value == "bar" diff --git a/gitlab/tests/mixins/test_object_mixins_attributes.py b/gitlab/tests/mixins/test_object_mixins_attributes.py new file mode 100644 index 000000000..3502a93f9 --- /dev/null +++ b/gitlab/tests/mixins/test_object_mixins_attributes.py @@ -0,0 +1,79 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2014 Mika Mäenpää , +# Tampere University of Technology +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see . + +from gitlab.mixins import ( + AccessRequestMixin, + SetMixin, + SubscribableMixin, + TimeTrackingMixin, + TodoMixin, + UserAgentDetailMixin, +) + + +def test_access_request_mixin(): + class O(AccessRequestMixin): + pass + + obj = O() + assert hasattr(obj, "approve") + + +def test_subscribable_mixin(): + class O(SubscribableMixin): + pass + + obj = O() + assert hasattr(obj, "subscribe") + assert hasattr(obj, "unsubscribe") + + +def test_todo_mixin(): + class O(TodoMixin): + pass + + obj = O() + assert hasattr(obj, "todo") + + +def test_time_tracking_mixin(): + class O(TimeTrackingMixin): + pass + + obj = O() + assert hasattr(obj, "time_stats") + assert hasattr(obj, "time_estimate") + assert hasattr(obj, "reset_time_estimate") + assert hasattr(obj, "add_spent_time") + assert hasattr(obj, "reset_spent_time") + + +def test_set_mixin(): + class O(SetMixin): + pass + + obj = O() + assert hasattr(obj, "set") + + +def test_user_agent_detail_mixin(): + class O(UserAgentDetailMixin): + pass + + obj = O() + assert hasattr(obj, "user_agent_detail") diff --git a/gitlab/tests/objects/test_application.py b/gitlab/tests/objects/test_application.py index a10691bbb..356f0d365 100644 --- a/gitlab/tests/objects/test_application.py +++ b/gitlab/tests/objects/test_application.py @@ -1,120 +1,108 @@ -import unittest -import gitlab -import os -import pickle -import tempfile +""" +GitLab API: https://docs.gitlab.com/ce/api/applications.html +""" + import json -import unittest -import requests -from gitlab import * # noqa -from gitlab.v4.objects import * # noqa -from httmock import HTTMock, urlmatch, response # noqa - - -headers = {"content-type": "application/json"} - - -class TestApplicationAppearance(unittest.TestCase): - def setUp(self): - self.gl = Gitlab( - "http://localhost", - private_token="private_token", - ssl_verify=True, - api_version="4", - ) - self.title = "GitLab Test Instance" - self.new_title = "new-title" - self.description = "gitlab-test.example.com" - self.new_description = "new-description" - - def test_get_update_appearance(self): - @urlmatch( - scheme="http", - netloc="localhost", - path="/api/v4/application/appearance", - method="get", - ) - def resp_get_appearance(url, request): - content = """{ - "title": "%s", - "description": "%s", - "logo": "/uploads/-/system/appearance/logo/1/logo.png", - "header_logo": "/uploads/-/system/appearance/header_logo/1/header.png", - "favicon": "/uploads/-/system/appearance/favicon/1/favicon.png", - "new_project_guidelines": "Please read the FAQs for help.", - "header_message": "", - "footer_message": "", - "message_background_color": "#e75e40", - "message_font_color": "#ffffff", - "email_header_and_footer_enabled": false}""" % ( - self.title, - self.description, - ) - content = content.encode("utf-8") - return response(200, content, headers, None, 25, request) - - @urlmatch( - scheme="http", - netloc="localhost", - path="/api/v4/application/appearance", - method="put", - ) - def resp_update_appearance(url, request): - content = """{ - "title": "%s", - "description": "%s", - "logo": "/uploads/-/system/appearance/logo/1/logo.png", - "header_logo": "/uploads/-/system/appearance/header_logo/1/header.png", - "favicon": "/uploads/-/system/appearance/favicon/1/favicon.png", - "new_project_guidelines": "Please read the FAQs for help.", - "header_message": "", - "footer_message": "", - "message_background_color": "#e75e40", - "message_font_color": "#ffffff", - "email_header_and_footer_enabled": false}""" % ( - self.new_title, - self.new_description, - ) - content = content.encode("utf-8") - return response(200, content, headers, None, 25, request) - - with HTTMock(resp_get_appearance), HTTMock(resp_update_appearance): - appearance = self.gl.appearance.get() - assert appearance.title == self.title - assert appearance.description == self.description - appearance.title = self.new_title - appearance.description = self.new_description - appearance.save() - assert appearance.title == self.new_title - assert appearance.description == self.new_description - - def test_update_appearance(self): - @urlmatch( - scheme="http", - netloc="localhost", - path="/api/v4/application/appearance", - method="put", - ) - def resp_update_appearance(url, request): - content = """{ - "title": "%s", - "description": "%s", - "logo": "/uploads/-/system/appearance/logo/1/logo.png", - "header_logo": "/uploads/-/system/appearance/header_logo/1/header.png", - "favicon": "/uploads/-/system/appearance/favicon/1/favicon.png", - "new_project_guidelines": "Please read the FAQs for help.", - "header_message": "", - "footer_message": "", - "message_background_color": "#e75e40", - "message_font_color": "#ffffff", - "email_header_and_footer_enabled": false}""" % ( - self.new_title, - self.new_description, - ) - content = content.encode("utf-8") - return response(200, content, headers, None, 25, request) - - with HTTMock(resp_update_appearance): - resp = self.gl.appearance.update( - title=self.new_title, description=self.new_description - ) + +from httmock import urlmatch, response, with_httmock # noqa + +from .mocks import headers + + +title = "GitLab Test Instance" +description = "gitlab-test.example.com" +new_title = "new-title" +new_description = "new-description" + + +@urlmatch( + scheme="http", netloc="localhost", path="/api/v4/applications", method="post", +) +def resp_application_create(url, request): + content = '{"name": "test_app", "redirect_uri": "http://localhost:8080", "scopes": ["api", "email"]}' + json_content = json.loads(content) + return response(200, json_content, headers, None, 5, request) + + +@urlmatch( + scheme="http", + netloc="localhost", + path="/api/v4/application/appearance", + method="get", +) +def resp_get_appearance(url, request): + content = """{ + "title": "%s", + "description": "%s", + "logo": "/uploads/-/system/appearance/logo/1/logo.png", + "header_logo": "/uploads/-/system/appearance/header_logo/1/header.png", + "favicon": "/uploads/-/system/appearance/favicon/1/favicon.png", + "new_project_guidelines": "Please read the FAQs for help.", + "header_message": "", + "footer_message": "", + "message_background_color": "#e75e40", + "message_font_color": "#ffffff", + "email_header_and_footer_enabled": false}""" % ( + title, + description, + ) + content = content.encode("utf-8") + return response(200, content, headers, None, 25, request) + + +@urlmatch( + scheme="http", + netloc="localhost", + path="/api/v4/application/appearance", + method="put", +) +def resp_update_appearance(url, request): + content = """{ + "title": "%s", + "description": "%s", + "logo": "/uploads/-/system/appearance/logo/1/logo.png", + "header_logo": "/uploads/-/system/appearance/header_logo/1/header.png", + "favicon": "/uploads/-/system/appearance/favicon/1/favicon.png", + "new_project_guidelines": "Please read the FAQs for help.", + "header_message": "", + "footer_message": "", + "message_background_color": "#e75e40", + "message_font_color": "#ffffff", + "email_header_and_footer_enabled": false}""" % ( + new_title, + new_description, + ) + content = content.encode("utf-8") + return response(200, content, headers, None, 25, request) + + +@with_httmock(resp_application_create) +def test_create_application(gl): + application = gl.applications.create( + { + "name": "test_app", + "redirect_uri": "http://localhost:8080", + "scopes": ["api", "email"], + "confidential": False, + } + ) + assert application.name == "test_app" + assert application.redirect_uri == "http://localhost:8080" + assert application.scopes == ["api", "email"] + + +@with_httmock(resp_get_appearance, resp_update_appearance) +def test_get_update_appearance(gl): + appearance = gl.appearance.get() + assert appearance.title == title + assert appearance.description == description + appearance.title = new_title + appearance.description = new_description + appearance.save() + assert appearance.title == new_title + assert appearance.description == new_description + + +@with_httmock(resp_update_appearance) +def test_update_application_appearance(gl): + resp = gl.appearance.update(title=new_title, description=new_description) diff --git a/gitlab/tests/objects/test_commits.py b/gitlab/tests/objects/test_commits.py index bf7d5a8ad..eaa7b82a9 100644 --- a/gitlab/tests/objects/test_commits.py +++ b/gitlab/tests/objects/test_commits.py @@ -1,7 +1,10 @@ +""" +GitLab API: https://docs.gitlab.com/ce/api/commits.html +""" + from httmock import urlmatch, response, with_httmock from .mocks import headers -from .test_projects import TestProject @urlmatch( @@ -69,39 +72,36 @@ def resp_get_commit_gpg_signature(url, request): return response(200, content, headers, None, 5, request) -class TestCommit(TestProject): - """ - Base class for commit tests. Inherits from TestProject, - since currently all commit methods are under projects. - """ - - @with_httmock(resp_get_commit) - def test_get_commit(self): - commit = self.project.commits.get("6b2257ea") - assert commit.short_id == "6b2257ea" - assert commit.title == "Initial commit" - - @with_httmock(resp_create_commit) - def test_create_commit(self): - data = { - "branch": "master", - "commit_message": "Commit message", - "actions": [{"action": "create", "file_path": "README", "content": "",}], - } - commit = self.project.commits.create(data) - assert commit.short_id == "ed899a2f" - assert commit.title == data["commit_message"] - - @with_httmock(resp_revert_commit) - def test_revert_commit(self): - commit = self.project.commits.get("6b2257ea", lazy=True) - revert_commit = commit.revert(branch="master") - assert revert_commit["short_id"] == "8b090c1b" - assert revert_commit["title"] == 'Revert "Initial commit"' - - @with_httmock(resp_get_commit_gpg_signature) - def test_get_commit_gpg_signature(self): - commit = self.project.commits.get("6b2257ea", lazy=True) - signature = commit.signature() - assert signature["gpg_key_primary_keyid"] == "8254AAB3FBD54AC9" - assert signature["verification_status"] == "verified" +@with_httmock(resp_get_commit) +def test_get_commit(project): + commit = project.commits.get("6b2257ea") + assert commit.short_id == "6b2257ea" + assert commit.title == "Initial commit" + + +@with_httmock(resp_create_commit) +def test_create_commit(project): + data = { + "branch": "master", + "commit_message": "Commit message", + "actions": [{"action": "create", "file_path": "README", "content": "",}], + } + commit = project.commits.create(data) + assert commit.short_id == "ed899a2f" + assert commit.title == data["commit_message"] + + +@with_httmock(resp_revert_commit) +def test_revert_commit(project): + commit = project.commits.get("6b2257ea", lazy=True) + revert_commit = commit.revert(branch="master") + assert revert_commit["short_id"] == "8b090c1b" + assert revert_commit["title"] == 'Revert "Initial commit"' + + +@with_httmock(resp_get_commit_gpg_signature) +def test_get_commit_gpg_signature(project): + commit = project.commits.get("6b2257ea", lazy=True) + signature = commit.signature() + assert signature["gpg_key_primary_keyid"] == "8254AAB3FBD54AC9" + assert signature["verification_status"] == "verified" diff --git a/gitlab/tests/objects/test_deploy_tokens.py b/gitlab/tests/objects/test_deploy_tokens.py new file mode 100644 index 000000000..b98a67076 --- /dev/null +++ b/gitlab/tests/objects/test_deploy_tokens.py @@ -0,0 +1,44 @@ +""" +GitLab API: https://docs.gitlab.com/ce/api/deploy_tokens.html +""" + +from httmock import response, urlmatch, with_httmock + +from gitlab.v4.objects import ProjectDeployToken + +from .mocks import headers + + +@urlmatch( + scheme="http", + netloc="localhost", + path="/api/v4/projects/1/deploy_tokens", + method="post", +) +def resp_deploy_token_create(url, request): + content = """{ + "id": 1, + "name": "test_deploy_token", + "username": "custom-user", + "expires_at": "2022-01-01T00:00:00.000Z", + "token": "jMRvtPNxrn3crTAGukpZ", + "scopes": [ "read_repository" ]}""" + content = content.encode("utf-8") + return response(200, content, headers, None, 5, request) + + +@with_httmock(resp_deploy_token_create) +def test_deploy_tokens(gl): + deploy_token = gl.projects.get(1, lazy=True).deploytokens.create( + { + "name": "test_deploy_token", + "expires_at": "2022-01-01T00:00:00.000Z", + "username": "custom-user", + "scopes": ["read_repository"], + } + ) + assert isinstance(deploy_token, ProjectDeployToken) + assert deploy_token.id == 1 + assert deploy_token.expires_at == "2022-01-01T00:00:00.000Z" + assert deploy_token.username == "custom-user" + assert deploy_token.scopes == ["read_repository"] diff --git a/gitlab/tests/objects/test_deployments.py b/gitlab/tests/objects/test_deployments.py new file mode 100644 index 000000000..098251a80 --- /dev/null +++ b/gitlab/tests/objects/test_deployments.py @@ -0,0 +1,53 @@ +""" +GitLab API: https://docs.gitlab.com/ce/api/deployments.html +""" + +import json + +from httmock import response, urlmatch, with_httmock + +from .mocks import headers + +content = '{"id": 42, "status": "success", "ref": "master"}' +json_content = json.loads(content) + + +@urlmatch( + scheme="http", + netloc="localhost", + path="/api/v4/projects/1/deployments", + method="post", +) +def resp_deployment_create(url, request): + return response(200, json_content, headers, None, 5, request) + + +@urlmatch( + scheme="http", + netloc="localhost", + path="/api/v4/projects/1/deployments/42", + method="put", +) +def resp_deployment_update(url, request): + return response(200, json_content, headers, None, 5, request) + + +@with_httmock(resp_deployment_create, resp_deployment_update) +def test_deployment(project): + deployment = project.deployments.create( + { + "environment": "Test", + "sha": "1agf4gs", + "ref": "master", + "tag": False, + "status": "created", + } + ) + assert deployment.id == 42 + assert deployment.status == "success" + assert deployment.ref == "master" + + json_content["status"] = "failed" + deployment.status = "failed" + deployment.save() + assert deployment.status == "failed" diff --git a/gitlab/tests/objects/test_environments.py b/gitlab/tests/objects/test_environments.py new file mode 100644 index 000000000..3175c64d2 --- /dev/null +++ b/gitlab/tests/objects/test_environments.py @@ -0,0 +1,31 @@ +""" +GitLab API: https://docs.gitlab.com/ce/api/environments.html +""" + +from httmock import response, urlmatch, with_httmock + +from gitlab.v4.objects import ProjectEnvironment + +from .mocks import headers + + +@urlmatch( + scheme="http", + netloc="localhost", + path="/api/v4/projects/1/environments/1", + method="get", +) +def resp_get_environment(url, request): + content = '{"name": "environment_name", "id": 1, "last_deployment": "sometime"}'.encode( + "utf-8" + ) + return response(200, content, headers, None, 5, request) + + +@with_httmock(resp_get_environment) +def test_project_environments(project): + environment = project.environments.get(1) + assert isinstance(environment, ProjectEnvironment) + assert environment.id == 1 + assert environment.last_deployment == "sometime" + assert environment.name == "environment_name" diff --git a/gitlab/tests/objects/test_groups.py b/gitlab/tests/objects/test_groups.py index 12ebdb297..b5464b591 100644 --- a/gitlab/tests/objects/test_groups.py +++ b/gitlab/tests/objects/test_groups.py @@ -1,4 +1,8 @@ -import unittest +""" +GitLab API: https://docs.gitlab.com/ce/api/groups.html +""" + +import pytest from httmock import response, urlmatch, with_httmock @@ -36,66 +40,55 @@ def resp_create_import(url, request): return response(202, content, headers, None, 25, request) -class TestGroup(unittest.TestCase): - def setUp(self): - self.gl = gitlab.Gitlab( - "http://localhost", - private_token="private_token", - ssl_verify=True, - api_version=4, - ) - - @with_httmock(resp_get_group) - def test_get_group(self): - data = self.gl.groups.get(1) - assert isinstance(data, gitlab.v4.objects.Group) - assert data.name == "name" - assert data.path == "path" - assert data.id == 1 - - @with_httmock(resp_create_group) - def test_create_group(self): - name, path = "name", "path" - data = self.gl.groups.create({"name": name, "path": path}) - assert isinstance(data, gitlab.v4.objects.Group) - assert data.name == name - assert data.path == path - - -class TestGroupExport(TestGroup): - def setUp(self): - super(TestGroupExport, self).setUp() - self.group = self.gl.groups.get(1, lazy=True) - - @with_httmock(resp_create_export) - def test_create_group_export(self): - export = self.group.exports.create() - assert export.message == "202 Accepted" - - @unittest.skip("GitLab API endpoint not implemented") - @with_httmock(resp_create_export) - def test_refresh_group_export_status(self): - export = self.group.exports.create() - export.refresh() - assert export.export_status == "finished" - - @with_httmock(resp_create_export, resp_download_export) - def test_download_group_export(self): - export = self.group.exports.create() - download = export.download() - assert isinstance(download, bytes) - assert download == binary_content - - -class TestGroupImport(TestGroup): - @with_httmock(resp_create_import) - def test_import_group(self): - group_import = self.gl.groups.import_group("file", "api-group", "API Group") - assert group_import["message"] == "202 Accepted" - - @unittest.skip("GitLab API endpoint not implemented") - @with_httmock(resp_create_import) - def test_refresh_group_import_status(self): - group_import = self.group.imports.get() - group_import.refresh() - assert group_import.import_status == "finished" +@with_httmock(resp_get_group) +def test_get_group(gl): + data = gl.groups.get(1) + assert isinstance(data, gitlab.v4.objects.Group) + assert data.name == "name" + assert data.path == "path" + assert data.id == 1 + + +@with_httmock(resp_create_group) +def test_create_group(gl): + name, path = "name", "path" + data = gl.groups.create({"name": name, "path": path}) + assert isinstance(data, gitlab.v4.objects.Group) + assert data.name == name + assert data.path == path + + +@with_httmock(resp_create_export) +def test_create_group_export(group): + export = group.exports.create() + assert export.message == "202 Accepted" + + +@pytest.mark.skip("GitLab API endpoint not implemented") +@with_httmock(resp_create_export) +def test_refresh_group_export_status(group): + export = group.exports.create() + export.refresh() + assert export.export_status == "finished" + + +@with_httmock(resp_create_export, resp_download_export) +def test_download_group_export(group): + export = group.exports.create() + download = export.download() + assert isinstance(download, bytes) + assert download == binary_content + + +@with_httmock(resp_create_import) +def test_import_group(gl): + group_import = gl.groups.import_group("file", "api-group", "API Group") + assert group_import["message"] == "202 Accepted" + + +@pytest.mark.skip("GitLab API endpoint not implemented") +@with_httmock(resp_create_import) +def test_refresh_group_import_status(group): + group_import = group.imports.get() + group_import.refresh() + assert group_import.import_status == "finished" diff --git a/gitlab/tests/objects/test_hooks.py b/gitlab/tests/objects/test_hooks.py new file mode 100644 index 000000000..45403c497 --- /dev/null +++ b/gitlab/tests/objects/test_hooks.py @@ -0,0 +1,23 @@ +""" +GitLab API: https://docs.gitlab.com/ce/api/system_hooks.html +""" + +from httmock import response, urlmatch, with_httmock + +from gitlab.v4.objects import Hook + +from .mocks import headers + + +@urlmatch(scheme="http", netloc="localhost", path="/api/v4/hooks/1", method="get") +def resp_get_hook(url, request): + content = '{"url": "testurl", "id": 1}'.encode("utf-8") + return response(200, content, headers, None, 5, request) + + +@with_httmock(resp_get_hook) +def test_hooks(gl): + data = gl.hooks.get(1) + assert isinstance(data, Hook) + assert data.url == "testurl" + assert data.id == 1 diff --git a/gitlab/tests/objects/test_issues.py b/gitlab/tests/objects/test_issues.py new file mode 100644 index 000000000..e09484104 --- /dev/null +++ b/gitlab/tests/objects/test_issues.py @@ -0,0 +1,42 @@ +""" +GitLab API: https://docs.gitlab.com/ce/api/issues.html +""" + +from httmock import urlmatch, response, with_httmock + +from .mocks import headers +from gitlab.v4.objects import ProjectIssuesStatistics + + +@urlmatch(scheme="http", netloc="localhost", path="/api/v4/issues", method="get") +def resp_get_issue(url, request): + content = '[{"name": "name", "id": 1}, ' '{"name": "other_name", "id": 2}]' + content = content.encode("utf-8") + return response(200, content, headers, None, 5, request) + + +@urlmatch( + scheme="http", + netloc="localhost", + path="/api/v4/projects/1/issues_statistics", + method="get", +) +def resp_get_environment(url, request): + content = """{"statistics": {"counts": {"all": 20, "closed": 5, "opened": 15}}}""".encode( + "utf-8" + ) + return response(200, content, headers, None, 5, request) + + +@with_httmock(resp_get_issue) +def test_issues(gl): + data = gl.issues.list() + assert data[1].id == 2 + assert data[1].name == "other_name" + + +@with_httmock(resp_get_environment) +def test_project_issues_statistics(project): + statistics = project.issuesstatistics.get() + assert isinstance(statistics, ProjectIssuesStatistics) + assert statistics.statistics["counts"]["all"] == 20 diff --git a/gitlab/tests/objects/test_pipeline_schedules.py b/gitlab/tests/objects/test_pipeline_schedules.py new file mode 100644 index 000000000..6b5630415 --- /dev/null +++ b/gitlab/tests/objects/test_pipeline_schedules.py @@ -0,0 +1,71 @@ +""" +GitLab API: https://docs.gitlab.com/ce/api/pipeline_schedules.html +""" + +from httmock import response, urlmatch, with_httmock + +from .mocks import headers + + +@urlmatch( + scheme="http", + netloc="localhost", + path="/api/v4/projects/1/pipeline_schedules$", + method="post", +) +def resp_create_project_pipeline_schedule(url, request): + """Mock for creating project pipeline Schedules POST response.""" + content = """{ + "id": 14, + "description": "Build packages", + "ref": "master", + "cron": "0 1 * * 5", + "cron_timezone": "UTC", + "next_run_at": "2017-05-26T01:00:00.000Z", + "active": true, + "created_at": "2017-05-19T13:43:08.169Z", + "updated_at": "2017-05-19T13:43:08.169Z", + "last_pipeline": null, + "owner": { + "name": "Administrator", + "username": "root", + "id": 1, + "state": "active", + "avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon", + "web_url": "https://gitlab.example.com/root" + } +}""" + content = content.encode("utf-8") + return response(200, content, headers, None, 5, request) + + +@urlmatch( + scheme="http", + netloc="localhost", + path="/api/v4/projects/1/pipeline_schedules/14/play", + method="post", +) +def resp_play_project_pipeline_schedule(url, request): + """Mock for playing a project pipeline schedule POST response.""" + content = """{"message": "201 Created"}""" + content = content.encode("utf-8") + return response(200, content, headers, None, 5, request) + + +@with_httmock( + resp_create_project_pipeline_schedule, resp_play_project_pipeline_schedule +) +def test_project_pipeline_schedule_play(project): + description = "Build packages" + cronline = "0 1 * * 5" + sched = project.pipelineschedules.create( + {"ref": "master", "description": description, "cron": cronline} + ) + assert sched is not None + assert description == sched.description + assert cronline == sched.cron + + play_result = sched.play() + assert play_result is not None + assert "message" in play_result + assert play_result["message"] == "201 Created" diff --git a/gitlab/tests/objects/test_project_import_export.py b/gitlab/tests/objects/test_project_import_export.py new file mode 100644 index 000000000..e5c37a84c --- /dev/null +++ b/gitlab/tests/objects/test_project_import_export.py @@ -0,0 +1,129 @@ +""" +GitLab API: https://docs.gitlab.com/ce/api/project_import_export.html +""" + +from httmock import response, urlmatch, with_httmock + +from .mocks import * + + +@urlmatch( + scheme="http", netloc="localhost", path="/api/v4/projects/1/export", method="get", +) +def resp_export_status(url, request): + """Mock for Project Export GET response.""" + content = """{ + "id": 1, + "description": "Itaque perspiciatis minima aspernatur", + "name": "Gitlab Test", + "name_with_namespace": "Gitlab Org / Gitlab Test", + "path": "gitlab-test", + "path_with_namespace": "gitlab-org/gitlab-test", + "created_at": "2017-08-29T04:36:44.383Z", + "export_status": "finished", + "_links": { + "api_url": "https://gitlab.test/api/v4/projects/1/export/download", + "web_url": "https://gitlab.test/gitlab-test/download_export" + } + } + """ + content = content.encode("utf-8") + return response(200, content, headers, None, 25, request) + + +@urlmatch( + scheme="http", netloc="localhost", path="/api/v4/projects/import", method="post", +) +def resp_import_project(url, request): + """Mock for Project Import POST response.""" + content = """{ + "id": 1, + "description": null, + "name": "api-project", + "name_with_namespace": "Administrator / api-project", + "path": "api-project", + "path_with_namespace": "root/api-project", + "created_at": "2018-02-13T09:05:58.023Z", + "import_status": "scheduled" + }""" + content = content.encode("utf-8") + return response(200, content, headers, None, 25, request) + + +@urlmatch( + scheme="http", netloc="localhost", path="/api/v4/projects/1/import", method="get", +) +def resp_import_status(url, request): + """Mock for Project Import GET response.""" + content = """{ + "id": 1, + "description": "Itaque perspiciatis minima aspernatur corporis consequatur.", + "name": "Gitlab Test", + "name_with_namespace": "Gitlab Org / Gitlab Test", + "path": "gitlab-test", + "path_with_namespace": "gitlab-org/gitlab-test", + "created_at": "2017-08-29T04:36:44.383Z", + "import_status": "finished" + }""" + content = content.encode("utf-8") + return response(200, content, headers, None, 25, request) + + +@urlmatch( + scheme="http", netloc="localhost", path="/api/v4/import/github", method="post", +) +def resp_import_github(url, request): + """Mock for GitHub Project Import POST response.""" + content = """{ + "id": 27, + "name": "my-repo", + "full_path": "/root/my-repo", + "full_name": "Administrator / my-repo" + }""" + content = content.encode("utf-8") + return response(200, content, headers, None, 25, request) + + +@with_httmock(resp_import_project) +def test_import_project(gl): + project_import = gl.projects.import_project("file", "api-project") + assert project_import["import_status"] == "scheduled" + + +@with_httmock(resp_import_status) +def test_refresh_project_import_status(project): + project_import = project.imports.get() + project_import.refresh() + assert project_import.import_status == "finished" + + +@with_httmock(resp_import_github) +def test_import_github(gl): + base_path = "/root" + name = "my-repo" + ret = gl.projects.import_github("githubkey", 1234, base_path, name) + assert isinstance(ret, dict) + assert ret["name"] == name + assert ret["full_path"] == "/".join((base_path, name)) + assert ret["full_name"].endswith(name) + + +@with_httmock(resp_create_export) +def test_create_project_export(project): + export = project.exports.create() + assert export.message == "202 Accepted" + + +@with_httmock(resp_create_export, resp_export_status) +def test_refresh_project_export_status(project): + export = project.exports.create() + export.refresh() + assert export.export_status == "finished" + + +@with_httmock(resp_create_export, resp_download_export) +def test_download_project_export(project): + export = project.exports.create() + download = export.download() + assert isinstance(download, bytes) + assert download == binary_content diff --git a/gitlab/tests/objects/test_project_statistics.py b/gitlab/tests/objects/test_project_statistics.py new file mode 100644 index 000000000..c2b194fee --- /dev/null +++ b/gitlab/tests/objects/test_project_statistics.py @@ -0,0 +1,29 @@ +""" +GitLab API: https://docs.gitlab.com/ce/api/project_statistics.html +""" + +from httmock import response, urlmatch, with_httmock + +from gitlab.v4.objects import ProjectAdditionalStatistics + +from .mocks import headers + + +@urlmatch( + scheme="http", + netloc="localhost", + path="/api/v4/projects/1/statistics", + method="get", +) +def resp_get_statistics(url, request): + content = """{"fetches": {"total": 50, "days": [{"count": 10, "date": "2018-01-10"}]}}""".encode( + "utf-8" + ) + return response(200, content, headers, None, 5, request) + + +@with_httmock(resp_get_statistics) +def test_project_additional_statistics(project): + statistics = project.additionalstatistics.get() + assert isinstance(statistics, ProjectAdditionalStatistics) + assert statistics.fetches["total"] == 50 diff --git a/gitlab/tests/objects/test_projects.py b/gitlab/tests/objects/test_projects.py index fa105aea3..7fefe3f6f 100644 --- a/gitlab/tests/objects/test_projects.py +++ b/gitlab/tests/objects/test_projects.py @@ -1,547 +1,205 @@ -import unittest -import gitlab -import os -import pickle -import tempfile -import json -import unittest -import requests -from gitlab import * # noqa -from gitlab.v4.objects import * # noqa -from httmock import HTTMock, urlmatch, response, with_httmock # noqa - -from .mocks import * # noqa - - -@urlmatch( - scheme="http", netloc="localhost", path="/api/v4/projects/1/export", method="get", -) -def resp_export_status(url, request): - """Mock for Project Export GET response.""" - content = """{ - "id": 1, - "description": "Itaque perspiciatis minima aspernatur", - "name": "Gitlab Test", - "name_with_namespace": "Gitlab Org / Gitlab Test", - "path": "gitlab-test", - "path_with_namespace": "gitlab-org/gitlab-test", - "created_at": "2017-08-29T04:36:44.383Z", - "export_status": "finished", - "_links": { - "api_url": "https://gitlab.test/api/v4/projects/1/export/download", - "web_url": "https://gitlab.test/gitlab-test/download_export" - } - } - """ - content = content.encode("utf-8") - return response(200, content, headers, None, 25, request) - - -@urlmatch( - scheme="http", netloc="localhost", path="/api/v4/projects/import", method="post", -) -def resp_import_project(url, request): - """Mock for Project Import POST response.""" - content = """{ - "id": 1, - "description": null, - "name": "api-project", - "name_with_namespace": "Administrator / api-project", - "path": "api-project", - "path_with_namespace": "root/api-project", - "created_at": "2018-02-13T09:05:58.023Z", - "import_status": "scheduled" - }""" - content = content.encode("utf-8") - return response(200, content, headers, None, 25, request) - - -@urlmatch( - scheme="http", netloc="localhost", path="/api/v4/projects/1/import", method="get", -) -def resp_import_status(url, request): - """Mock for Project Import GET response.""" - content = """{ - "id": 1, - "description": "Itaque perspiciatis minima aspernatur corporis consequatur.", - "name": "Gitlab Test", - "name_with_namespace": "Gitlab Org / Gitlab Test", - "path": "gitlab-test", - "path_with_namespace": "gitlab-org/gitlab-test", - "created_at": "2017-08-29T04:36:44.383Z", - "import_status": "finished" - }""" - content = content.encode("utf-8") - return response(200, content, headers, None, 25, request) - - -@urlmatch( - scheme="http", netloc="localhost", path="/api/v4/import/github", method="post", -) -def resp_import_github(url, request): - """Mock for GitHub Project Import POST response.""" - content = """{ - "id": 27, - "name": "my-repo", - "full_path": "/root/my-repo", - "full_name": "Administrator / my-repo" - }""" - content = content.encode("utf-8") - return response(200, content, headers, None, 25, request) - - -@urlmatch( - scheme="http", - netloc="localhost", - path="/api/v4/projects/1/remote_mirrors", - method="get", -) -def resp_get_remote_mirrors(url, request): - """Mock for Project Remote Mirrors GET response.""" - content = """[ - { - "enabled": true, - "id": 101486, - "last_error": null, - "last_successful_update_at": "2020-01-06T17:32:02.823Z", - "last_update_at": "2020-01-06T17:32:02.823Z", - "last_update_started_at": "2020-01-06T17:31:55.864Z", - "only_protected_branches": true, - "update_status": "finished", - "url": "https://*****:*****@gitlab.com/gitlab-org/security/gitlab.git" - } - ]""" - content = content.encode("utf-8") - return response(200, content, headers, None, 5, request) +""" +GitLab API: https://docs.gitlab.com/ce/api/projects.html +""" +import pytest -@urlmatch( - scheme="http", - netloc="localhost", - path="/api/v4/projects/1/remote_mirrors", - method="post", -) -def resp_create_remote_mirror(url, request): - """Mock for Project Remote Mirrors POST response.""" - content = """{ - "enabled": false, - "id": 101486, - "last_error": null, - "last_successful_update_at": null, - "last_update_at": null, - "last_update_started_at": null, - "only_protected_branches": false, - "update_status": "none", - "url": "https://*****:*****@example.com/gitlab/example.git" - }""" - content = content.encode("utf-8") - return response(200, content, headers, None, 5, request) +from gitlab.v4.objects import Project +from httmock import urlmatch, response, with_httmock -@urlmatch( - scheme="http", - netloc="localhost", - path="/api/v4/projects/1/remote_mirrors/1", - method="put", -) -def resp_update_remote_mirror(url, request): - """Mock for Project Remote Mirrors PUT response.""" - content = """{ - "enabled": false, - "id": 101486, - "last_error": null, - "last_successful_update_at": "2020-01-06T17:32:02.823Z", - "last_update_at": "2020-01-06T17:32:02.823Z", - "last_update_started_at": "2020-01-06T17:31:55.864Z", - "only_protected_branches": true, - "update_status": "finished", - "url": "https://*****:*****@gitlab.com/gitlab-org/security/gitlab.git" - }""" - content = content.encode("utf-8") - return response(200, content, headers, None, 5, request) +from .mocks import headers -@urlmatch( - scheme="http", - netloc="localhost", - path="/api/v4/projects/1/services/pipelines-email", - method="put", -) -def resp_update_service(url, request): - """Mock for Service update PUT response.""" - content = """{ - "id": 100152, - "title": "Pipelines emails", - "slug": "pipelines-email", - "created_at": "2019-01-14T08:46:43.637+01:00", - "updated_at": "2019-07-01T14:10:36.156+02:00", - "active": true, - "commit_events": true, - "push_events": true, - "issues_events": true, - "confidential_issues_events": true, - "merge_requests_events": true, - "tag_push_events": true, - "note_events": true, - "confidential_note_events": true, - "pipeline_events": true, - "wiki_page_events": true, - "job_events": true, - "comment_on_event_enabled": true, - "project_id": 1 - }""" - content = content.encode("utf-8") +@urlmatch(scheme="http", netloc="localhost", path="/api/v4/projects/1", method="get") +def resp_get_project(url, request): + content = '{"name": "name", "id": 1}'.encode("utf-8") return response(200, content, headers, None, 5, request) -@urlmatch( - scheme="http", - netloc="localhost", - path="/api/v4/projects/1/services/pipelines-email", - method="get", -) -def resp_get_service(url, request): - """Mock for Service GET response.""" - content = """{ - "id": 100152, - "title": "Pipelines emails", - "slug": "pipelines-email", - "created_at": "2019-01-14T08:46:43.637+01:00", - "updated_at": "2019-07-01T14:10:36.156+02:00", - "active": true, - "commit_events": true, - "push_events": true, - "issues_events": true, - "confidential_issues_events": true, - "merge_requests_events": true, - "tag_push_events": true, - "note_events": true, - "confidential_note_events": true, - "pipeline_events": true, - "wiki_page_events": true, - "job_events": true, - "comment_on_event_enabled": true, - "project_id": 1 - }""" - content = content.encode("utf-8") - return response(200, content, headers, None, 5, request) +@with_httmock(resp_get_project) +def test_get_project(gl): + data = gl.projects.get(1) + assert isinstance(data, Project) + assert data.name == "name" + assert data.id == 1 -@urlmatch( - scheme="http", netloc="localhost", path="/api/v4/projects/1/services", method="get", -) -def resp_get_active_services(url, request): - """Mock for active Services GET response.""" - content = """[{ - "id": 100152, - "title": "Pipelines emails", - "slug": "pipelines-email", - "created_at": "2019-01-14T08:46:43.637+01:00", - "updated_at": "2019-07-01T14:10:36.156+02:00", - "active": true, - "commit_events": true, - "push_events": true, - "issues_events": true, - "confidential_issues_events": true, - "merge_requests_events": true, - "tag_push_events": true, - "note_events": true, - "confidential_note_events": true, - "pipeline_events": true, - "wiki_page_events": true, - "job_events": true, - "comment_on_event_enabled": true, - "project_id": 1 - }]""" - content = content.encode("utf-8") - return response(200, content, headers, None, 5, request) +@pytest.mark.skip(reason="missing test") +def test_list_projects(gl): + pass -@urlmatch( - scheme="http", - netloc="localhost", - path="/api/v4/projects/1/pipeline_schedules$", - method="post", -) -def resp_create_project_pipeline_schedule(url, request): - """Mock for creating project pipeline Schedules POST response.""" - content = """{ - "id": 14, - "description": "Build packages", - "ref": "master", - "cron": "0 1 * * 5", - "cron_timezone": "UTC", - "next_run_at": "2017-05-26T01:00:00.000Z", - "active": true, - "created_at": "2017-05-19T13:43:08.169Z", - "updated_at": "2017-05-19T13:43:08.169Z", - "last_pipeline": null, - "owner": { - "name": "Administrator", - "username": "root", - "id": 1, - "state": "active", - "avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon", - "web_url": "https://gitlab.example.com/root" - } -}""" - content = content.encode("utf-8") - return response(200, content, headers, None, 5, request) +@pytest.mark.skip(reason="missing test") +def test_list_user_projects(gl): + pass -@urlmatch( - scheme="http", - netloc="localhost", - path="/api/v4/projects/1/pipeline_schedules/14/play", - method="post", -) -def resp_play_project_pipeline_schedule(url, request): - """Mock for playing a project pipeline schedule POST response.""" - content = """{"message": "201 Created"}""" - content = content.encode("utf-8") - return response(200, content, headers, None, 5, request) +@pytest.mark.skip(reason="missing test") +def test_list_user_starred_projects(gl): + pass + + +@pytest.mark.skip(reason="missing test") +def test_list_project_users(gl): + pass + + +@pytest.mark.skip(reason="missing test") +def test_create_project(gl): + pass + + +@pytest.mark.skip(reason="missing test") +def test_create_user_project(gl): + pass + + +@pytest.mark.skip(reason="missing test") +def test_update_project(gl): + pass + + +@pytest.mark.skip(reason="missing test") +def test_fork_project(gl): + pass + + +@pytest.mark.skip(reason="missing test") +def test_list_project_forks(gl): + pass + + +@pytest.mark.skip(reason="missing test") +def test_star_project(gl): + pass + + +@pytest.mark.skip(reason="missing test") +def test_unstar_project(gl): + pass + + +@pytest.mark.skip(reason="missing test") +def test_list_project_starrers(gl): + pass + + +@pytest.mark.skip(reason="missing test") +def test_get_project_languages(gl): + pass + + +@pytest.mark.skip(reason="missing test") +def test_archive_project(gl): + pass + + +@pytest.mark.skip(reason="missing test") +def test_unarchive_project(gl): + pass + + +@pytest.mark.skip(reason="missing test") +def test_remove_project(gl): + pass + + +@pytest.mark.skip(reason="missing test") +def test_restore_project(gl): + pass + + +@pytest.mark.skip(reason="missing test") +def test_upload_file(gl): + pass + + +@pytest.mark.skip(reason="missing test") +def test_share_project(gl): + pass + + +@pytest.mark.skip(reason="missing test") +def test_delete_shared_project_link(gl): + pass + + +@pytest.mark.skip(reason="missing test") +def test_list_project_hooks(gl): + pass + + +@pytest.mark.skip(reason="missing test") +def test_get_project_hook(gl): + pass + + +@pytest.mark.skip(reason="missing test") +def test_create_project_hook(gl): + pass + + +@pytest.mark.skip(reason="missing test") +def test_update_project_hook(gl): + pass + + +@pytest.mark.skip(reason="missing test") +def test_delete_project_hook(gl): + pass + + +@pytest.mark.skip(reason="missing test") +def test_create_forked_from_relationship(gl): + pass + + +@pytest.mark.skip(reason="missing test") +def test_delete_forked_from_relationship(gl): + pass + + +@pytest.mark.skip(reason="missing test") +def test_search_projects_by_name(gl): + pass + + +@pytest.mark.skip(reason="missing test") +def test_project_housekeeping(gl): + pass + + +@pytest.mark.skip(reason="missing test") +def test_get_project_push_rules(gl): + pass + + +@pytest.mark.skip(reason="missing test") +def test_create_project_push_rule(gl): + pass + + +@pytest.mark.skip(reason="missing test") +def test_update_project_push_rule(gl): + pass + + +@pytest.mark.skip(reason="missing test") +def test_delete_project_push_rule(gl): + pass + + +@pytest.mark.skip(reason="missing test") +def test_transfer_project(gl): + pass + + +@pytest.mark.skip(reason="missing test") +def test_project_pull_mirror(gl): + pass -class TestProject(unittest.TestCase): - """Base class for GitLab Project tests.""" - - def setUp(self): - self.gl = Gitlab( - "http://localhost", - private_token="private_token", - ssl_verify=True, - api_version="4", - ) - self.project = self.gl.projects.get(1, lazy=True) - - -class TestProjectSnippets(TestProject): - def test_list_project_snippets(self): - title = "Example Snippet Title" - visibility = "private" - - @urlmatch( - scheme="http", - netloc="localhost", - path="/api/v4/projects/1/snippets", - method="get", - ) - def resp_list_snippet(url, request): - content = """[{ - "title": "%s", - "description": "More verbose snippet description", - "file_name": "example.txt", - "content": "source code with multiple lines", - "visibility": "%s"}]""" % ( - title, - visibility, - ) - content = content.encode("utf-8") - return response(200, content, headers, None, 25, request) - - with HTTMock(resp_list_snippet): - snippets = self.project.snippets.list() - assert len(snippets) == 1 - assert snippets[0].title == title - assert snippets[0].visibility == visibility - - def test_get_project_snippets(self): - title = "Example Snippet Title" - visibility = "private" - - @urlmatch( - scheme="http", - netloc="localhost", - path="/api/v4/projects/1/snippets/1", - method="get", - ) - def resp_get_snippet(url, request): - content = """{ - "title": "%s", - "description": "More verbose snippet description", - "file_name": "example.txt", - "content": "source code with multiple lines", - "visibility": "%s"}""" % ( - title, - visibility, - ) - content = content.encode("utf-8") - return response(200, content, headers, None, 25, request) - - with HTTMock(resp_get_snippet): - snippet = self.project.snippets.get(1) - assert snippet.title == title - assert snippet.visibility == visibility - - def test_create_update_project_snippets(self): - title = "Example Snippet Title" - visibility = "private" - - @urlmatch( - scheme="http", - netloc="localhost", - path="/api/v4/projects/1/snippets", - method="put", - ) - def resp_update_snippet(url, request): - content = """{ - "title": "%s", - "description": "More verbose snippet description", - "file_name": "example.txt", - "content": "source code with multiple lines", - "visibility": "%s"}""" % ( - title, - visibility, - ) - content = content.encode("utf-8") - return response(200, content, headers, None, 25, request) - - @urlmatch( - scheme="http", - netloc="localhost", - path="/api/v4/projects/1/snippets", - method="post", - ) - def resp_create_snippet(url, request): - content = """{ - "title": "%s", - "description": "More verbose snippet description", - "file_name": "example.txt", - "content": "source code with multiple lines", - "visibility": "%s"}""" % ( - title, - visibility, - ) - content = content.encode("utf-8") - return response(200, content, headers, None, 25, request) - - with HTTMock(resp_create_snippet, resp_update_snippet): - snippet = self.project.snippets.create( - { - "title": title, - "file_name": title, - "content": title, - "visibility": visibility, - } - ) - assert snippet.title == title - assert snippet.visibility == visibility - title = "new-title" - snippet.title = title - snippet.save() - assert snippet.title == title - assert snippet.visibility == visibility - - -class TestProjectExport(TestProject): - @with_httmock(resp_create_export) - def test_create_project_export(self): - export = self.project.exports.create() - assert export.message == "202 Accepted" - - @with_httmock(resp_create_export, resp_export_status) - def test_refresh_project_export_status(self): - export = self.project.exports.create() - export.refresh() - assert export.export_status == "finished" - - @with_httmock(resp_create_export, resp_download_export) - def test_download_project_export(self): - export = self.project.exports.create() - download = export.download() - assert isinstance(download, bytes) - assert download == binary_content - - -class TestProjectImport(TestProject): - @with_httmock(resp_import_project) - def test_import_project(self): - project_import = self.gl.projects.import_project("file", "api-project") - assert project_import["import_status"] == "scheduled" - - @with_httmock(resp_import_status) - def test_refresh_project_import_status(self): - project_import = self.project.imports.get() - project_import.refresh() - assert project_import.import_status == "finished" - - @with_httmock(resp_import_github) - def test_import_github(self): - base_path = "/root" - name = "my-repo" - ret = self.gl.projects.import_github("githubkey", 1234, base_path, name) - assert isinstance(ret, dict) - assert ret["name"] == name - assert ret["full_path"] == "/".join((base_path, name)) - assert ret["full_name"].endswith(name) - - -class TestProjectRemoteMirrors(TestProject): - @with_httmock(resp_get_remote_mirrors) - def test_list_project_remote_mirrors(self): - mirrors = self.project.remote_mirrors.list() - assert isinstance(mirrors, list) - assert isinstance(mirrors[0], ProjectRemoteMirror) - assert mirrors[0].enabled - - @with_httmock(resp_create_remote_mirror) - def test_create_project_remote_mirror(self): - mirror = self.project.remote_mirrors.create({"url": "https://example.com"}) - assert isinstance(mirror, ProjectRemoteMirror) - assert mirror.update_status == "none" - - @with_httmock(resp_create_remote_mirror, resp_update_remote_mirror) - def test_update_project_remote_mirror(self): - mirror = self.project.remote_mirrors.create({"url": "https://example.com"}) - mirror.only_protected_branches = True - mirror.save() - assert mirror.update_status == "finished" - assert mirror.only_protected_branches - - -class TestProjectServices(TestProject): - @with_httmock(resp_get_active_services) - def test_list_active_services(self): - services = self.project.services.list() - assert isinstance(services, list) - assert isinstance(services[0], ProjectService) - assert services[0].active - assert services[0].push_events - - def test_list_available_services(self): - services = self.project.services.available() - assert isinstance(services, list) - assert isinstance(services[0], str) - - @with_httmock(resp_get_service) - def test_get_service(self): - service = self.project.services.get("pipelines-email") - assert isinstance(service, ProjectService) - assert service.push_events == True - - @with_httmock(resp_get_service, resp_update_service) - def test_update_service(self): - service = self.project.services.get("pipelines-email") - service.issues_events = True - service.save() - assert service.issues_events == True - - -class TestProjectPipelineSchedule(TestProject): - @with_httmock( - resp_create_project_pipeline_schedule, resp_play_project_pipeline_schedule - ) - def test_project_pipeline_schedule_play(self): - description = "Build packages" - cronline = "0 1 * * 5" - sched = self.project.pipelineschedules.create( - {"ref": "master", "description": description, "cron": cronline} - ) - self.assertIsNotNone(sched) - self.assertEqual(description, sched.description) - self.assertEqual(cronline, sched.cron) - - play_result = sched.play() - self.assertIsNotNone(play_result) - self.assertIn("message", play_result) - self.assertEqual("201 Created", play_result["message"]) +@pytest.mark.skip(reason="missing test") +def test_project_snapshot(gl): + pass diff --git a/gitlab/tests/objects/test_remote_mirrors.py b/gitlab/tests/objects/test_remote_mirrors.py new file mode 100644 index 000000000..e62a71e57 --- /dev/null +++ b/gitlab/tests/objects/test_remote_mirrors.py @@ -0,0 +1,103 @@ +""" +GitLab API: https://docs.gitlab.com/ce/api/remote_mirrors.html +""" + +from httmock import response, urlmatch, with_httmock + +from gitlab.v4.objects import ProjectRemoteMirror +from .mocks import headers + + +@urlmatch( + scheme="http", + netloc="localhost", + path="/api/v4/projects/1/remote_mirrors", + method="get", +) +def resp_get_remote_mirrors(url, request): + """Mock for Project Remote Mirrors GET response.""" + content = """[ + { + "enabled": true, + "id": 101486, + "last_error": null, + "last_successful_update_at": "2020-01-06T17:32:02.823Z", + "last_update_at": "2020-01-06T17:32:02.823Z", + "last_update_started_at": "2020-01-06T17:31:55.864Z", + "only_protected_branches": true, + "update_status": "finished", + "url": "https://*****:*****@gitlab.com/gitlab-org/security/gitlab.git" + } + ]""" + content = content.encode("utf-8") + return response(200, content, headers, None, 5, request) + + +@urlmatch( + scheme="http", + netloc="localhost", + path="/api/v4/projects/1/remote_mirrors", + method="post", +) +def resp_create_remote_mirror(url, request): + """Mock for Project Remote Mirrors POST response.""" + content = """{ + "enabled": false, + "id": 101486, + "last_error": null, + "last_successful_update_at": null, + "last_update_at": null, + "last_update_started_at": null, + "only_protected_branches": false, + "update_status": "none", + "url": "https://*****:*****@example.com/gitlab/example.git" + }""" + content = content.encode("utf-8") + return response(200, content, headers, None, 5, request) + + +@urlmatch( + scheme="http", + netloc="localhost", + path="/api/v4/projects/1/remote_mirrors/1", + method="put", +) +def resp_update_remote_mirror(url, request): + """Mock for Project Remote Mirrors PUT response.""" + content = """{ + "enabled": false, + "id": 101486, + "last_error": null, + "last_successful_update_at": "2020-01-06T17:32:02.823Z", + "last_update_at": "2020-01-06T17:32:02.823Z", + "last_update_started_at": "2020-01-06T17:31:55.864Z", + "only_protected_branches": true, + "update_status": "finished", + "url": "https://*****:*****@gitlab.com/gitlab-org/security/gitlab.git" + }""" + content = content.encode("utf-8") + return response(200, content, headers, None, 5, request) + + +@with_httmock(resp_get_remote_mirrors) +def test_list_project_remote_mirrors(project): + mirrors = project.remote_mirrors.list() + assert isinstance(mirrors, list) + assert isinstance(mirrors[0], ProjectRemoteMirror) + assert mirrors[0].enabled + + +@with_httmock(resp_create_remote_mirror) +def test_create_project_remote_mirror(project): + mirror = project.remote_mirrors.create({"url": "https://example.com"}) + assert isinstance(mirror, ProjectRemoteMirror) + assert mirror.update_status == "none" + + +@with_httmock(resp_create_remote_mirror, resp_update_remote_mirror) +def test_update_project_remote_mirror(project): + mirror = project.remote_mirrors.create({"url": "https://example.com"}) + mirror.only_protected_branches = True + mirror.save() + assert mirror.update_status == "finished" + assert mirror.only_protected_branches diff --git a/gitlab/tests/objects/test_services.py b/gitlab/tests/objects/test_services.py new file mode 100644 index 000000000..a0cded733 --- /dev/null +++ b/gitlab/tests/objects/test_services.py @@ -0,0 +1,134 @@ +""" +GitLab API: https://docs.gitlab.com/ce/api/services.html +""" + +from httmock import urlmatch, response, with_httmock + +from gitlab.v4.objects import ProjectService +from .mocks import headers + + +@urlmatch( + scheme="http", + netloc="localhost", + path="/api/v4/projects/1/services/pipelines-email", + method="put", +) +def resp_update_service(url, request): + """Mock for Service update PUT response.""" + content = """{ + "id": 100152, + "title": "Pipelines emails", + "slug": "pipelines-email", + "created_at": "2019-01-14T08:46:43.637+01:00", + "updated_at": "2019-07-01T14:10:36.156+02:00", + "active": true, + "commit_events": true, + "push_events": true, + "issues_events": true, + "confidential_issues_events": true, + "merge_requests_events": true, + "tag_push_events": true, + "note_events": true, + "confidential_note_events": true, + "pipeline_events": true, + "wiki_page_events": true, + "job_events": true, + "comment_on_event_enabled": true, + "project_id": 1 + }""" + content = content.encode("utf-8") + return response(200, content, headers, None, 5, request) + + +@urlmatch( + scheme="http", + netloc="localhost", + path="/api/v4/projects/1/services/pipelines-email", + method="get", +) +def resp_get_service(url, request): + """Mock for Service GET response.""" + content = """{ + "id": 100152, + "title": "Pipelines emails", + "slug": "pipelines-email", + "created_at": "2019-01-14T08:46:43.637+01:00", + "updated_at": "2019-07-01T14:10:36.156+02:00", + "active": true, + "commit_events": true, + "push_events": true, + "issues_events": true, + "confidential_issues_events": true, + "merge_requests_events": true, + "tag_push_events": true, + "note_events": true, + "confidential_note_events": true, + "pipeline_events": true, + "wiki_page_events": true, + "job_events": true, + "comment_on_event_enabled": true, + "project_id": 1 + }""" + content = content.encode("utf-8") + return response(200, content, headers, None, 5, request) + + +@urlmatch( + scheme="http", netloc="localhost", path="/api/v4/projects/1/services", method="get", +) +def resp_get_active_services(url, request): + """Mock for active Services GET response.""" + content = """[{ + "id": 100152, + "title": "Pipelines emails", + "slug": "pipelines-email", + "created_at": "2019-01-14T08:46:43.637+01:00", + "updated_at": "2019-07-01T14:10:36.156+02:00", + "active": true, + "commit_events": true, + "push_events": true, + "issues_events": true, + "confidential_issues_events": true, + "merge_requests_events": true, + "tag_push_events": true, + "note_events": true, + "confidential_note_events": true, + "pipeline_events": true, + "wiki_page_events": true, + "job_events": true, + "comment_on_event_enabled": true, + "project_id": 1 + }]""" + content = content.encode("utf-8") + return response(200, content, headers, None, 5, request) + + +@with_httmock(resp_get_active_services) +def test_list_active_services(project): + services = project.services.list() + assert isinstance(services, list) + assert isinstance(services[0], ProjectService) + assert services[0].active + assert services[0].push_events + + +def test_list_available_services(project): + services = project.services.available() + assert isinstance(services, list) + assert isinstance(services[0], str) + + +@with_httmock(resp_get_service) +def test_get_service(project): + service = project.services.get("pipelines-email") + assert isinstance(service, ProjectService) + assert service.push_events is True + + +@with_httmock(resp_get_service, resp_update_service) +def test_update_service(project): + service = project.services.get("pipelines-email") + service.issues_events = True + service.save() + assert service.issues_events is True diff --git a/gitlab/tests/objects/test_snippets.py b/gitlab/tests/objects/test_snippets.py new file mode 100644 index 000000000..86eb54c1b --- /dev/null +++ b/gitlab/tests/objects/test_snippets.py @@ -0,0 +1,121 @@ +""" +GitLab API: https://docs.gitlab.com/ce/api/project_snippets.html + https://docs.gitlab.com/ee/api/snippets.html (todo) +""" + +from httmock import response, urlmatch, with_httmock + +from .mocks import headers + + +title = "Example Snippet Title" +visibility = "private" +new_title = "new-title" + + +@urlmatch( + scheme="http", netloc="localhost", path="/api/v4/projects/1/snippets", method="get", +) +def resp_list_snippet(url, request): + content = """[{ + "title": "%s", + "description": "More verbose snippet description", + "file_name": "example.txt", + "content": "source code with multiple lines", + "visibility": "%s"}]""" % ( + title, + visibility, + ) + content = content.encode("utf-8") + return response(200, content, headers, None, 25, request) + + +@urlmatch( + scheme="http", + netloc="localhost", + path="/api/v4/projects/1/snippets/1", + method="get", +) +def resp_get_snippet(url, request): + content = """{ + "title": "%s", + "description": "More verbose snippet description", + "file_name": "example.txt", + "content": "source code with multiple lines", + "visibility": "%s"}""" % ( + title, + visibility, + ) + content = content.encode("utf-8") + return response(200, content, headers, None, 25, request) + + +@urlmatch( + scheme="http", + netloc="localhost", + path="/api/v4/projects/1/snippets", + method="post", +) +def resp_create_snippet(url, request): + content = """{ + "title": "%s", + "description": "More verbose snippet description", + "file_name": "example.txt", + "content": "source code with multiple lines", + "visibility": "%s"}""" % ( + title, + visibility, + ) + content = content.encode("utf-8") + return response(200, content, headers, None, 25, request) + + +@urlmatch( + scheme="http", netloc="localhost", path="/api/v4/projects/1/snippets", method="put", +) +def resp_update_snippet(url, request): + content = """{ + "title": "%s", + "description": "More verbose snippet description", + "file_name": "example.txt", + "content": "source code with multiple lines", + "visibility": "%s"}""" % ( + new_title, + visibility, + ) + content = content.encode("utf-8") + return response(200, content, headers, None, 25, request) + + +@with_httmock(resp_list_snippet) +def test_list_project_snippets(project): + snippets = project.snippets.list() + assert len(snippets) == 1 + assert snippets[0].title == title + assert snippets[0].visibility == visibility + + +@with_httmock(resp_get_snippet) +def test_get_project_snippets(project): + snippet = project.snippets.get(1) + assert snippet.title == title + assert snippet.visibility == visibility + + +@with_httmock(resp_create_snippet, resp_update_snippet) +def test_create_update_project_snippets(project): + snippet = project.snippets.create( + { + "title": title, + "file_name": title, + "content": title, + "visibility": visibility, + } + ) + assert snippet.title == title + assert snippet.visibility == visibility + + snippet.title = new_title + snippet.save() + assert snippet.title == new_title + assert snippet.visibility == visibility diff --git a/gitlab/tests/objects/test_submodules.py b/gitlab/tests/objects/test_submodules.py new file mode 100644 index 000000000..2e7630275 --- /dev/null +++ b/gitlab/tests/objects/test_submodules.py @@ -0,0 +1,58 @@ +""" +GitLab API: https://docs.gitlab.com/ce/api/repository_submodules.html +""" + +from httmock import response, urlmatch, with_httmock + +from gitlab.v4.objects import Project + +from .mocks import headers + + +@urlmatch(scheme="http", netloc="localhost", path="/api/v4/projects/1$", method="get") +def resp_get_project(url, request): + content = '{"name": "name", "id": 1}'.encode("utf-8") + return response(200, content, headers, None, 5, request) + + +@urlmatch( + scheme="http", + netloc="localhost", + path="/api/v4/projects/1/repository/submodules/foo%2Fbar", + method="put", +) +def resp_update_submodule(url, request): + content = """{ + "id": "ed899a2f4b50b4370feeea94676502b42383c746", + "short_id": "ed899a2f4b5", + "title": "Message", + "author_name": "Author", + "author_email": "author@example.com", + "committer_name": "Author", + "committer_email": "author@example.com", + "created_at": "2018-09-20T09:26:24.000-07:00", + "message": "Message", + "parent_ids": [ "ae1d9fb46aa2b07ee9836d49862ec4e2c46fbbba" ], + "committed_date": "2018-09-20T09:26:24.000-07:00", + "authored_date": "2018-09-20T09:26:24.000-07:00", + "status": null}""" + content = content.encode("utf-8") + return response(200, content, headers, None, 5, request) + + +@with_httmock(resp_get_project, resp_update_submodule) +def test_update_submodule(gl): + project = gl.projects.get(1) + assert isinstance(project, Project) + assert project.name == "name" + assert project.id == 1 + + ret = project.update_submodule( + submodule="foo/bar", + branch="master", + commit_sha="4c3674f66071e30b3311dac9b9ccc90502a72664", + commit_message="Message", + ) + assert isinstance(ret, dict) + assert ret["message"] == "Message" + assert ret["id"] == "ed899a2f4b50b4370feeea94676502b42383c746" diff --git a/gitlab/tests/objects/test_todos.py b/gitlab/tests/objects/test_todos.py new file mode 100644 index 000000000..5b30dc95f --- /dev/null +++ b/gitlab/tests/objects/test_todos.py @@ -0,0 +1,58 @@ +""" +GitLab API: https://docs.gitlab.com/ce/api/todos.html +""" + +import json +import os + +from httmock import response, urlmatch, with_httmock + +from gitlab.v4.objects import Todo + +from .mocks import headers + + +with open(os.path.dirname(__file__) + "/../data/todo.json", "r") as json_file: + todo_content = json_file.read() + json_content = json.loads(todo_content) + encoded_content = todo_content.encode("utf-8") + + +@urlmatch(scheme="http", netloc="localhost", path="/api/v4/todos", method="get") +def resp_get_todo(url, request): + return response(200, encoded_content, headers, None, 5, request) + + +@urlmatch( + scheme="http", + netloc="localhost", + path="/api/v4/todos/102/mark_as_done", + method="post", +) +def resp_mark_as_done(url, request): + single_todo = json.dumps(json_content[0]) + content = single_todo.encode("utf-8") + return response(200, content, headers, None, 5, request) + + +@urlmatch( + scheme="http", netloc="localhost", path="/api/v4/todos/mark_as_done", method="post", +) +def resp_mark_all_as_done(url, request): + return response(204, {}, headers, None, 5, request) + + +@with_httmock(resp_get_todo, resp_mark_as_done) +def test_todo(gl): + todo = gl.todos.list()[0] + assert isinstance(todo, Todo) + assert todo.id == 102 + assert todo.target_type == "MergeRequest" + assert todo.target["assignee"]["username"] == "root" + + todo.mark_as_done() + + +@with_httmock(resp_mark_all_as_done) +def test_todo_mark_all_as_done(gl): + gl.todos.mark_all_as_done() diff --git a/gitlab/tests/objects/test_users.py b/gitlab/tests/objects/test_users.py new file mode 100644 index 000000000..88175d09d --- /dev/null +++ b/gitlab/tests/objects/test_users.py @@ -0,0 +1,94 @@ +""" +GitLab API: https://docs.gitlab.com/ce/api/users.html +""" + +from httmock import response, urlmatch, with_httmock + +from gitlab.v4.objects import User, UserMembership, UserStatus +from .mocks import headers + + +@urlmatch(scheme="http", netloc="localhost", path="/api/v4/users/1", method="get") +def resp_get_user(url, request): + content = ( + '{"name": "name", "id": 1, "password": "password", ' + '"username": "username", "email": "email"}' + ) + content = content.encode("utf-8") + return response(200, content, headers, None, 5, request) + + +@urlmatch( + scheme="http", netloc="localhost", path="/api/v4/users/1/memberships", method="get", +) +def resp_get_user_memberships(url, request): + content = """[ + { + "source_id": 1, + "source_name": "Project one", + "source_type": "Project", + "access_level": "20" + }, + { + "source_id": 3, + "source_name": "Group three", + "source_type": "Namespace", + "access_level": "20" + } + ]""" + content = content.encode("utf-8") + return response(200, content, headers, None, 5, request) + + +@urlmatch( + scheme="http", netloc="localhost", path="/api/v4/users/1/activate", method="post", +) +def resp_activate(url, request): + return response(201, {}, headers, None, 5, request) + + +@urlmatch( + scheme="http", netloc="localhost", path="/api/v4/users/1/deactivate", method="post", +) +def resp_deactivate(url, request): + return response(201, {}, headers, None, 5, request) + + +@urlmatch( + scheme="http", netloc="localhost", path="/api/v4/users/1/status", method="get", +) +def resp_get_user_status(url, request): + content = ( + '{"message": "test", "message_html": "

Message

", "emoji": "thumbsup"}' + ) + content = content.encode("utf-8") + return response(200, content, headers, None, 5, request) + + +@with_httmock(resp_get_user) +def test_get_user(gl): + user = gl.users.get(1) + assert isinstance(user, User) + assert user.name == "name" + assert user.id == 1 + + +@with_httmock(resp_get_user_memberships) +def test_user_memberships(user): + memberships = user.memberships.list() + assert isinstance(memberships[0], UserMembership) + assert memberships[0].source_type == "Project" + + +@with_httmock(resp_get_user_status) +def test_user_status(user): + status = user.status.get() + assert isinstance(status, UserStatus) + assert status.message == "test" + assert status.emoji == "thumbsup" + + +@with_httmock(resp_activate, resp_deactivate) +def test_user_activate_deactivate(user): + user.activate() + user.deactivate() diff --git a/gitlab/tests/test_base.py b/gitlab/tests/test_base.py index 666060c4f..58c0d4748 100644 --- a/gitlab/tests/test_base.py +++ b/gitlab/tests/test_base.py @@ -16,7 +16,6 @@ # along with this program. If not, see . import pickle -import unittest from gitlab import base import pytest @@ -35,7 +34,17 @@ class FakeManager(base.RESTManager): _path = "/tests" -class TestRESTManager(unittest.TestCase): +@pytest.fixture +def fake_gitlab(): + return FakeGitlab() + + +@pytest.fixture +def fake_manager(fake_gitlab): + return FakeManager(fake_gitlab) + + +class TestRESTManager: def test_computed_path_simple(self): class MGR(base.RESTManager): _path = "/tests" @@ -65,22 +74,18 @@ class MGR(base.RESTManager): assert mgr.path == "/tests" -class TestRESTObject(unittest.TestCase): - def setUp(self): - self.gitlab = FakeGitlab() - self.manager = FakeManager(self.gitlab) - - def test_instanciate(self): - obj = FakeObject(self.manager, {"foo": "bar"}) +class TestRESTObject: + def test_instantiate(self, fake_gitlab, fake_manager): + obj = FakeObject(fake_manager, {"foo": "bar"}) assert {"foo": "bar"} == obj._attrs assert {} == obj._updated_attrs assert None == obj._create_managers() - assert self.manager == obj.manager - assert self.gitlab == obj.manager.gitlab + assert fake_manager == obj.manager + assert fake_gitlab == obj.manager.gitlab - def test_pickability(self): - obj = FakeObject(self.manager, {"foo": "bar"}) + def test_picklability(self, fake_manager): + obj = FakeObject(fake_manager, {"foo": "bar"}) original_obj_module = obj._module pickled = pickle.dumps(obj) unpickled = pickle.loads(pickled) @@ -89,8 +94,8 @@ def test_pickability(self): assert unpickled._module == original_obj_module pickled2 = pickle.dumps(unpickled) - def test_attrs(self): - obj = FakeObject(self.manager, {"foo": "bar"}) + def test_attrs(self, fake_manager): + obj = FakeObject(fake_manager, {"foo": "bar"}) assert "bar" == obj.foo with pytest.raises(AttributeError): @@ -101,57 +106,57 @@ def test_attrs(self): assert {"foo": "bar"} == obj._attrs assert {"bar": "baz"} == obj._updated_attrs - def test_get_id(self): - obj = FakeObject(self.manager, {"foo": "bar"}) + def test_get_id(self, fake_manager): + obj = FakeObject(fake_manager, {"foo": "bar"}) obj.id = 42 assert 42 == obj.get_id() obj.id = None assert None == obj.get_id() - def test_custom_id_attr(self): + def test_custom_id_attr(self, fake_manager): class OtherFakeObject(FakeObject): _id_attr = "foo" - obj = OtherFakeObject(self.manager, {"foo": "bar"}) + obj = OtherFakeObject(fake_manager, {"foo": "bar"}) assert "bar" == obj.get_id() - def test_update_attrs(self): - obj = FakeObject(self.manager, {"foo": "bar"}) + def test_update_attrs(self, fake_manager): + obj = FakeObject(fake_manager, {"foo": "bar"}) obj.bar = "baz" obj._update_attrs({"foo": "foo", "bar": "bar"}) assert {"foo": "foo", "bar": "bar"} == obj._attrs assert {} == obj._updated_attrs - def test_create_managers(self): + def test_create_managers(self, fake_gitlab, fake_manager): class ObjectWithManager(FakeObject): _managers = (("fakes", "FakeManager"),) - obj = ObjectWithManager(self.manager, {"foo": "bar"}) + obj = ObjectWithManager(fake_manager, {"foo": "bar"}) obj.id = 42 assert isinstance(obj.fakes, FakeManager) - assert obj.fakes.gitlab == self.gitlab + assert obj.fakes.gitlab == fake_gitlab assert obj.fakes._parent == obj - def test_equality(self): - obj1 = FakeObject(self.manager, {"id": "foo"}) - obj2 = FakeObject(self.manager, {"id": "foo", "other_attr": "bar"}) + def test_equality(self, fake_manager): + obj1 = FakeObject(fake_manager, {"id": "foo"}) + obj2 = FakeObject(fake_manager, {"id": "foo", "other_attr": "bar"}) assert obj1 == obj2 - def test_equality_custom_id(self): + def test_equality_custom_id(self, fake_manager): class OtherFakeObject(FakeObject): _id_attr = "foo" - obj1 = OtherFakeObject(self.manager, {"foo": "bar"}) - obj2 = OtherFakeObject(self.manager, {"foo": "bar", "other_attr": "baz"}) + obj1 = OtherFakeObject(fake_manager, {"foo": "bar"}) + obj2 = OtherFakeObject(fake_manager, {"foo": "bar", "other_attr": "baz"}) assert obj1 == obj2 - def test_inequality(self): - obj1 = FakeObject(self.manager, {"id": "foo"}) - obj2 = FakeObject(self.manager, {"id": "bar"}) + def test_inequality(self, fake_manager): + obj1 = FakeObject(fake_manager, {"id": "foo"}) + obj2 = FakeObject(fake_manager, {"id": "bar"}) assert obj1 != obj2 - def test_inequality_no_id(self): - obj1 = FakeObject(self.manager, {"attr1": "foo"}) - obj2 = FakeObject(self.manager, {"attr1": "bar"}) + def test_inequality_no_id(self, fake_manager): + obj1 = FakeObject(fake_manager, {"attr1": "foo"}) + obj2 = FakeObject(fake_manager, {"attr1": "bar"}) assert obj1 != obj2 diff --git a/gitlab/tests/test_cli.py b/gitlab/tests/test_cli.py index 63a57937a..2246369e5 100644 --- a/gitlab/tests/test_cli.py +++ b/gitlab/tests/test_cli.py @@ -19,128 +19,118 @@ import argparse import os import tempfile -import unittest import io -try: - from contextlib import redirect_stderr # noqa: H302 -except ImportError: - from contextlib import contextmanager # noqa: H302 - import sys - - @contextmanager - def redirect_stderr(new_target): - old_target, sys.stderr = sys.stderr, new_target - yield - sys.stderr = old_target +from contextlib import redirect_stderr # noqa: H302 +import pytest from gitlab import cli import gitlab.v4.cli -import pytest -class TestCLI(unittest.TestCase): - def test_what_to_cls(self): - assert "Foo" == cli.what_to_cls("foo") - assert "FooBar" == cli.what_to_cls("foo-bar") - - def test_cls_to_what(self): - class Class(object): - pass - - class TestClass(object): - pass - - assert "test-class" == cli.cls_to_what(TestClass) - assert "class" == cli.cls_to_what(Class) - - def test_die(self): - fl = io.StringIO() - with redirect_stderr(fl): - with pytest.raises(SystemExit) as test: - cli.die("foobar") - assert fl.getvalue() == "foobar\n" - assert test.value.code == 1 - - def test_parse_value(self): - ret = cli._parse_value("foobar") - assert ret == "foobar" - - ret = cli._parse_value(True) - assert ret == True - - ret = cli._parse_value(1) - assert ret == 1 - - ret = cli._parse_value(None) - assert ret == None - - fd, temp_path = tempfile.mkstemp() - os.write(fd, b"content") - os.close(fd) - ret = cli._parse_value("@%s" % temp_path) - assert ret == "content" - os.unlink(temp_path) - - fl = io.StringIO() - with redirect_stderr(fl): - with pytest.raises(SystemExit) as exc: - cli._parse_value("@/thisfileprobablydoesntexist") - assert ( - fl.getvalue() == "[Errno 2] No such file or directory:" - " '/thisfileprobablydoesntexist'\n" - ) - assert exc.value.code == 1 - - def test_base_parser(self): - parser = cli._get_base_parser() - args = parser.parse_args( - ["-v", "-g", "gl_id", "-c", "foo.cfg", "-c", "bar.cfg"] - ) - assert args.verbose - assert args.gitlab == "gl_id" - assert args.config_file == ["foo.cfg", "bar.cfg"] - - -class TestV4CLI(unittest.TestCase): - def test_parse_args(self): - parser = cli._get_parser(gitlab.v4.cli) - args = parser.parse_args(["project", "list"]) - assert args.what == "project" - assert args.whaction == "list" - - def test_parser(self): - parser = cli._get_parser(gitlab.v4.cli) - subparsers = next( - action - for action in parser._actions - if isinstance(action, argparse._SubParsersAction) - ) - assert subparsers is not None - assert "project" in subparsers.choices +def test_what_to_cls(): + assert "Foo" == cli.what_to_cls("foo") + assert "FooBar" == cli.what_to_cls("foo-bar") - user_subparsers = next( - action - for action in subparsers.choices["project"]._actions - if isinstance(action, argparse._SubParsersAction) - ) - assert user_subparsers is not None - assert "list" in user_subparsers.choices - assert "get" in user_subparsers.choices - assert "delete" in user_subparsers.choices - assert "update" in user_subparsers.choices - assert "create" in user_subparsers.choices - assert "archive" in user_subparsers.choices - assert "unarchive" in user_subparsers.choices - - actions = user_subparsers.choices["create"]._option_string_actions - assert not actions["--description"].required - - user_subparsers = next( - action - for action in subparsers.choices["group"]._actions - if isinstance(action, argparse._SubParsersAction) + +def test_cls_to_what(): + class Class(object): + pass + + class TestClass(object): + pass + + assert "test-class" == cli.cls_to_what(TestClass) + assert "class" == cli.cls_to_what(Class) + + +def test_die(): + fl = io.StringIO() + with redirect_stderr(fl): + with pytest.raises(SystemExit) as test: + cli.die("foobar") + assert fl.getvalue() == "foobar\n" + assert test.value.code == 1 + + +def test_parse_value(): + ret = cli._parse_value("foobar") + assert ret == "foobar" + + ret = cli._parse_value(True) + assert ret is True + + ret = cli._parse_value(1) + assert ret == 1 + + ret = cli._parse_value(None) + assert ret is None + + fd, temp_path = tempfile.mkstemp() + os.write(fd, b"content") + os.close(fd) + ret = cli._parse_value("@%s" % temp_path) + assert ret == "content" + os.unlink(temp_path) + + fl = io.StringIO() + with redirect_stderr(fl): + with pytest.raises(SystemExit) as exc: + cli._parse_value("@/thisfileprobablydoesntexist") + assert ( + fl.getvalue() == "[Errno 2] No such file or directory:" + " '/thisfileprobablydoesntexist'\n" ) - actions = user_subparsers.choices["create"]._option_string_actions - assert actions["--name"].required + assert exc.value.code == 1 + + +def test_base_parser(): + parser = cli._get_base_parser() + args = parser.parse_args(["-v", "-g", "gl_id", "-c", "foo.cfg", "-c", "bar.cfg"]) + assert args.verbose + assert args.gitlab == "gl_id" + assert args.config_file == ["foo.cfg", "bar.cfg"] + + +def test_v4_parse_args(): + parser = cli._get_parser(gitlab.v4.cli) + args = parser.parse_args(["project", "list"]) + assert args.what == "project" + assert args.whaction == "list" + + +def test_v4_parser(): + parser = cli._get_parser(gitlab.v4.cli) + subparsers = next( + action + for action in parser._actions + if isinstance(action, argparse._SubParsersAction) + ) + assert subparsers is not None + assert "project" in subparsers.choices + + user_subparsers = next( + action + for action in subparsers.choices["project"]._actions + if isinstance(action, argparse._SubParsersAction) + ) + assert user_subparsers is not None + assert "list" in user_subparsers.choices + assert "get" in user_subparsers.choices + assert "delete" in user_subparsers.choices + assert "update" in user_subparsers.choices + assert "create" in user_subparsers.choices + assert "archive" in user_subparsers.choices + assert "unarchive" in user_subparsers.choices + + actions = user_subparsers.choices["create"]._option_string_actions + assert not actions["--description"].required + + user_subparsers = next( + action + for action in subparsers.choices["group"]._actions + if isinstance(action, argparse._SubParsersAction) + ) + actions = user_subparsers.choices["create"]._option_string_actions + assert actions["--name"].required diff --git a/gitlab/tests/test_config.py b/gitlab/tests/test_config.py index abdeed040..7fb03e00d 100644 --- a/gitlab/tests/test_config.py +++ b/gitlab/tests/test_config.py @@ -74,105 +74,107 @@ """ -class TestEnvConfig(unittest.TestCase): - def test_env_present(self): - with mock.patch.dict(os.environ, {"PYTHON_GITLAB_CFG": "/some/path"}): - assert ["/some/path"] == config._env_config() - - def test_env_missing(self): - with mock.patch.dict(os.environ, {}, clear=True): - assert [] == config._env_config() - - -class TestConfigParser(unittest.TestCase): - @mock.patch("os.path.exists") - def test_missing_config(self, path_exists): - path_exists.return_value = False - with pytest.raises(config.GitlabConfigMissingError): - config.GitlabConfigParser("test") - - @mock.patch("os.path.exists") - @mock.patch("builtins.open") - def test_invalid_id(self, m_open, path_exists): - fd = io.StringIO(no_default_config) - fd.close = mock.Mock(return_value=None) - m_open.return_value = fd - path_exists.return_value = True - config.GitlabConfigParser("there") - with pytest.raises(config.GitlabIDError): - config.GitlabConfigParser() - - fd = io.StringIO(valid_config) - fd.close = mock.Mock(return_value=None) - m_open.return_value = fd - with pytest.raises(config.GitlabDataError): - config.GitlabConfigParser(gitlab_id="not_there") - - @mock.patch("os.path.exists") - @mock.patch("builtins.open") - def test_invalid_data(self, m_open, path_exists): - fd = io.StringIO(missing_attr_config) - fd.close = mock.Mock(return_value=None, side_effect=lambda: fd.seek(0)) - m_open.return_value = fd - path_exists.return_value = True - - config.GitlabConfigParser("one") - config.GitlabConfigParser("one") - with pytest.raises(config.GitlabDataError): - config.GitlabConfigParser(gitlab_id="two") - with pytest.raises(config.GitlabDataError): - config.GitlabConfigParser(gitlab_id="three") - with pytest.raises(config.GitlabDataError) as emgr: - config.GitlabConfigParser("four") - assert "Unsupported per_page number: 200" == emgr.value.args[0] - - @mock.patch("os.path.exists") - @mock.patch("builtins.open") - def test_valid_data(self, m_open, path_exists): - fd = io.StringIO(valid_config) - fd.close = mock.Mock(return_value=None) - m_open.return_value = fd - path_exists.return_value = True - - cp = config.GitlabConfigParser() - assert "one" == cp.gitlab_id - assert "http://one.url" == cp.url - assert "ABCDEF" == cp.private_token - assert None == cp.oauth_token - assert 2 == cp.timeout - assert True == cp.ssl_verify - assert cp.per_page is None - - fd = io.StringIO(valid_config) - fd.close = mock.Mock(return_value=None) - m_open.return_value = fd - cp = config.GitlabConfigParser(gitlab_id="two") - assert "two" == cp.gitlab_id - assert "https://two.url" == cp.url - assert "GHIJKL" == cp.private_token - assert None == cp.oauth_token - assert 10 == cp.timeout - assert False == cp.ssl_verify - - fd = io.StringIO(valid_config) - fd.close = mock.Mock(return_value=None) - m_open.return_value = fd - cp = config.GitlabConfigParser(gitlab_id="three") - assert "three" == cp.gitlab_id - assert "https://three.url" == cp.url - assert "MNOPQR" == cp.private_token - assert None == cp.oauth_token - assert 2 == cp.timeout - assert "/path/to/CA/bundle.crt" == cp.ssl_verify - assert 50 == cp.per_page - - fd = io.StringIO(valid_config) - fd.close = mock.Mock(return_value=None) - m_open.return_value = fd - cp = config.GitlabConfigParser(gitlab_id="four") - assert "four" == cp.gitlab_id - assert "https://four.url" == cp.url - assert None == cp.private_token - assert "STUV" == cp.oauth_token - assert 2 == cp.timeout - assert True == cp.ssl_verify +@mock.patch.dict(os.environ, {"PYTHON_GITLAB_CFG": "/some/path"}) +def test_env_config_present(): + assert ["/some/path"] == config._env_config() + + +@mock.patch.dict(os.environ, {}, clear=True) +def test_env_config_missing(): + assert [] == config._env_config() + + +@mock.patch("os.path.exists") +def test_missing_config(path_exists): + path_exists.return_value = False + with pytest.raises(config.GitlabConfigMissingError): + config.GitlabConfigParser("test") + + +@mock.patch("os.path.exists") +@mock.patch("builtins.open") +def test_invalid_id(m_open, path_exists): + fd = io.StringIO(no_default_config) + fd.close = mock.Mock(return_value=None) + m_open.return_value = fd + path_exists.return_value = True + config.GitlabConfigParser("there") + with pytest.raises(config.GitlabIDError): + config.GitlabConfigParser() + + fd = io.StringIO(valid_config) + fd.close = mock.Mock(return_value=None) + m_open.return_value = fd + with pytest.raises(config.GitlabDataError): + config.GitlabConfigParser(gitlab_id="not_there") + + +@mock.patch("os.path.exists") +@mock.patch("builtins.open") +def test_invalid_data(m_open, path_exists): + fd = io.StringIO(missing_attr_config) + fd.close = mock.Mock(return_value=None, side_effect=lambda: fd.seek(0)) + m_open.return_value = fd + path_exists.return_value = True + + config.GitlabConfigParser("one") + config.GitlabConfigParser("one") + with pytest.raises(config.GitlabDataError): + config.GitlabConfigParser(gitlab_id="two") + with pytest.raises(config.GitlabDataError): + config.GitlabConfigParser(gitlab_id="three") + with pytest.raises(config.GitlabDataError) as emgr: + config.GitlabConfigParser("four") + assert "Unsupported per_page number: 200" == emgr.value.args[0] + + +@mock.patch("os.path.exists") +@mock.patch("builtins.open") +def test_valid_data(m_open, path_exists): + fd = io.StringIO(valid_config) + fd.close = mock.Mock(return_value=None) + m_open.return_value = fd + path_exists.return_value = True + + cp = config.GitlabConfigParser() + assert "one" == cp.gitlab_id + assert "http://one.url" == cp.url + assert "ABCDEF" == cp.private_token + assert None == cp.oauth_token + assert 2 == cp.timeout + assert True == cp.ssl_verify + assert cp.per_page is None + + fd = io.StringIO(valid_config) + fd.close = mock.Mock(return_value=None) + m_open.return_value = fd + cp = config.GitlabConfigParser(gitlab_id="two") + assert "two" == cp.gitlab_id + assert "https://two.url" == cp.url + assert "GHIJKL" == cp.private_token + assert None == cp.oauth_token + assert 10 == cp.timeout + assert False == cp.ssl_verify + + fd = io.StringIO(valid_config) + fd.close = mock.Mock(return_value=None) + m_open.return_value = fd + cp = config.GitlabConfigParser(gitlab_id="three") + assert "three" == cp.gitlab_id + assert "https://three.url" == cp.url + assert "MNOPQR" == cp.private_token + assert None == cp.oauth_token + assert 2 == cp.timeout + assert "/path/to/CA/bundle.crt" == cp.ssl_verify + assert 50 == cp.per_page + + fd = io.StringIO(valid_config) + fd.close = mock.Mock(return_value=None) + m_open.return_value = fd + cp = config.GitlabConfigParser(gitlab_id="four") + assert "four" == cp.gitlab_id + assert "https://four.url" == cp.url + assert None == cp.private_token + assert "STUV" == cp.oauth_token + assert 2 == cp.timeout + assert True == cp.ssl_verify diff --git a/gitlab/tests/test_exceptions.py b/gitlab/tests/test_exceptions.py index 57622c09f..57b394bae 100644 --- a/gitlab/tests/test_exceptions.py +++ b/gitlab/tests/test_exceptions.py @@ -1,20 +1,18 @@ -import unittest +import pytest from gitlab import exceptions -import pytest -class TestExceptions(unittest.TestCase): - def test_error_raises_from_http_error(self): - """Methods decorated with @on_http_error should raise from GitlabHttpError.""" +def test_error_raises_from_http_error(): + """Methods decorated with @on_http_error should raise from GitlabHttpError.""" - class TestError(Exception): - pass + class TestError(Exception): + pass - @exceptions.on_http_error(TestError) - def raise_error_from_http_error(): - raise exceptions.GitlabHttpError + @exceptions.on_http_error(TestError) + def raise_error_from_http_error(): + raise exceptions.GitlabHttpError - with pytest.raises(TestError) as context: - raise_error_from_http_error() - assert isinstance(context.value.__cause__, exceptions.GitlabHttpError) + with pytest.raises(TestError) as context: + raise_error_from_http_error() + assert isinstance(context.value.__cause__, exceptions.GitlabHttpError) diff --git a/gitlab/tests/test_gitlab.py b/gitlab/tests/test_gitlab.py index 59139e4af..553afb3a4 100644 --- a/gitlab/tests/test_gitlab.py +++ b/gitlab/tests/test_gitlab.py @@ -5,7 +5,7 @@ # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation, either version 3 of the License, or +# the Free Software Foundation, either version 3 of the License, or` # (at your option) any later version. # # This program is distributed in the hope that it will be useful, @@ -16,946 +16,126 @@ # You should have received a copy of the GNU Lesser General Public License # along with this program. If not, see . -import os import pickle -import tempfile -import json -import unittest -from httmock import HTTMock # noqa -from httmock import response # noqa -from httmock import urlmatch # noqa -import requests +from httmock import HTTMock, response, urlmatch, with_httmock # noqa -import gitlab -from gitlab import * # noqa -from gitlab.v4.objects import * # noqa -import pytest +from gitlab import Gitlab, GitlabList +from gitlab.v4.objects import CurrentUser + + +username = "username" +user_id = 1 -valid_config = b"""[global] -default = one -ssl_verify = true -timeout = 2 +@urlmatch(scheme="http", netloc="localhost", path="/api/v4/user", method="get") +def resp_get_user(url, request): + headers = {"content-type": "application/json"} + content = '{{"id": {0:d}, "username": "{1:s}"}}'.format(user_id, username).encode( + "utf-8" + ) + return response(200, content, headers, None, 5, request) -[one] -url = http://one.url -private_token = ABCDEF -""" +@urlmatch(scheme="http", netloc="localhost", path="/api/v4/tests", method="get") +def resp_page_1(url, request): + headers = { + "content-type": "application/json", + "X-Page": 1, + "X-Next-Page": 2, + "X-Per-Page": 1, + "X-Total-Pages": 2, + "X-Total": 2, + "Link": (";" ' rel="next"'), + } + content = '[{"a": "b"}]' + return response(200, content, headers, None, 5, request) -class TestSanitize(unittest.TestCase): - def test_do_nothing(self): - assert 1 == gitlab._sanitize(1) - assert 1.5 == gitlab._sanitize(1.5) - assert "foo" == gitlab._sanitize("foo") - def test_slash(self): - assert "foo%2Fbar" == gitlab._sanitize("foo/bar") +@urlmatch( + scheme="http", + netloc="localhost", + path="/api/v4/tests", + method="get", + query=r".*page=2", +) +def resp_page_2(url, request): + headers = { + "content-type": "application/json", + "X-Page": 2, + "X-Next-Page": 2, + "X-Per-Page": 1, + "X-Total-Pages": 2, + "X-Total": 2, + } + content = '[{"c": "d"}]' + return response(200, content, headers, None, 5, request) - def test_dict(self): - source = {"url": "foo/bar", "id": 1} - expected = {"url": "foo%2Fbar", "id": 1} - assert expected == gitlab._sanitize(source) +def test_gitlab_build_list(gl): + with HTTMock(resp_page_1): + obj = gl.http_list("/tests", as_list=False) + assert len(obj) == 2 + assert obj._next_url == "http://localhost/api/v4/tests?per_page=1&page=2" + assert obj.current_page == 1 + assert obj.prev_page is None + assert obj.next_page == 2 + assert obj.per_page == 1 + assert obj.total_pages == 2 + assert obj.total == 2 -class TestGitlabList(unittest.TestCase): - def setUp(self): - self.gl = Gitlab( - "http://localhost", private_token="private_token", api_version=4 - ) + with HTTMock(resp_page_2): + l = list(obj) + assert len(l) == 2 + assert l[0]["a"] == "b" + assert l[1]["c"] == "d" - def test_build_list(self): - @urlmatch(scheme="http", netloc="localhost", path="/api/v4/tests", method="get") - def resp_1(url, request): - headers = { - "content-type": "application/json", - "X-Page": 1, - "X-Next-Page": 2, - "X-Per-Page": 1, - "X-Total-Pages": 2, - "X-Total": 2, - "Link": ( - ";" ' rel="next"' - ), - } - content = '[{"a": "b"}]' - return response(200, content, headers, None, 5, request) - @urlmatch( - scheme="http", - netloc="localhost", - path="/api/v4/tests", - method="get", - query=r".*page=2", - ) - def resp_2(url, request): - headers = { - "content-type": "application/json", - "X-Page": 2, - "X-Next-Page": 2, - "X-Per-Page": 1, - "X-Total-Pages": 2, - "X-Total": 2, - } - content = '[{"c": "d"}]' - return response(200, content, headers, None, 5, request) +@with_httmock(resp_page_1, resp_page_2) +def test_gitlab_all_omitted_when_as_list(gl): + result = gl.http_list("/tests", as_list=False, all=True) + assert isinstance(result, GitlabList) - with HTTMock(resp_1): - obj = self.gl.http_list("/tests", as_list=False) - assert len(obj) == 2 - assert obj._next_url == "http://localhost/api/v4/tests?per_page=1&page=2" - assert obj.current_page == 1 - assert obj.prev_page == None - assert obj.next_page == 2 - assert obj.per_page == 1 - assert obj.total_pages == 2 - assert obj.total == 2 - with HTTMock(resp_2): - l = list(obj) - assert len(l) == 2 - assert l[0]["a"] == "b" - assert l[1]["c"] == "d" +def test_gitlab_strip_base_url(gl_trailing): + assert gl_trailing.url == "http://localhost" - def test_all_omitted_when_as_list(self): - @urlmatch(scheme="http", netloc="localhost", path="/api/v4/tests", method="get") - def resp(url, request): - headers = { - "content-type": "application/json", - "X-Page": 2, - "X-Next-Page": 2, - "X-Per-Page": 1, - "X-Total-Pages": 2, - "X-Total": 2, - } - content = '[{"c": "d"}]' - return response(200, content, headers, None, 5, request) - with HTTMock(resp): - result = self.gl.http_list("/tests", as_list=False, all=True) - assert isinstance(result, GitlabList) +def test_gitlab_strip_api_url(gl_trailing): + assert gl_trailing.api_url == "http://localhost/api/v4" -class TestGitlabHttpMethods(unittest.TestCase): - def setUp(self): - self.gl = Gitlab( - "http://localhost", private_token="private_token", api_version=4 - ) +def test_gitlab_build_url(gl_trailing): + r = gl_trailing._build_url("/projects") + assert r == "http://localhost/api/v4/projects" - def test_build_url(self): - r = self.gl._build_url("http://localhost/api/v4") - assert r == "http://localhost/api/v4" - r = self.gl._build_url("https://localhost/api/v4") - assert r == "https://localhost/api/v4" - r = self.gl._build_url("/projects") - assert r == "http://localhost/api/v4/projects" - def test_http_request(self): - @urlmatch( - scheme="http", netloc="localhost", path="/api/v4/projects", method="get" - ) - def resp_cont(url, request): - headers = {"content-type": "application/json"} - content = '[{"name": "project1"}]' - return response(200, content, headers, None, 5, request) +def test_gitlab_pickability(gl): + original_gl_objects = gl._objects + pickled = pickle.dumps(gl) + unpickled = pickle.loads(pickled) + assert isinstance(unpickled, Gitlab) + assert hasattr(unpickled, "_objects") + assert unpickled._objects == original_gl_objects - with HTTMock(resp_cont): - http_r = self.gl.http_request("get", "/projects") - http_r.json() - assert http_r.status_code == 200 - - def test_http_request_404(self): - @urlmatch( - scheme="http", netloc="localhost", path="/api/v4/not_there", method="get" - ) - def resp_cont(url, request): - content = {"Here is wh it failed"} - return response(404, content, {}, None, 5, request) - - with HTTMock(resp_cont): - with pytest.raises(GitlabHttpError): - self.gl.http_request("get", "/not_there") - - def test_get_request(self): - @urlmatch( - scheme="http", netloc="localhost", path="/api/v4/projects", method="get" - ) - def resp_cont(url, request): - headers = {"content-type": "application/json"} - content = '{"name": "project1"}' - return response(200, content, headers, None, 5, request) - - with HTTMock(resp_cont): - result = self.gl.http_get("/projects") - assert isinstance(result, dict) - assert result["name"] == "project1" - - def test_get_request_raw(self): - @urlmatch( - scheme="http", netloc="localhost", path="/api/v4/projects", method="get" - ) - def resp_cont(url, request): - headers = {"content-type": "application/octet-stream"} - content = "content" - return response(200, content, headers, None, 5, request) - - with HTTMock(resp_cont): - result = self.gl.http_get("/projects") - assert result.content.decode("utf-8") == "content" - - def test_get_request_404(self): - @urlmatch( - scheme="http", netloc="localhost", path="/api/v4/not_there", method="get" - ) - def resp_cont(url, request): - content = {"Here is wh it failed"} - return response(404, content, {}, None, 5, request) - - with HTTMock(resp_cont): - with pytest.raises(GitlabHttpError): - self.gl.http_get("/not_there") - - def test_get_request_invalid_data(self): - @urlmatch( - scheme="http", netloc="localhost", path="/api/v4/projects", method="get" - ) - def resp_cont(url, request): - headers = {"content-type": "application/json"} - content = '["name": "project1"]' - return response(200, content, headers, None, 5, request) - - with HTTMock(resp_cont): - with pytest.raises(GitlabParsingError): - self.gl.http_get("/projects") - - def test_list_request(self): - @urlmatch( - scheme="http", netloc="localhost", path="/api/v4/projects", method="get" - ) - def resp_cont(url, request): - headers = {"content-type": "application/json", "X-Total": 1} - content = '[{"name": "project1"}]' - return response(200, content, headers, None, 5, request) - - with HTTMock(resp_cont): - result = self.gl.http_list("/projects", as_list=True) - assert isinstance(result, list) - assert len(result) == 1 - - with HTTMock(resp_cont): - result = self.gl.http_list("/projects", as_list=False) - assert isinstance(result, GitlabList) - assert len(result) == 1 - - with HTTMock(resp_cont): - result = self.gl.http_list("/projects", all=True) - assert isinstance(result, list) - assert len(result) == 1 - - def test_list_request_404(self): - @urlmatch( - scheme="http", netloc="localhost", path="/api/v4/not_there", method="get" - ) - def resp_cont(url, request): - content = {"Here is why it failed"} - return response(404, content, {}, None, 5, request) - - with HTTMock(resp_cont): - with pytest.raises(GitlabHttpError): - self.gl.http_list("/not_there") - - def test_list_request_invalid_data(self): - @urlmatch( - scheme="http", netloc="localhost", path="/api/v4/projects", method="get" - ) - def resp_cont(url, request): - headers = {"content-type": "application/json"} - content = '["name": "project1"]' - return response(200, content, headers, None, 5, request) - - with HTTMock(resp_cont): - with pytest.raises(GitlabParsingError): - self.gl.http_list("/projects") - - def test_post_request(self): - @urlmatch( - scheme="http", netloc="localhost", path="/api/v4/projects", method="post" - ) - def resp_cont(url, request): - headers = {"content-type": "application/json"} - content = '{"name": "project1"}' - return response(200, content, headers, None, 5, request) - - with HTTMock(resp_cont): - result = self.gl.http_post("/projects") - assert isinstance(result, dict) - assert result["name"] == "project1" - - def test_post_request_404(self): - @urlmatch( - scheme="http", netloc="localhost", path="/api/v4/not_there", method="post" - ) - def resp_cont(url, request): - content = {"Here is wh it failed"} - return response(404, content, {}, None, 5, request) - - with HTTMock(resp_cont): - with pytest.raises(GitlabHttpError): - self.gl.http_post("/not_there") - - def test_post_request_invalid_data(self): - @urlmatch( - scheme="http", netloc="localhost", path="/api/v4/projects", method="post" - ) - def resp_cont(url, request): - headers = {"content-type": "application/json"} - content = '["name": "project1"]' - return response(200, content, headers, None, 5, request) - - with HTTMock(resp_cont): - with pytest.raises(GitlabParsingError): - self.gl.http_post("/projects") - - def test_put_request(self): - @urlmatch( - scheme="http", netloc="localhost", path="/api/v4/projects", method="put" - ) - def resp_cont(url, request): - headers = {"content-type": "application/json"} - content = '{"name": "project1"}' - return response(200, content, headers, None, 5, request) - - with HTTMock(resp_cont): - result = self.gl.http_put("/projects") - assert isinstance(result, dict) - assert result["name"] == "project1" - - def test_put_request_404(self): - @urlmatch( - scheme="http", netloc="localhost", path="/api/v4/not_there", method="put" - ) - def resp_cont(url, request): - content = {"Here is wh it failed"} - return response(404, content, {}, None, 5, request) - - with HTTMock(resp_cont): - with pytest.raises(GitlabHttpError): - self.gl.http_put("/not_there") - - def test_put_request_invalid_data(self): - @urlmatch( - scheme="http", netloc="localhost", path="/api/v4/projects", method="put" - ) - def resp_cont(url, request): - headers = {"content-type": "application/json"} - content = '["name": "project1"]' - return response(200, content, headers, None, 5, request) - - with HTTMock(resp_cont): - with pytest.raises(GitlabParsingError): - self.gl.http_put("/projects") - - def test_delete_request(self): - @urlmatch( - scheme="http", netloc="localhost", path="/api/v4/projects", method="delete" - ) - def resp_cont(url, request): - headers = {"content-type": "application/json"} - content = "true" - return response(200, content, headers, None, 5, request) - - with HTTMock(resp_cont): - result = self.gl.http_delete("/projects") - assert isinstance(result, requests.Response) - assert result.json() == True - - def test_delete_request_404(self): - @urlmatch( - scheme="http", netloc="localhost", path="/api/v4/not_there", method="delete" - ) - def resp_cont(url, request): - content = {"Here is wh it failed"} - return response(404, content, {}, None, 5, request) - - with HTTMock(resp_cont): - with pytest.raises(GitlabHttpError): - self.gl.http_delete("/not_there") - - -class TestGitlabStripBaseUrl(unittest.TestCase): - def setUp(self): - self.gl = Gitlab( - "http://localhost/", private_token="private_token", api_version=4 - ) - - def test_strip_base_url(self): - assert self.gl.url == "http://localhost" - - def test_strip_api_url(self): - assert self.gl.api_url == "http://localhost/api/v4" - - def test_build_url(self): - r = self.gl._build_url("/projects") - assert r == "http://localhost/api/v4/projects" - - -class TestGitlabAuth(unittest.TestCase): - def test_invalid_auth_args(self): - with pytest.raises(ValueError): - Gitlab( - "http://localhost", - api_version="4", - private_token="private_token", - oauth_token="bearer", - ) - with pytest.raises(ValueError): - Gitlab( - "http://localhost", - api_version="4", - oauth_token="bearer", - http_username="foo", - http_password="bar", - ) - with pytest.raises(ValueError): - Gitlab( - "http://localhost", - api_version="4", - private_token="private_token", - http_password="bar", - ) - with pytest.raises(ValueError): - Gitlab( - "http://localhost", - api_version="4", - private_token="private_token", - http_username="foo", - ) - - def test_private_token_auth(self): - gl = Gitlab("http://localhost", private_token="private_token", api_version="4") - assert gl.private_token == "private_token" - assert gl.oauth_token == None - assert gl.job_token == None - assert gl._http_auth == None - assert "Authorization" not in gl.headers - assert gl.headers["PRIVATE-TOKEN"] == "private_token" - assert "JOB-TOKEN" not in gl.headers - - def test_oauth_token_auth(self): - gl = Gitlab("http://localhost", oauth_token="oauth_token", api_version="4") - assert gl.private_token == None - assert gl.oauth_token == "oauth_token" - assert gl.job_token == None - assert gl._http_auth == None - assert gl.headers["Authorization"] == "Bearer oauth_token" - assert "PRIVATE-TOKEN" not in gl.headers - assert "JOB-TOKEN" not in gl.headers - - def test_job_token_auth(self): - gl = Gitlab("http://localhost", job_token="CI_JOB_TOKEN", api_version="4") - assert gl.private_token == None - assert gl.oauth_token == None - assert gl.job_token == "CI_JOB_TOKEN" - assert gl._http_auth == None - assert "Authorization" not in gl.headers - assert "PRIVATE-TOKEN" not in gl.headers - assert gl.headers["JOB-TOKEN"] == "CI_JOB_TOKEN" - - def test_http_auth(self): - gl = Gitlab( - "http://localhost", - private_token="private_token", - http_username="foo", - http_password="bar", - api_version="4", - ) - assert gl.private_token == "private_token" - assert gl.oauth_token == None - assert gl.job_token == None - assert isinstance(gl._http_auth, requests.auth.HTTPBasicAuth) - assert gl.headers["PRIVATE-TOKEN"] == "private_token" - assert "Authorization" not in gl.headers - - -class TestGitlab(unittest.TestCase): - def setUp(self): - self.gl = Gitlab( - "http://localhost", - private_token="private_token", - ssl_verify=True, - api_version=4, - ) - - def test_pickability(self): - original_gl_objects = self.gl._objects - pickled = pickle.dumps(self.gl) - unpickled = pickle.loads(pickled) - assert isinstance(unpickled, Gitlab) - assert hasattr(unpickled, "_objects") - assert unpickled._objects == original_gl_objects - - def test_token_auth(self, callback=None): - name = "username" - id_ = 1 - - @urlmatch(scheme="http", netloc="localhost", path="/api/v4/user", method="get") - def resp_cont(url, request): - headers = {"content-type": "application/json"} - content = '{{"id": {0:d}, "username": "{1:s}"}}'.format(id_, name).encode( - "utf-8" - ) - return response(200, content, headers, None, 5, request) - - with HTTMock(resp_cont): - self.gl.auth() - assert self.gl.user.username == name - assert self.gl.user.id == id_ - assert isinstance(self.gl.user, CurrentUser) - - def test_hooks(self): - @urlmatch( - scheme="http", netloc="localhost", path="/api/v4/hooks/1", method="get" - ) - def resp_get_hook(url, request): - headers = {"content-type": "application/json"} - content = '{"url": "testurl", "id": 1}'.encode("utf-8") - return response(200, content, headers, None, 5, request) - - with HTTMock(resp_get_hook): - data = self.gl.hooks.get(1) - assert isinstance(data, Hook) - assert data.url == "testurl" - assert data.id == 1 - - def test_projects(self): - @urlmatch( - scheme="http", netloc="localhost", path="/api/v4/projects/1", method="get" - ) - def resp_get_project(url, request): - headers = {"content-type": "application/json"} - content = '{"name": "name", "id": 1}'.encode("utf-8") - return response(200, content, headers, None, 5, request) - - with HTTMock(resp_get_project): - data = self.gl.projects.get(1) - assert isinstance(data, Project) - assert data.name == "name" - assert data.id == 1 - - def test_project_environments(self): - @urlmatch( - scheme="http", netloc="localhost", path="/api/v4/projects/1$", method="get" - ) - def resp_get_project(url, request): - headers = {"content-type": "application/json"} - content = '{"name": "name", "id": 1}'.encode("utf-8") - return response(200, content, headers, None, 5, request) - - @urlmatch( - scheme="http", - netloc="localhost", - path="/api/v4/projects/1/environments/1", - method="get", - ) - def resp_get_environment(url, request): - headers = {"content-type": "application/json"} - content = '{"name": "environment_name", "id": 1, "last_deployment": "sometime"}'.encode( - "utf-8" - ) - return response(200, content, headers, None, 5, request) - - with HTTMock(resp_get_project, resp_get_environment): - project = self.gl.projects.get(1) - environment = project.environments.get(1) - assert isinstance(environment, ProjectEnvironment) - assert environment.id == 1 - assert environment.last_deployment == "sometime" - assert environment.name == "environment_name" - - def test_project_additional_statistics(self): - @urlmatch( - scheme="http", netloc="localhost", path="/api/v4/projects/1$", method="get" - ) - def resp_get_project(url, request): - headers = {"content-type": "application/json"} - content = '{"name": "name", "id": 1}'.encode("utf-8") - return response(200, content, headers, None, 5, request) - - @urlmatch( - scheme="http", - netloc="localhost", - path="/api/v4/projects/1/statistics", - method="get", - ) - def resp_get_environment(url, request): - headers = {"content-type": "application/json"} - content = """{"fetches": {"total": 50, "days": [{"count": 10, "date": "2018-01-10"}]}}""".encode( - "utf-8" - ) - return response(200, content, headers, None, 5, request) - - with HTTMock(resp_get_project, resp_get_environment): - project = self.gl.projects.get(1) - statistics = project.additionalstatistics.get() - assert isinstance(statistics, ProjectAdditionalStatistics) - assert statistics.fetches["total"] == 50 - - def test_project_issues_statistics(self): - @urlmatch( - scheme="http", netloc="localhost", path="/api/v4/projects/1$", method="get" - ) - def resp_get_project(url, request): - headers = {"content-type": "application/json"} - content = '{"name": "name", "id": 1}'.encode("utf-8") - return response(200, content, headers, None, 5, request) - - @urlmatch( - scheme="http", - netloc="localhost", - path="/api/v4/projects/1/issues_statistics", - method="get", - ) - def resp_get_environment(url, request): - headers = {"content-type": "application/json"} - content = """{"statistics": {"counts": {"all": 20, "closed": 5, "opened": 15}}}""".encode( - "utf-8" - ) - return response(200, content, headers, None, 5, request) - - with HTTMock(resp_get_project, resp_get_environment): - project = self.gl.projects.get(1) - statistics = project.issuesstatistics.get() - assert isinstance(statistics, ProjectIssuesStatistics) - assert statistics.statistics["counts"]["all"] == 20 - - def test_issues(self): - @urlmatch( - scheme="http", netloc="localhost", path="/api/v4/issues", method="get" - ) - def resp_get_issue(url, request): - headers = {"content-type": "application/json"} - content = '[{"name": "name", "id": 1}, ' '{"name": "other_name", "id": 2}]' - content = content.encode("utf-8") - return response(200, content, headers, None, 5, request) - - with HTTMock(resp_get_issue): - data = self.gl.issues.list() - assert data[1].id == 2 - assert data[1].name == "other_name" - - @urlmatch(scheme="http", netloc="localhost", path="/api/v4/users/1", method="get") - def resp_get_user(self, url, request): - headers = {"content-type": "application/json"} - content = ( - '{"name": "name", "id": 1, "password": "password", ' - '"username": "username", "email": "email"}' - ) - content = content.encode("utf-8") - return response(200, content, headers, None, 5, request) - - def test_users(self): - with HTTMock(self.resp_get_user): - user = self.gl.users.get(1) - assert isinstance(user, User) - assert user.name == "name" - assert user.id == 1 - - def test_user_memberships(self): - @urlmatch( - scheme="http", - netloc="localhost", - path="/api/v4/users/1/memberships", - method="get", - ) - def resp_get_user_memberships(url, request): - headers = {"content-type": "application/json"} - content = """[ - { - "source_id": 1, - "source_name": "Project one", - "source_type": "Project", - "access_level": "20" - }, - { - "source_id": 3, - "source_name": "Group three", - "source_type": "Namespace", - "access_level": "20" - } - ]""" - content = content.encode("utf-8") - return response(200, content, headers, None, 5, request) - - with HTTMock(resp_get_user_memberships): - user = self.gl.users.get(1, lazy=True) - memberships = user.memberships.list() - assert isinstance(memberships[0], UserMembership) - assert memberships[0].source_type == "Project" - - def test_user_status(self): - @urlmatch( - scheme="http", - netloc="localhost", - path="/api/v4/users/1/status", - method="get", - ) - def resp_get_user_status(url, request): - headers = {"content-type": "application/json"} - content = '{"message": "test", "message_html": "

Message

", "emoji": "thumbsup"}' - content = content.encode("utf-8") - return response(200, content, headers, None, 5, request) - - with HTTMock(self.resp_get_user): - user = self.gl.users.get(1) - with HTTMock(resp_get_user_status): - status = user.status.get() - assert isinstance(status, UserStatus) - assert status.message == "test" - assert status.emoji == "thumbsup" - - def test_todo(self): - with open(os.path.dirname(__file__) + "/data/todo.json", "r") as json_file: - todo_content = json_file.read() - json_content = json.loads(todo_content) - encoded_content = todo_content.encode("utf-8") - - @urlmatch(scheme="http", netloc="localhost", path="/api/v4/todos", method="get") - def resp_get_todo(url, request): - headers = {"content-type": "application/json"} - return response(200, encoded_content, headers, None, 5, request) - - @urlmatch( - scheme="http", - netloc="localhost", - path="/api/v4/todos/102/mark_as_done", - method="post", - ) - def resp_mark_as_done(url, request): - headers = {"content-type": "application/json"} - single_todo = json.dumps(json_content[0]) - content = single_todo.encode("utf-8") - return response(200, content, headers, None, 5, request) - - with HTTMock(resp_get_todo): - todo = self.gl.todos.list()[0] - assert isinstance(todo, Todo) - assert todo.id == 102 - assert todo.target_type == "MergeRequest" - assert todo.target["assignee"]["username"] == "root" - with HTTMock(resp_mark_as_done): - todo.mark_as_done() - - def test_todo_mark_all_as_done(self): - @urlmatch( - scheme="http", - netloc="localhost", - path="/api/v4/todos/mark_as_done", - method="post", - ) - def resp_mark_all_as_done(url, request): - headers = {"content-type": "application/json"} - return response(204, {}, headers, None, 5, request) - - with HTTMock(resp_mark_all_as_done): - self.gl.todos.mark_all_as_done() - - def test_deployment(self): - content = '{"id": 42, "status": "success", "ref": "master"}' - json_content = json.loads(content) - - @urlmatch( - scheme="http", - netloc="localhost", - path="/api/v4/projects/1/deployments", - method="post", - ) - def resp_deployment_create(url, request): - headers = {"content-type": "application/json"} - return response(200, json_content, headers, None, 5, request) - - @urlmatch( - scheme="http", - netloc="localhost", - path="/api/v4/projects/1/deployments/42", - method="put", - ) - def resp_deployment_update(url, request): - headers = {"content-type": "application/json"} - return response(200, json_content, headers, None, 5, request) - - with HTTMock(resp_deployment_create): - deployment = self.gl.projects.get(1, lazy=True).deployments.create( - { - "environment": "Test", - "sha": "1agf4gs", - "ref": "master", - "tag": False, - "status": "created", - } - ) - assert deployment.id == 42 - assert deployment.status == "success" - assert deployment.ref == "master" - - with HTTMock(resp_deployment_update): - json_content["status"] = "failed" - deployment.status = "failed" - deployment.save() - assert deployment.status == "failed" - - def test_user_activate_deactivate(self): - @urlmatch( - scheme="http", - netloc="localhost", - path="/api/v4/users/1/activate", - method="post", - ) - def resp_activate(url, request): - headers = {"content-type": "application/json"} - return response(201, {}, headers, None, 5, request) - - @urlmatch( - scheme="http", - netloc="localhost", - path="/api/v4/users/1/deactivate", - method="post", - ) - def resp_deactivate(url, request): - headers = {"content-type": "application/json"} - return response(201, {}, headers, None, 5, request) - - with HTTMock(resp_activate), HTTMock(resp_deactivate): - self.gl.users.get(1, lazy=True).activate() - self.gl.users.get(1, lazy=True).deactivate() - - def test_update_submodule(self): - @urlmatch( - scheme="http", netloc="localhost", path="/api/v4/projects/1$", method="get" - ) - def resp_get_project(url, request): - headers = {"content-type": "application/json"} - content = '{"name": "name", "id": 1}'.encode("utf-8") - return response(200, content, headers, None, 5, request) - - @urlmatch( - scheme="http", - netloc="localhost", - path="/api/v4/projects/1/repository/submodules/foo%2Fbar", - method="put", - ) - def resp_update_submodule(url, request): - headers = {"content-type": "application/json"} - content = """{ - "id": "ed899a2f4b50b4370feeea94676502b42383c746", - "short_id": "ed899a2f4b5", - "title": "Message", - "author_name": "Author", - "author_email": "author@example.com", - "committer_name": "Author", - "committer_email": "author@example.com", - "created_at": "2018-09-20T09:26:24.000-07:00", - "message": "Message", - "parent_ids": [ "ae1d9fb46aa2b07ee9836d49862ec4e2c46fbbba" ], - "committed_date": "2018-09-20T09:26:24.000-07:00", - "authored_date": "2018-09-20T09:26:24.000-07:00", - "status": null}""" - content = content.encode("utf-8") - return response(200, content, headers, None, 5, request) - - with HTTMock(resp_get_project): - project = self.gl.projects.get(1) - assert isinstance(project, Project) - assert project.name == "name" - assert project.id == 1 - with HTTMock(resp_update_submodule): - ret = project.update_submodule( - submodule="foo/bar", - branch="master", - commit_sha="4c3674f66071e30b3311dac9b9ccc90502a72664", - commit_message="Message", - ) - assert isinstance(ret, dict) - assert ret["message"] == "Message" - assert ret["id"] == "ed899a2f4b50b4370feeea94676502b42383c746" - - def test_applications(self): - content = '{"name": "test_app", "redirect_uri": "http://localhost:8080", "scopes": ["api", "email"]}' - json_content = json.loads(content) - - @urlmatch( - scheme="http", - netloc="localhost", - path="/api/v4/applications", - method="post", - ) - def resp_application_create(url, request): - headers = {"content-type": "application/json"} - return response(200, json_content, headers, None, 5, request) - - with HTTMock(resp_application_create): - application = self.gl.applications.create( - { - "name": "test_app", - "redirect_uri": "http://localhost:8080", - "scopes": ["api", "email"], - "confidential": False, - } - ) - assert application.name == "test_app" - assert application.redirect_uri == "http://localhost:8080" - assert application.scopes == ["api", "email"] - - def test_deploy_tokens(self): - @urlmatch( - scheme="http", - netloc="localhost", - path="/api/v4/projects/1/deploy_tokens", - method="post", - ) - def resp_deploy_token_create(url, request): - headers = {"content-type": "application/json"} - content = """{ - "id": 1, - "name": "test_deploy_token", - "username": "custom-user", - "expires_at": "2022-01-01T00:00:00.000Z", - "token": "jMRvtPNxrn3crTAGukpZ", - "scopes": [ "read_repository" ]}""" - content = content.encode("utf-8") - return response(200, content, headers, None, 5, request) - - with HTTMock(resp_deploy_token_create): - deploy_token = self.gl.projects.get(1, lazy=True).deploytokens.create( - { - "name": "test_deploy_token", - "expires_at": "2022-01-01T00:00:00.000Z", - "username": "custom-user", - "scopes": ["read_repository"], - } - ) - assert isinstance(deploy_token, ProjectDeployToken) - assert deploy_token.id == 1 - assert deploy_token.expires_at == "2022-01-01T00:00:00.000Z" - assert deploy_token.username == "custom-user" - assert deploy_token.scopes == ["read_repository"] - - def _default_config(self): - fd, temp_path = tempfile.mkstemp() - os.write(fd, valid_config) - os.close(fd) - return temp_path - - def test_from_config(self): - config_path = self._default_config() - gitlab.Gitlab.from_config("one", [config_path]) - os.unlink(config_path) - - def test_subclass_from_config(self): - class MyGitlab(gitlab.Gitlab): - pass - - config_path = self._default_config() - gl = MyGitlab.from_config("one", [config_path]) - assert isinstance(gl, MyGitlab) - os.unlink(config_path) + +@with_httmock(resp_get_user) +def test_gitlab_token_auth(gl, callback=None): + gl.auth() + assert gl.user.username == username + assert gl.user.id == user_id + assert isinstance(gl.user, CurrentUser) + + +def test_gitlab_from_config(default_config): + config_path = default_config + Gitlab.from_config("one", [config_path]) + + +def test_gitlab_subclass_from_config(default_config): + class MyGitlab(Gitlab): + pass + + config_path = default_config + gl = MyGitlab.from_config("one", [config_path]) + assert isinstance(gl, MyGitlab) diff --git a/gitlab/tests/test_gitlab_auth.py b/gitlab/tests/test_gitlab_auth.py new file mode 100644 index 000000000..314fbedb9 --- /dev/null +++ b/gitlab/tests/test_gitlab_auth.py @@ -0,0 +1,85 @@ +import pytest +import requests + +from gitlab import Gitlab + + +def test_invalid_auth_args(): + with pytest.raises(ValueError): + Gitlab( + "http://localhost", + api_version="4", + private_token="private_token", + oauth_token="bearer", + ) + with pytest.raises(ValueError): + Gitlab( + "http://localhost", + api_version="4", + oauth_token="bearer", + http_username="foo", + http_password="bar", + ) + with pytest.raises(ValueError): + Gitlab( + "http://localhost", + api_version="4", + private_token="private_token", + http_password="bar", + ) + with pytest.raises(ValueError): + Gitlab( + "http://localhost", + api_version="4", + private_token="private_token", + http_username="foo", + ) + + +def test_private_token_auth(): + gl = Gitlab("http://localhost", private_token="private_token", api_version="4") + assert gl.private_token == "private_token" + assert gl.oauth_token is None + assert gl.job_token is None + assert gl._http_auth is None + assert "Authorization" not in gl.headers + assert gl.headers["PRIVATE-TOKEN"] == "private_token" + assert "JOB-TOKEN" not in gl.headers + + +def test_oauth_token_auth(): + gl = Gitlab("http://localhost", oauth_token="oauth_token", api_version="4") + assert gl.private_token is None + assert gl.oauth_token == "oauth_token" + assert gl.job_token is None + assert gl._http_auth is None + assert gl.headers["Authorization"] == "Bearer oauth_token" + assert "PRIVATE-TOKEN" not in gl.headers + assert "JOB-TOKEN" not in gl.headers + + +def test_job_token_auth(): + gl = Gitlab("http://localhost", job_token="CI_JOB_TOKEN", api_version="4") + assert gl.private_token is None + assert gl.oauth_token is None + assert gl.job_token == "CI_JOB_TOKEN" + assert gl._http_auth is None + assert "Authorization" not in gl.headers + assert "PRIVATE-TOKEN" not in gl.headers + assert gl.headers["JOB-TOKEN"] == "CI_JOB_TOKEN" + + +def test_http_auth(): + gl = Gitlab( + "http://localhost", + private_token="private_token", + http_username="foo", + http_password="bar", + api_version="4", + ) + assert gl.private_token == "private_token" + assert gl.oauth_token is None + assert gl.job_token is None + assert isinstance(gl._http_auth, requests.auth.HTTPBasicAuth) + assert gl.headers["PRIVATE-TOKEN"] == "private_token" + assert "Authorization" not in gl.headers diff --git a/gitlab/tests/test_gitlab_http_methods.py b/gitlab/tests/test_gitlab_http_methods.py new file mode 100644 index 000000000..fac89b9a9 --- /dev/null +++ b/gitlab/tests/test_gitlab_http_methods.py @@ -0,0 +1,234 @@ +import pytest + +from httmock import HTTMock, urlmatch, response + +from gitlab import * + + +def test_build_url(gl): + r = gl._build_url("http://localhost/api/v4") + assert r == "http://localhost/api/v4" + r = gl._build_url("https://localhost/api/v4") + assert r == "https://localhost/api/v4" + r = gl._build_url("/projects") + assert r == "http://localhost/api/v4/projects" + + +def test_http_request(gl): + @urlmatch(scheme="http", netloc="localhost", path="/api/v4/projects", method="get") + def resp_cont(url, request): + headers = {"content-type": "application/json"} + content = '[{"name": "project1"}]' + return response(200, content, headers, None, 5, request) + + with HTTMock(resp_cont): + http_r = gl.http_request("get", "/projects") + http_r.json() + assert http_r.status_code == 200 + + +def test_http_request_404(gl): + @urlmatch(scheme="http", netloc="localhost", path="/api/v4/not_there", method="get") + def resp_cont(url, request): + content = {"Here is wh it failed"} + return response(404, content, {}, None, 5, request) + + with HTTMock(resp_cont): + with pytest.raises(GitlabHttpError): + gl.http_request("get", "/not_there") + + +def test_get_request(gl): + @urlmatch(scheme="http", netloc="localhost", path="/api/v4/projects", method="get") + def resp_cont(url, request): + headers = {"content-type": "application/json"} + content = '{"name": "project1"}' + return response(200, content, headers, None, 5, request) + + with HTTMock(resp_cont): + result = gl.http_get("/projects") + assert isinstance(result, dict) + assert result["name"] == "project1" + + +def test_get_request_raw(gl): + @urlmatch(scheme="http", netloc="localhost", path="/api/v4/projects", method="get") + def resp_cont(url, request): + headers = {"content-type": "application/octet-stream"} + content = "content" + return response(200, content, headers, None, 5, request) + + with HTTMock(resp_cont): + result = gl.http_get("/projects") + assert result.content.decode("utf-8") == "content" + + +def test_get_request_404(gl): + @urlmatch(scheme="http", netloc="localhost", path="/api/v4/not_there", method="get") + def resp_cont(url, request): + content = {"Here is wh it failed"} + return response(404, content, {}, None, 5, request) + + with HTTMock(resp_cont): + with pytest.raises(GitlabHttpError): + gl.http_get("/not_there") + + +def test_get_request_invalid_data(gl): + @urlmatch(scheme="http", netloc="localhost", path="/api/v4/projects", method="get") + def resp_cont(url, request): + headers = {"content-type": "application/json"} + content = '["name": "project1"]' + return response(200, content, headers, None, 5, request) + + with HTTMock(resp_cont): + with pytest.raises(GitlabParsingError): + gl.http_get("/projects") + + +def test_list_request(gl): + @urlmatch(scheme="http", netloc="localhost", path="/api/v4/projects", method="get") + def resp_cont(url, request): + headers = {"content-type": "application/json", "X-Total": 1} + content = '[{"name": "project1"}]' + return response(200, content, headers, None, 5, request) + + with HTTMock(resp_cont): + result = gl.http_list("/projects", as_list=True) + assert isinstance(result, list) + assert len(result) == 1 + + with HTTMock(resp_cont): + result = gl.http_list("/projects", as_list=False) + assert isinstance(result, GitlabList) + assert len(result) == 1 + + with HTTMock(resp_cont): + result = gl.http_list("/projects", all=True) + assert isinstance(result, list) + assert len(result) == 1 + + +def test_list_request_404(gl): + @urlmatch(scheme="http", netloc="localhost", path="/api/v4/not_there", method="get") + def resp_cont(url, request): + content = {"Here is why it failed"} + return response(404, content, {}, None, 5, request) + + with HTTMock(resp_cont): + with pytest.raises(GitlabHttpError): + gl.http_list("/not_there") + + +def test_list_request_invalid_data(gl): + @urlmatch(scheme="http", netloc="localhost", path="/api/v4/projects", method="get") + def resp_cont(url, request): + headers = {"content-type": "application/json"} + content = '["name": "project1"]' + return response(200, content, headers, None, 5, request) + + with HTTMock(resp_cont): + with pytest.raises(GitlabParsingError): + gl.http_list("/projects") + + +def test_post_request(gl): + @urlmatch(scheme="http", netloc="localhost", path="/api/v4/projects", method="post") + def resp_cont(url, request): + headers = {"content-type": "application/json"} + content = '{"name": "project1"}' + return response(200, content, headers, None, 5, request) + + with HTTMock(resp_cont): + result = gl.http_post("/projects") + assert isinstance(result, dict) + assert result["name"] == "project1" + + +def test_post_request_404(gl): + @urlmatch( + scheme="http", netloc="localhost", path="/api/v4/not_there", method="post" + ) + def resp_cont(url, request): + content = {"Here is wh it failed"} + return response(404, content, {}, None, 5, request) + + with HTTMock(resp_cont): + with pytest.raises(GitlabHttpError): + gl.http_post("/not_there") + + +def test_post_request_invalid_data(gl): + @urlmatch(scheme="http", netloc="localhost", path="/api/v4/projects", method="post") + def resp_cont(url, request): + headers = {"content-type": "application/json"} + content = '["name": "project1"]' + return response(200, content, headers, None, 5, request) + + with HTTMock(resp_cont): + with pytest.raises(GitlabParsingError): + gl.http_post("/projects") + + +def test_put_request(gl): + @urlmatch(scheme="http", netloc="localhost", path="/api/v4/projects", method="put") + def resp_cont(url, request): + headers = {"content-type": "application/json"} + content = '{"name": "project1"}' + return response(200, content, headers, None, 5, request) + + with HTTMock(resp_cont): + result = gl.http_put("/projects") + assert isinstance(result, dict) + assert result["name"] == "project1" + + +def test_put_request_404(gl): + @urlmatch(scheme="http", netloc="localhost", path="/api/v4/not_there", method="put") + def resp_cont(url, request): + content = {"Here is wh it failed"} + return response(404, content, {}, None, 5, request) + + with HTTMock(resp_cont): + with pytest.raises(GitlabHttpError): + gl.http_put("/not_there") + + +def test_put_request_invalid_data(gl): + @urlmatch(scheme="http", netloc="localhost", path="/api/v4/projects", method="put") + def resp_cont(url, request): + headers = {"content-type": "application/json"} + content = '["name": "project1"]' + return response(200, content, headers, None, 5, request) + + with HTTMock(resp_cont): + with pytest.raises(GitlabParsingError): + gl.http_put("/projects") + + +def test_delete_request(gl): + @urlmatch( + scheme="http", netloc="localhost", path="/api/v4/projects", method="delete" + ) + def resp_cont(url, request): + headers = {"content-type": "application/json"} + content = "true" + return response(200, content, headers, None, 5, request) + + with HTTMock(resp_cont): + result = gl.http_delete("/projects") + assert isinstance(result, requests.Response) + assert result.json() == True + + +def test_delete_request_404(gl): + @urlmatch( + scheme="http", netloc="localhost", path="/api/v4/not_there", method="delete" + ) + def resp_cont(url, request): + content = {"Here is wh it failed"} + return response(404, content, {}, None, 5, request) + + with HTTMock(resp_cont): + with pytest.raises(GitlabHttpError): + gl.http_delete("/not_there") diff --git a/gitlab/tests/test_mixins.py b/gitlab/tests/test_mixins.py deleted file mode 100644 index e8613f2da..000000000 --- a/gitlab/tests/test_mixins.py +++ /dev/null @@ -1,446 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright (C) 2014 Mika Mäenpää , -# Tampere University of Technology -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with this program. If not, see . - -import unittest - -from httmock import HTTMock # noqa -from httmock import response # noqa -from httmock import urlmatch # noqa - -from gitlab import * # noqa -from gitlab.base import * # noqa -from gitlab.mixins import * # noqa -import pytest - - -class TestObjectMixinsAttributes(unittest.TestCase): - def test_access_request_mixin(self): - class O(AccessRequestMixin): - pass - - obj = O() - assert hasattr(obj, "approve") - - def test_subscribable_mixin(self): - class O(SubscribableMixin): - pass - - obj = O() - assert hasattr(obj, "subscribe") - assert hasattr(obj, "unsubscribe") - - def test_todo_mixin(self): - class O(TodoMixin): - pass - - obj = O() - assert hasattr(obj, "todo") - - def test_time_tracking_mixin(self): - class O(TimeTrackingMixin): - pass - - obj = O() - assert hasattr(obj, "time_stats") - assert hasattr(obj, "time_estimate") - assert hasattr(obj, "reset_time_estimate") - assert hasattr(obj, "add_spent_time") - assert hasattr(obj, "reset_spent_time") - - def test_set_mixin(self): - class O(SetMixin): - pass - - obj = O() - assert hasattr(obj, "set") - - def test_user_agent_detail_mixin(self): - class O(UserAgentDetailMixin): - pass - - obj = O() - assert hasattr(obj, "user_agent_detail") - - -class TestMetaMixins(unittest.TestCase): - def test_retrieve_mixin(self): - class M(RetrieveMixin): - pass - - obj = M() - assert hasattr(obj, "list") - assert hasattr(obj, "get") - assert not hasattr(obj, "create") - assert not hasattr(obj, "update") - assert not hasattr(obj, "delete") - assert isinstance(obj, ListMixin) - assert isinstance(obj, GetMixin) - - def test_crud_mixin(self): - class M(CRUDMixin): - pass - - obj = M() - assert hasattr(obj, "get") - assert hasattr(obj, "list") - assert hasattr(obj, "create") - assert hasattr(obj, "update") - assert hasattr(obj, "delete") - assert isinstance(obj, ListMixin) - assert isinstance(obj, GetMixin) - assert isinstance(obj, CreateMixin) - assert isinstance(obj, UpdateMixin) - assert isinstance(obj, DeleteMixin) - - def test_no_update_mixin(self): - class M(NoUpdateMixin): - pass - - obj = M() - assert hasattr(obj, "get") - assert hasattr(obj, "list") - assert hasattr(obj, "create") - assert not hasattr(obj, "update") - assert hasattr(obj, "delete") - assert isinstance(obj, ListMixin) - assert isinstance(obj, GetMixin) - assert isinstance(obj, CreateMixin) - assert not isinstance(obj, UpdateMixin) - assert isinstance(obj, DeleteMixin) - - -class FakeObject(base.RESTObject): - pass - - -class FakeManager(base.RESTManager): - _path = "/tests" - _obj_cls = FakeObject - - -class TestMixinMethods(unittest.TestCase): - def setUp(self): - self.gl = Gitlab( - "http://localhost", private_token="private_token", api_version=4 - ) - - def test_get_mixin(self): - class M(GetMixin, FakeManager): - pass - - @urlmatch( - scheme="http", netloc="localhost", path="/api/v4/tests/42", method="get" - ) - def resp_cont(url, request): - headers = {"Content-Type": "application/json"} - content = '{"id": 42, "foo": "bar"}' - return response(200, content, headers, None, 5, request) - - with HTTMock(resp_cont): - mgr = M(self.gl) - obj = mgr.get(42) - assert isinstance(obj, FakeObject) - assert obj.foo == "bar" - assert obj.id == 42 - - def test_refresh_mixin(self): - class O(RefreshMixin, FakeObject): - pass - - @urlmatch( - scheme="http", netloc="localhost", path="/api/v4/tests/42", method="get" - ) - def resp_cont(url, request): - headers = {"Content-Type": "application/json"} - content = '{"id": 42, "foo": "bar"}' - return response(200, content, headers, None, 5, request) - - with HTTMock(resp_cont): - mgr = FakeManager(self.gl) - obj = O(mgr, {"id": 42}) - res = obj.refresh() - assert res is None - assert obj.foo == "bar" - assert obj.id == 42 - - def test_get_without_id_mixin(self): - class M(GetWithoutIdMixin, FakeManager): - pass - - @urlmatch(scheme="http", netloc="localhost", path="/api/v4/tests", method="get") - def resp_cont(url, request): - headers = {"Content-Type": "application/json"} - content = '{"foo": "bar"}' - return response(200, content, headers, None, 5, request) - - with HTTMock(resp_cont): - mgr = M(self.gl) - obj = mgr.get() - assert isinstance(obj, FakeObject) - assert obj.foo == "bar" - assert not hasattr(obj, "id") - - def test_list_mixin(self): - class M(ListMixin, FakeManager): - pass - - @urlmatch(scheme="http", netloc="localhost", path="/api/v4/tests", method="get") - def resp_cont(url, request): - headers = {"Content-Type": "application/json"} - content = '[{"id": 42, "foo": "bar"},{"id": 43, "foo": "baz"}]' - return response(200, content, headers, None, 5, request) - - with HTTMock(resp_cont): - # test RESTObjectList - mgr = M(self.gl) - obj_list = mgr.list(as_list=False) - assert isinstance(obj_list, base.RESTObjectList) - for obj in obj_list: - assert isinstance(obj, FakeObject) - assert obj.id in (42, 43) - - # test list() - obj_list = mgr.list(all=True) - assert isinstance(obj_list, list) - assert obj_list[0].id == 42 - assert obj_list[1].id == 43 - assert isinstance(obj_list[0], FakeObject) - assert len(obj_list) == 2 - - def test_list_other_url(self): - class M(ListMixin, FakeManager): - pass - - @urlmatch( - scheme="http", netloc="localhost", path="/api/v4/others", method="get" - ) - def resp_cont(url, request): - headers = {"Content-Type": "application/json"} - content = '[{"id": 42, "foo": "bar"}]' - return response(200, content, headers, None, 5, request) - - with HTTMock(resp_cont): - mgr = M(self.gl) - obj_list = mgr.list(path="/others", as_list=False) - assert isinstance(obj_list, base.RESTObjectList) - obj = obj_list.next() - assert obj.id == 42 - assert obj.foo == "bar" - with pytest.raises(StopIteration): - obj_list.next() - - def test_create_mixin_get_attrs(self): - class M1(CreateMixin, FakeManager): - pass - - class M2(CreateMixin, FakeManager): - _create_attrs = (("foo",), ("bar", "baz")) - _update_attrs = (("foo",), ("bam",)) - - mgr = M1(self.gl) - required, optional = mgr.get_create_attrs() - assert len(required) == 0 - assert len(optional) == 0 - - mgr = M2(self.gl) - required, optional = mgr.get_create_attrs() - assert "foo" in required - assert "bar" in optional - assert "baz" in optional - assert "bam" not in optional - - def test_create_mixin_missing_attrs(self): - class M(CreateMixin, FakeManager): - _create_attrs = (("foo",), ("bar", "baz")) - - mgr = M(self.gl) - data = {"foo": "bar", "baz": "blah"} - mgr._check_missing_create_attrs(data) - - data = {"baz": "blah"} - with pytest.raises(AttributeError) as error: - mgr._check_missing_create_attrs(data) - assert "foo" in str(error.value) - - def test_create_mixin(self): - class M(CreateMixin, FakeManager): - _create_attrs = (("foo",), ("bar", "baz")) - _update_attrs = (("foo",), ("bam",)) - - @urlmatch( - scheme="http", netloc="localhost", path="/api/v4/tests", method="post" - ) - def resp_cont(url, request): - headers = {"Content-Type": "application/json"} - content = '{"id": 42, "foo": "bar"}' - return response(200, content, headers, None, 5, request) - - with HTTMock(resp_cont): - mgr = M(self.gl) - obj = mgr.create({"foo": "bar"}) - assert isinstance(obj, FakeObject) - assert obj.id == 42 - assert obj.foo == "bar" - - def test_create_mixin_custom_path(self): - class M(CreateMixin, FakeManager): - _create_attrs = (("foo",), ("bar", "baz")) - _update_attrs = (("foo",), ("bam",)) - - @urlmatch( - scheme="http", netloc="localhost", path="/api/v4/others", method="post" - ) - def resp_cont(url, request): - headers = {"Content-Type": "application/json"} - content = '{"id": 42, "foo": "bar"}' - return response(200, content, headers, None, 5, request) - - with HTTMock(resp_cont): - mgr = M(self.gl) - obj = mgr.create({"foo": "bar"}, path="/others") - assert isinstance(obj, FakeObject) - assert obj.id == 42 - assert obj.foo == "bar" - - def test_update_mixin_get_attrs(self): - class M1(UpdateMixin, FakeManager): - pass - - class M2(UpdateMixin, FakeManager): - _create_attrs = (("foo",), ("bar", "baz")) - _update_attrs = (("foo",), ("bam",)) - - mgr = M1(self.gl) - required, optional = mgr.get_update_attrs() - assert len(required) == 0 - assert len(optional) == 0 - - mgr = M2(self.gl) - required, optional = mgr.get_update_attrs() - assert "foo" in required - assert "bam" in optional - assert "bar" not in optional - assert "baz" not in optional - - def test_update_mixin_missing_attrs(self): - class M(UpdateMixin, FakeManager): - _update_attrs = (("foo",), ("bar", "baz")) - - mgr = M(self.gl) - data = {"foo": "bar", "baz": "blah"} - mgr._check_missing_update_attrs(data) - - data = {"baz": "blah"} - with pytest.raises(AttributeError) as error: - mgr._check_missing_update_attrs(data) - assert "foo" in str(error.value) - - def test_update_mixin(self): - class M(UpdateMixin, FakeManager): - _create_attrs = (("foo",), ("bar", "baz")) - _update_attrs = (("foo",), ("bam",)) - - @urlmatch( - scheme="http", netloc="localhost", path="/api/v4/tests/42", method="put" - ) - def resp_cont(url, request): - headers = {"Content-Type": "application/json"} - content = '{"id": 42, "foo": "baz"}' - return response(200, content, headers, None, 5, request) - - with HTTMock(resp_cont): - mgr = M(self.gl) - server_data = mgr.update(42, {"foo": "baz"}) - assert isinstance(server_data, dict) - assert server_data["id"] == 42 - assert server_data["foo"] == "baz" - - def test_update_mixin_no_id(self): - class M(UpdateMixin, FakeManager): - _create_attrs = (("foo",), ("bar", "baz")) - _update_attrs = (("foo",), ("bam",)) - - @urlmatch(scheme="http", netloc="localhost", path="/api/v4/tests", method="put") - def resp_cont(url, request): - headers = {"Content-Type": "application/json"} - content = '{"foo": "baz"}' - return response(200, content, headers, None, 5, request) - - with HTTMock(resp_cont): - mgr = M(self.gl) - server_data = mgr.update(new_data={"foo": "baz"}) - assert isinstance(server_data, dict) - assert server_data["foo"] == "baz" - - def test_delete_mixin(self): - class M(DeleteMixin, FakeManager): - pass - - @urlmatch( - scheme="http", netloc="localhost", path="/api/v4/tests/42", method="delete" - ) - def resp_cont(url, request): - headers = {"Content-Type": "application/json"} - content = "" - return response(200, content, headers, None, 5, request) - - with HTTMock(resp_cont): - mgr = M(self.gl) - mgr.delete(42) - - def test_save_mixin(self): - class M(UpdateMixin, FakeManager): - pass - - class O(SaveMixin, RESTObject): - pass - - @urlmatch( - scheme="http", netloc="localhost", path="/api/v4/tests/42", method="put" - ) - def resp_cont(url, request): - headers = {"Content-Type": "application/json"} - content = '{"id": 42, "foo": "baz"}' - return response(200, content, headers, None, 5, request) - - with HTTMock(resp_cont): - mgr = M(self.gl) - obj = O(mgr, {"id": 42, "foo": "bar"}) - obj.foo = "baz" - obj.save() - assert obj._attrs["foo"] == "baz" - assert obj._updated_attrs == {} - - def test_set_mixin(self): - class M(SetMixin, FakeManager): - pass - - @urlmatch( - scheme="http", netloc="localhost", path="/api/v4/tests/foo", method="put" - ) - def resp_cont(url, request): - headers = {"Content-Type": "application/json"} - content = '{"key": "foo", "value": "bar"}' - return response(200, content, headers, None, 5, request) - - with HTTMock(resp_cont): - mgr = M(self.gl) - obj = mgr.set("foo", "bar") - assert isinstance(obj, FakeObject) - assert obj.key == "foo" - assert obj.value == "bar" diff --git a/gitlab/tests/test_types.py b/gitlab/tests/test_types.py index 8471bdff6..f84eddbb0 100644 --- a/gitlab/tests/test_types.py +++ b/gitlab/tests/test_types.py @@ -15,57 +15,55 @@ # You should have received a copy of the GNU Lesser General Public License # along with this program. If not, see . -import unittest - from gitlab import types -class TestGitlabAttribute(unittest.TestCase): - def test_all(self): - o = types.GitlabAttribute("whatever") - assert "whatever" == o.get() +def test_gitlab_attribute_get(): + o = types.GitlabAttribute("whatever") + assert o.get() == "whatever" + + o.set_from_cli("whatever2") + assert o.get() == "whatever2" + assert o.get_for_api() == "whatever2" + + o = types.GitlabAttribute() + assert o._value is None + - o.set_from_cli("whatever2") - assert "whatever2" == o.get() +def test_list_attribute_input(): + o = types.ListAttribute() + o.set_from_cli("foo,bar,baz") + assert o.get() == ["foo", "bar", "baz"] - assert "whatever2" == o.get_for_api() + o.set_from_cli("foo") + assert o.get() == ["foo"] - o = types.GitlabAttribute() - assert None == o._value +def test_list_attribute_empty_input(): + o = types.ListAttribute() + o.set_from_cli("") + assert o.get() == [] -class TestListAttribute(unittest.TestCase): - def test_list_input(self): - o = types.ListAttribute() - o.set_from_cli("foo,bar,baz") - assert ["foo", "bar", "baz"] == o.get() + o.set_from_cli(" ") + assert o.get() == [] - o.set_from_cli("foo") - assert ["foo"] == o.get() - def test_empty_input(self): - o = types.ListAttribute() - o.set_from_cli("") - assert [] == o.get() +def test_list_attribute_get_for_api_from_cli(): + o = types.ListAttribute() + o.set_from_cli("foo,bar,baz") + assert o.get_for_api() == "foo,bar,baz" - o.set_from_cli(" ") - assert [] == o.get() - def test_get_for_api_from_cli(self): - o = types.ListAttribute() - o.set_from_cli("foo,bar,baz") - assert "foo,bar,baz" == o.get_for_api() +def test_list_attribute_get_for_api_from_list(): + o = types.ListAttribute(["foo", "bar", "baz"]) + assert o.get_for_api() == "foo,bar,baz" - def test_get_for_api_from_list(self): - o = types.ListAttribute(["foo", "bar", "baz"]) - assert "foo,bar,baz" == o.get_for_api() - def test_get_for_api_does_not_split_string(self): - o = types.ListAttribute("foo") - assert "foo" == o.get_for_api() +def test_list_attribute_does_not_split_string(): + o = types.ListAttribute("foo") + assert o.get_for_api() == "foo" -class TestLowercaseStringAttribute(unittest.TestCase): - def test_get_for_api(self): - o = types.LowercaseStringAttribute("FOO") - assert "foo" == o.get_for_api() +def test_lowercase_string_attribute_get_for_api(): + o = types.LowercaseStringAttribute("FOO") + assert o.get_for_api() == "foo" diff --git a/gitlab/tests/test_utils.py b/gitlab/tests/test_utils.py index 7ebd006a7..50aaecf2a 100644 --- a/gitlab/tests/test_utils.py +++ b/gitlab/tests/test_utils.py @@ -15,26 +15,40 @@ # You should have received a copy of the GNU Lesser General Public License # along with this program. If not, see . -import unittest - from gitlab import utils -class TestUtils(unittest.TestCase): - def test_clean_str_id(self): - src = "nothing_special" - dest = "nothing_special" - assert dest == utils.clean_str_id(src) +def test_clean_str_id(): + src = "nothing_special" + dest = "nothing_special" + assert dest == utils.clean_str_id(src) + + src = "foo#bar/baz/" + dest = "foo%23bar%2Fbaz%2F" + assert dest == utils.clean_str_id(src) + + +def test_sanitized_url(): + src = "http://localhost/foo/bar" + dest = "http://localhost/foo/bar" + assert dest == utils.sanitized_url(src) + + src = "http://localhost/foo.bar.baz" + dest = "http://localhost/foo%2Ebar%2Ebaz" + assert dest == utils.sanitized_url(src) + + +def test_sanitize_parameters_does_nothing(): + assert 1 == utils.sanitize_parameters(1) + assert 1.5 == utils.sanitize_parameters(1.5) + assert "foo" == utils.sanitize_parameters("foo") + - src = "foo#bar/baz/" - dest = "foo%23bar%2Fbaz%2F" - assert dest == utils.clean_str_id(src) +def test_sanitize_parameters_slash(): + assert "foo%2Fbar" == utils.sanitize_parameters("foo/bar") - def test_sanitized_url(self): - src = "http://localhost/foo/bar" - dest = "http://localhost/foo/bar" - assert dest == utils.sanitized_url(src) - src = "http://localhost/foo.bar.baz" - dest = "http://localhost/foo%2Ebar%2Ebaz" - assert dest == utils.sanitized_url(src) +def test_sanitize_parameters_dict(): + source = {"url": "foo/bar", "id": 1} + expected = {"url": "foo%2Fbar", "id": 1} + assert expected == utils.sanitize_parameters(source) diff --git a/gitlab/utils.py b/gitlab/utils.py index 4241787a8..67cb7f45b 100644 --- a/gitlab/utils.py +++ b/gitlab/utils.py @@ -51,6 +51,14 @@ def clean_str_id(id): return id.replace("/", "%2F").replace("#", "%23") +def sanitize_parameters(value): + if isinstance(value, dict): + return dict((k, sanitize_parameters(v)) for k, v in value.items()) + if isinstance(value, str): + return value.replace("/", "%2F") + return value + + def sanitized_url(url): parsed = urlparse(url) new_path = parsed.path.replace(".", "%2E") From 204782a117f77f367dee87aa2c70822587829147 Mon Sep 17 00:00:00 2001 From: Nejc Habjan Date: Sun, 23 Aug 2020 21:16:20 +0200 Subject: [PATCH 4/4] refactor: rewrite unit tests for objects with responses --- gitlab/tests/conftest.py | 5 +- gitlab/tests/objects/conftest.py | 65 +++++++ gitlab/tests/objects/mocks.py | 35 ---- gitlab/tests/objects/test_appearance.py | 66 +++++++ gitlab/tests/objects/test_application.py | 108 ----------- gitlab/tests/objects/test_applications.py | 45 +++++ gitlab/tests/objects/test_commits.py | 158 ++++++++-------- gitlab/tests/objects/test_deploy_tokens.py | 36 ++-- gitlab/tests/objects/test_deployments.py | 51 +++--- gitlab/tests/objects/test_environments.py | 31 ++-- gitlab/tests/objects/test_groups.py | 97 +++++----- gitlab/tests/objects/test_hooks.py | 24 ++- gitlab/tests/objects/test_issues.py | 52 +++--- .../tests/objects/test_pipeline_schedules.py | 97 +++++----- .../objects/test_project_import_export.py | 169 ++++++++---------- .../tests/objects/test_project_statistics.py | 31 ++-- gitlab/tests/objects/test_projects.py | 44 +++-- gitlab/tests/objects/test_remote_mirrors.py | 111 +++++------- gitlab/tests/objects/test_runners.py | 8 +- gitlab/tests/objects/test_services.py | 163 +++++++---------- gitlab/tests/objects/test_snippets.py | 135 ++++++-------- gitlab/tests/objects/test_submodules.py | 74 ++++---- gitlab/tests/objects/test_todos.py | 69 +++---- gitlab/tests/objects/test_users.py | 166 +++++++++-------- 24 files changed, 900 insertions(+), 940 deletions(-) create mode 100644 gitlab/tests/objects/conftest.py delete mode 100644 gitlab/tests/objects/mocks.py create mode 100644 gitlab/tests/objects/test_appearance.py delete mode 100644 gitlab/tests/objects/test_application.py create mode 100644 gitlab/tests/objects/test_applications.py diff --git a/gitlab/tests/conftest.py b/gitlab/tests/conftest.py index 2d4cb3a9d..98d97ae6e 100644 --- a/gitlab/tests/conftest.py +++ b/gitlab/tests/conftest.py @@ -16,9 +16,7 @@ def gl(): @pytest.fixture def gl_trailing(): return gitlab.Gitlab( - "http://localhost/", - private_token="private_token", - api_version=4 + "http://localhost/", private_token="private_token", api_version=4 ) @@ -38,6 +36,7 @@ def default_config(tmpdir): config_path.write(valid_config) return str(config_path) + @pytest.fixture def group(gl): return gl.groups.get(1, lazy=True) diff --git a/gitlab/tests/objects/conftest.py b/gitlab/tests/objects/conftest.py new file mode 100644 index 000000000..76f76d1cf --- /dev/null +++ b/gitlab/tests/objects/conftest.py @@ -0,0 +1,65 @@ +"""Common mocks for resources in gitlab.v4.objects""" + +import re + +import pytest +import responses + + +@pytest.fixture +def binary_content(): + return b"binary content" + + +@pytest.fixture +def accepted_content(): + return {"message": "202 Accepted"} + + +@pytest.fixture +def created_content(): + return {"message": "201 Created"} + + +@pytest.fixture +def resp_export(accepted_content, binary_content): + """Common fixture for group and project exports.""" + export_status_content = { + "id": 1, + "description": "Itaque perspiciatis minima aspernatur", + "name": "Gitlab Test", + "name_with_namespace": "Gitlab Org / Gitlab Test", + "path": "gitlab-test", + "path_with_namespace": "gitlab-org/gitlab-test", + "created_at": "2017-08-29T04:36:44.383Z", + "export_status": "finished", + "_links": { + "api_url": "https://gitlab.test/api/v4/projects/1/export/download", + "web_url": "https://gitlab.test/gitlab-test/download_export", + }, + } + + with responses.RequestsMock(assert_all_requests_are_fired=False) as rsps: + rsps.add( + method=responses.POST, + url=re.compile(r".*/api/v4/(groups|projects)/1/export"), + json=accepted_content, + content_type="application/json", + status=202, + ) + rsps.add( + method=responses.GET, + url=re.compile(r".*/api/v4/(groups|projects)/1/export/download"), + body=binary_content, + content_type="application/octet-stream", + status=200, + ) + # Currently only project export supports status checks + rsps.add( + method=responses.GET, + url="http://localhost/api/v4/projects/1/export", + json=export_status_content, + content_type="application/json", + status=200, + ) + yield rsps diff --git a/gitlab/tests/objects/mocks.py b/gitlab/tests/objects/mocks.py deleted file mode 100644 index e05133998..000000000 --- a/gitlab/tests/objects/mocks.py +++ /dev/null @@ -1,35 +0,0 @@ -"""Common mocks for resources in gitlab.v4.objects""" - -from httmock import response, urlmatch - - -headers = {"content-type": "application/json"} -binary_content = b"binary content" - - -@urlmatch( - scheme="http", - netloc="localhost", - path="/api/v4/(groups|projects)/1/export", - method="post", -) -def resp_create_export(url, request): - """Common mock for Group/Project Export POST response.""" - content = """{ - "message": "202 Accepted" - }""" - content = content.encode("utf-8") - return response(202, content, headers, None, 25, request) - - -@urlmatch( - scheme="http", - netloc="localhost", - path="/api/v4/(groups|projects)/1/export/download", - method="get", -) -def resp_download_export(url, request): - """Common mock for Group/Project Export Download GET response.""" - headers = {"content-type": "application/octet-stream"} - content = binary_content - return response(200, content, headers, None, 25, request) diff --git a/gitlab/tests/objects/test_appearance.py b/gitlab/tests/objects/test_appearance.py new file mode 100644 index 000000000..7c5230146 --- /dev/null +++ b/gitlab/tests/objects/test_appearance.py @@ -0,0 +1,66 @@ +""" +GitLab API: https://docs.gitlab.com/ce/api/appearance.html +""" + +import pytest +import responses + + +title = "GitLab Test Instance" +description = "gitlab-test.example.com" +new_title = "new-title" +new_description = "new-description" + + +@pytest.fixture +def resp_application_appearance(): + content = { + "title": title, + "description": description, + "logo": "/uploads/-/system/appearance/logo/1/logo.png", + "header_logo": "/uploads/-/system/appearance/header_logo/1/header.png", + "favicon": "/uploads/-/system/appearance/favicon/1/favicon.png", + "new_project_guidelines": "Please read the FAQs for help.", + "header_message": "", + "footer_message": "", + "message_background_color": "#e75e40", + "message_font_color": "#ffffff", + "email_header_and_footer_enabled": False, + } + + with responses.RequestsMock(assert_all_requests_are_fired=False) as rsps: + rsps.add( + method=responses.GET, + url="http://localhost/api/v4/application/appearance", + json=content, + content_type="application/json", + status=200, + ) + + updated_content = dict(content) + updated_content["title"] = new_title + updated_content["description"] = new_description + + rsps.add( + method=responses.PUT, + url="http://localhost/api/v4/application/appearance", + json=updated_content, + content_type="application/json", + status=200, + ) + yield rsps + + +def test_get_update_appearance(gl, resp_application_appearance): + appearance = gl.appearance.get() + assert appearance.title == title + assert appearance.description == description + appearance.title = new_title + appearance.description = new_description + appearance.save() + assert appearance.title == new_title + assert appearance.description == new_description + + +def test_update_appearance(gl, resp_application_appearance): + resp = gl.appearance.update(title=new_title, description=new_description) diff --git a/gitlab/tests/objects/test_application.py b/gitlab/tests/objects/test_application.py deleted file mode 100644 index 356f0d365..000000000 --- a/gitlab/tests/objects/test_application.py +++ /dev/null @@ -1,108 +0,0 @@ -""" -GitLab API: https://docs.gitlab.com/ce/api/applications.html -""" - -import json - -from httmock import urlmatch, response, with_httmock # noqa - -from .mocks import headers - - -title = "GitLab Test Instance" -description = "gitlab-test.example.com" -new_title = "new-title" -new_description = "new-description" - - -@urlmatch( - scheme="http", netloc="localhost", path="/api/v4/applications", method="post", -) -def resp_application_create(url, request): - content = '{"name": "test_app", "redirect_uri": "http://localhost:8080", "scopes": ["api", "email"]}' - json_content = json.loads(content) - return response(200, json_content, headers, None, 5, request) - - -@urlmatch( - scheme="http", - netloc="localhost", - path="/api/v4/application/appearance", - method="get", -) -def resp_get_appearance(url, request): - content = """{ - "title": "%s", - "description": "%s", - "logo": "/uploads/-/system/appearance/logo/1/logo.png", - "header_logo": "/uploads/-/system/appearance/header_logo/1/header.png", - "favicon": "/uploads/-/system/appearance/favicon/1/favicon.png", - "new_project_guidelines": "Please read the FAQs for help.", - "header_message": "", - "footer_message": "", - "message_background_color": "#e75e40", - "message_font_color": "#ffffff", - "email_header_and_footer_enabled": false}""" % ( - title, - description, - ) - content = content.encode("utf-8") - return response(200, content, headers, None, 25, request) - - -@urlmatch( - scheme="http", - netloc="localhost", - path="/api/v4/application/appearance", - method="put", -) -def resp_update_appearance(url, request): - content = """{ - "title": "%s", - "description": "%s", - "logo": "/uploads/-/system/appearance/logo/1/logo.png", - "header_logo": "/uploads/-/system/appearance/header_logo/1/header.png", - "favicon": "/uploads/-/system/appearance/favicon/1/favicon.png", - "new_project_guidelines": "Please read the FAQs for help.", - "header_message": "", - "footer_message": "", - "message_background_color": "#e75e40", - "message_font_color": "#ffffff", - "email_header_and_footer_enabled": false}""" % ( - new_title, - new_description, - ) - content = content.encode("utf-8") - return response(200, content, headers, None, 25, request) - - -@with_httmock(resp_application_create) -def test_create_application(gl): - application = gl.applications.create( - { - "name": "test_app", - "redirect_uri": "http://localhost:8080", - "scopes": ["api", "email"], - "confidential": False, - } - ) - assert application.name == "test_app" - assert application.redirect_uri == "http://localhost:8080" - assert application.scopes == ["api", "email"] - - -@with_httmock(resp_get_appearance, resp_update_appearance) -def test_get_update_appearance(gl): - appearance = gl.appearance.get() - assert appearance.title == title - assert appearance.description == description - appearance.title = new_title - appearance.description = new_description - appearance.save() - assert appearance.title == new_title - assert appearance.description == new_description - - -@with_httmock(resp_update_appearance) -def test_update_application_appearance(gl): - resp = gl.appearance.update(title=new_title, description=new_description) diff --git a/gitlab/tests/objects/test_applications.py b/gitlab/tests/objects/test_applications.py new file mode 100644 index 000000000..f8b5d88c9 --- /dev/null +++ b/gitlab/tests/objects/test_applications.py @@ -0,0 +1,45 @@ +""" +GitLab API: https://docs.gitlab.com/ce/api/applications.html +""" + +import pytest +import responses + + +title = "GitLab Test Instance" +description = "gitlab-test.example.com" +new_title = "new-title" +new_description = "new-description" + + +@pytest.fixture +def resp_application_create(): + content = { + "name": "test_app", + "redirect_uri": "http://localhost:8080", + "scopes": ["api", "email"], + } + + with responses.RequestsMock() as rsps: + rsps.add( + method=responses.POST, + url="http://localhost/api/v4/applications", + json=content, + content_type="application/json", + status=200, + ) + yield rsps + + +def test_create_application(gl, resp_application_create): + application = gl.applications.create( + { + "name": "test_app", + "redirect_uri": "http://localhost:8080", + "scopes": ["api", "email"], + "confidential": False, + } + ) + assert application.name == "test_app" + assert application.redirect_uri == "http://localhost:8080" + assert application.scopes == ["api", "email"] diff --git a/gitlab/tests/objects/test_commits.py b/gitlab/tests/objects/test_commits.py index eaa7b82a9..9d11508c6 100644 --- a/gitlab/tests/objects/test_commits.py +++ b/gitlab/tests/objects/test_commits.py @@ -2,85 +2,89 @@ GitLab API: https://docs.gitlab.com/ce/api/commits.html """ -from httmock import urlmatch, response, with_httmock - -from .mocks import headers - - -@urlmatch( - scheme="http", - netloc="localhost", - path="/api/v4/projects/1/repository/commits/6b2257ea", - method="get", -) -def resp_get_commit(url, request): - """Mock for commit GET response.""" - content = """{ - "id": "6b2257eabcec3db1f59dafbd84935e3caea04235", - "short_id": "6b2257ea", - "title": "Initial commit" - }""" - content = content.encode("utf-8") - return response(200, content, headers, None, 5, request) - - -@urlmatch( - scheme="http", path="/api/v4/projects/1/repository/commits", method="post", -) -def resp_create_commit(url, request): - """Mock for commit create POST response.""" - content = """{ - "id": "ed899a2f4b50b4370feeea94676502b42383c746", - "short_id": "ed899a2f", - "title": "Commit message" - }""" - content = content.encode("utf-8") - return response(200, content, headers, None, 5, request) - - -@urlmatch( - scheme="http", path="/api/v4/projects/1/repository/commits/6b2257ea", method="post", -) -def resp_revert_commit(url, request): - """Mock for commit revert POST response.""" - content = """{ - "id": "8b090c1b79a14f2bd9e8a738f717824ff53aebad", - "short_id": "8b090c1b", - "title":"Revert \\"Initial commit\\"" - }""" - content = content.encode("utf-8") - return response(200, content, headers, None, 5, request) - - -@urlmatch( - scheme="http", - netloc="localhost", - path="/api/v4/projects/1/repository/commits/6b2257ea/signature", - method="get", -) -def resp_get_commit_gpg_signature(url, request): - """Mock for commit GPG signature GET response.""" - content = """{ - "gpg_key_id": 1, - "gpg_key_primary_keyid": "8254AAB3FBD54AC9", - "gpg_key_user_name": "John Doe", - "gpg_key_user_email": "johndoe@example.com", - "verification_status": "verified", - "gpg_key_subkey_id": null - }""" - content = content.encode("utf-8") - return response(200, content, headers, None, 5, request) - - -@with_httmock(resp_get_commit) -def test_get_commit(project): +import pytest +import responses + + +@pytest.fixture +def resp_create_commit(): + content = { + "id": "ed899a2f4b50b4370feeea94676502b42383c746", + "short_id": "ed899a2f", + "title": "Commit message", + } + + with responses.RequestsMock() as rsps: + rsps.add( + method=responses.POST, + url="http://localhost/api/v4/projects/1/repository/commits", + json=content, + content_type="application/json", + status=200, + ) + yield rsps + + +@pytest.fixture +def resp_commit(): + get_content = { + "id": "6b2257eabcec3db1f59dafbd84935e3caea04235", + "short_id": "6b2257ea", + "title": "Initial commit", + } + revert_content = { + "id": "8b090c1b79a14f2bd9e8a738f717824ff53aebad", + "short_id": "8b090c1b", + "title": 'Revert "Initial commit"', + } + + with responses.RequestsMock(assert_all_requests_are_fired=False) as rsps: + rsps.add( + method=responses.GET, + url="http://localhost/api/v4/projects/1/repository/commits/6b2257ea", + json=get_content, + content_type="application/json", + status=200, + ) + rsps.add( + method=responses.POST, + url="http://localhost/api/v4/projects/1/repository/commits/6b2257ea/revert", + json=revert_content, + content_type="application/json", + status=200, + ) + yield rsps + + +@pytest.fixture +def resp_get_commit_gpg_signature(): + content = { + "gpg_key_id": 1, + "gpg_key_primary_keyid": "8254AAB3FBD54AC9", + "gpg_key_user_name": "John Doe", + "gpg_key_user_email": "johndoe@example.com", + "verification_status": "verified", + "gpg_key_subkey_id": None, + } + + with responses.RequestsMock() as rsps: + rsps.add( + method=responses.GET, + url="http://localhost/api/v4/projects/1/repository/commits/6b2257ea/signature", + json=content, + content_type="application/json", + status=200, + ) + yield rsps + + +def test_get_commit(project, resp_commit): commit = project.commits.get("6b2257ea") assert commit.short_id == "6b2257ea" assert commit.title == "Initial commit" -@with_httmock(resp_create_commit) -def test_create_commit(project): +def test_create_commit(project, resp_create_commit): data = { "branch": "master", "commit_message": "Commit message", @@ -91,16 +95,14 @@ def test_create_commit(project): assert commit.title == data["commit_message"] -@with_httmock(resp_revert_commit) -def test_revert_commit(project): +def test_revert_commit(project, resp_commit): commit = project.commits.get("6b2257ea", lazy=True) revert_commit = commit.revert(branch="master") assert revert_commit["short_id"] == "8b090c1b" assert revert_commit["title"] == 'Revert "Initial commit"' -@with_httmock(resp_get_commit_gpg_signature) -def test_get_commit_gpg_signature(project): +def test_get_commit_gpg_signature(project, resp_get_commit_gpg_signature): commit = project.commits.get("6b2257ea", lazy=True) signature = commit.signature() assert signature["gpg_key_primary_keyid"] == "8254AAB3FBD54AC9" diff --git a/gitlab/tests/objects/test_deploy_tokens.py b/gitlab/tests/objects/test_deploy_tokens.py index b98a67076..9cfa59860 100644 --- a/gitlab/tests/objects/test_deploy_tokens.py +++ b/gitlab/tests/objects/test_deploy_tokens.py @@ -1,34 +1,36 @@ """ GitLab API: https://docs.gitlab.com/ce/api/deploy_tokens.html """ - -from httmock import response, urlmatch, with_httmock +import pytest +import responses from gitlab.v4.objects import ProjectDeployToken -from .mocks import headers - -@urlmatch( - scheme="http", - netloc="localhost", - path="/api/v4/projects/1/deploy_tokens", - method="post", -) -def resp_deploy_token_create(url, request): - content = """{ +create_content = { "id": 1, "name": "test_deploy_token", "username": "custom-user", "expires_at": "2022-01-01T00:00:00.000Z", "token": "jMRvtPNxrn3crTAGukpZ", - "scopes": [ "read_repository" ]}""" - content = content.encode("utf-8") - return response(200, content, headers, None, 5, request) + "scopes": ["read_repository"], +} + + +@pytest.fixture +def resp_deploy_token_create(): + with responses.RequestsMock() as rsps: + rsps.add( + method=responses.POST, + url="http://localhost/api/v4/projects/1/deploy_tokens", + json=create_content, + content_type="application/json", + status=200, + ) + yield rsps -@with_httmock(resp_deploy_token_create) -def test_deploy_tokens(gl): +def test_deploy_tokens(gl, resp_deploy_token_create): deploy_token = gl.projects.get(1, lazy=True).deploytokens.create( { "name": "test_deploy_token", diff --git a/gitlab/tests/objects/test_deployments.py b/gitlab/tests/objects/test_deployments.py index 098251a80..3cde8fe1a 100644 --- a/gitlab/tests/objects/test_deployments.py +++ b/gitlab/tests/objects/test_deployments.py @@ -1,39 +1,37 @@ """ GitLab API: https://docs.gitlab.com/ce/api/deployments.html """ +import pytest +import responses -import json -from httmock import response, urlmatch, with_httmock +@pytest.fixture +def resp_deployment(): + content = {"id": 42, "status": "success", "ref": "master"} -from .mocks import headers + with responses.RequestsMock() as rsps: + rsps.add( + method=responses.POST, + url="http://localhost/api/v4/projects/1/deployments", + json=content, + content_type="application/json", + status=200, + ) -content = '{"id": 42, "status": "success", "ref": "master"}' -json_content = json.loads(content) + updated_content = dict(content) + updated_content["status"] = "failed" + rsps.add( + method=responses.PUT, + url="http://localhost/api/v4/projects/1/deployments/42", + json=updated_content, + content_type="application/json", + status=200, + ) + yield rsps -@urlmatch( - scheme="http", - netloc="localhost", - path="/api/v4/projects/1/deployments", - method="post", -) -def resp_deployment_create(url, request): - return response(200, json_content, headers, None, 5, request) - -@urlmatch( - scheme="http", - netloc="localhost", - path="/api/v4/projects/1/deployments/42", - method="put", -) -def resp_deployment_update(url, request): - return response(200, json_content, headers, None, 5, request) - - -@with_httmock(resp_deployment_create, resp_deployment_update) -def test_deployment(project): +def test_deployment(project, resp_deployment): deployment = project.deployments.create( { "environment": "Test", @@ -47,7 +45,6 @@ def test_deployment(project): assert deployment.status == "success" assert deployment.ref == "master" - json_content["status"] = "failed" deployment.status = "failed" deployment.save() assert deployment.status == "failed" diff --git a/gitlab/tests/objects/test_environments.py b/gitlab/tests/objects/test_environments.py index 3175c64d2..b49a1db4e 100644 --- a/gitlab/tests/objects/test_environments.py +++ b/gitlab/tests/objects/test_environments.py @@ -1,29 +1,28 @@ """ GitLab API: https://docs.gitlab.com/ce/api/environments.html """ - -from httmock import response, urlmatch, with_httmock +import pytest +import responses from gitlab.v4.objects import ProjectEnvironment -from .mocks import headers +@pytest.fixture +def resp_get_environment(): + content = {"name": "environment_name", "id": 1, "last_deployment": "sometime"} -@urlmatch( - scheme="http", - netloc="localhost", - path="/api/v4/projects/1/environments/1", - method="get", -) -def resp_get_environment(url, request): - content = '{"name": "environment_name", "id": 1, "last_deployment": "sometime"}'.encode( - "utf-8" - ) - return response(200, content, headers, None, 5, request) + with responses.RequestsMock() as rsps: + rsps.add( + method=responses.GET, + url="http://localhost/api/v4/projects/1/environments/1", + json=content, + content_type="application/json", + status=200, + ) + yield rsps -@with_httmock(resp_get_environment) -def test_project_environments(project): +def test_project_environments(project, resp_get_environment): environment = project.environments.get(1) assert isinstance(environment, ProjectEnvironment) assert environment.id == 1 diff --git a/gitlab/tests/objects/test_groups.py b/gitlab/tests/objects/test_groups.py index b5464b591..d4786f43a 100644 --- a/gitlab/tests/objects/test_groups.py +++ b/gitlab/tests/objects/test_groups.py @@ -3,45 +3,54 @@ """ import pytest - -from httmock import response, urlmatch, with_httmock +import responses import gitlab -from .mocks import * # noqa - - -@urlmatch(scheme="http", netloc="localhost", path="/api/v4/groups/1", method="get") -def resp_get_group(url, request): - content = '{"name": "name", "id": 1, "path": "path"}' - content = content.encode("utf-8") - return response(200, content, headers, None, 5, request) - - -@urlmatch(scheme="http", netloc="localhost", path="/api/v4/groups", method="post") -def resp_create_group(url, request): - content = '{"name": "name", "id": 1, "path": "path"}' - content = content.encode("utf-8") - return response(200, content, headers, None, 5, request) - - -@urlmatch( - scheme="http", netloc="localhost", path="/api/v4/groups/import", method="post", -) -def resp_create_import(url, request): - """Mock for Group import tests. - - GitLab does not respond with import status for group imports. - """ - - content = """{ - "message": "202 Accepted" - }""" - content = content.encode("utf-8") - return response(202, content, headers, None, 25, request) -@with_httmock(resp_get_group) -def test_get_group(gl): +@pytest.fixture +def resp_groups(): + content = {"name": "name", "id": 1, "path": "path"} + + with responses.RequestsMock(assert_all_requests_are_fired=False) as rsps: + rsps.add( + method=responses.GET, + url="http://localhost/api/v4/groups/1", + json=content, + content_type="application/json", + status=200, + ) + rsps.add( + method=responses.GET, + url="http://localhost/api/v4/groups", + json=[content], + content_type="application/json", + status=200, + ) + rsps.add( + method=responses.POST, + url="http://localhost/api/v4/groups", + json=content, + content_type="application/json", + status=200, + ) + yield rsps + + +@pytest.fixture +def resp_create_import(accepted_content): + with responses.RequestsMock() as rsps: + rsps.add( + method=responses.POST, + url="http://localhost/api/v4/groups/import", + json=accepted_content, + content_type="application/json", + status=202, + ) + yield rsps + + +def test_get_group(gl, resp_groups): data = gl.groups.get(1) assert isinstance(data, gitlab.v4.objects.Group) assert data.name == "name" @@ -49,8 +58,7 @@ def test_get_group(gl): assert data.id == 1 -@with_httmock(resp_create_group) -def test_create_group(gl): +def test_create_group(gl, resp_groups): name, path = "name", "path" data = gl.groups.create({"name": name, "path": path}) assert isinstance(data, gitlab.v4.objects.Group) @@ -58,37 +66,32 @@ def test_create_group(gl): assert data.path == path -@with_httmock(resp_create_export) -def test_create_group_export(group): +def test_create_group_export(group, resp_export): export = group.exports.create() assert export.message == "202 Accepted" @pytest.mark.skip("GitLab API endpoint not implemented") -@with_httmock(resp_create_export) -def test_refresh_group_export_status(group): +def test_refresh_group_export_status(group, resp_export): export = group.exports.create() export.refresh() assert export.export_status == "finished" -@with_httmock(resp_create_export, resp_download_export) -def test_download_group_export(group): +def test_download_group_export(group, resp_export, binary_content): export = group.exports.create() download = export.download() assert isinstance(download, bytes) assert download == binary_content -@with_httmock(resp_create_import) -def test_import_group(gl): +def test_import_group(gl, resp_create_import): group_import = gl.groups.import_group("file", "api-group", "API Group") assert group_import["message"] == "202 Accepted" @pytest.mark.skip("GitLab API endpoint not implemented") -@with_httmock(resp_create_import) -def test_refresh_group_import_status(group): +def test_refresh_group_import_status(group, resp_groups): group_import = group.imports.get() group_import.refresh() assert group_import.import_status == "finished" diff --git a/gitlab/tests/objects/test_hooks.py b/gitlab/tests/objects/test_hooks.py index 45403c497..fe5c21c98 100644 --- a/gitlab/tests/objects/test_hooks.py +++ b/gitlab/tests/objects/test_hooks.py @@ -1,22 +1,28 @@ """ GitLab API: https://docs.gitlab.com/ce/api/system_hooks.html """ - -from httmock import response, urlmatch, with_httmock +import pytest +import responses from gitlab.v4.objects import Hook -from .mocks import headers +@pytest.fixture +def resp_get_hook(): + content = {"url": "testurl", "id": 1} -@urlmatch(scheme="http", netloc="localhost", path="/api/v4/hooks/1", method="get") -def resp_get_hook(url, request): - content = '{"url": "testurl", "id": 1}'.encode("utf-8") - return response(200, content, headers, None, 5, request) + with responses.RequestsMock() as rsps: + rsps.add( + method=responses.GET, + url="http://localhost/api/v4/hooks/1", + json=content, + content_type="application/json", + status=200, + ) + yield rsps -@with_httmock(resp_get_hook) -def test_hooks(gl): +def test_hooks(gl, resp_get_hook): data = gl.hooks.get(1) assert isinstance(data, Hook) assert data.url == "testurl" diff --git a/gitlab/tests/objects/test_issues.py b/gitlab/tests/objects/test_issues.py index e09484104..f67d7209f 100644 --- a/gitlab/tests/objects/test_issues.py +++ b/gitlab/tests/objects/test_issues.py @@ -2,41 +2,49 @@ GitLab API: https://docs.gitlab.com/ce/api/issues.html """ -from httmock import urlmatch, response, with_httmock +import pytest +import responses -from .mocks import headers from gitlab.v4.objects import ProjectIssuesStatistics -@urlmatch(scheme="http", netloc="localhost", path="/api/v4/issues", method="get") -def resp_get_issue(url, request): - content = '[{"name": "name", "id": 1}, ' '{"name": "other_name", "id": 2}]' - content = content.encode("utf-8") - return response(200, content, headers, None, 5, request) +@pytest.fixture +def resp_issue(): + content = [{"name": "name", "id": 1}, {"name": "other_name", "id": 2}] + with responses.RequestsMock() as rsps: + rsps.add( + method=responses.GET, + url="http://localhost/api/v4/issues", + json=content, + content_type="application/json", + status=200, + ) + yield rsps -@urlmatch( - scheme="http", - netloc="localhost", - path="/api/v4/projects/1/issues_statistics", - method="get", -) -def resp_get_environment(url, request): - content = """{"statistics": {"counts": {"all": 20, "closed": 5, "opened": 15}}}""".encode( - "utf-8" - ) - return response(200, content, headers, None, 5, request) +@pytest.fixture +def resp_issue_statistics(): + content = {"statistics": {"counts": {"all": 20, "closed": 5, "opened": 15}}} -@with_httmock(resp_get_issue) -def test_issues(gl): + with responses.RequestsMock() as rsps: + rsps.add( + method=responses.GET, + url="http://localhost/api/v4/projects/1/issues_statistics", + json=content, + content_type="application/json", + status=200, + ) + yield rsps + + +def test_issues(gl, resp_issue): data = gl.issues.list() assert data[1].id == 2 assert data[1].name == "other_name" -@with_httmock(resp_get_environment) -def test_project_issues_statistics(project): +def test_project_issues_statistics(project, resp_issue_statistics): statistics = project.issuesstatistics.get() assert isinstance(statistics, ProjectIssuesStatistics) assert statistics.statistics["counts"]["all"] == 20 diff --git a/gitlab/tests/objects/test_pipeline_schedules.py b/gitlab/tests/objects/test_pipeline_schedules.py index 6b5630415..c5dcc76b9 100644 --- a/gitlab/tests/objects/test_pipeline_schedules.py +++ b/gitlab/tests/objects/test_pipeline_schedules.py @@ -1,61 +1,52 @@ """ GitLab API: https://docs.gitlab.com/ce/api/pipeline_schedules.html """ - -from httmock import response, urlmatch, with_httmock - -from .mocks import headers - - -@urlmatch( - scheme="http", - netloc="localhost", - path="/api/v4/projects/1/pipeline_schedules$", - method="post", -) -def resp_create_project_pipeline_schedule(url, request): - """Mock for creating project pipeline Schedules POST response.""" - content = """{ - "id": 14, - "description": "Build packages", - "ref": "master", - "cron": "0 1 * * 5", - "cron_timezone": "UTC", - "next_run_at": "2017-05-26T01:00:00.000Z", - "active": true, - "created_at": "2017-05-19T13:43:08.169Z", - "updated_at": "2017-05-19T13:43:08.169Z", - "last_pipeline": null, - "owner": { - "name": "Administrator", - "username": "root", - "id": 1, - "state": "active", - "avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon", - "web_url": "https://gitlab.example.com/root" +import pytest +import responses + + +@pytest.fixture +def resp_project_pipeline_schedule(created_content): + content = { + "id": 14, + "description": "Build packages", + "ref": "master", + "cron": "0 1 * * 5", + "cron_timezone": "UTC", + "next_run_at": "2017-05-26T01:00:00.000Z", + "active": True, + "created_at": "2017-05-19T13:43:08.169Z", + "updated_at": "2017-05-19T13:43:08.169Z", + "last_pipeline": None, + "owner": { + "name": "Administrator", + "username": "root", + "id": 1, + "state": "active", + "avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon", + "web_url": "https://gitlab.example.com/root", + }, } -}""" - content = content.encode("utf-8") - return response(200, content, headers, None, 5, request) - - -@urlmatch( - scheme="http", - netloc="localhost", - path="/api/v4/projects/1/pipeline_schedules/14/play", - method="post", -) -def resp_play_project_pipeline_schedule(url, request): - """Mock for playing a project pipeline schedule POST response.""" - content = """{"message": "201 Created"}""" - content = content.encode("utf-8") - return response(200, content, headers, None, 5, request) - -@with_httmock( - resp_create_project_pipeline_schedule, resp_play_project_pipeline_schedule -) -def test_project_pipeline_schedule_play(project): + with responses.RequestsMock() as rsps: + rsps.add( + method=responses.POST, + url="http://localhost/api/v4/projects/1/pipeline_schedules", + json=content, + content_type="application/json", + status=200, + ) + rsps.add( + method=responses.POST, + url="http://localhost/api/v4/projects/1/pipeline_schedules/14/play", + json=created_content, + content_type="application/json", + status=201, + ) + yield rsps + + +def test_project_pipeline_schedule_play(project, resp_project_pipeline_schedule): description = "Build packages" cronline = "0 1 * * 5" sched = project.pipelineschedules.create( diff --git a/gitlab/tests/objects/test_project_import_export.py b/gitlab/tests/objects/test_project_import_export.py index e5c37a84c..78e51b1fe 100644 --- a/gitlab/tests/objects/test_project_import_export.py +++ b/gitlab/tests/objects/test_project_import_export.py @@ -1,104 +1,90 @@ """ GitLab API: https://docs.gitlab.com/ce/api/project_import_export.html """ +import pytest +import responses + + +@pytest.fixture +def resp_import_project(): + content = { + "id": 1, + "description": None, + "name": "api-project", + "name_with_namespace": "Administrator / api-project", + "path": "api-project", + "path_with_namespace": "root/api-project", + "created_at": "2018-02-13T09:05:58.023Z", + "import_status": "scheduled", + } + + with responses.RequestsMock() as rsps: + rsps.add( + method=responses.POST, + url="http://localhost/api/v4/projects/import", + json=content, + content_type="application/json", + status=200, + ) + yield rsps + + +@pytest.fixture +def resp_import_status(): + content = { + "id": 1, + "description": "Itaque perspiciatis minima aspernatur corporis consequatur.", + "name": "Gitlab Test", + "name_with_namespace": "Gitlab Org / Gitlab Test", + "path": "gitlab-test", + "path_with_namespace": "gitlab-org/gitlab-test", + "created_at": "2017-08-29T04:36:44.383Z", + "import_status": "finished", + } -from httmock import response, urlmatch, with_httmock - -from .mocks import * - - -@urlmatch( - scheme="http", netloc="localhost", path="/api/v4/projects/1/export", method="get", -) -def resp_export_status(url, request): - """Mock for Project Export GET response.""" - content = """{ - "id": 1, - "description": "Itaque perspiciatis minima aspernatur", - "name": "Gitlab Test", - "name_with_namespace": "Gitlab Org / Gitlab Test", - "path": "gitlab-test", - "path_with_namespace": "gitlab-org/gitlab-test", - "created_at": "2017-08-29T04:36:44.383Z", - "export_status": "finished", - "_links": { - "api_url": "https://gitlab.test/api/v4/projects/1/export/download", - "web_url": "https://gitlab.test/gitlab-test/download_export" - } + with responses.RequestsMock() as rsps: + rsps.add( + method=responses.GET, + url="http://localhost/api/v4/projects/1/import", + json=content, + content_type="application/json", + status=200, + ) + yield rsps + + +@pytest.fixture +def resp_import_github(): + content = { + "id": 27, + "name": "my-repo", + "full_path": "/root/my-repo", + "full_name": "Administrator / my-repo", } - """ - content = content.encode("utf-8") - return response(200, content, headers, None, 25, request) - - -@urlmatch( - scheme="http", netloc="localhost", path="/api/v4/projects/import", method="post", -) -def resp_import_project(url, request): - """Mock for Project Import POST response.""" - content = """{ - "id": 1, - "description": null, - "name": "api-project", - "name_with_namespace": "Administrator / api-project", - "path": "api-project", - "path_with_namespace": "root/api-project", - "created_at": "2018-02-13T09:05:58.023Z", - "import_status": "scheduled" - }""" - content = content.encode("utf-8") - return response(200, content, headers, None, 25, request) - - -@urlmatch( - scheme="http", netloc="localhost", path="/api/v4/projects/1/import", method="get", -) -def resp_import_status(url, request): - """Mock for Project Import GET response.""" - content = """{ - "id": 1, - "description": "Itaque perspiciatis minima aspernatur corporis consequatur.", - "name": "Gitlab Test", - "name_with_namespace": "Gitlab Org / Gitlab Test", - "path": "gitlab-test", - "path_with_namespace": "gitlab-org/gitlab-test", - "created_at": "2017-08-29T04:36:44.383Z", - "import_status": "finished" - }""" - content = content.encode("utf-8") - return response(200, content, headers, None, 25, request) - - -@urlmatch( - scheme="http", netloc="localhost", path="/api/v4/import/github", method="post", -) -def resp_import_github(url, request): - """Mock for GitHub Project Import POST response.""" - content = """{ - "id": 27, - "name": "my-repo", - "full_path": "/root/my-repo", - "full_name": "Administrator / my-repo" - }""" - content = content.encode("utf-8") - return response(200, content, headers, None, 25, request) - - -@with_httmock(resp_import_project) -def test_import_project(gl): + + with responses.RequestsMock() as rsps: + rsps.add( + method=responses.POST, + url="http://localhost/api/v4/import/github", + json=content, + content_type="application/json", + status=200, + ) + yield rsps + + +def test_import_project(gl, resp_import_project): project_import = gl.projects.import_project("file", "api-project") assert project_import["import_status"] == "scheduled" -@with_httmock(resp_import_status) -def test_refresh_project_import_status(project): +def test_refresh_project_import_status(project, resp_import_status): project_import = project.imports.get() project_import.refresh() assert project_import.import_status == "finished" -@with_httmock(resp_import_github) -def test_import_github(gl): +def test_import_github(gl, resp_import_github): base_path = "/root" name = "my-repo" ret = gl.projects.import_github("githubkey", 1234, base_path, name) @@ -108,21 +94,18 @@ def test_import_github(gl): assert ret["full_name"].endswith(name) -@with_httmock(resp_create_export) -def test_create_project_export(project): +def test_create_project_export(project, resp_export): export = project.exports.create() assert export.message == "202 Accepted" -@with_httmock(resp_create_export, resp_export_status) -def test_refresh_project_export_status(project): +def test_refresh_project_export_status(project, resp_export): export = project.exports.create() export.refresh() assert export.export_status == "finished" -@with_httmock(resp_create_export, resp_download_export) -def test_download_project_export(project): +def test_download_project_export(project, resp_export, binary_content): export = project.exports.create() download = export.download() assert isinstance(download, bytes) diff --git a/gitlab/tests/objects/test_project_statistics.py b/gitlab/tests/objects/test_project_statistics.py index c2b194fee..50d9a6d79 100644 --- a/gitlab/tests/objects/test_project_statistics.py +++ b/gitlab/tests/objects/test_project_statistics.py @@ -1,29 +1,28 @@ """ GitLab API: https://docs.gitlab.com/ce/api/project_statistics.html """ - -from httmock import response, urlmatch, with_httmock +import pytest +import responses from gitlab.v4.objects import ProjectAdditionalStatistics -from .mocks import headers +@pytest.fixture +def resp_project_statistics(): + content = {"fetches": {"total": 50, "days": [{"count": 10, "date": "2018-01-10"}]}} -@urlmatch( - scheme="http", - netloc="localhost", - path="/api/v4/projects/1/statistics", - method="get", -) -def resp_get_statistics(url, request): - content = """{"fetches": {"total": 50, "days": [{"count": 10, "date": "2018-01-10"}]}}""".encode( - "utf-8" - ) - return response(200, content, headers, None, 5, request) + with responses.RequestsMock() as rsps: + rsps.add( + method=responses.GET, + url="http://localhost/api/v4/projects/1/statistics", + json=content, + content_type="application/json", + status=200, + ) + yield rsps -@with_httmock(resp_get_statistics) -def test_project_additional_statistics(project): +def test_project_additional_statistics(project, resp_project_statistics): statistics = project.additionalstatistics.get() assert isinstance(statistics, ProjectAdditionalStatistics) assert statistics.fetches["total"] == 50 diff --git a/gitlab/tests/objects/test_projects.py b/gitlab/tests/objects/test_projects.py index 7fefe3f6f..1e8b8b604 100644 --- a/gitlab/tests/objects/test_projects.py +++ b/gitlab/tests/objects/test_projects.py @@ -3,31 +3,51 @@ """ import pytest - +import responses from gitlab.v4.objects import Project -from httmock import urlmatch, response, with_httmock -from .mocks import headers +project_content = {"name": "name", "id": 1} + + +@pytest.fixture +def resp_get_project(): + with responses.RequestsMock() as rsps: + rsps.add( + method=responses.GET, + url="http://localhost/api/v4/projects/1", + json=project_content, + content_type="application/json", + status=200, + ) + yield rsps -@urlmatch(scheme="http", netloc="localhost", path="/api/v4/projects/1", method="get") -def resp_get_project(url, request): - content = '{"name": "name", "id": 1}'.encode("utf-8") - return response(200, content, headers, None, 5, request) +@pytest.fixture +def resp_list_projects(): + with responses.RequestsMock() as rsps: + rsps.add( + method=responses.GET, + url="http://localhost/api/v4/projects", + json=[project_content], + content_type="application/json", + status=200, + ) + yield rsps -@with_httmock(resp_get_project) -def test_get_project(gl): + +def test_get_project(gl, resp_get_project): data = gl.projects.get(1) assert isinstance(data, Project) assert data.name == "name" assert data.id == 1 -@pytest.mark.skip(reason="missing test") -def test_list_projects(gl): - pass +def test_list_projects(gl, resp_list_projects): + projects = gl.projects.list() + assert isinstance(projects[0], Project) + assert projects[0].name == "name" @pytest.mark.skip(reason="missing test") diff --git a/gitlab/tests/objects/test_remote_mirrors.py b/gitlab/tests/objects/test_remote_mirrors.py index e62a71e57..1ac35a25b 100644 --- a/gitlab/tests/objects/test_remote_mirrors.py +++ b/gitlab/tests/objects/test_remote_mirrors.py @@ -2,100 +2,69 @@ GitLab API: https://docs.gitlab.com/ce/api/remote_mirrors.html """ -from httmock import response, urlmatch, with_httmock +import pytest +import responses from gitlab.v4.objects import ProjectRemoteMirror -from .mocks import headers -@urlmatch( - scheme="http", - netloc="localhost", - path="/api/v4/projects/1/remote_mirrors", - method="get", -) -def resp_get_remote_mirrors(url, request): - """Mock for Project Remote Mirrors GET response.""" - content = """[ - { - "enabled": true, - "id": 101486, - "last_error": null, +@pytest.fixture +def resp_remote_mirrors(): + content = { + "enabled": True, + "id": 1, + "last_error": None, "last_successful_update_at": "2020-01-06T17:32:02.823Z", "last_update_at": "2020-01-06T17:32:02.823Z", "last_update_started_at": "2020-01-06T17:31:55.864Z", - "only_protected_branches": true, - "update_status": "finished", - "url": "https://*****:*****@gitlab.com/gitlab-org/security/gitlab.git" - } - ]""" - content = content.encode("utf-8") - return response(200, content, headers, None, 5, request) - - -@urlmatch( - scheme="http", - netloc="localhost", - path="/api/v4/projects/1/remote_mirrors", - method="post", -) -def resp_create_remote_mirror(url, request): - """Mock for Project Remote Mirrors POST response.""" - content = """{ - "enabled": false, - "id": 101486, - "last_error": null, - "last_successful_update_at": null, - "last_update_at": null, - "last_update_started_at": null, - "only_protected_branches": false, + "only_protected_branches": True, "update_status": "none", - "url": "https://*****:*****@example.com/gitlab/example.git" - }""" - content = content.encode("utf-8") - return response(200, content, headers, None, 5, request) + "url": "https://*****:*****@gitlab.com/gitlab-org/security/gitlab.git", + } + with responses.RequestsMock(assert_all_requests_are_fired=False) as rsps: + rsps.add( + method=responses.GET, + url="http://localhost/api/v4/projects/1/remote_mirrors", + json=[content], + content_type="application/json", + status=200, + ) + rsps.add( + method=responses.POST, + url="http://localhost/api/v4/projects/1/remote_mirrors", + json=content, + content_type="application/json", + status=200, + ) -@urlmatch( - scheme="http", - netloc="localhost", - path="/api/v4/projects/1/remote_mirrors/1", - method="put", -) -def resp_update_remote_mirror(url, request): - """Mock for Project Remote Mirrors PUT response.""" - content = """{ - "enabled": false, - "id": 101486, - "last_error": null, - "last_successful_update_at": "2020-01-06T17:32:02.823Z", - "last_update_at": "2020-01-06T17:32:02.823Z", - "last_update_started_at": "2020-01-06T17:31:55.864Z", - "only_protected_branches": true, - "update_status": "finished", - "url": "https://*****:*****@gitlab.com/gitlab-org/security/gitlab.git" - }""" - content = content.encode("utf-8") - return response(200, content, headers, None, 5, request) + updated_content = dict(content) + updated_content["update_status"] = "finished" + + rsps.add( + method=responses.PUT, + url="http://localhost/api/v4/projects/1/remote_mirrors/1", + json=updated_content, + content_type="application/json", + status=200, + ) + yield rsps -@with_httmock(resp_get_remote_mirrors) -def test_list_project_remote_mirrors(project): +def test_list_project_remote_mirrors(project, resp_remote_mirrors): mirrors = project.remote_mirrors.list() assert isinstance(mirrors, list) assert isinstance(mirrors[0], ProjectRemoteMirror) assert mirrors[0].enabled -@with_httmock(resp_create_remote_mirror) -def test_create_project_remote_mirror(project): +def test_create_project_remote_mirror(project, resp_remote_mirrors): mirror = project.remote_mirrors.create({"url": "https://example.com"}) assert isinstance(mirror, ProjectRemoteMirror) assert mirror.update_status == "none" -@with_httmock(resp_create_remote_mirror, resp_update_remote_mirror) -def test_update_project_remote_mirror(project): +def test_update_project_remote_mirror(project, resp_remote_mirrors): mirror = project.remote_mirrors.create({"url": "https://example.com"}) mirror.only_protected_branches = True mirror.save() diff --git a/gitlab/tests/objects/test_runners.py b/gitlab/tests/objects/test_runners.py index 2f86bef8d..490ba36a0 100644 --- a/gitlab/tests/objects/test_runners.py +++ b/gitlab/tests/objects/test_runners.py @@ -1,9 +1,9 @@ -import unittest +import re + +import pytest import responses + import gitlab -import pytest -import re -from .mocks import * # noqa runner_detail = { diff --git a/gitlab/tests/objects/test_services.py b/gitlab/tests/objects/test_services.py index a0cded733..5b2bcb80d 100644 --- a/gitlab/tests/objects/test_services.py +++ b/gitlab/tests/objects/test_services.py @@ -2,110 +2,71 @@ GitLab API: https://docs.gitlab.com/ce/api/services.html """ -from httmock import urlmatch, response, with_httmock +import pytest +import responses from gitlab.v4.objects import ProjectService -from .mocks import headers -@urlmatch( - scheme="http", - netloc="localhost", - path="/api/v4/projects/1/services/pipelines-email", - method="put", -) -def resp_update_service(url, request): - """Mock for Service update PUT response.""" - content = """{ +@pytest.fixture +def resp_service(): + content = { "id": 100152, "title": "Pipelines emails", "slug": "pipelines-email", "created_at": "2019-01-14T08:46:43.637+01:00", "updated_at": "2019-07-01T14:10:36.156+02:00", - "active": true, - "commit_events": true, - "push_events": true, - "issues_events": true, - "confidential_issues_events": true, - "merge_requests_events": true, - "tag_push_events": true, - "note_events": true, - "confidential_note_events": true, - "pipeline_events": true, - "wiki_page_events": true, - "job_events": true, - "comment_on_event_enabled": true, - "project_id": 1 - }""" - content = content.encode("utf-8") - return response(200, content, headers, None, 5, request) - - -@urlmatch( - scheme="http", - netloc="localhost", - path="/api/v4/projects/1/services/pipelines-email", - method="get", -) -def resp_get_service(url, request): - """Mock for Service GET response.""" - content = """{ - "id": 100152, - "title": "Pipelines emails", - "slug": "pipelines-email", - "created_at": "2019-01-14T08:46:43.637+01:00", - "updated_at": "2019-07-01T14:10:36.156+02:00", - "active": true, - "commit_events": true, - "push_events": true, - "issues_events": true, - "confidential_issues_events": true, - "merge_requests_events": true, - "tag_push_events": true, - "note_events": true, - "confidential_note_events": true, - "pipeline_events": true, - "wiki_page_events": true, - "job_events": true, - "comment_on_event_enabled": true, - "project_id": 1 - }""" - content = content.encode("utf-8") - return response(200, content, headers, None, 5, request) - - -@urlmatch( - scheme="http", netloc="localhost", path="/api/v4/projects/1/services", method="get", -) -def resp_get_active_services(url, request): - """Mock for active Services GET response.""" - content = """[{ - "id": 100152, - "title": "Pipelines emails", - "slug": "pipelines-email", - "created_at": "2019-01-14T08:46:43.637+01:00", - "updated_at": "2019-07-01T14:10:36.156+02:00", - "active": true, - "commit_events": true, - "push_events": true, - "issues_events": true, - "confidential_issues_events": true, - "merge_requests_events": true, - "tag_push_events": true, - "note_events": true, - "confidential_note_events": true, - "pipeline_events": true, - "wiki_page_events": true, - "job_events": true, - "comment_on_event_enabled": true, - "project_id": 1 - }]""" - content = content.encode("utf-8") - return response(200, content, headers, None, 5, request) - - -@with_httmock(resp_get_active_services) -def test_list_active_services(project): + "active": True, + "commit_events": True, + "push_events": True, + "issues_events": True, + "confidential_issues_events": True, + "merge_requests_events": True, + "tag_push_events": True, + "note_events": True, + "confidential_note_events": True, + "pipeline_events": True, + "wiki_page_events": True, + "job_events": True, + "comment_on_event_enabled": True, + "project_id": 1, + } + + with responses.RequestsMock(assert_all_requests_are_fired=False) as rsps: + rsps.add( + method=responses.GET, + url="http://localhost/api/v4/projects/1/services", + json=[content], + content_type="application/json", + status=200, + ) + rsps.add( + method=responses.GET, + url="http://localhost/api/v4/projects/1/services", + json=content, + content_type="application/json", + status=200, + ) + rsps.add( + method=responses.GET, + url="http://localhost/api/v4/projects/1/services/pipelines-email", + json=content, + content_type="application/json", + status=200, + ) + updated_content = dict(content) + updated_content["issues_events"] = False + rsps.add( + method=responses.PUT, + url="http://localhost/api/v4/projects/1/services/pipelines-email", + json=updated_content, + content_type="application/json", + status=200, + ) + yield rsps + + +def test_list_active_services(project, resp_service): services = project.services.list() assert isinstance(services, list) assert isinstance(services[0], ProjectService) @@ -113,22 +74,20 @@ def test_list_active_services(project): assert services[0].push_events -def test_list_available_services(project): +def test_list_available_services(project, resp_service): services = project.services.available() assert isinstance(services, list) assert isinstance(services[0], str) -@with_httmock(resp_get_service) -def test_get_service(project): +def test_get_service(project, resp_service): service = project.services.get("pipelines-email") assert isinstance(service, ProjectService) assert service.push_events is True -@with_httmock(resp_get_service, resp_update_service) -def test_update_service(project): +def test_update_service(project, resp_service): service = project.services.get("pipelines-email") - service.issues_events = True + service.issues_events = False service.save() - assert service.issues_events is True + assert service.issues_events is False diff --git a/gitlab/tests/objects/test_snippets.py b/gitlab/tests/objects/test_snippets.py index 86eb54c1b..7e8afc2f0 100644 --- a/gitlab/tests/objects/test_snippets.py +++ b/gitlab/tests/objects/test_snippets.py @@ -3,9 +3,8 @@ https://docs.gitlab.com/ee/api/snippets.html (todo) """ -from httmock import response, urlmatch, with_httmock - -from .mocks import headers +import pytest +import responses title = "Example Snippet Title" @@ -13,97 +12,67 @@ new_title = "new-title" -@urlmatch( - scheme="http", netloc="localhost", path="/api/v4/projects/1/snippets", method="get", -) -def resp_list_snippet(url, request): - content = """[{ - "title": "%s", - "description": "More verbose snippet description", - "file_name": "example.txt", - "content": "source code with multiple lines", - "visibility": "%s"}]""" % ( - title, - visibility, - ) - content = content.encode("utf-8") - return response(200, content, headers, None, 25, request) - - -@urlmatch( - scheme="http", - netloc="localhost", - path="/api/v4/projects/1/snippets/1", - method="get", -) -def resp_get_snippet(url, request): - content = """{ - "title": "%s", - "description": "More verbose snippet description", - "file_name": "example.txt", - "content": "source code with multiple lines", - "visibility": "%s"}""" % ( - title, - visibility, - ) - content = content.encode("utf-8") - return response(200, content, headers, None, 25, request) - - -@urlmatch( - scheme="http", - netloc="localhost", - path="/api/v4/projects/1/snippets", - method="post", -) -def resp_create_snippet(url, request): - content = """{ - "title": "%s", - "description": "More verbose snippet description", - "file_name": "example.txt", - "content": "source code with multiple lines", - "visibility": "%s"}""" % ( - title, - visibility, - ) - content = content.encode("utf-8") - return response(200, content, headers, None, 25, request) - - -@urlmatch( - scheme="http", netloc="localhost", path="/api/v4/projects/1/snippets", method="put", -) -def resp_update_snippet(url, request): - content = """{ - "title": "%s", - "description": "More verbose snippet description", - "file_name": "example.txt", - "content": "source code with multiple lines", - "visibility": "%s"}""" % ( - new_title, - visibility, - ) - content = content.encode("utf-8") - return response(200, content, headers, None, 25, request) - - -@with_httmock(resp_list_snippet) -def test_list_project_snippets(project): +@pytest.fixture +def resp_snippet(): + content = { + "title": title, + "description": "More verbose snippet description", + "file_name": "example.txt", + "content": "source code with multiple lines", + "visibility": visibility, + } + + with responses.RequestsMock(assert_all_requests_are_fired=False) as rsps: + rsps.add( + method=responses.GET, + url="http://localhost/api/v4/projects/1/snippets", + json=[content], + content_type="application/json", + status=200, + ) + rsps.add( + method=responses.GET, + url="http://localhost/api/v4/projects/1/snippets/1", + json=content, + content_type="application/json", + status=200, + ) + rsps.add( + method=responses.POST, + url="http://localhost/api/v4/projects/1/snippets", + json=content, + content_type="application/json", + status=200, + ) + + updated_content = dict(content) + updated_content["title"] = new_title + updated_content["visibility"] = visibility + + rsps.add( + method=responses.PUT, + url="http://localhost/api/v4/projects/1/snippets", + json=updated_content, + content_type="application/json", + status=200, + ) + yield rsps + + +def test_list_project_snippets(project, resp_snippet): snippets = project.snippets.list() assert len(snippets) == 1 assert snippets[0].title == title assert snippets[0].visibility == visibility -@with_httmock(resp_get_snippet) -def test_get_project_snippets(project): +def test_get_project_snippet(project, resp_snippet): snippet = project.snippets.get(1) assert snippet.title == title assert snippet.visibility == visibility -@with_httmock(resp_create_snippet, resp_update_snippet) -def test_create_update_project_snippets(project): +def test_create_update_project_snippets(project, resp_snippet): snippet = project.snippets.create( { "title": title, diff --git a/gitlab/tests/objects/test_submodules.py b/gitlab/tests/objects/test_submodules.py index 2e7630275..539af7b5c 100644 --- a/gitlab/tests/objects/test_submodules.py +++ b/gitlab/tests/objects/test_submodules.py @@ -1,52 +1,42 @@ """ GitLab API: https://docs.gitlab.com/ce/api/repository_submodules.html """ - -from httmock import response, urlmatch, with_httmock +import pytest +import responses from gitlab.v4.objects import Project -from .mocks import headers - - -@urlmatch(scheme="http", netloc="localhost", path="/api/v4/projects/1$", method="get") -def resp_get_project(url, request): - content = '{"name": "name", "id": 1}'.encode("utf-8") - return response(200, content, headers, None, 5, request) - - -@urlmatch( - scheme="http", - netloc="localhost", - path="/api/v4/projects/1/repository/submodules/foo%2Fbar", - method="put", -) -def resp_update_submodule(url, request): - content = """{ - "id": "ed899a2f4b50b4370feeea94676502b42383c746", - "short_id": "ed899a2f4b5", - "title": "Message", - "author_name": "Author", - "author_email": "author@example.com", - "committer_name": "Author", - "committer_email": "author@example.com", - "created_at": "2018-09-20T09:26:24.000-07:00", - "message": "Message", - "parent_ids": [ "ae1d9fb46aa2b07ee9836d49862ec4e2c46fbbba" ], - "committed_date": "2018-09-20T09:26:24.000-07:00", - "authored_date": "2018-09-20T09:26:24.000-07:00", - "status": null}""" - content = content.encode("utf-8") - return response(200, content, headers, None, 5, request) - - -@with_httmock(resp_get_project, resp_update_submodule) -def test_update_submodule(gl): - project = gl.projects.get(1) - assert isinstance(project, Project) - assert project.name == "name" - assert project.id == 1 +@pytest.fixture +def resp_update_submodule(): + content = { + "id": "ed899a2f4b50b4370feeea94676502b42383c746", + "short_id": "ed899a2f4b5", + "title": "Message", + "author_name": "Author", + "author_email": "author@example.com", + "committer_name": "Author", + "committer_email": "author@example.com", + "created_at": "2018-09-20T09:26:24.000-07:00", + "message": "Message", + "parent_ids": ["ae1d9fb46aa2b07ee9836d49862ec4e2c46fbbba"], + "committed_date": "2018-09-20T09:26:24.000-07:00", + "authored_date": "2018-09-20T09:26:24.000-07:00", + "status": None, + } + + with responses.RequestsMock() as rsps: + rsps.add( + method=responses.PUT, + url="http://localhost/api/v4/projects/1/repository/submodules/foo%2Fbar", + json=content, + content_type="application/json", + status=200, + ) + yield rsps + + +def test_update_submodule(project, resp_update_submodule): ret = project.update_submodule( submodule="foo/bar", branch="master", diff --git a/gitlab/tests/objects/test_todos.py b/gitlab/tests/objects/test_todos.py index 5b30dc95f..07bb6803c 100644 --- a/gitlab/tests/objects/test_todos.py +++ b/gitlab/tests/objects/test_todos.py @@ -5,45 +5,51 @@ import json import os -from httmock import response, urlmatch, with_httmock +import pytest +import responses from gitlab.v4.objects import Todo -from .mocks import headers - with open(os.path.dirname(__file__) + "/../data/todo.json", "r") as json_file: todo_content = json_file.read() json_content = json.loads(todo_content) - encoded_content = todo_content.encode("utf-8") - - -@urlmatch(scheme="http", netloc="localhost", path="/api/v4/todos", method="get") -def resp_get_todo(url, request): - return response(200, encoded_content, headers, None, 5, request) - - -@urlmatch( - scheme="http", - netloc="localhost", - path="/api/v4/todos/102/mark_as_done", - method="post", -) -def resp_mark_as_done(url, request): - single_todo = json.dumps(json_content[0]) - content = single_todo.encode("utf-8") - return response(200, content, headers, None, 5, request) - - -@urlmatch( - scheme="http", netloc="localhost", path="/api/v4/todos/mark_as_done", method="post", -) -def resp_mark_all_as_done(url, request): - return response(204, {}, headers, None, 5, request) -@with_httmock(resp_get_todo, resp_mark_as_done) -def test_todo(gl): +@pytest.fixture +def resp_todo(): + with responses.RequestsMock(assert_all_requests_are_fired=False) as rsps: + rsps.add( + method=responses.GET, + url="http://localhost/api/v4/todos", + json=json_content, + content_type="application/json", + status=200, + ) + rsps.add( + method=responses.POST, + url="http://localhost/api/v4/todos/102/mark_as_done", + json=json_content[0], + content_type="application/json", + status=200, + ) + yield rsps + + +@pytest.fixture +def resp_mark_all_as_done(): + with responses.RequestsMock() as rsps: + rsps.add( + method=responses.POST, + url="http://localhost/api/v4/todos/mark_as_done", + json={}, + content_type="application/json", + status=204, + ) + yield rsps + + +def test_todo(gl, resp_todo): todo = gl.todos.list()[0] assert isinstance(todo, Todo) assert todo.id == 102 @@ -53,6 +59,5 @@ def test_todo(gl): todo.mark_as_done() -@with_httmock(resp_mark_all_as_done) -def test_todo_mark_all_as_done(gl): +def test_todo_mark_all_as_done(gl, resp_mark_all_as_done): gl.todos.mark_all_as_done() diff --git a/gitlab/tests/objects/test_users.py b/gitlab/tests/objects/test_users.py index 88175d09d..ec282cfc0 100644 --- a/gitlab/tests/objects/test_users.py +++ b/gitlab/tests/objects/test_users.py @@ -1,94 +1,120 @@ """ GitLab API: https://docs.gitlab.com/ce/api/users.html """ - -from httmock import response, urlmatch, with_httmock +import pytest +import responses from gitlab.v4.objects import User, UserMembership, UserStatus -from .mocks import headers - - -@urlmatch(scheme="http", netloc="localhost", path="/api/v4/users/1", method="get") -def resp_get_user(url, request): - content = ( - '{"name": "name", "id": 1, "password": "password", ' - '"username": "username", "email": "email"}' - ) - content = content.encode("utf-8") - return response(200, content, headers, None, 5, request) - - -@urlmatch( - scheme="http", netloc="localhost", path="/api/v4/users/1/memberships", method="get", -) -def resp_get_user_memberships(url, request): - content = """[ - { - "source_id": 1, - "source_name": "Project one", - "source_type": "Project", - "access_level": "20" - }, - { - "source_id": 3, - "source_name": "Group three", - "source_type": "Namespace", - "access_level": "20" - } - ]""" - content = content.encode("utf-8") - return response(200, content, headers, None, 5, request) - - -@urlmatch( - scheme="http", netloc="localhost", path="/api/v4/users/1/activate", method="post", -) -def resp_activate(url, request): - return response(201, {}, headers, None, 5, request) - - -@urlmatch( - scheme="http", netloc="localhost", path="/api/v4/users/1/deactivate", method="post", -) -def resp_deactivate(url, request): - return response(201, {}, headers, None, 5, request) - - -@urlmatch( - scheme="http", netloc="localhost", path="/api/v4/users/1/status", method="get", -) -def resp_get_user_status(url, request): - content = ( - '{"message": "test", "message_html": "

Message

", "emoji": "thumbsup"}' - ) - content = content.encode("utf-8") - return response(200, content, headers, None, 5, request) - - -@with_httmock(resp_get_user) -def test_get_user(gl): + + +@pytest.fixture +def resp_get_user(): + content = { + "name": "name", + "id": 1, + "password": "password", + "username": "username", + "email": "email", + } + + with responses.RequestsMock() as rsps: + rsps.add( + method=responses.GET, + url="http://localhost/api/v4/users/1", + json=content, + content_type="application/json", + status=200, + ) + yield rsps + + +@pytest.fixture +def resp_get_user_memberships(): + content = [ + { + "source_id": 1, + "source_name": "Project one", + "source_type": "Project", + "access_level": "20", + }, + { + "source_id": 3, + "source_name": "Group three", + "source_type": "Namespace", + "access_level": "20", + }, + ] + + with responses.RequestsMock() as rsps: + rsps.add( + method=responses.GET, + url="http://localhost/api/v4/users/1/memberships", + json=content, + content_type="application/json", + status=200, + ) + yield rsps + + +@pytest.fixture +def resp_activate(): + with responses.RequestsMock(assert_all_requests_are_fired=False) as rsps: + rsps.add( + method=responses.POST, + url="http://localhost/api/v4/users/1/activate", + json={}, + content_type="application/json", + status=201, + ) + rsps.add( + method=responses.POST, + url="http://localhost/api/v4/users/1/deactivate", + json={}, + content_type="application/json", + status=201, + ) + yield rsps + + +@pytest.fixture +def resp_get_user_status(): + content = { + "message": "test", + "message_html": "

Message

", + "emoji": "thumbsup", + } + + with responses.RequestsMock() as rsps: + rsps.add( + method=responses.GET, + url="http://localhost/api/v4/users/1/status", + json=content, + content_type="application/json", + status=200, + ) + yield rsps + + +def test_get_user(gl, resp_get_user): user = gl.users.get(1) assert isinstance(user, User) assert user.name == "name" assert user.id == 1 -@with_httmock(resp_get_user_memberships) -def test_user_memberships(user): +def test_user_memberships(user, resp_get_user_memberships): memberships = user.memberships.list() assert isinstance(memberships[0], UserMembership) assert memberships[0].source_type == "Project" -@with_httmock(resp_get_user_status) -def test_user_status(user): +def test_user_status(user, resp_get_user_status): status = user.status.get() assert isinstance(status, UserStatus) assert status.message == "test" assert status.emoji == "thumbsup" -@with_httmock(resp_activate, resp_deactivate) -def test_user_activate_deactivate(user): +def test_user_activate_deactivate(user, resp_activate): user.activate() user.deactivate()