diff --git a/HISTORY.md b/HISTORY.md index 238d819..28f2e86 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -6,6 +6,20 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). + +## [1.7.0](https://github.com/cortexapps/cli/releases/tag/1.7.0) - 2025-11-19 + +[Compare with 1.6.0](https://github.com/cortexapps/cli/compare/1.6.0...1.7.0) + +## [1.6.0](https://github.com/cortexapps/cli/releases/tag/1.6.0) - 2025-11-14 + +[Compare with 1.5.0](https://github.com/cortexapps/cli/compare/1.5.0...1.6.0) + +### Bug Fixes + +- remove rate limiter initialization log message (#169) #patch ([015107a](https://github.com/cortexapps/cli/commit/015107aca15d5a4cf4eb746834bcbb7dac607e1d) by Jeff Schnitter). + + ## [1.5.0](https://github.com/cortexapps/cli/releases/tag/1.5.0) - 2025-11-13 [Compare with 1.4.0](https://github.com/cortexapps/cli/compare/1.4.0...1.5.0) diff --git a/cortexapps_cli/commands/backup.py b/cortexapps_cli/commands/backup.py index 0b39592..2c4fe9d 100644 --- a/cortexapps_cli/commands/backup.py +++ b/cortexapps_cli/commands/backup.py @@ -698,3 +698,7 @@ def import_tenant( print(f"cortex scorecards create -f \"{file_path}\"") elif import_type == "workflows": print(f"cortex workflows create -f \"{file_path}\"") + + # Exit with non-zero code if any imports failed + if total_failed > 0: + raise typer.Exit(1) diff --git a/cortexapps_cli/cortex_client.py b/cortexapps_cli/cortex_client.py index 9692efd..abc014f 100644 --- a/cortexapps_cli/cortex_client.py +++ b/cortexapps_cli/cortex_client.py @@ -156,8 +156,12 @@ def request(self, method, endpoint, params={}, headers={}, data=None, raw_body=F print(error_str) raise typer.Exit(code=1) except json.JSONDecodeError: - # if we can't parse the error message, just raise the HTTP error - response.raise_for_status() + # if we can't parse the error message, print a clean error and exit + status = response.status_code + reason = response.reason or 'Unknown error' + error_str = f'[red][bold]HTTP Error {status}[/bold][/red]: {reason}' + print(error_str) + raise typer.Exit(code=1) if raw_response: return response diff --git a/tests/test_backup.py b/tests/test_backup.py new file mode 100644 index 0000000..be9c138 --- /dev/null +++ b/tests/test_backup.py @@ -0,0 +1,40 @@ +from tests.helpers.utils import * +import os +import tempfile + +def test_backup_import_invalid_api_key(monkeypatch): + """ + Test that backup import exits with non-zero return code when API calls fail. + """ + monkeypatch.setenv("CORTEX_API_KEY", "invalidKey") + + # Create a temp directory with a catalog subdirectory and a simple yaml file + with tempfile.TemporaryDirectory() as tmpdir: + catalog_dir = os.path.join(tmpdir, "catalog") + os.makedirs(catalog_dir) + + # Create a minimal catalog entity file + entity_file = os.path.join(catalog_dir, "test-entity.yaml") + with open(entity_file, "w") as f: + f.write(""" +info: + x-cortex-tag: test-entity + title: Test Entity + x-cortex-type: service +""") + + result = cli(["backup", "import", "-d", tmpdir], return_type=ReturnType.RAW) + assert result.exit_code != 0, f"backup import should exit with non-zero code on failure, got exit_code={result.exit_code}" + + +def test_backup_export_invalid_api_key(monkeypatch): + """ + Test that backup export exits with non-zero return code and clean error message when API calls fail. + """ + monkeypatch.setenv("CORTEX_API_KEY", "invalidKey") + + with tempfile.TemporaryDirectory() as tmpdir: + result = cli(["backup", "export", "-d", tmpdir], return_type=ReturnType.RAW) + assert result.exit_code != 0, f"backup export should exit with non-zero code on failure, got exit_code={result.exit_code}" + assert "HTTP Error 401" in result.stdout, "Should show HTTP 401 error message" + assert "Traceback" not in result.stdout, "Should not show Python traceback" diff --git a/tests/test_config_file.py b/tests/test_config_file.py index f37cc81..1f86c97 100644 --- a/tests/test_config_file.py +++ b/tests/test_config_file.py @@ -31,12 +31,12 @@ def test_config_file_bad_api_key(monkeypatch, tmp_path): monkeypatch.setattr('sys.stdin', io.StringIO('y')) f = tmp_path / "test-config-bad-api-key.txt" response = cli(["-c", str(f), "-k", "invalidApiKey", "scorecards", "list"], return_type=ReturnType.RAW) - assert "401 Client Error: Unauthorized" in str(response), "should get Unauthorized error" + assert "HTTP Error 401" in response.stdout, "should get Unauthorized error" def test_environment_variable_invalid_key(monkeypatch): monkeypatch.setenv("CORTEX_API_KEY", "invalidKey") response = cli(["scorecards", "list"], return_type=ReturnType.RAW) - assert "401 Client Error: Unauthorized" in str(response), "should get Unauthorized error" + assert "HTTP Error 401" in response.stdout, "should get Unauthorized error" def test_config_file_bad_url(monkeypatch, tmp_path): monkeypatch.delenv("CORTEX_BASE_URL") diff --git a/tests/test_scim.py b/tests/test_scim.py index 3215747..ed5bda7 100644 --- a/tests/test_scim.py +++ b/tests/test_scim.py @@ -2,6 +2,7 @@ from urllib.error import HTTPError import pytest +@pytest.mark.skip(reason="Disabled until CET-23082 is resolved.") def test(): response = cli(["scim", "list"], ReturnType.STDOUT)