| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent c51b538 commit df072e1
12 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -208,7 +208,9 @@ def __setstate__(self, state: Dict[str, Any]) -> None: | |||
| 208 | 208 | self.__dict__.update(state) | |
| 209 | 209 | # We only support v4 API at this time | |
| 210 | 210 | if self._api_version not in ("4",): | |
| 211 | - raise ModuleNotFoundError(name=f"gitlab.v{self._api_version}.objects") | ||
| 211 | + raise ModuleNotFoundError( | ||
| 212 | + name=f"gitlab.v{self._api_version}.objects" | ||
| 213 | + ) # pragma: no cover, dead code currently | ||
| 212 | 214 | # NOTE: We must delay import of gitlab.v4.objects until now or | |
| 213 | 215 | # otherwise it will cause circular import errors | |
| 214 | 216 | import gitlab.v4.objects | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -154,7 +154,7 @@ def _parse_config(self) -> None: | |||
| 154 | 154 | # CA bundle. | |
| 155 | 155 | try: | |
| 156 | 156 | self.ssl_verify = _config.get("global", "ssl_verify") | |
| 157 | - except Exception: | ||
| 157 | + except Exception: # pragma: no cover | ||
| 158 | 158 | pass | |
| 159 | 159 | except Exception: | |
| 160 | 160 | pass | |
@@ -166,7 +166,7 @@ def _parse_config(self) -> None: | |||
| 166 | 166 | # CA bundle. | |
| 167 | 167 | try: | |
| 168 | 168 | self.ssl_verify = _config.get(self.gitlab_id, "ssl_verify") | |
| 169 | - except Exception: | ||
| 169 | + except Exception: # pragma: no cover | ||
| 170 | 170 | pass | |
| 171 | 171 | except Exception: | |
| 172 | 172 | pass | |
@@ -197,7 +197,9 @@ def _parse_config(self) -> None: | |||
| 197 | 197 | ||
| 198 | 198 | try: | |
| 199 | 199 | self.http_username = _config.get(self.gitlab_id, "http_username") | |
| 200 | - self.http_password = _config.get(self.gitlab_id, "http_password") | ||
| 200 | + self.http_password = _config.get( | ||
| 201 | + self.gitlab_id, "http_password" | ||
| 202 | + ) # pragma: no cover | ||
| 201 | 203 | except Exception: | |
| 202 | 204 | pass | |
| 203 | 205 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -27,6 +27,12 @@ def test_version(script_runner): | |||
| 27 | 27 | assert ret.stdout.strip() == __version__ | |
| 28 | 28 | ||
| 29 | 29 | ||
| 30 | + def test_config_error_with_help_prints_help(script_runner): | ||
| 31 | + ret = script_runner.run("gitlab", "-c", "invalid-file", "--help") | ||
| 32 | + assert ret.stdout.startswith("usage:") | ||
| 33 | + assert ret.returncode == 0 | ||
| 34 | + | ||
| 35 | + | ||
| 30 | 36 | @pytest.mark.script_launch_mode("inprocess") | |
| 31 | 37 | @responses.activate | |
| 32 | 38 | def test_defaults_to_gitlab_com(script_runner, resp_get_project, monkeypatch): | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -4,6 +4,9 @@ | |||
| 4 | 4 | from typing import Optional | |
| 5 | 5 | ||
| 6 | 6 | import requests | |
| 7 | + import responses | ||
| 8 | + | ||
| 9 | + MATCH_EMPTY_QUERY_PARAMS = [responses.matchers.query_param_matcher({})] | ||
| 7 | 10 | ||
| 8 | 11 | ||
| 9 | 12 | # NOTE: The function `httmock_response` and the class `Headers` is taken from | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -97,8 +97,17 @@ class M(ListMixin, FakeManager): | |||
| 97 | 97 | pass | |
| 98 | 98 | ||
| 99 | 99 | url = "http://localhost/api/v4/tests" | |
| 100 | + headers = { | ||
| 101 | + "X-Page": "1", | ||
| 102 | + "X-Next-Page": "2", | ||
| 103 | + "X-Per-Page": "1", | ||
| 104 | + "X-Total-Pages": "2", | ||
| 105 | + "X-Total": "2", | ||
| 106 | + "Link": ("<http://localhost/api/v4/tests" ' rel="next"'), | ||
| 107 | + } | ||
| 100 | 108 | responses.add( | |
| 101 | 109 | method=responses.GET, | |
| 110 | + headers=headers, | ||
| 102 | 111 | url=url, | |
| 103 | 112 | json=[{"id": 42, "foo": "bar"}, {"id": 43, "foo": "baz"}], | |
| 104 | 113 | status=200, | |
@@ -109,6 +118,14 @@ class M(ListMixin, FakeManager): | |||
| 109 | 118 | mgr = M(gl) | |
| 110 | 119 | obj_list = mgr.list(iterator=True) | |
| 111 | 120 | assert isinstance(obj_list, base.RESTObjectList) | |
| 121 | + assert obj_list.current_page == 1 | ||
| 122 | + assert obj_list.prev_page is None | ||
| 123 | + assert obj_list.next_page == 2 | ||
| 124 | + assert obj_list.per_page == 1 | ||
| 125 | + assert obj_list.total == 2 | ||
| 126 | + assert obj_list.total_pages == 2 | ||
| 127 | + assert len(obj_list) == 2 | ||
| 128 | + | ||
| 112 | 129 | for obj in obj_list: | |
| 113 | 130 | assert isinstance(obj, FakeObject) | |
| 114 | 131 | assert obj.id in (42, 43) | |
@@ -254,6 +271,25 @@ class M(UpdateMixin, FakeManager): | |||
| 254 | 271 | assert responses.assert_call_count(url, 1) is True | |
| 255 | 272 | ||
| 256 | 273 | ||
| 274 | + @responses.activate | ||
| 275 | + def test_update_mixin_uses_post(gl): | ||
| 276 | + class M(UpdateMixin, FakeManager): | ||
| 277 | + _update_uses_post = True | ||
| 278 | + | ||
| 279 | + url = "http://localhost/api/v4/tests/1" | ||
| 280 | + responses.add( | ||
| 281 | + method=responses.POST, | ||
| 282 | + url=url, | ||
| 283 | + json={}, | ||
| 284 | + status=200, | ||
| 285 | + match=[responses.matchers.query_param_matcher({})], | ||
| 286 | + ) | ||
| 287 | + | ||
| 288 | + mgr = M(gl) | ||
| 289 | + mgr.update(1, {}) | ||
| 290 | + assert responses.assert_call_count(url, 1) is True | ||
| 291 | + | ||
| 292 | + | ||
| 257 | 293 | @responses.activate | |
| 258 | 294 | def test_update_mixin_no_id(gl): | |
| 259 | 295 | class M(UpdateMixin, FakeManager): | |
@@ -323,6 +359,25 @@ class TestClass(SaveMixin, base.RESTObject): | |||
| 323 | 359 | assert responses.assert_call_count(url, 1) is True | |
| 324 | 360 | ||
| 325 | 361 | ||
| 362 | + @responses.activate | ||
| 363 | + def test_save_mixin_without_new_data(gl): | ||
| 364 | + class M(UpdateMixin, FakeManager): | ||
| 365 | + pass | ||
| 366 | + | ||
| 367 | + class TestClass(SaveMixin, base.RESTObject): | ||
| 368 | + pass | ||
| 369 | + | ||
| 370 | + url = "http://localhost/api/v4/tests/1" | ||
| 371 | + responses.add(method=responses.PUT, url=url) | ||
| 372 | + | ||
| 373 | + mgr = M(gl) | ||
| 374 | + obj = TestClass(mgr, {"id": 1, "foo": "bar"}) | ||
| 375 | + obj.save() | ||
| 376 | + | ||
| 377 | + assert obj._attrs["foo"] == "bar" | ||
| 378 | + assert responses.assert_call_count(url, 0) is True | ||
| 379 | + | ||
| 380 | + | ||
| 326 | 381 | @responses.activate | |
| 327 | 382 | def test_set_mixin(gl): | |
| 328 | 383 | class M(SetMixin, FakeManager): | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -78,13 +78,15 @@ class MGR(base.RESTManager): | |||
| 78 | 78 | ||
| 79 | 79 | class TestRESTObject: | |
| 80 | 80 | def test_instantiate(self, fake_gitlab, fake_manager): | |
| 81 | - obj = FakeObject(fake_manager, {"foo": "bar"}) | ||
| 81 | + attrs = {"foo": "bar"} | ||
| 82 | + obj = FakeObject(fake_manager, attrs.copy()) | ||
| 82 | 83 | ||
| 83 | - assert {"foo": "bar"} == obj._attrs | ||
| 84 | + assert attrs == obj._attrs | ||
| 84 | 85 | assert {} == obj._updated_attrs | |
| 85 | 86 | assert obj._create_managers() is None | |
| 86 | 87 | assert fake_manager == obj.manager | |
| 87 | 88 | assert fake_gitlab == obj.manager.gitlab | |
| 89 | + assert str(obj) == f"{type(obj)} => {attrs}" | ||
| 88 | 90 | ||
| 89 | 91 | def test_instantiate_non_dict(self, fake_gitlab, fake_manager): | |
| 90 | 92 | with pytest.raises(gitlab.exceptions.GitlabParsingError): | |
@@ -201,6 +203,7 @@ def test_equality(self, fake_manager): | |||
| 201 | 203 | obj1 = FakeObject(fake_manager, {"id": "foo"}) | |
| 202 | 204 | obj2 = FakeObject(fake_manager, {"id": "foo", "other_attr": "bar"}) | |
| 203 | 205 | assert obj1 == obj2 | |
| 206 | + assert len(set((obj1, obj2))) == 1 | ||
| 204 | 207 | ||
| 205 | 208 | def test_equality_custom_id(self, fake_manager): | |
| 206 | 209 | class OtherFakeObject(FakeObject): | |
@@ -210,6 +213,11 @@ class OtherFakeObject(FakeObject): | |||
| 210 | 213 | obj2 = OtherFakeObject(fake_manager, {"foo": "bar", "other_attr": "baz"}) | |
| 211 | 214 | assert obj1 == obj2 | |
| 212 | 215 | ||
| 216 | + def test_equality_no_id(self, fake_manager): | ||
| 217 | + obj1 = FakeObject(fake_manager, {"attr1": "foo"}) | ||
| 218 | + obj2 = FakeObject(fake_manager, {"attr1": "bar"}) | ||
| 219 | + assert not obj1 == obj2 | ||
| 220 | + | ||
| 213 | 221 | def test_inequality(self, fake_manager): | |
| 214 | 222 | obj1 = FakeObject(fake_manager, {"id": "foo"}) | |
| 215 | 223 | obj2 = FakeObject(fake_manager, {"id": "bar"}) | |
@@ -219,6 +227,12 @@ def test_inequality_no_id(self, fake_manager): | |||
| 219 | 227 | obj1 = FakeObject(fake_manager, {"attr1": "foo"}) | |
| 220 | 228 | obj2 = FakeObject(fake_manager, {"attr1": "bar"}) | |
| 221 | 229 | assert obj1 != obj2 | |
| 230 | + assert len(set((obj1, obj2))) == 2 | ||
| 231 | + | ||
| 232 | + def test_equality_with_other_objects(self, fake_manager): | ||
| 233 | + obj1 = FakeObject(fake_manager, {"id": "foo"}) | ||
| 234 | + obj2 = None | ||
| 235 | + assert not obj1 == obj2 | ||
| 222 | 236 | ||
| 223 | 237 | def test_dunder_str(self, fake_manager): | |
| 224 | 238 | fake_object = FakeObject(fake_manager, {"attr1": "foo"}) | |
@@ -280,3 +294,11 @@ def test_pprint(self, capfd, fake_manager): | |||
| 280 | 294 | " 'ham': 'eggseggseggseggseggseggseggseggseggseggseggseggseggseggseggs'}\n" | |
| 281 | 295 | ) | |
| 282 | 296 | assert stderr == "" | |
| 297 | + | ||
| 298 | + def test_repr(self, fake_manager): | ||
| 299 | + attrs = {"attr1": "foo"} | ||
| 300 | + obj = FakeObject(fake_manager, attrs) | ||
| 301 | + assert repr(obj) == "<FakeObject id:None>" | ||
| 302 | + | ||
| 303 | + FakeObject._id_attr = None | ||
| 304 | + assert repr(obj) == "<FakeObject>" | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -54,6 +54,15 @@ | |||
| 54 | 54 | oauth_token = STUV | |
| 55 | 55 | """ | |
| 56 | 56 | ||
| 57 | + ssl_verify_str_config = """[global] | ||
| 58 | + default = one | ||
| 59 | + ssl_verify = /etc/ssl/certs/ca-certificates.crt | ||
| 60 | + | ||
| 61 | + [one] | ||
| 62 | + url = http://one.url | ||
| 63 | + private_token = ABCDEF | ||
| 64 | + """ | ||
| 65 | + | ||
| 57 | 66 | custom_user_agent_config = f"""[global] | |
| 58 | 67 | default = one | |
| 59 | 68 | user_agent = {custom_user_agent} | |
@@ -69,7 +78,7 @@ | |||
| 69 | 78 | private_token = ABCDEF | |
| 70 | 79 | """ | |
| 71 | 80 | ||
| 72 | - missing_attr_config = """[global] | ||
| 81 | + invalid_data_config = """[global] | ||
| 73 | 82 | [one] | |
| 74 | 83 | url = http://one.url | |
| 75 | 84 | ||
@@ -83,6 +92,11 @@ | |||
| 83 | 92 | url = http://four.url | |
| 84 | 93 | private_token = ABCDEF | |
| 85 | 94 | per_page = 200 | |
| 95 | + | ||
| 96 | + [invalid-api-version] | ||
| 97 | + url = http://invalid-api-version.url | ||
| 98 | + private_token = ABCDEF | ||
| 99 | + api_version = 1 | ||
| 86 | 100 | """ | |
| 87 | 101 | ||
| 88 | 102 | ||
@@ -173,7 +187,7 @@ def test_invalid_id(m_open, mock_clean_env, monkeypatch): | |||
| 173 | 187 | ||
| 174 | 188 | @mock.patch("builtins.open") | |
| 175 | 189 | def test_invalid_data(m_open, monkeypatch): | |
| 176 | - fd = io.StringIO(missing_attr_config) | ||
| 190 | + fd = io.StringIO(invalid_data_config) | ||
| 177 | 191 | fd.close = mock.Mock(return_value=None, side_effect=lambda: fd.seek(0)) | |
| 178 | 192 | m_open.return_value = fd | |
| 179 | 193 | ||
@@ -185,9 +199,14 @@ def test_invalid_data(m_open, monkeypatch): | |||
| 185 | 199 | config.GitlabConfigParser(gitlab_id="two") | |
| 186 | 200 | with pytest.raises(config.GitlabDataError): | |
| 187 | 201 | config.GitlabConfigParser(gitlab_id="three") | |
| 188 | - with pytest.raises(config.GitlabDataError) as emgr: | ||
| 202 | + | ||
| 203 | + with pytest.raises(config.GitlabDataError) as e: | ||
| 189 | 204 | config.GitlabConfigParser("four") | |
| 190 | - assert "Unsupported per_page number: 200" == emgr.value.args[0] | ||
| 205 | + assert str(e.value) == "Unsupported per_page number: 200" | ||
| 206 | + | ||
| 207 | + with pytest.raises(config.GitlabDataError) as e: | ||
| 208 | + config.GitlabConfigParser("invalid-api-version") | ||
| 209 | + assert str(e.value) == "Unsupported API version: 1" | ||
| 191 | 210 | ||
| 192 | 211 | ||
| 193 | 212 | @mock.patch("builtins.open") | |
@@ -248,6 +267,18 @@ def test_valid_data(m_open, monkeypatch): | |||
| 248 | 267 | assert cp.ssl_verify is True | |
| 249 | 268 | ||
| 250 | 269 | ||
| 270 | + @mock.patch("builtins.open") | ||
| 271 | + def test_ssl_verify_as_str(m_open, monkeypatch): | ||
| 272 | + fd = io.StringIO(ssl_verify_str_config) | ||
| 273 | + fd.close = mock.Mock(return_value=None) | ||
| 274 | + m_open.return_value = fd | ||
| 275 | + | ||
| 276 | + with monkeypatch.context() as m: | ||
| 277 | + m.setattr(Path, "resolve", _mock_existent_file) | ||
| 278 | + cp = config.GitlabConfigParser() | ||
| 279 | + assert cp.ssl_verify == "/etc/ssl/certs/ca-certificates.crt" | ||
| 280 | + | ||
| 281 | + | ||
| 251 | 282 | @mock.patch("builtins.open") | |
| 252 | 283 | @pytest.mark.skipif(sys.platform.startswith("win"), reason="Not supported on Windows") | |
| 253 | 284 | def test_data_from_helper(m_open, monkeypatch, tmp_path): | |
@@ -286,6 +317,33 @@ def test_data_from_helper(m_open, monkeypatch, tmp_path): | |||
| 286 | 317 | assert "secret" == cp.oauth_token | |
| 287 | 318 | ||
| 288 | 319 | ||
| 320 | + @mock.patch("builtins.open") | ||
| 321 | + @pytest.mark.skipif(sys.platform.startswith("win"), reason="Not supported on Windows") | ||
| 322 | + def test_from_helper_subprocess_error_raises_error(m_open, monkeypatch): | ||
| 323 | + # using /usr/bin/false here to force a non-zero return code | ||
| 324 | + fd = io.StringIO( | ||
| 325 | + dedent( | ||
| 326 | + """\ | ||
| 327 | + [global] | ||
| 328 | + default = helper | ||
| 329 | + | ||
| 330 | + [helper] | ||
| 331 | + url = https://helper.url | ||
| 332 | + oauth_token = helper: /usr/bin/false | ||
| 333 | + """ | ||
| 334 | + ) | ||
| 335 | + ) | ||
| 336 | + | ||
| 337 | + fd.close = mock.Mock(return_value=None) | ||
| 338 | + m_open.return_value = fd | ||
| 339 | + with monkeypatch.context() as m: | ||
| 340 | + m.setattr(Path, "resolve", _mock_existent_file) | ||
| 341 | + with pytest.raises(config.GitlabConfigHelperError) as e: | ||
| 342 | + config.GitlabConfigParser(gitlab_id="helper") | ||
| 343 | + | ||
| 344 | + assert "Failed to read oauth_token value from helper" in str(e.value) | ||
| 345 | + | ||
| 346 | + | ||
| 289 | 347 | @mock.patch("builtins.open") | |
| 290 | 348 | @pytest.mark.parametrize( | |
| 291 | 349 | "config_string,expected_agent", | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -3,6 +3,18 @@ | |||
| 3 | 3 | from gitlab import exceptions | |
| 4 | 4 | ||
| 5 | 5 | ||
| 6 | + @pytest.mark.parametrize( | ||
| 7 | + "kwargs,expected", | ||
| 8 | + [ | ||
| 9 | + ({"error_message": "foo"}, "foo"), | ||
| 10 | + ({"error_message": "foo", "response_code": "400"}, "400: foo"), | ||
| 11 | + ], | ||
| 12 | + ) | ||
| 13 | + def test_gitlab_error(kwargs, expected): | ||
| 14 | + error = exceptions.GitlabError(**kwargs) | ||
| 15 | + assert str(error) == expected | ||
| 16 | + | ||
| 17 | + | ||
| 6 | 18 | def test_error_raises_from_http_error(): | |
| 7 | 19 | """Methods decorated with @on_http_error should raise from GitlabHttpError.""" | |
| 8 | 20 | ||
| Back | FazBrowse Home | New Git URL |
0 commit comments