summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gitlab/client.py4
-rw-r--r--gitlab/config.py8
-rw-r--r--tests/functional/cli/test_cli.py6
-rw-r--r--tests/unit/helpers.py3
-rw-r--r--tests/unit/mixins/test_mixin_methods.py55
-rw-r--r--tests/unit/test_base.py26
-rw-r--r--tests/unit/test_config.py66
-rw-r--r--tests/unit/test_exceptions.py12
-rw-r--r--tests/unit/test_gitlab.py61
-rw-r--r--tests/unit/test_gitlab_http_methods.py44
-rw-r--r--tests/unit/test_utils.py52
-rw-r--r--tox.ini1
12 files changed, 304 insertions, 34 deletions
diff --git a/gitlab/client.py b/gitlab/client.py
index 2ac5158..bba5c1d 100644
--- a/gitlab/client.py
+++ b/gitlab/client.py
@@ -208,7 +208,9 @@ class Gitlab:
self.__dict__.update(state)
# We only support v4 API at this time
if self._api_version not in ("4",):
- raise ModuleNotFoundError(name=f"gitlab.v{self._api_version}.objects")
+ raise ModuleNotFoundError(
+ name=f"gitlab.v{self._api_version}.objects"
+ ) # pragma: no cover, dead code currently
# NOTE: We must delay import of gitlab.v4.objects until now or
# otherwise it will cause circular import errors
import gitlab.v4.objects
diff --git a/gitlab/config.py b/gitlab/config.py
index c85d7e5..337a265 100644
--- a/gitlab/config.py
+++ b/gitlab/config.py
@@ -154,7 +154,7 @@ class GitlabConfigParser:
# CA bundle.
try:
self.ssl_verify = _config.get("global", "ssl_verify")
- except Exception:
+ except Exception: # pragma: no cover
pass
except Exception:
pass
@@ -166,7 +166,7 @@ class GitlabConfigParser:
# CA bundle.
try:
self.ssl_verify = _config.get(self.gitlab_id, "ssl_verify")
- except Exception:
+ except Exception: # pragma: no cover
pass
except Exception:
pass
@@ -197,7 +197,9 @@ class GitlabConfigParser:
try:
self.http_username = _config.get(self.gitlab_id, "http_username")
- self.http_password = _config.get(self.gitlab_id, "http_password")
+ self.http_password = _config.get(
+ self.gitlab_id, "http_password"
+ ) # pragma: no cover
except Exception:
pass
diff --git a/tests/functional/cli/test_cli.py b/tests/functional/cli/test_cli.py
index a889066..0da50e6 100644
--- a/tests/functional/cli/test_cli.py
+++ b/tests/functional/cli/test_cli.py
@@ -27,6 +27,12 @@ def test_version(script_runner):
assert ret.stdout.strip() == __version__
+def test_config_error_with_help_prints_help(script_runner):
+ ret = script_runner.run("gitlab", "-c", "invalid-file", "--help")
+ assert ret.stdout.startswith("usage:")
+ assert ret.returncode == 0
+
+
@pytest.mark.script_launch_mode("inprocess")
@responses.activate
def test_defaults_to_gitlab_com(script_runner, resp_get_project, monkeypatch):
diff --git a/tests/unit/helpers.py b/tests/unit/helpers.py
index 33a7c78..54b2b74 100644
--- a/tests/unit/helpers.py
+++ b/tests/unit/helpers.py
@@ -4,6 +4,9 @@ import json
from typing import Optional
import requests
+import responses
+
+MATCH_EMPTY_QUERY_PARAMS = [responses.matchers.query_param_matcher({})]
# NOTE: The function `httmock_response` and the class `Headers` is taken from
diff --git a/tests/unit/mixins/test_mixin_methods.py b/tests/unit/mixins/test_mixin_methods.py
index 241cba3..c0b0a58 100644
--- a/tests/unit/mixins/test_mixin_methods.py
+++ b/tests/unit/mixins/test_mixin_methods.py
@@ -97,8 +97,17 @@ def test_list_mixin(gl):
pass
url = "http://localhost/api/v4/tests"
+ headers = {
+ "X-Page": "1",
+ "X-Next-Page": "2",
+ "X-Per-Page": "1",
+ "X-Total-Pages": "2",
+ "X-Total": "2",
+ "Link": ("<http://localhost/api/v4/tests" ' rel="next"'),
+ }
responses.add(
method=responses.GET,
+ headers=headers,
url=url,
json=[{"id": 42, "foo": "bar"}, {"id": 43, "foo": "baz"}],
status=200,
@@ -109,6 +118,14 @@ def test_list_mixin(gl):
mgr = M(gl)
obj_list = mgr.list(iterator=True)
assert isinstance(obj_list, base.RESTObjectList)
+ assert obj_list.current_page == 1
+ assert obj_list.prev_page is None
+ assert obj_list.next_page == 2
+ assert obj_list.per_page == 1
+ assert obj_list.total == 2
+ assert obj_list.total_pages == 2
+ assert len(obj_list) == 2
+
for obj in obj_list:
assert isinstance(obj, FakeObject)
assert obj.id in (42, 43)
@@ -255,6 +272,25 @@ def test_update_mixin(gl):
@responses.activate
+def test_update_mixin_uses_post(gl):
+ class M(UpdateMixin, FakeManager):
+ _update_uses_post = True
+
+ url = "http://localhost/api/v4/tests/1"
+ responses.add(
+ method=responses.POST,
+ url=url,
+ json={},
+ status=200,
+ match=[responses.matchers.query_param_matcher({})],
+ )
+
+ mgr = M(gl)
+ mgr.update(1, {})
+ assert responses.assert_call_count(url, 1) is True
+
+
+@responses.activate
def test_update_mixin_no_id(gl):
class M(UpdateMixin, FakeManager):
_create_attrs = base.RequiredOptional(
@@ -324,6 +360,25 @@ def test_save_mixin(gl):
@responses.activate
+def test_save_mixin_without_new_data(gl):
+ class M(UpdateMixin, FakeManager):
+ pass
+
+ class TestClass(SaveMixin, base.RESTObject):
+ pass
+
+ url = "http://localhost/api/v4/tests/1"
+ responses.add(method=responses.PUT, url=url)
+
+ mgr = M(gl)
+ obj = TestClass(mgr, {"id": 1, "foo": "bar"})
+ obj.save()
+
+ assert obj._attrs["foo"] == "bar"
+ assert responses.assert_call_count(url, 0) is True
+
+
+@responses.activate
def test_set_mixin(gl):
class M(SetMixin, FakeManager):
pass
diff --git a/tests/unit/test_base.py b/tests/unit/test_base.py
index 0a7f353..9c4b65f 100644
--- a/tests/unit/test_base.py
+++ b/tests/unit/test_base.py
@@ -78,13 +78,15 @@ class TestRESTManager:
class TestRESTObject:
def test_instantiate(self, fake_gitlab, fake_manager):
- obj = FakeObject(fake_manager, {"foo": "bar"})
+ attrs = {"foo": "bar"}
+ obj = FakeObject(fake_manager, attrs.copy())
- assert {"foo": "bar"} == obj._attrs
+ assert attrs == obj._attrs
assert {} == obj._updated_attrs
assert obj._create_managers() is None
assert fake_manager == obj.manager
assert fake_gitlab == obj.manager.gitlab
+ assert str(obj) == f"{type(obj)} => {attrs}"
def test_instantiate_non_dict(self, fake_gitlab, fake_manager):
with pytest.raises(gitlab.exceptions.GitlabParsingError):
@@ -201,6 +203,7 @@ class TestRESTObject:
obj1 = FakeObject(fake_manager, {"id": "foo"})
obj2 = FakeObject(fake_manager, {"id": "foo", "other_attr": "bar"})
assert obj1 == obj2
+ assert len(set((obj1, obj2))) == 1
def test_equality_custom_id(self, fake_manager):
class OtherFakeObject(FakeObject):
@@ -210,6 +213,11 @@ class TestRESTObject:
obj2 = OtherFakeObject(fake_manager, {"foo": "bar", "other_attr": "baz"})
assert obj1 == obj2
+ def test_equality_no_id(self, fake_manager):
+ obj1 = FakeObject(fake_manager, {"attr1": "foo"})
+ obj2 = FakeObject(fake_manager, {"attr1": "bar"})
+ assert not obj1 == obj2
+
def test_inequality(self, fake_manager):
obj1 = FakeObject(fake_manager, {"id": "foo"})
obj2 = FakeObject(fake_manager, {"id": "bar"})
@@ -219,6 +227,12 @@ class TestRESTObject:
obj1 = FakeObject(fake_manager, {"attr1": "foo"})
obj2 = FakeObject(fake_manager, {"attr1": "bar"})
assert obj1 != obj2
+ assert len(set((obj1, obj2))) == 2
+
+ def test_equality_with_other_objects(self, fake_manager):
+ obj1 = FakeObject(fake_manager, {"id": "foo"})
+ obj2 = None
+ assert not obj1 == obj2
def test_dunder_str(self, fake_manager):
fake_object = FakeObject(fake_manager, {"attr1": "foo"})
@@ -280,3 +294,11 @@ class TestRESTObject:
" 'ham': 'eggseggseggseggseggseggseggseggseggseggseggseggseggseggseggs'}\n"
)
assert stderr == ""
+
+ def test_repr(self, fake_manager):
+ attrs = {"attr1": "foo"}
+ obj = FakeObject(fake_manager, attrs)
+ assert repr(obj) == "<FakeObject id:None>"
+
+ FakeObject._id_attr = None
+ assert repr(obj) == "<FakeObject>"
diff --git a/tests/unit/test_config.py b/tests/unit/test_config.py
index 7ba312b..4a96bf8 100644
--- a/tests/unit/test_config.py
+++ b/tests/unit/test_config.py
@@ -54,6 +54,15 @@ url = https://four.url
oauth_token = STUV
"""
+ssl_verify_str_config = """[global]
+default = one
+ssl_verify = /etc/ssl/certs/ca-certificates.crt
+
+[one]
+url = http://one.url
+private_token = ABCDEF
+"""
+
custom_user_agent_config = f"""[global]
default = one
user_agent = {custom_user_agent}
@@ -69,7 +78,7 @@ url = http://there.url
private_token = ABCDEF
"""
-missing_attr_config = """[global]
+invalid_data_config = """[global]
[one]
url = http://one.url
@@ -83,6 +92,11 @@ meh = hem
url = http://four.url
private_token = ABCDEF
per_page = 200
+
+[invalid-api-version]
+url = http://invalid-api-version.url
+private_token = ABCDEF
+api_version = 1
"""
@@ -173,7 +187,7 @@ def test_invalid_id(m_open, mock_clean_env, monkeypatch):
@mock.patch("builtins.open")
def test_invalid_data(m_open, monkeypatch):
- fd = io.StringIO(missing_attr_config)
+ fd = io.StringIO(invalid_data_config)
fd.close = mock.Mock(return_value=None, side_effect=lambda: fd.seek(0))
m_open.return_value = fd
@@ -185,9 +199,14 @@ def test_invalid_data(m_open, monkeypatch):
config.GitlabConfigParser(gitlab_id="two")
with pytest.raises(config.GitlabDataError):
config.GitlabConfigParser(gitlab_id="three")
- with pytest.raises(config.GitlabDataError) as emgr:
+
+ with pytest.raises(config.GitlabDataError) as e:
config.GitlabConfigParser("four")
- assert "Unsupported per_page number: 200" == emgr.value.args[0]
+ assert str(e.value) == "Unsupported per_page number: 200"
+
+ with pytest.raises(config.GitlabDataError) as e:
+ config.GitlabConfigParser("invalid-api-version")
+ assert str(e.value) == "Unsupported API version: 1"
@mock.patch("builtins.open")
@@ -249,6 +268,18 @@ def test_valid_data(m_open, monkeypatch):
@mock.patch("builtins.open")
+def test_ssl_verify_as_str(m_open, monkeypatch):
+ fd = io.StringIO(ssl_verify_str_config)
+ fd.close = mock.Mock(return_value=None)
+ m_open.return_value = fd
+
+ with monkeypatch.context() as m:
+ m.setattr(Path, "resolve", _mock_existent_file)
+ cp = config.GitlabConfigParser()
+ assert cp.ssl_verify == "/etc/ssl/certs/ca-certificates.crt"
+
+
+@mock.patch("builtins.open")
@pytest.mark.skipif(sys.platform.startswith("win"), reason="Not supported on Windows")
def test_data_from_helper(m_open, monkeypatch, tmp_path):
helper = tmp_path / "helper.sh"
@@ -287,6 +318,33 @@ def test_data_from_helper(m_open, monkeypatch, tmp_path):
@mock.patch("builtins.open")
+@pytest.mark.skipif(sys.platform.startswith("win"), reason="Not supported on Windows")
+def test_from_helper_subprocess_error_raises_error(m_open, monkeypatch):
+ # using /usr/bin/false here to force a non-zero return code
+ fd = io.StringIO(
+ dedent(
+ """\
+ [global]
+ default = helper
+
+ [helper]
+ url = https://helper.url
+ oauth_token = helper: /usr/bin/false
+ """
+ )
+ )
+
+ fd.close = mock.Mock(return_value=None)
+ m_open.return_value = fd
+ with monkeypatch.context() as m:
+ m.setattr(Path, "resolve", _mock_existent_file)
+ with pytest.raises(config.GitlabConfigHelperError) as e:
+ config.GitlabConfigParser(gitlab_id="helper")
+
+ assert "Failed to read oauth_token value from helper" in str(e.value)
+
+
+@mock.patch("builtins.open")
@pytest.mark.parametrize(
"config_string,expected_agent",
[
diff --git a/tests/unit/test_exceptions.py b/tests/unit/test_exceptions.py
index 57b394b..6ef0939 100644
--- a/tests/unit/test_exceptions.py
+++ b/tests/unit/test_exceptions.py
@@ -3,6 +3,18 @@ import pytest
from gitlab import exceptions
+@pytest.mark.parametrize(
+ "kwargs,expected",
+ [
+ ({"error_message": "foo"}, "foo"),
+ ({"error_message": "foo", "response_code": "400"}, "400: foo"),
+ ],
+)
+def test_gitlab_error(kwargs, expected):
+ error = exceptions.GitlabError(**kwargs)
+ assert str(error) == expected
+
+
def test_error_raises_from_http_error():
"""Methods decorated with @on_http_error should raise from GitlabHttpError."""
diff --git a/tests/unit/test_gitlab.py b/tests/unit/test_gitlab.py
index 44abfc1..070f215 100644
--- a/tests/unit/test_gitlab.py
+++ b/tests/unit/test_gitlab.py
@@ -24,6 +24,7 @@ import pytest
import responses
import gitlab
+from tests.unit import helpers
localhost = "http://localhost"
token = "abc123"
@@ -58,7 +59,7 @@ def resp_page_1():
"headers": headers,
"content_type": "application/json",
"status": 200,
- "match": [responses.matchers.query_param_matcher({})],
+ "match": helpers.MATCH_EMPTY_QUERY_PARAMS,
}
@@ -84,6 +85,64 @@ def resp_page_2():
}
+def test_gitlab_init_with_valid_api_version():
+ gl = gitlab.Gitlab(api_version="4")
+ assert gl.api_version == "4"
+
+
+def test_gitlab_init_with_invalid_api_version():
+ with pytest.raises(ModuleNotFoundError):
+ gitlab.Gitlab(api_version="1")
+
+
+def test_gitlab_as_context_manager():
+ with gitlab.Gitlab() as gl:
+ assert isinstance(gl, gitlab.Gitlab)
+
+
+@responses.activate
+@pytest.mark.parametrize(
+ "status_code,response_json,expected",
+ [
+ (200, {"version": "0.0.0-pre", "revision": "abcdef"}, ("0.0.0-pre", "abcdef")),
+ (200, None, ("unknown", "unknown")),
+ (401, None, ("unknown", "unknown")),
+ ],
+)
+def test_gitlab_get_version(gl, status_code, response_json, expected):
+ responses.add(
+ method=responses.GET,
+ url="http://localhost/api/v4/version",
+ json=response_json,
+ status=status_code,
+ match=helpers.MATCH_EMPTY_QUERY_PARAMS,
+ )
+
+ version = gl.version()
+ assert version == expected
+
+
+@responses.activate
+@pytest.mark.parametrize(
+ "response_json,expected",
+ [
+ ({"id": "1", "plan": "premium"}, {"id": "1", "plan": "premium"}),
+ (None, {}),
+ ],
+)
+def test_gitlab_get_license(gl, response_json, expected):
+ responses.add(
+ method=responses.GET,
+ url="http://localhost/api/v4/license",
+ json=response_json,
+ status=200,
+ match=helpers.MATCH_EMPTY_QUERY_PARAMS,
+ )
+
+ gitlab_license = gl.get_license()
+ assert gitlab_license == expected
+
+
@responses.activate
def test_gitlab_build_list(gl, resp_page_1, resp_page_2):
responses.add(**resp_page_1)
diff --git a/tests/unit/test_gitlab_http_methods.py b/tests/unit/test_gitlab_http_methods.py
index f3e298f..be5b35f 100644
--- a/tests/unit/test_gitlab_http_methods.py
+++ b/tests/unit/test_gitlab_http_methods.py
@@ -9,8 +9,6 @@ from gitlab import GitlabHttpError, GitlabList, GitlabParsingError, RedirectErro
from gitlab.client import RETRYABLE_TRANSIENT_ERROR_CODES
from tests.unit import helpers
-MATCH_EMPTY_QUERY_PARAMS = [responses.matchers.query_param_matcher({})]
-
def test_build_url(gl):
r = gl._build_url("http://localhost/api/v4")
@@ -29,7 +27,7 @@ def test_http_request(gl):
url=url,
json=[{"name": "project1"}],
status=200,
- match=MATCH_EMPTY_QUERY_PARAMS,
+ match=helpers.MATCH_EMPTY_QUERY_PARAMS,
)
http_r = gl.http_request("get", "/projects")
@@ -46,7 +44,7 @@ def test_http_request_404(gl):
url=url,
json={},
status=400,
- match=MATCH_EMPTY_QUERY_PARAMS,
+ match=helpers.MATCH_EMPTY_QUERY_PARAMS,
)
with pytest.raises(GitlabHttpError):
@@ -63,7 +61,7 @@ def test_http_request_with_only_failures(gl, status_code):
url=url,
json={},
status=status_code,
- match=MATCH_EMPTY_QUERY_PARAMS,
+ match=helpers.MATCH_EMPTY_QUERY_PARAMS,
)
with pytest.raises(GitlabHttpError):
@@ -322,7 +320,7 @@ def test_http_request_302_get_does_not_raise(gl):
method=responses.GET,
url=url,
status=302,
- match=MATCH_EMPTY_QUERY_PARAMS,
+ match=helpers.MATCH_EMPTY_QUERY_PARAMS,
)
gl.http_request(verb=method, path=api_path)
@@ -346,7 +344,7 @@ def test_http_request_302_put_raises_redirect_error(gl):
method=responses.PUT,
url=url,
status=302,
- match=MATCH_EMPTY_QUERY_PARAMS,
+ match=helpers.MATCH_EMPTY_QUERY_PARAMS,
)
with pytest.raises(RedirectError) as exc:
gl.http_request(verb=method, path=api_path)
@@ -364,7 +362,7 @@ def test_get_request(gl):
url=url,
json={"name": "project1"},
status=200,
- match=MATCH_EMPTY_QUERY_PARAMS,
+ match=helpers.MATCH_EMPTY_QUERY_PARAMS,
)
result = gl.http_get("/projects")
@@ -382,7 +380,7 @@ def test_get_request_raw(gl):
content_type="application/octet-stream",
body="content",
status=200,
- match=MATCH_EMPTY_QUERY_PARAMS,
+ match=helpers.MATCH_EMPTY_QUERY_PARAMS,
)
result = gl.http_get("/projects")
@@ -398,7 +396,7 @@ def test_get_request_404(gl):
url=url,
json=[],
status=404,
- match=MATCH_EMPTY_QUERY_PARAMS,
+ match=helpers.MATCH_EMPTY_QUERY_PARAMS,
)
with pytest.raises(GitlabHttpError):
@@ -415,7 +413,7 @@ def test_get_request_invalid_data(gl):
body='["name": "project1"]',
content_type="application/json",
status=200,
- match=MATCH_EMPTY_QUERY_PARAMS,
+ match=helpers.MATCH_EMPTY_QUERY_PARAMS,
)
with pytest.raises(GitlabParsingError):
@@ -434,7 +432,7 @@ def test_list_request(gl):
json=[{"name": "project1"}],
headers={"X-Total": "1"},
status=200,
- match=MATCH_EMPTY_QUERY_PARAMS,
+ match=helpers.MATCH_EMPTY_QUERY_PARAMS,
)
with warnings.catch_warnings(record=True) as caught_warnings:
@@ -480,7 +478,7 @@ large_list_response = {
],
"headers": {"X-Total": "30", "x-per-page": "20"},
"status": 200,
- "match": MATCH_EMPTY_QUERY_PARAMS,
+ "match": helpers.MATCH_EMPTY_QUERY_PARAMS,
}
@@ -572,7 +570,7 @@ def test_list_request_404(gl):
url=url,
json=[],
status=404,
- match=MATCH_EMPTY_QUERY_PARAMS,
+ match=helpers.MATCH_EMPTY_QUERY_PARAMS,
)
with pytest.raises(GitlabHttpError):
@@ -589,7 +587,7 @@ def test_list_request_invalid_data(gl):
body='["name": "project1"]',
content_type="application/json",
status=200,
- match=MATCH_EMPTY_QUERY_PARAMS,
+ match=helpers.MATCH_EMPTY_QUERY_PARAMS,
)
with pytest.raises(GitlabParsingError):
@@ -605,7 +603,7 @@ def test_post_request(gl):
url=url,
json={"name": "project1"},
status=200,
- match=MATCH_EMPTY_QUERY_PARAMS,
+ match=helpers.MATCH_EMPTY_QUERY_PARAMS,
)
result = gl.http_post("/projects")
@@ -622,7 +620,7 @@ def test_post_request_404(gl):
url=url,
json=[],
status=404,
- match=MATCH_EMPTY_QUERY_PARAMS,
+ match=helpers.MATCH_EMPTY_QUERY_PARAMS,
)
with pytest.raises(GitlabHttpError):
@@ -639,7 +637,7 @@ def test_post_request_invalid_data(gl):
content_type="application/json",
body='["name": "project1"]',
status=200,
- match=MATCH_EMPTY_QUERY_PARAMS,
+ match=helpers.MATCH_EMPTY_QUERY_PARAMS,
)
with pytest.raises(GitlabParsingError):
@@ -655,7 +653,7 @@ def test_put_request(gl):
url=url,
json={"name": "project1"},
status=200,
- match=MATCH_EMPTY_QUERY_PARAMS,
+ match=helpers.MATCH_EMPTY_QUERY_PARAMS,
)
result = gl.http_put("/projects")
@@ -672,7 +670,7 @@ def test_put_request_404(gl):
url=url,
json=[],
status=404,
- match=MATCH_EMPTY_QUERY_PARAMS,
+ match=helpers.MATCH_EMPTY_QUERY_PARAMS,
)
with pytest.raises(GitlabHttpError):
@@ -689,7 +687,7 @@ def test_put_request_invalid_data(gl):
body='["name": "project1"]',
content_type="application/json",
status=200,
- match=MATCH_EMPTY_QUERY_PARAMS,
+ match=helpers.MATCH_EMPTY_QUERY_PARAMS,
)
with pytest.raises(GitlabParsingError):
@@ -705,7 +703,7 @@ def test_delete_request(gl):
url=url,
json=True,
status=200,
- match=MATCH_EMPTY_QUERY_PARAMS,
+ match=helpers.MATCH_EMPTY_QUERY_PARAMS,
)
result = gl.http_delete("/projects")
@@ -722,7 +720,7 @@ def test_delete_request_404(gl):
url=url,
json=[],
status=404,
- match=MATCH_EMPTY_QUERY_PARAMS,
+ match=helpers.MATCH_EMPTY_QUERY_PARAMS,
)
with pytest.raises(GitlabHttpError):
diff --git a/tests/unit/test_utils.py b/tests/unit/test_utils.py
index 3a92604..bd425f8 100644
--- a/tests/unit/test_utils.py
+++ b/tests/unit/test_utils.py
@@ -18,9 +18,30 @@
import json
import warnings
+import pytest
+import requests
+import responses
+
from gitlab import types, utils
+@responses.activate
+def test_response_content(capsys):
+ responses.add(
+ method="GET",
+ url="https://example.com",
+ status=200,
+ body="test",
+ content_type="application/octet-stream",
+ )
+
+ resp = requests.get("https://example.com", stream=True)
+ utils.response_content(resp, streamed=True, action=None, chunk_size=1024)
+
+ captured = capsys.readouterr()
+ assert "test" in captured.out
+
+
class TestEncodedId:
def test_init_str(self):
obj = utils.EncodedId("Hello")
@@ -38,6 +59,10 @@ class TestEncodedId:
assert "23" == str(obj)
assert "23" == f"{obj}"
+ def test_init_invalid_type_raises(self):
+ with pytest.raises(TypeError):
+ utils.EncodedId(None)
+
def test_init_encodeid_str(self):
value = "Goodbye"
obj_init = utils.EncodedId(value)
@@ -97,6 +122,33 @@ class TestWarningsWrapper:
assert warn_source == warning.source
+@pytest.mark.parametrize(
+ "source,expected",
+ [
+ ({"a": "", "b": "spam", "c": None}, {"a": "", "b": "spam", "c": None}),
+ ({"a": "", "b": {"c": "spam"}}, {"a": "", "b[c]": "spam"}),
+ ],
+)
+def test_copy_dict(source, expected):
+ dest = {}
+
+ utils.copy_dict(src=source, dest=dest)
+ assert dest == expected
+
+
+@pytest.mark.parametrize(
+ "dictionary,expected",
+ [
+ ({"a": None, "b": "spam"}, {"b": "spam"}),
+ ({"a": "", "b": "spam"}, {"a": "", "b": "spam"}),
+ ({"a": None, "b": None}, {}),
+ ],
+)
+def test_remove_none_from_dict(dictionary, expected):
+ result = utils.remove_none_from_dict(dictionary)
+ assert result == expected
+
+
def test_transform_types_copies_data_with_empty_files():
data = {"attr": "spam"}
new_data, files = utils._transform_types(data, {})
diff --git a/tox.ini b/tox.ini
index 8e67068..144c521 100644
--- a/tox.ini
+++ b/tox.ini
@@ -99,6 +99,7 @@ exclude_lines =
pragma: no cover
if TYPE_CHECKING:
if debug:
+ return NotImplemented
[testenv:cli_func_v4]
deps = -r{toxinidir}/requirements-docker.txt