summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNejc Habjan <hab.nejc@gmail.com>2020-04-17 02:26:28 +0200
committerNejc Habjan <nejc.habjan@siemens.com>2020-08-22 20:09:52 +0200
commit76b2cadf1418e4ea2ac420ebba5a4b4f16fbd4c7 (patch)
tree4dbe6ae57c4f03e1351f7a49e590cbbc6d31121a
parent11383e70f74c70e6fe8a56f18b5b170db982f402 (diff)
downloadgitlab-76b2cadf1418e4ea2ac420ebba5a4b4f16fbd4c7.tar.gz
refactor: split unit tests by GitLab API resources
-rw-r--r--gitlab/__init__.py10
-rw-r--r--gitlab/tests/conftest.py41
-rw-r--r--gitlab/tests/mixins/test_meta_mixins.py58
-rw-r--r--gitlab/tests/mixins/test_mixin_methods.py331
-rw-r--r--gitlab/tests/mixins/test_object_mixins_attributes.py79
-rw-r--r--gitlab/tests/objects/test_application.py226
-rw-r--r--gitlab/tests/objects/test_commits.py74
-rw-r--r--gitlab/tests/objects/test_deploy_tokens.py44
-rw-r--r--gitlab/tests/objects/test_deployments.py53
-rw-r--r--gitlab/tests/objects/test_environments.py31
-rw-r--r--gitlab/tests/objects/test_groups.py121
-rw-r--r--gitlab/tests/objects/test_hooks.py23
-rw-r--r--gitlab/tests/objects/test_issues.py42
-rw-r--r--gitlab/tests/objects/test_pipeline_schedules.py71
-rw-r--r--gitlab/tests/objects/test_project_import_export.py129
-rw-r--r--gitlab/tests/objects/test_project_statistics.py29
-rw-r--r--gitlab/tests/objects/test_projects.py718
-rw-r--r--gitlab/tests/objects/test_remote_mirrors.py103
-rw-r--r--gitlab/tests/objects/test_services.py134
-rw-r--r--gitlab/tests/objects/test_snippets.py121
-rw-r--r--gitlab/tests/objects/test_submodules.py58
-rw-r--r--gitlab/tests/objects/test_todos.py58
-rw-r--r--gitlab/tests/objects/test_users.py94
-rw-r--r--gitlab/tests/test_base.py77
-rw-r--r--gitlab/tests/test_cli.py220
-rw-r--r--gitlab/tests/test_config.py206
-rw-r--r--gitlab/tests/test_exceptions.py24
-rw-r--r--gitlab/tests/test_gitlab.py1022
-rw-r--r--gitlab/tests/test_gitlab_auth.py85
-rw-r--r--gitlab/tests/test_gitlab_http_methods.py234
-rw-r--r--gitlab/tests/test_mixins.py446
-rw-r--r--gitlab/tests/test_types.py74
-rw-r--r--gitlab/tests/test_utils.py48
-rw-r--r--gitlab/utils.py8
34 files changed, 2645 insertions, 2447 deletions
diff --git a/gitlab/__init__.py b/gitlab/__init__.py
index f5db455..1959adc 100644
--- a/gitlab/__init__.py
+++ b/gitlab/__init__.py
@@ -45,14 +45,6 @@ REDIRECT_MSG = (
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 @@ class Gitlab(object):
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 91752c6..2d4cb3a 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 0000000..025e9f4
--- /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 0000000..171e90c
--- /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 0000000..3502a93
--- /dev/null
+++ b/gitlab/tests/mixins/test_object_mixins_attributes.py
@@ -0,0 +1,79 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (C) 2014 Mika Mäenpää <mika.j.maenpaa@tut.fi>,
+# 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 <http://www.gnu.org/licenses/>.
+
+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 a10691b..356f0d3 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 bf7d5a8..eaa7b82 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 0000000..b98a670
--- /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 0000000..098251a
--- /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 0000000..3175c64
--- /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 12ebdb2..b5464b5 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 0000000..45403c4
--- /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 0000000..e094841
--- /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 0000000..6b56304
--- /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 0000000..e5c37a8
--- /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 0000000..c2b194f
--- /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 fa105ae..7fefe3f 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 0000000..e62a71e
--- /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 0000000..a0cded7
--- /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 0000000..86eb54c
--- /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 0000000..2e76302
--- /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 0000000..5b30dc9
--- /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 0000000..88175d0
--- /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": "<h1>Message</h1>", "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 666060c..58c0d47 100644
--- a/gitlab/tests/test_base.py
+++ b/gitlab/tests/test_base.py
@@ -16,7 +16,6 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
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 TestRESTManager(unittest.TestCase):
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 @@ class TestRESTObject(unittest.TestCase):
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 @@ class TestRESTObject(unittest.TestCase):
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 63a5793..2246369 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 abdeed0..7fb03e0 100644
--- a/gitlab/tests/test_config.py
+++ b/gitlab/tests/test_config.py
@@ -74,105 +74,107 @@ per_page = 200
"""
-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 57622c0..57b394b 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 59139e4..553afb3 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 <http://www.gnu.org/licenses/>.
-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": ("<http://localhost/api/v4/tests?per_page=1&page=2>;" ' 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": (
- "<http://localhost/api/v4/tests?per_page=1&page=2>;" ' 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": "<h1>Message</h1>", "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 0000000..314fbed
--- /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 0000000..fac89b9
--- /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 e8613f2..0000000
--- a/gitlab/tests/test_mixins.py
+++ /dev/null
@@ -1,446 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# Copyright (C) 2014 Mika Mäenpää <mika.j.maenpaa@tut.fi>,
-# 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 <http://www.gnu.org/licenses/>.
-
-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 8471bdf..f84eddb 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 <http://www.gnu.org/licenses/>.
-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 7ebd006..50aaecf 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 <http://www.gnu.org/licenses/>.
-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 4241787..67cb7f4 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")