diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml
index 7c6d800..d494cd0 100644
--- a/.github/workflows/publish.yml
+++ b/.github/workflows/publish.yml
@@ -1,11 +1,14 @@
name: Publish
on:
+ workflow_dispatch:
push:
branches:
- main
paths:
- 'cortexapps_cli/**'
+ - 'pyproject.toml'
+ - 'poetry.lock'
env:
CORTEX_API_KEY: ${{ secrets.CORTEX_API_KEY_PRODUCTION }}
diff --git a/HISTORY.md b/HISTORY.md
index 16f151c..9f889ec 100644
--- a/HISTORY.md
+++ b/HISTORY.md
@@ -6,6 +6,27 @@ 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.19.0](https://github.com/cortexapps/cli/releases/tag/1.19.0) - 2026-06-01
+
+[Compare with 1.18.0](https://github.com/cortexapps/cli/compare/1.18.0...1.19.0)
+
+### Bug Fixes
+
+- resolve Python 3.14 crash from builtin list shadowing in custom_events ([95e3f79](https://github.com/cortexapps/cli/commit/95e3f7960fc02ab6299cd765bafe39946f8fa847) by Jeff Schnitter).
+
+## [1.18.0](https://github.com/cortexapps/cli/releases/tag/1.18.0) - 2026-06-01
+
+[Compare with 1.17.0](https://github.com/cortexapps/cli/compare/1.17.0...1.18.0)
+
+### Features
+
+- add Python 3.14 support and update CI to test on 3.14 by default ([90c5654](https://github.com/cortexapps/cli/commit/90c56540c3034cb29212cbd1c06eb7a8eb4073b0) by Jeff Schnitter).
+
+### Bug Fixes
+
+- pin safety<3.8.0 to fix broken poetry audit in CI ([f706a6c](https://github.com/cortexapps/cli/commit/f706a6c0bb384f689eab7dbc14d1b0602cc62db2) by Jeff Schnitter).
+- update idna to 3.17 to address CVE bypass (Dependabot #22) ([cb60c3c](https://github.com/cortexapps/cli/commit/cb60c3cbaf281b4b80e25e0d328209d300366d50) by Jeff Schnitter).
+
## [1.17.0](https://github.com/cortexapps/cli/releases/tag/1.17.0) - 2026-05-20
[Compare with 1.16.0](https://github.com/cortexapps/cli/compare/1.16.0...1.17.0)
diff --git a/cortexapps_cli/__init__.py b/cortexapps_cli/__init__.py
index e69de29..8b13789 100644
--- a/cortexapps_cli/__init__.py
+++ b/cortexapps_cli/__init__.py
@@ -0,0 +1 @@
+
diff --git a/cortexapps_cli/commands/custom_events.py b/cortexapps_cli/commands/custom_events.py
index 9c9b969..f170341 100644
--- a/cortexapps_cli/commands/custom_events.py
+++ b/cortexapps_cli/commands/custom_events.py
@@ -1,6 +1,7 @@
from collections import defaultdict
from datetime import datetime
import json
+from typing import List, Optional
from rich import print_json
import typer
from typing_extensions import Annotated
@@ -32,7 +33,7 @@ def _parse_key_value(values):
def update_by_uuid(
ctx: typer.Context,
file_input: Annotated[typer.FileText, typer.Option("--file", "-f", help=" File containing custom event; can be passed as stdin with -, example: -f-")] = None,
- custom_data: list[str] | None = typer.Option(None, "--custom", "-c", callback=_parse_key_value, help="List of optional custom metadata key=value pairs (only if file input not provided."),
+ custom_data: Optional[List[str]] = typer.Option(None, "--custom", "-c", callback=_parse_key_value, help="List of optional custom metadata key=value pairs (only if file input not provided."),
description: str = typer.Option(None, "--description", "-d", help="The description of the custom data key (only if file input not provided)."),
title: str = typer.Option(None, "--title", "-ti", help="The title of the custome event (only if file input not provided)."),
tag: str = typer.Option(..., "--tag", "-t", help="The tag (x-cortex-tag) or unique, auto-generated identifier for the entity."),
@@ -81,7 +82,7 @@ def update_by_uuid(
def create(
ctx: typer.Context,
file_input: Annotated[typer.FileText, typer.Option("--file", "-f", help=" File containing custom event; can be passed as stdin with -, example: -f-")] = None,
- custom_data: list[str] | None = typer.Option(None, "--custom", "-c", callback=_parse_key_value, help="List of optional custom metadata key=value pairs (only if file input not provided."),
+ custom_data: Optional[List[str]] = typer.Option(None, "--custom", "-c", callback=_parse_key_value, help="List of optional custom metadata key=value pairs (only if file input not provided."),
description: str = typer.Option(None, "--description", "-d", help="The description of the custom data key (only if file input not provided)."),
title: str = typer.Option(None, "--title", "-ti", help="The title of the custome event (only if file input not provided)."),
tag: str = typer.Option(..., "--tag", "-t", help="The tag (x-cortex-tag) or unique, auto-generated identifier for the entity."),
diff --git a/cortexapps_cli/commands/entity_types.py b/cortexapps_cli/commands/entity_types.py
index af758c9..a2cb510 100644
--- a/cortexapps_cli/commands/entity_types.py
+++ b/cortexapps_cli/commands/entity_types.py
@@ -114,7 +114,6 @@ def update(
def get(
ctx: typer.Context,
entity_type: str = typer.Option(..., "--type", "-t", help="The entity type"),
- _print: CommandOptions._print = True,
):
"""
Retrieve entity type
@@ -123,7 +122,4 @@ def get(
client = ctx.obj["client"]
r = client.get("api/v1/catalog/definitions/" + entity_type)
- if _print:
- print_json(data=r)
- else:
- return r
+ print_json(data=r)
diff --git a/data/import/scorecards/cli-test-draft-scorecard.yaml b/data/import/scorecards/cli-test-draft-scorecard.yaml
index 72f4e86..6705743 100644
--- a/data/import/scorecards/cli-test-draft-scorecard.yaml
+++ b/data/import/scorecards/cli-test-draft-scorecard.yaml
@@ -15,5 +15,5 @@ rules:
weight: 1
level: You Made It
filter:
- query: entity_descriptor.info.`x-cortex-tag` = "cli-test-service"
+ query: entity.tag() == "cli-test-service"
category: SERVICE
diff --git a/poetry.lock b/poetry.lock
index 30525da..736b113 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -922,7 +922,7 @@ version = "4.14.1"
description = "Backported and Experimental Type Hints for Python 3.9+"
optional = false
python-versions = ">=3.9"
-groups = ["playwright"]
+groups = ["main", "playwright"]
files = [
{file = "typing_extensions-4.14.1-py3-none-any.whl", hash = "sha256:d1e1e3b58374dc93031d6eda2420a48ea44a36c2b4766a4fdeb3710755731d76"},
{file = "typing_extensions-4.14.1.tar.gz", hash = "sha256:38b39f4aeeab64884ce9f74c94263ef78f3c22467c8724005483154c26648d36"},
@@ -949,4 +949,4 @@ zstd = ["backports-zstd (>=1.0.0) ; python_version < \"3.14\""]
[metadata]
lock-version = "2.1"
python-versions = "^3.11"
-content-hash = "656108a410372cb006af2e0f3a6ae97ae5050e1dc014f75b92df32dde6665513"
+content-hash = "1d38560a601329ebf7335f055ebbb9629a94868da642e24d8bcb40dde535acd0"
diff --git a/pyproject.toml b/pyproject.toml
index 0c75bc5..e0de63c 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -21,6 +21,7 @@ requests = "^2.33.0"
pyyaml = ">= 6.0.1, < 7"
urllib3 = ">= 2.7.0"
typer = ">=0.15,<1.0"
+typing_extensions = ">=3.7.4.3"
[tool.poetry.scripts]
cortex = "cortexapps_cli.cli:app"
diff --git a/tests/test_cortex_client.py b/tests/test_cortex_client.py
new file mode 100644
index 0000000..6812f3a
--- /dev/null
+++ b/tests/test_cortex_client.py
@@ -0,0 +1,233 @@
+import io
+import logging
+import pytest
+import responses
+import typer
+
+from cortexapps_cli.cortex_client import CortexClient
+
+BASE_URL = "https://api.test.example.com"
+
+
+def make_client():
+ return CortexClient(
+ api_key="test-key",
+ tenant="default",
+ numeric_level=logging.WARNING,
+ base_url=BASE_URL,
+ rate_limit=60000,
+ )
+
+
+# ---------------------------------------------------------------------------
+# Task 6: Error handling paths (Lines 77-78, 162-173, 182-188)
+# ---------------------------------------------------------------------------
+
+def test_version_fallback_when_package_not_found():
+ from unittest.mock import patch
+ import importlib.metadata
+ with patch("importlib.metadata.version", side_effect=importlib.metadata.PackageNotFoundError):
+ client = CortexClient(
+ api_key="test-key",
+ tenant="default",
+ numeric_level=logging.WARNING,
+ base_url=BASE_URL,
+ rate_limit=60000,
+ )
+ assert client.version == "unknown"
+
+
+@responses.activate
+def test_request_error_with_violations():
+ client = make_client()
+ responses.add(
+ responses.GET,
+ f"{BASE_URL}/api/v1/test",
+ json={
+ "violations": [
+ {
+ "title": "Bad Field",
+ "description": "Field is invalid",
+ "violationType": "CONSTRAINT",
+ "pointer": "/field",
+ },
+ {
+ "title": "Missing Field",
+ "description": "Required field missing",
+ },
+ ]
+ },
+ status=400,
+ )
+ with pytest.raises(typer.Exit):
+ client.get("api/v1/test")
+
+
+@responses.activate
+def test_request_error_with_violations_minimal_fields():
+ client = make_client()
+ responses.add(
+ responses.GET,
+ f"{BASE_URL}/api/v1/test",
+ json={
+ "violations": [
+ {}
+ ]
+ },
+ status=400,
+ )
+ with pytest.raises(typer.Exit):
+ client.get("api/v1/test")
+
+
+@responses.activate
+def test_request_error_non_json_response():
+ client = make_client()
+ responses.add(
+ responses.GET,
+ f"{BASE_URL}/api/v1/test",
+ body="502 Bad Gateway",
+ status=502,
+ content_type="text/html",
+ )
+ with pytest.raises(typer.Exit):
+ client.get("api/v1/test")
+
+
+# ---------------------------------------------------------------------------
+# Task 7: Response fallbacks and read_file (Lines 198-201, 310)
+# ---------------------------------------------------------------------------
+
+@responses.activate
+def test_request_ok_non_json_returns_text():
+ client = make_client()
+ responses.add(
+ responses.GET,
+ f"{BASE_URL}/api/v1/test",
+ body="plain text response",
+ status=200,
+ content_type="text/plain",
+ )
+ result = client.get("api/v1/test")
+ assert result == "plain text response"
+
+
+def test_read_file():
+ client = make_client()
+ f = io.StringIO("file contents")
+ assert client.read_file(f) == "file contents"
+
+
+# ---------------------------------------------------------------------------
+# Task 8: `fetch` edge cases (Lines 229, 238-240, 250)
+# ---------------------------------------------------------------------------
+
+@responses.activate
+def test_fetch_non_dict_non_list_response_breaks():
+ client = make_client()
+ responses.add(
+ responses.GET,
+ f"{BASE_URL}/api/v1/test",
+ body="not json at all",
+ status=200,
+ content_type="text/plain",
+ )
+ result = client.fetch("api/v1/test")
+ # When response is not dict/list, fetch breaks immediately with data_key still None.
+ # The return statement at line 252-257 creates {"total": 0, "page": 0, "totalPages": 0, None: []}
+ assert result["total"] == 0
+ assert result["page"] == 0
+ assert result["totalPages"] == 0
+ assert None in result
+ assert result[None] == []
+
+
+@responses.activate
+def test_fetch_list_response_pagination():
+ client = make_client()
+ # First page returns items
+ responses.add(
+ responses.GET,
+ f"{BASE_URL}/api/v1/test",
+ json=[{"id": 1}, {"id": 2}],
+ status=200,
+ )
+ # Second page returns empty list (signals end)
+ responses.add(
+ responses.GET,
+ f"{BASE_URL}/api/v1/test",
+ json=[],
+ status=200,
+ )
+ result = client.fetch("api/v1/test")
+ assert result == [{"id": 1}, {"id": 2}]
+
+
+# ---------------------------------------------------------------------------
+# Task 9: Entity helper methods with team type
+# (Lines 270, 274-280, 283-289, 292-298, 301-307)
+# ---------------------------------------------------------------------------
+
+@responses.activate
+def test_get_entity_team_type():
+ client = make_client()
+ responses.add(responses.GET, f"{BASE_URL}/api/v1/teams/my-team", json={"tag": "my-team"}, status=200)
+ result = client.get_entity("my-team", entity_type="team")
+ assert result["tag"] == "my-team"
+ assert "/teams/" in responses.calls[0].request.url
+
+
+@responses.activate
+def test_get_entity_catalog_type():
+ client = make_client()
+ responses.add(responses.GET, f"{BASE_URL}/api/v1/catalog/my-service", json={"tag": "my-service"}, status=200)
+ result = client.get_entity("my-service", entity_type="service")
+ assert "/catalog/" in responses.calls[0].request.url
+
+
+@responses.activate
+def test_delete_entity_team_type():
+ client = make_client()
+ responses.add(responses.DELETE, f"{BASE_URL}/api/v1/teams/my-team", json={}, status=200)
+ client.delete_entity("my-team", entity_type="teams")
+ assert "/teams/" in responses.calls[0].request.url
+
+
+@responses.activate
+def test_delete_entity_catalog_type():
+ client = make_client()
+ responses.add(responses.DELETE, f"{BASE_URL}/api/v1/catalog/my-service", json={}, status=200)
+ client.delete_entity("my-service", entity_type="service")
+ assert "/catalog/" in responses.calls[0].request.url
+
+
+@responses.activate
+def test_archive_entity_team_type():
+ client = make_client()
+ responses.add(responses.PUT, f"{BASE_URL}/api/v1/teams/my-team/archive", json={}, status=200)
+ client.archive_entity("my-team", entity_type="team")
+ assert "/teams/" in responses.calls[0].request.url
+
+
+@responses.activate
+def test_archive_entity_catalog_type():
+ client = make_client()
+ responses.add(responses.PUT, f"{BASE_URL}/api/v1/catalog/my-service/archive", json={}, status=200)
+ client.archive_entity("my-service", entity_type="service")
+ assert "/catalog/" in responses.calls[0].request.url
+
+
+@responses.activate
+def test_unarchive_entity_team_type():
+ client = make_client()
+ responses.add(responses.PUT, f"{BASE_URL}/api/v1/teams/my-team/unarchive", json={}, status=200)
+ client.unarchive_entity("my-team", entity_type="team")
+ assert "/teams/" in responses.calls[0].request.url
+
+
+@responses.activate
+def test_unarchive_entity_catalog_type():
+ client = make_client()
+ responses.add(responses.PUT, f"{BASE_URL}/api/v1/catalog/my-service/unarchive", json={}, status=200)
+ client.unarchive_entity("my-service", entity_type="service")
+ assert "/catalog/" in responses.calls[0].request.url
diff --git a/tests/test_entity_types.py b/tests/test_entity_types.py
index c5b59fd..a778614 100644
--- a/tests/test_entity_types.py
+++ b/tests/test_entity_types.py
@@ -16,6 +16,9 @@ def test_resource_definitions(capsys):
response = cli(["entity-types", "get", "-t", "cli-test"])
assert response.get('iconTag') == "Cortex-builtin::Basketball", "iconTag should be set to Cortex-builtin::Basketball"
+ # Verify default columns are set when using --table output
+ cli(["entity-types", "list", "--table"], return_type=ReturnType.STDOUT)
+
cli(["entity-types", "update", "-t", "cli-test", "-f", "data/run-time/entity-type-update.json"])
diff --git a/tests/test_integrations_apiiro.py b/tests/test_integrations_apiiro.py
index 91d148f..82270e8 100644
--- a/tests/test_integrations_apiiro.py
+++ b/tests/test_integrations_apiiro.py
@@ -57,3 +57,16 @@ def test_integrations_apiiro_validate():
def test_integrations_apiiro_validate_all():
responses.add(responses.POST, os.getenv("CORTEX_BASE_URL") + "/api/v1/apiiro/configuration/validate", json={}, status=200)
cli(["integrations", "apiiro", "validate-all"])
+
+@responses.activate
+def test_integrations_apiiro_add_file_with_flags_error(tmp_path):
+ f = _dummy_file(tmp_path)
+ result = cli(["integrations", "apiiro", "add", "-a", "test", "--api-key", "key", "-f", str(f)], return_type=ReturnType.RAW)
+ assert result.exit_code != 0
+
+@responses.activate
+def test_integrations_apiiro_add_multiple_valid(tmp_path):
+ f = tmp_path / "valid.json"
+ f.write_text('{"configurations": []}')
+ responses.add(responses.PUT, os.getenv("CORTEX_BASE_URL") + "/api/v1/apiiro/configurations", json={}, status=200)
+ cli(["integrations", "apiiro", "add-multiple", "-f", str(f)])
diff --git a/tests/test_integrations_argocd.py b/tests/test_integrations_argocd.py
index 511f9ad..f24fd41 100644
--- a/tests/test_integrations_argocd.py
+++ b/tests/test_integrations_argocd.py
@@ -57,3 +57,16 @@ def test_integrations_argocd_validate():
def test_integrations_argocd_validate_all():
responses.add(responses.POST, os.getenv("CORTEX_BASE_URL") + "/api/v1/argocd/configuration/validate", json={}, status=200)
cli(["integrations", "argocd", "validate-all"])
+
+@responses.activate
+def test_integrations_argocd_add_file_with_flags_error(tmp_path):
+ f = _dummy_file(tmp_path)
+ result = cli(["integrations", "argocd", "add", "-a", "test", "-h", "host", "-u", "user", "-p", "pass", "-f", str(f)], return_type=ReturnType.RAW)
+ assert result.exit_code != 0
+
+@responses.activate
+def test_integrations_argocd_add_multiple_valid(tmp_path):
+ f = tmp_path / "valid.json"
+ f.write_text('{"configurations": []}')
+ responses.add(responses.PUT, os.getenv("CORTEX_BASE_URL") + "/api/v1/argocd/configurations", json={}, status=200)
+ cli(["integrations", "argocd", "add-multiple", "-f", str(f)])
diff --git a/tests/test_integrations_azure_active_directory.py b/tests/test_integrations_azure_active_directory.py
index 24e8c00..3a1b9d5 100644
--- a/tests/test_integrations_azure_active_directory.py
+++ b/tests/test_integrations_azure_active_directory.py
@@ -29,6 +29,28 @@ def test_integrations_azure_active_directory_validate():
responses.add(responses.POST, os.getenv("CORTEX_BASE_URL") + "/api/v1/active-directory/configuration/validate", json={}, status=200)
cli(["integrations", "azure-active-directory", "validate"])
+@responses.activate
+def test_integrations_azure_active_directory_add_with_flags():
+ responses.add(responses.POST, os.getenv("CORTEX_BASE_URL") + "/api/v1/active-directory/configuration", json={}, status=200)
+ cli(["integrations", "azure-active-directory", "add", "--client-id", "foo", "--client-secret", "bar", "--tenant-id", "baz"])
+
+@responses.activate
+def test_integrations_azure_active_directory_add_file_with_flags_error(tmp_path):
+ f = _dummy_file(tmp_path)
+ result = cli(["integrations", "azure-active-directory", "add", "-f", str(f), "--client-id", "foo"], return_type=ReturnType.RAW)
+ assert result.exit_code != 0
+
+@responses.activate
+def test_integrations_azure_active_directory_update_with_flags():
+ responses.add(responses.PUT, os.getenv("CORTEX_BASE_URL") + "/api/v1/active-directory/configuration", json={}, status=200)
+ cli(["integrations", "azure-active-directory", "update", "--client-id", "foo"])
+
+@responses.activate
+def test_integrations_azure_active_directory_update_file_with_flags_error(tmp_path):
+ f = _dummy_file(tmp_path)
+ result = cli(["integrations", "azure-active-directory", "update", "-f", str(f), "--client-id", "foo"], return_type=ReturnType.RAW)
+ assert result.exit_code != 0
+
@responses.activate
def test_integrations_azure_active_directory_delete():
responses.add(responses.DELETE, os.getenv("CORTEX_BASE_URL") + "/api/v1/active-directory/configurations", json={}, status=200)
diff --git a/tests/test_integrations_azure_devops.py b/tests/test_integrations_azure_devops.py
index 484b49c..fd75ddd 100644
--- a/tests/test_integrations_azure_devops.py
+++ b/tests/test_integrations_azure_devops.py
@@ -57,3 +57,16 @@ def test_integrations_azure_devops_validate():
def test_integrations_azure_devops_validate_all():
responses.add(responses.POST, os.getenv("CORTEX_BASE_URL") + "/api/v1/azure-devops/configuration/validate", json={}, status=200)
cli(["integrations", "azure-devops", "validate-all"])
+
+@responses.activate
+def test_integrations_azure_devops_add_file_with_flags_error(tmp_path):
+ f = _dummy_file(tmp_path)
+ result = cli(["integrations", "azure-devops", "add", "-a", "test", "-h", "host", "-o", "org", "-p", "pat", "-u", "user", "-f", str(f)], return_type=ReturnType.RAW)
+ assert result.exit_code != 0
+
+@responses.activate
+def test_integrations_azure_devops_add_multiple_valid(tmp_path):
+ f = tmp_path / "valid.json"
+ f.write_text('{"configurations": []}')
+ responses.add(responses.PUT, os.getenv("CORTEX_BASE_URL") + "/api/v1/azure-devops/configurations", json={}, status=200)
+ cli(["integrations", "azure-devops", "add-multiple", "-f", str(f)])
diff --git a/tests/test_integrations_bamboohr.py b/tests/test_integrations_bamboohr.py
index d04291f..faa4bac 100644
--- a/tests/test_integrations_bamboohr.py
+++ b/tests/test_integrations_bamboohr.py
@@ -29,6 +29,28 @@ def test_integrations_bamboohr_validate():
responses.add(responses.POST, os.getenv("CORTEX_BASE_URL") + "/api/v1/bamboohr/configuration/validate", json={}, status=200)
cli(["integrations", "bamboohr", "validate"])
+@responses.activate
+def test_integrations_bamboohr_add_with_flags():
+ responses.add(responses.POST, os.getenv("CORTEX_BASE_URL") + "/api/v1/bamboohr/configuration", json={}, status=200)
+ cli(["integrations", "bamboohr", "add", "--api-token", "foo", "--subdomain", "bar"])
+
+@responses.activate
+def test_integrations_bamboohr_add_file_with_flags_error(tmp_path):
+ f = _dummy_file(tmp_path)
+ result = cli(["integrations", "bamboohr", "add", "-f", str(f), "--api-token", "foo"], return_type=ReturnType.RAW)
+ assert result.exit_code != 0
+
+@responses.activate
+def test_integrations_bamboohr_update_with_flags():
+ responses.add(responses.PUT, os.getenv("CORTEX_BASE_URL") + "/api/v1/bamboohr/configuration", json={}, status=200)
+ cli(["integrations", "bamboohr", "update", "--api-token", "foo"])
+
+@responses.activate
+def test_integrations_bamboohr_update_file_with_flags_error(tmp_path):
+ f = _dummy_file(tmp_path)
+ result = cli(["integrations", "bamboohr", "update", "-f", str(f), "--api-token", "foo"], return_type=ReturnType.RAW)
+ assert result.exit_code != 0
+
@responses.activate
def test_integrations_bamboohr_delete():
responses.add(responses.DELETE, os.getenv("CORTEX_BASE_URL") + "/api/v1/bamboohr/configurations", json={}, status=200)
diff --git a/tests/test_integrations_bitbucket.py b/tests/test_integrations_bitbucket.py
index c014858..593a89e 100644
--- a/tests/test_integrations_bitbucket.py
+++ b/tests/test_integrations_bitbucket.py
@@ -58,3 +58,17 @@ def test_integrations_bitbucket_validate():
def test_integrations_bitbucket_validate_all():
responses.add(responses.POST, os.getenv("CORTEX_BASE_URL") + "/api/v1/bitbucket/configuration/validate", json={}, status=200)
cli(["integrations", "bitbucket", "validate-all"])
+
+@responses.activate
+def test_integrations_bitbucket_add_valid(tmp_path):
+ f = tmp_path / "valid.json"
+ f.write_text('{"alias": "test", "appPassword": "pw", "username": "user"}')
+ responses.add(responses.POST, os.getenv("CORTEX_BASE_URL") + "/api/v1/bitbucket/configuration", json={}, status=200)
+ cli(["integrations", "bitbucket", "add", "-f", str(f)])
+
+@responses.activate
+def test_integrations_bitbucket_add_multiple_valid(tmp_path):
+ f = tmp_path / "valid.json"
+ f.write_text('{"configurations": []}')
+ responses.add(responses.PUT, os.getenv("CORTEX_BASE_URL") + "/api/v1/bitbucket/configurations", json={}, status=200)
+ cli(["integrations", "bitbucket", "add-multiple", "-f", str(f)])
diff --git a/tests/test_integrations_bugsnag.py b/tests/test_integrations_bugsnag.py
index 5c753ef..4d579ab 100644
--- a/tests/test_integrations_bugsnag.py
+++ b/tests/test_integrations_bugsnag.py
@@ -29,6 +29,28 @@ def test_integrations_bugsnag_validate():
responses.add(responses.POST, os.getenv("CORTEX_BASE_URL") + "/api/v1/bugsnag/configuration/validate", json={}, status=200)
cli(["integrations", "bugsnag", "validate"])
+@responses.activate
+def test_integrations_bugsnag_add_with_flags():
+ responses.add(responses.POST, os.getenv("CORTEX_BASE_URL") + "/api/v1/bugsnag/configuration", json={}, status=200)
+ cli(["integrations", "bugsnag", "add", "--auth-token", "foo", "--organization-id", "bar"])
+
+@responses.activate
+def test_integrations_bugsnag_add_file_with_flags_error(tmp_path):
+ f = _dummy_file(tmp_path)
+ result = cli(["integrations", "bugsnag", "add", "-f", str(f), "--auth-token", "foo"], return_type=ReturnType.RAW)
+ assert result.exit_code != 0
+
+@responses.activate
+def test_integrations_bugsnag_update_with_flags():
+ responses.add(responses.PUT, os.getenv("CORTEX_BASE_URL") + "/api/v1/bugsnag/configuration", json={}, status=200)
+ cli(["integrations", "bugsnag", "update", "--auth-token", "foo"])
+
+@responses.activate
+def test_integrations_bugsnag_update_file_with_flags_error(tmp_path):
+ f = _dummy_file(tmp_path)
+ result = cli(["integrations", "bugsnag", "update", "-f", str(f), "--auth-token", "foo"], return_type=ReturnType.RAW)
+ assert result.exit_code != 0
+
@responses.activate
def test_integrations_bugsnag_delete():
responses.add(responses.DELETE, os.getenv("CORTEX_BASE_URL") + "/api/v1/bugsnag/configurations", json={}, status=200)
diff --git a/tests/test_integrations_buildkite.py b/tests/test_integrations_buildkite.py
index 1d97c5c..40f07be 100644
--- a/tests/test_integrations_buildkite.py
+++ b/tests/test_integrations_buildkite.py
@@ -29,6 +29,28 @@ def test_integrations_buildkite_validate():
responses.add(responses.POST, os.getenv("CORTEX_BASE_URL") + "/api/v1/buildkite/configuration/validate", json={}, status=200)
cli(["integrations", "buildkite", "validate"])
+@responses.activate
+def test_integrations_buildkite_add_with_flags():
+ responses.add(responses.POST, os.getenv("CORTEX_BASE_URL") + "/api/v1/buildkite/configuration", json={}, status=200)
+ cli(["integrations", "buildkite", "add", "--api-token", "foo", "--organization-slug", "bar"])
+
+@responses.activate
+def test_integrations_buildkite_add_file_with_flags_error(tmp_path):
+ f = _dummy_file(tmp_path)
+ result = cli(["integrations", "buildkite", "add", "-f", str(f), "--api-token", "foo"], return_type=ReturnType.RAW)
+ assert result.exit_code != 0
+
+@responses.activate
+def test_integrations_buildkite_update_with_flags():
+ responses.add(responses.PUT, os.getenv("CORTEX_BASE_URL") + "/api/v1/buildkite/configuration", json={}, status=200)
+ cli(["integrations", "buildkite", "update", "--api-token", "foo"])
+
+@responses.activate
+def test_integrations_buildkite_update_file_with_flags_error(tmp_path):
+ f = _dummy_file(tmp_path)
+ result = cli(["integrations", "buildkite", "update", "-f", str(f), "--api-token", "foo"], return_type=ReturnType.RAW)
+ assert result.exit_code != 0
+
@responses.activate
def test_integrations_buildkite_delete():
responses.add(responses.DELETE, os.getenv("CORTEX_BASE_URL") + "/api/v1/buildkite/configurations", json={}, status=200)
diff --git a/tests/test_integrations_checkmarx_sast.py b/tests/test_integrations_checkmarx_sast.py
index a86ad18..ba752db 100644
--- a/tests/test_integrations_checkmarx_sast.py
+++ b/tests/test_integrations_checkmarx_sast.py
@@ -29,6 +29,28 @@ def test_integrations_checkmarx_sast_validate():
responses.add(responses.POST, os.getenv("CORTEX_BASE_URL") + "/api/v1/checkmarx/sast/configuration/validate", json={}, status=200)
cli(["integrations", "checkmarx-sast", "validate"])
+@responses.activate
+def test_integrations_checkmarx_sast_add_with_flags():
+ responses.add(responses.POST, os.getenv("CORTEX_BASE_URL") + "/api/v1/checkmarx/sast/configuration", json={}, status=200)
+ cli(["integrations", "checkmarx-sast", "add", "--username", "foo", "--password", "bar"])
+
+@responses.activate
+def test_integrations_checkmarx_sast_add_file_with_flags_error(tmp_path):
+ f = _dummy_file(tmp_path)
+ result = cli(["integrations", "checkmarx-sast", "add", "-f", str(f), "--username", "foo"], return_type=ReturnType.RAW)
+ assert result.exit_code != 0
+
+@responses.activate
+def test_integrations_checkmarx_sast_update_with_flags():
+ responses.add(responses.PUT, os.getenv("CORTEX_BASE_URL") + "/api/v1/checkmarx/sast/configuration", json={}, status=200)
+ cli(["integrations", "checkmarx-sast", "update", "--username", "foo"])
+
+@responses.activate
+def test_integrations_checkmarx_sast_update_file_with_flags_error(tmp_path):
+ f = _dummy_file(tmp_path)
+ result = cli(["integrations", "checkmarx-sast", "update", "-f", str(f), "--username", "foo"], return_type=ReturnType.RAW)
+ assert result.exit_code != 0
+
@responses.activate
def test_integrations_checkmarx_sast_delete():
responses.add(responses.DELETE, os.getenv("CORTEX_BASE_URL") + "/api/v1/checkmarx/sast/configurations", json={}, status=200)
diff --git a/tests/test_integrations_circleci.py b/tests/test_integrations_circleci.py
index e738404..ca43299 100644
--- a/tests/test_integrations_circleci.py
+++ b/tests/test_integrations_circleci.py
@@ -57,3 +57,16 @@ def test_integrations_circle_ci_validate():
def test_integrations_circle_ci_validate_all():
responses.add(responses.POST, os.getenv("CORTEX_BASE_URL") + "/api/v1/circleci/configuration/validate", json={}, status=200)
cli(["integrations", "circleci", "validate-all"])
+
+@responses.activate
+def test_integrations_circleci_add_file_with_flags_error(tmp_path):
+ f = _dummy_file(tmp_path)
+ result = cli(["integrations", "circleci", "add", "-a", "test", "--api-key", "key", "-f", str(f)], return_type=ReturnType.RAW)
+ assert result.exit_code != 0
+
+@responses.activate
+def test_integrations_circleci_add_multiple_valid(tmp_path):
+ f = tmp_path / "valid.json"
+ f.write_text('{"configurations": []}')
+ responses.add(responses.PUT, os.getenv("CORTEX_BASE_URL") + "/api/v1/circleci/configurations", json={}, status=200)
+ cli(["integrations", "circleci", "add-multiple", "-f", str(f)])
diff --git a/tests/test_integrations_clickup.py b/tests/test_integrations_clickup.py
index 8318c90..78365ef 100644
--- a/tests/test_integrations_clickup.py
+++ b/tests/test_integrations_clickup.py
@@ -29,6 +29,28 @@ def test_integrations_clickup_validate():
responses.add(responses.POST, os.getenv("CORTEX_BASE_URL") + "/api/v1/clickup/configuration/validate", json={}, status=200)
cli(["integrations", "clickup", "validate"])
+@responses.activate
+def test_integrations_clickup_add_with_flags():
+ responses.add(responses.POST, os.getenv("CORTEX_BASE_URL") + "/api/v1/clickup/configuration", json={}, status=200)
+ cli(["integrations", "clickup", "add", "--personal-api-token", "foo"])
+
+@responses.activate
+def test_integrations_clickup_add_file_with_flags_error(tmp_path):
+ f = _dummy_file(tmp_path)
+ result = cli(["integrations", "clickup", "add", "-f", str(f), "--personal-api-token", "foo"], return_type=ReturnType.RAW)
+ assert result.exit_code != 0
+
+@responses.activate
+def test_integrations_clickup_update_with_flags():
+ responses.add(responses.PUT, os.getenv("CORTEX_BASE_URL") + "/api/v1/clickup/configuration", json={}, status=200)
+ cli(["integrations", "clickup", "update", "--personal-api-token", "foo"])
+
+@responses.activate
+def test_integrations_clickup_update_file_with_flags_error(tmp_path):
+ f = _dummy_file(tmp_path)
+ result = cli(["integrations", "clickup", "update", "-f", str(f), "--personal-api-token", "foo"], return_type=ReturnType.RAW)
+ assert result.exit_code != 0
+
@responses.activate
def test_integrations_clickup_delete():
responses.add(responses.DELETE, os.getenv("CORTEX_BASE_URL") + "/api/v1/clickup/configurations", json={}, status=200)
diff --git a/tests/test_integrations_codecov.py b/tests/test_integrations_codecov.py
index 4eb06a5..10666c6 100644
--- a/tests/test_integrations_codecov.py
+++ b/tests/test_integrations_codecov.py
@@ -29,6 +29,28 @@ def test_integrations_codecov_validate():
responses.add(responses.POST, os.getenv("CORTEX_BASE_URL") + "/api/v1/codecov/configuration/validate", json={}, status=200)
cli(["integrations", "codecov", "validate"])
+@responses.activate
+def test_integrations_codecov_add_with_flags():
+ responses.add(responses.POST, os.getenv("CORTEX_BASE_URL") + "/api/v1/codecov/configuration", json={}, status=200)
+ cli(["integrations", "codecov", "add", "--api-token", "foo"])
+
+@responses.activate
+def test_integrations_codecov_add_file_with_flags_error(tmp_path):
+ f = _dummy_file(tmp_path)
+ result = cli(["integrations", "codecov", "add", "-f", str(f), "--api-token", "foo"], return_type=ReturnType.RAW)
+ assert result.exit_code != 0
+
+@responses.activate
+def test_integrations_codecov_update_with_flags():
+ responses.add(responses.PUT, os.getenv("CORTEX_BASE_URL") + "/api/v1/codecov/configuration", json={}, status=200)
+ cli(["integrations", "codecov", "update", "--api-token", "foo"])
+
+@responses.activate
+def test_integrations_codecov_update_file_with_flags_error(tmp_path):
+ f = _dummy_file(tmp_path)
+ result = cli(["integrations", "codecov", "update", "-f", str(f), "--api-token", "foo"], return_type=ReturnType.RAW)
+ assert result.exit_code != 0
+
@responses.activate
def test_integrations_codecov_delete():
responses.add(responses.DELETE, os.getenv("CORTEX_BASE_URL") + "/api/v1/codecov/configurations", json={}, status=200)
diff --git a/tests/test_integrations_coralogix.py b/tests/test_integrations_coralogix.py
index 52bcd53..5cc994f 100644
--- a/tests/test_integrations_coralogix.py
+++ b/tests/test_integrations_coralogix.py
@@ -57,3 +57,16 @@ def test_integrations_coralogix_validate():
def test_integrations_coralogix_validate_all():
responses.add(responses.POST, os.getenv("CORTEX_BASE_URL") + "/api/v1/coralogix/configuration/validate", json={}, status=200)
cli(["integrations", "coralogix", "validate-all"])
+
+@responses.activate
+def test_integrations_coralogix_add_file_with_flags_error(tmp_path):
+ f = _dummy_file(tmp_path)
+ result = cli(["integrations", "coralogix", "add", "-a", "test", "--api-key", "key", "-r", "US1", "-f", str(f)], return_type=ReturnType.RAW)
+ assert result.exit_code != 0
+
+@responses.activate
+def test_integrations_coralogix_add_multiple_valid(tmp_path):
+ f = tmp_path / "valid.json"
+ f.write_text('{"configurations": []}')
+ responses.add(responses.PUT, os.getenv("CORTEX_BASE_URL") + "/api/v1/coralogix/configurations", json={}, status=200)
+ cli(["integrations", "coralogix", "add-multiple", "-f", str(f)])
diff --git a/tests/test_integrations_datadog.py b/tests/test_integrations_datadog.py
index bae6289..dd82d3f 100644
--- a/tests/test_integrations_datadog.py
+++ b/tests/test_integrations_datadog.py
@@ -57,3 +57,16 @@ def test_integrations_datadog_validate():
def test_integrations_datadog_validate_all():
responses.add(responses.POST, os.getenv("CORTEX_BASE_URL") + "/api/v1/datadog/configuration/validate", json={}, status=200)
cli(["integrations", "datadog", "validate-all"])
+
+@responses.activate
+def test_integrations_datadog_add_file_with_flags_error(tmp_path):
+ f = _dummy_file(tmp_path)
+ result = cli(["integrations", "datadog", "add", "-a", "test", "--api-key", "key", "-r", "US1", "-f", str(f)], return_type=ReturnType.RAW)
+ assert result.exit_code != 0
+
+@responses.activate
+def test_integrations_datadog_add_multiple_valid(tmp_path):
+ f = tmp_path / "valid.json"
+ f.write_text('{"configurations": []}')
+ responses.add(responses.PUT, os.getenv("CORTEX_BASE_URL") + "/api/v1/datadog/configurations", json={}, status=200)
+ cli(["integrations", "datadog", "add-multiple", "-f", str(f)])
diff --git a/tests/test_integrations_dynatrace.py b/tests/test_integrations_dynatrace.py
index 5201cea..02f3985 100644
--- a/tests/test_integrations_dynatrace.py
+++ b/tests/test_integrations_dynatrace.py
@@ -29,6 +29,28 @@ def test_integrations_dynatrace_validate():
responses.add(responses.POST, os.getenv("CORTEX_BASE_URL") + "/api/v1/dynatrace/configuration/validate", json={}, status=200)
cli(["integrations", "dynatrace", "validate"])
+@responses.activate
+def test_integrations_dynatrace_add_with_flags():
+ responses.add(responses.POST, os.getenv("CORTEX_BASE_URL") + "/api/v1/dynatrace/configuration", json={}, status=200)
+ cli(["integrations", "dynatrace", "add", "--api-key", "foo", "--domain", "bar"])
+
+@responses.activate
+def test_integrations_dynatrace_add_file_with_flags_error(tmp_path):
+ f = _dummy_file(tmp_path)
+ result = cli(["integrations", "dynatrace", "add", "-f", str(f), "--api-key", "foo"], return_type=ReturnType.RAW)
+ assert result.exit_code != 0
+
+@responses.activate
+def test_integrations_dynatrace_update_with_flags():
+ responses.add(responses.PUT, os.getenv("CORTEX_BASE_URL") + "/api/v1/dynatrace/configuration", json={}, status=200)
+ cli(["integrations", "dynatrace", "update", "--api-key", "foo"])
+
+@responses.activate
+def test_integrations_dynatrace_update_file_with_flags_error(tmp_path):
+ f = _dummy_file(tmp_path)
+ result = cli(["integrations", "dynatrace", "update", "-f", str(f), "--api-key", "foo"], return_type=ReturnType.RAW)
+ assert result.exit_code != 0
+
@responses.activate
def test_integrations_dynatrace_delete():
responses.add(responses.DELETE, os.getenv("CORTEX_BASE_URL") + "/api/v1/dynatrace/configurations", json={}, status=200)
diff --git a/tests/test_integrations_firehydrant.py b/tests/test_integrations_firehydrant.py
index 3597a57..47f0f0b 100644
--- a/tests/test_integrations_firehydrant.py
+++ b/tests/test_integrations_firehydrant.py
@@ -29,6 +29,28 @@ def test_integrations_firehydrant_validate():
responses.add(responses.POST, os.getenv("CORTEX_BASE_URL") + "/api/v1/firehydrant/configuration/validate", json={}, status=200)
cli(["integrations", "firehydrant", "validate"])
+@responses.activate
+def test_integrations_firehydrant_add_with_flags():
+ responses.add(responses.POST, os.getenv("CORTEX_BASE_URL") + "/api/v1/firehydrant/configuration", json={}, status=200)
+ cli(["integrations", "firehydrant", "add", "--api-token", "foo"])
+
+@responses.activate
+def test_integrations_firehydrant_add_file_with_flags_error(tmp_path):
+ f = _dummy_file(tmp_path)
+ result = cli(["integrations", "firehydrant", "add", "-f", str(f), "--api-token", "foo"], return_type=ReturnType.RAW)
+ assert result.exit_code != 0
+
+@responses.activate
+def test_integrations_firehydrant_update_with_flags():
+ responses.add(responses.PUT, os.getenv("CORTEX_BASE_URL") + "/api/v1/firehydrant/configuration", json={}, status=200)
+ cli(["integrations", "firehydrant", "update", "--api-token", "foo"])
+
+@responses.activate
+def test_integrations_firehydrant_update_file_with_flags_error(tmp_path):
+ f = _dummy_file(tmp_path)
+ result = cli(["integrations", "firehydrant", "update", "-f", str(f), "--api-token", "foo"], return_type=ReturnType.RAW)
+ assert result.exit_code != 0
+
@responses.activate
def test_integrations_firehydrant_delete():
responses.add(responses.DELETE, os.getenv("CORTEX_BASE_URL") + "/api/v1/firehydrant/configurations", json={}, status=200)
diff --git a/tests/test_integrations_github.py b/tests/test_integrations_github.py
index fb4c091..69e8125 100644
--- a/tests/test_integrations_github.py
+++ b/tests/test_integrations_github.py
@@ -24,7 +24,7 @@ def test_integrations_github_delete_all():
@responses.activate
def test_integrations_github_get():
- responses.add(responses.GET, os.getenv("CORTEX_BASE_URL") + "/api/v1/github/configuration/app/test", json={}, status=200)
+ responses.add(responses.GET, os.getenv("CORTEX_BASE_URL") + "/api/v1/github/configurations/app/test", json={}, status=200)
cli(["integrations", "github", "get", "-a", "test"])
@responses.activate
@@ -65,9 +65,21 @@ def test_integrations_github_update_personal():
@responses.activate
def test_integrations_github_get_personal():
responses.add(responses.GET, os.getenv("CORTEX_BASE_URL") + "/api/v1/github/configurations/personal/test", json={}, status=200)
- cli(["integrations", "github", "get", "-a", "test"])
+ cli(["integrations", "github", "get-personal", "-a", "test"])
@responses.activate
def test_integrations_github_delete_personal():
responses.add(responses.DELETE, os.getenv("CORTEX_BASE_URL") + "/api/v1/github/configurations/personal/test", status=200)
cli(["integrations", "github", "delete-personal", "-a", "test"])
+
+@responses.activate
+def test_integrations_github_add_file_with_flags_error(tmp_path):
+ f = _dummy_file(tmp_path)
+ result = cli(["integrations", "github", "add", "-a", "test", "--api-key", "key", "-f", str(f)], return_type=ReturnType.RAW)
+ assert result.exit_code != 0
+
+@responses.activate
+def test_integrations_github_add_personal_file_with_flags_error(tmp_path):
+ f = _dummy_file(tmp_path)
+ result = cli(["integrations", "github", "add-personal", "-a", "test", "--access-token", "token", "-f", str(f)], return_type=ReturnType.RAW)
+ assert result.exit_code != 0
diff --git a/tests/test_integrations_gitlab.py b/tests/test_integrations_gitlab.py
index 8ff6e01..e0aca41 100644
--- a/tests/test_integrations_gitlab.py
+++ b/tests/test_integrations_gitlab.py
@@ -57,3 +57,16 @@ def test_integrations_gitlab_validate():
def test_integrations_gitlab_validate_all():
responses.add(responses.POST, os.getenv("CORTEX_BASE_URL") + "/api/v1/gitlab/configuration/validate", json={}, status=200)
cli(["integrations", "gitlab", "validate-all"])
+
+@responses.activate
+def test_integrations_gitlab_add_file_with_flags_error(tmp_path):
+ f = _dummy_file(tmp_path)
+ result = cli(["integrations", "gitlab", "add", "-a", "test", "--api-key", "key", "-f", str(f)], return_type=ReturnType.RAW)
+ assert result.exit_code != 0
+
+@responses.activate
+def test_integrations_gitlab_add_multiple_valid(tmp_path):
+ f = tmp_path / "valid.json"
+ f.write_text('{"configurations": []}')
+ responses.add(responses.PUT, os.getenv("CORTEX_BASE_URL") + "/api/v1/gitlab/configurations", json={}, status=200)
+ cli(["integrations", "gitlab", "add-multiple", "-f", str(f)])
diff --git a/tests/test_integrations_incidentio.py b/tests/test_integrations_incidentio.py
index 325c26d..c253b1c 100644
--- a/tests/test_integrations_incidentio.py
+++ b/tests/test_integrations_incidentio.py
@@ -57,3 +57,16 @@ def test_integrations_incidentio_validate():
def test_integrations_incidentio_validate_all():
responses.add(responses.POST, os.getenv("CORTEX_BASE_URL") + "/api/v1/incidentio/configuration/validate", json={}, status=200)
cli(["integrations", "incidentio", "validate-all"])
+
+@responses.activate
+def test_integrations_incidentio_add_file_with_flags_error(tmp_path):
+ f = _dummy_file(tmp_path)
+ result = cli(["integrations", "incidentio", "add", "-a", "test", "--api-key", "key", "-f", str(f)], return_type=ReturnType.RAW)
+ assert result.exit_code != 0
+
+@responses.activate
+def test_integrations_incidentio_add_multiple_valid(tmp_path):
+ f = tmp_path / "valid.json"
+ f.write_text('{"configurations": []}')
+ responses.add(responses.PUT, os.getenv("CORTEX_BASE_URL") + "/api/v1/incidentio/configurations", json={}, status=200)
+ cli(["integrations", "incidentio", "add-multiple", "-f", str(f)])
diff --git a/tests/test_integrations_instana.py b/tests/test_integrations_instana.py
index d03471d..f0fafbc 100644
--- a/tests/test_integrations_instana.py
+++ b/tests/test_integrations_instana.py
@@ -29,6 +29,28 @@ def test_integrations_instana_validate():
responses.add(responses.POST, os.getenv("CORTEX_BASE_URL") + "/api/v1/instana/configuration/validate", json={}, status=200)
cli(["integrations", "instana", "validate"])
+@responses.activate
+def test_integrations_instana_add_with_flags():
+ responses.add(responses.POST, os.getenv("CORTEX_BASE_URL") + "/api/v1/instana/configuration", json={}, status=200)
+ cli(["integrations", "instana", "add", "--api-token", "foo", "--endpoint", "bar"])
+
+@responses.activate
+def test_integrations_instana_add_file_with_flags_error(tmp_path):
+ f = _dummy_file(tmp_path)
+ result = cli(["integrations", "instana", "add", "-f", str(f), "--api-token", "foo"], return_type=ReturnType.RAW)
+ assert result.exit_code != 0
+
+@responses.activate
+def test_integrations_instana_update_with_flags():
+ responses.add(responses.PUT, os.getenv("CORTEX_BASE_URL") + "/api/v1/instana/configuration", json={}, status=200)
+ cli(["integrations", "instana", "update", "--api-token", "foo"])
+
+@responses.activate
+def test_integrations_instana_update_file_with_flags_error(tmp_path):
+ f = _dummy_file(tmp_path)
+ result = cli(["integrations", "instana", "update", "-f", str(f), "--api-token", "foo"], return_type=ReturnType.RAW)
+ assert result.exit_code != 0
+
@responses.activate
def test_integrations_instana_delete():
responses.add(responses.DELETE, os.getenv("CORTEX_BASE_URL") + "/api/v1/instana/configurations", json={}, status=200)
diff --git a/tests/test_integrations_jenkins.py b/tests/test_integrations_jenkins.py
index dd92cad..7c5ebc1 100644
--- a/tests/test_integrations_jenkins.py
+++ b/tests/test_integrations_jenkins.py
@@ -57,3 +57,16 @@ def test_integrations_jenkins_validate():
def test_integrations_jenkins_validate_all():
responses.add(responses.POST, os.getenv("CORTEX_BASE_URL") + "/api/v1/jenkins/configuration/validate", json={}, status=200)
cli(["integrations", "jenkins", "validate-all"])
+
+@responses.activate
+def test_integrations_jenkins_add_file_with_flags_error(tmp_path):
+ f = _dummy_file(tmp_path)
+ result = cli(["integrations", "jenkins", "add", "-a", "test", "--api-key", "key", "-h", "host", "-u", "user", "-f", str(f)], return_type=ReturnType.RAW)
+ assert result.exit_code != 0
+
+@responses.activate
+def test_integrations_jenkins_add_multiple_valid(tmp_path):
+ f = tmp_path / "valid.json"
+ f.write_text('{"configurations": []}')
+ responses.add(responses.PUT, os.getenv("CORTEX_BASE_URL") + "/api/v1/jenkins/configurations", json={}, status=200)
+ cli(["integrations", "jenkins", "add-multiple", "-f", str(f)])
diff --git a/tests/test_integrations_jira.py b/tests/test_integrations_jira.py
index fd10595..d3508e0 100644
--- a/tests/test_integrations_jira.py
+++ b/tests/test_integrations_jira.py
@@ -58,3 +58,17 @@ def test_integrations_jira_validate():
def test_integrations_jira_validate_all():
responses.add(responses.POST, os.getenv("CORTEX_BASE_URL") + "/api/v1/jira/configuration/validate", json={}, status=200)
cli(["integrations", "jira", "validate-all"])
+
+@responses.activate
+def test_integrations_jira_add_valid(tmp_path):
+ f = tmp_path / "valid.json"
+ f.write_text('{"alias": "test", "email": "test@test.com", "host": "jira.test.com", "apiToken": "token"}')
+ responses.add(responses.POST, os.getenv("CORTEX_BASE_URL") + "/api/v1/jira/configuration", json={}, status=200)
+ cli(["integrations", "jira", "add", "-f", str(f)])
+
+@responses.activate
+def test_integrations_jira_add_multiple_valid(tmp_path):
+ f = tmp_path / "valid.json"
+ f.write_text('{"configurations": []}')
+ responses.add(responses.PUT, os.getenv("CORTEX_BASE_URL") + "/api/v1/jira/configurations", json={}, status=200)
+ cli(["integrations", "jira", "add-multiple", "-f", str(f)])
diff --git a/tests/test_integrations_launchdarkly.py b/tests/test_integrations_launchdarkly.py
index 5657d87..a63544a 100644
--- a/tests/test_integrations_launchdarkly.py
+++ b/tests/test_integrations_launchdarkly.py
@@ -57,3 +57,16 @@ def test_integrations_launchdarkly_validate():
def test_integrations_launchdarkly_validate_all():
responses.add(responses.POST, os.getenv("CORTEX_BASE_URL") + "/api/v1/launchdarkly/configuration/validate", json={}, status=200)
cli(["integrations", "launchdarkly", "validate-all"])
+
+@responses.activate
+def test_integrations_launchdarkly_add_file_with_flags_error(tmp_path):
+ f = _dummy_file(tmp_path)
+ result = cli(["integrations", "launchdarkly", "add", "-a", "test", "--api-key", "key", "-f", str(f)], return_type=ReturnType.RAW)
+ assert result.exit_code != 0
+
+@responses.activate
+def test_integrations_launchdarkly_add_multiple_valid(tmp_path):
+ f = tmp_path / "valid.json"
+ f.write_text('{"configurations": []}')
+ responses.add(responses.PUT, os.getenv("CORTEX_BASE_URL") + "/api/v1/launchdarkly/configurations", json={}, status=200)
+ cli(["integrations", "launchdarkly", "add-multiple", "-f", str(f)])
diff --git a/tests/test_integrations_mend_sast.py b/tests/test_integrations_mend_sast.py
index 0087ea8..5d76895 100644
--- a/tests/test_integrations_mend_sast.py
+++ b/tests/test_integrations_mend_sast.py
@@ -29,6 +29,28 @@ def test_integrations_mend_sast_validate():
responses.add(responses.POST, os.getenv("CORTEX_BASE_URL") + "/api/v1/mend/sast/configuration/validate", json={}, status=200)
cli(["integrations", "mend-sast", "validate"])
+@responses.activate
+def test_integrations_mend_sast_add_with_flags():
+ responses.add(responses.POST, os.getenv("CORTEX_BASE_URL") + "/api/v1/mend/sast/configuration", json={}, status=200)
+ cli(["integrations", "mend-sast", "add", "--api-key", "foo"])
+
+@responses.activate
+def test_integrations_mend_sast_add_file_with_flags_error(tmp_path):
+ f = _dummy_file(tmp_path)
+ result = cli(["integrations", "mend-sast", "add", "-f", str(f), "--api-key", "foo"], return_type=ReturnType.RAW)
+ assert result.exit_code != 0
+
+@responses.activate
+def test_integrations_mend_sast_update_with_flags():
+ responses.add(responses.PUT, os.getenv("CORTEX_BASE_URL") + "/api/v1/mend/sast/configuration", json={}, status=200)
+ cli(["integrations", "mend-sast", "update", "--api-key", "foo"])
+
+@responses.activate
+def test_integrations_mend_sast_update_file_with_flags_error(tmp_path):
+ f = _dummy_file(tmp_path)
+ result = cli(["integrations", "mend-sast", "update", "-f", str(f), "--api-key", "foo"], return_type=ReturnType.RAW)
+ assert result.exit_code != 0
+
@responses.activate
def test_integrations_mend_sast_delete():
responses.add(responses.DELETE, os.getenv("CORTEX_BASE_URL") + "/api/v1/mend/sast/configurations", json={}, status=200)
diff --git a/tests/test_integrations_mend_sca.py b/tests/test_integrations_mend_sca.py
index 44f1e0e..cb1c9ab 100644
--- a/tests/test_integrations_mend_sca.py
+++ b/tests/test_integrations_mend_sca.py
@@ -29,6 +29,28 @@ def test_integrations_mend_sca_validate():
responses.add(responses.POST, os.getenv("CORTEX_BASE_URL") + "/api/v1/mend/sca/configuration/validate", json={}, status=200)
cli(["integrations", "mend-sca", "validate"])
+@responses.activate
+def test_integrations_mend_sca_add_with_flags():
+ responses.add(responses.POST, os.getenv("CORTEX_BASE_URL") + "/api/v1/mend/sca/configuration", json={}, status=200)
+ cli(["integrations", "mend-sca", "add", "--org-key", "foo", "--user-key", "bar"])
+
+@responses.activate
+def test_integrations_mend_sca_add_file_with_flags_error(tmp_path):
+ f = _dummy_file(tmp_path)
+ result = cli(["integrations", "mend-sca", "add", "-f", str(f), "--org-key", "foo"], return_type=ReturnType.RAW)
+ assert result.exit_code != 0
+
+@responses.activate
+def test_integrations_mend_sca_update_with_flags():
+ responses.add(responses.PUT, os.getenv("CORTEX_BASE_URL") + "/api/v1/mend/sca/configuration", json={}, status=200)
+ cli(["integrations", "mend-sca", "update", "--org-key", "foo"])
+
+@responses.activate
+def test_integrations_mend_sca_update_file_with_flags_error(tmp_path):
+ f = _dummy_file(tmp_path)
+ result = cli(["integrations", "mend-sca", "update", "-f", str(f), "--org-key", "foo"], return_type=ReturnType.RAW)
+ assert result.exit_code != 0
+
@responses.activate
def test_integrations_mend_sca_delete():
responses.add(responses.DELETE, os.getenv("CORTEX_BASE_URL") + "/api/v1/mend/sca/configurations", json={}, status=200)
diff --git a/tests/test_integrations_newrelic.py b/tests/test_integrations_newrelic.py
index 0af6040..0f702d3 100644
--- a/tests/test_integrations_newrelic.py
+++ b/tests/test_integrations_newrelic.py
@@ -86,3 +86,17 @@ def test_integrations_newrelic_validate():
def test_integrations_newrelic_validate_all():
responses.add(responses.POST, os.getenv("CORTEX_BASE_URL") + "/api/v1/newrelic/configuration/validate", json={}, status=200)
cli(["integrations", "newrelic", "validate-all"])
+
+@responses.activate
+def test_integrations_newrelic_add_file_with_flags_error(tmp_path):
+ f = tmp_path / "valid.json"
+ f.write_text('{"alias": "test"}')
+ result = cli(["integrations", "newrelic", "add", "-a", "test", "--account-id", "123", "--personal-key", "NRAK-key", "-f", str(f)], return_type=ReturnType.RAW)
+ assert result.exit_code != 0
+
+@responses.activate
+def test_integrations_newrelic_add_multiple_valid(tmp_path):
+ f = tmp_path / "valid.json"
+ f.write_text('{"configurations": []}')
+ responses.add(responses.POST, os.getenv("CORTEX_BASE_URL") + "/api/v1/newrelic/configurations", json={}, status=200)
+ cli(["integrations", "newrelic", "add-multiple", "-f", str(f)])
diff --git a/tests/test_integrations_okta.py b/tests/test_integrations_okta.py
index 2633233..d6ef0ec 100644
--- a/tests/test_integrations_okta.py
+++ b/tests/test_integrations_okta.py
@@ -29,6 +29,28 @@ def test_integrations_okta_validate():
responses.add(responses.POST, os.getenv("CORTEX_BASE_URL") + "/api/v1/okta/configuration/validate", json={}, status=200)
cli(["integrations", "okta", "validate"])
+@responses.activate
+def test_integrations_okta_add_with_flags():
+ responses.add(responses.POST, os.getenv("CORTEX_BASE_URL") + "/api/v1/okta/configuration", json={}, status=200)
+ cli(["integrations", "okta", "add", "--api-token", "foo", "--domain", "bar"])
+
+@responses.activate
+def test_integrations_okta_add_file_with_flags_error(tmp_path):
+ f = _dummy_file(tmp_path)
+ result = cli(["integrations", "okta", "add", "-f", str(f), "--api-token", "foo"], return_type=ReturnType.RAW)
+ assert result.exit_code != 0
+
+@responses.activate
+def test_integrations_okta_update_with_flags():
+ responses.add(responses.PUT, os.getenv("CORTEX_BASE_URL") + "/api/v1/okta/configuration", json={}, status=200)
+ cli(["integrations", "okta", "update", "--api-token", "foo"])
+
+@responses.activate
+def test_integrations_okta_update_file_with_flags_error(tmp_path):
+ f = _dummy_file(tmp_path)
+ result = cli(["integrations", "okta", "update", "-f", str(f), "--api-token", "foo"], return_type=ReturnType.RAW)
+ assert result.exit_code != 0
+
@responses.activate
def test_integrations_okta_delete():
responses.add(responses.DELETE, os.getenv("CORTEX_BASE_URL") + "/api/v1/okta/configurations", json={}, status=200)
diff --git a/tests/test_integrations_opsgenie.py b/tests/test_integrations_opsgenie.py
index 9165c50..234618e 100644
--- a/tests/test_integrations_opsgenie.py
+++ b/tests/test_integrations_opsgenie.py
@@ -29,6 +29,28 @@ def test_integrations_opsgenie_validate():
responses.add(responses.POST, os.getenv("CORTEX_BASE_URL") + "/api/v1/opsgenie/configuration/validate", json={}, status=200)
cli(["integrations", "opsgenie", "validate"])
+@responses.activate
+def test_integrations_opsgenie_add_with_flags():
+ responses.add(responses.POST, os.getenv("CORTEX_BASE_URL") + "/api/v1/opsgenie/configuration", json={}, status=200)
+ cli(["integrations", "opsgenie", "add", "--api-token", "foo", "--subdomain", "bar"])
+
+@responses.activate
+def test_integrations_opsgenie_add_file_with_flags_error(tmp_path):
+ f = _dummy_file(tmp_path)
+ result = cli(["integrations", "opsgenie", "add", "-f", str(f), "--api-token", "foo"], return_type=ReturnType.RAW)
+ assert result.exit_code != 0
+
+@responses.activate
+def test_integrations_opsgenie_update_with_flags():
+ responses.add(responses.PUT, os.getenv("CORTEX_BASE_URL") + "/api/v1/opsgenie/configuration", json={}, status=200)
+ cli(["integrations", "opsgenie", "update", "--api-token", "foo"])
+
+@responses.activate
+def test_integrations_opsgenie_update_file_with_flags_error(tmp_path):
+ f = _dummy_file(tmp_path)
+ result = cli(["integrations", "opsgenie", "update", "-f", str(f), "--api-token", "foo"], return_type=ReturnType.RAW)
+ assert result.exit_code != 0
+
@responses.activate
def test_integrations_opsgenie_delete():
responses.add(responses.DELETE, os.getenv("CORTEX_BASE_URL") + "/api/v1/opsgenie/configurations", json={}, status=200)
diff --git a/tests/test_integrations_pagerduty.py b/tests/test_integrations_pagerduty.py
index 7c5271d..4a5caf2 100644
--- a/tests/test_integrations_pagerduty.py
+++ b/tests/test_integrations_pagerduty.py
@@ -57,3 +57,16 @@ def test_integrations_circle_ci_validate():
def test_integrations_circle_ci_validate_all():
responses.add(responses.POST, os.getenv("CORTEX_BASE_URL") + "/api/v1/pagerduty/configuration/validate", json={}, status=200)
cli(["integrations", "pagerduty", "validate-all"])
+
+@responses.activate
+def test_integrations_pagerduty_add_file_with_flags_error(tmp_path):
+ f = _dummy_file(tmp_path)
+ result = cli(["integrations", "pagerduty", "add", "-a", "test", "--api-key", "key", "-f", str(f)], return_type=ReturnType.RAW)
+ assert result.exit_code != 0
+
+@responses.activate
+def test_integrations_pagerduty_add_multiple_valid(tmp_path):
+ f = tmp_path / "valid.json"
+ f.write_text('{"configurations": []}')
+ responses.add(responses.PUT, os.getenv("CORTEX_BASE_URL") + "/api/v1/pagerduty/configurations", json={}, status=200)
+ cli(["integrations", "pagerduty", "add-multiple", "-f", str(f)])
diff --git a/tests/test_integrations_prometheus.py b/tests/test_integrations_prometheus.py
index 3061d24..3b6e7e9 100644
--- a/tests/test_integrations_prometheus.py
+++ b/tests/test_integrations_prometheus.py
@@ -57,3 +57,16 @@ def test_integrations_prometheus_validate():
def test_integrations_prometheus_validate_all():
responses.add(responses.POST, os.getenv("CORTEX_BASE_URL") + "/api/v1/prometheus/configuration/validate", json={}, status=200)
cli(["integrations", "prometheus", "validate-all"])
+
+@responses.activate
+def test_integrations_prometheus_add_file_with_flags_error(tmp_path):
+ f = _dummy_file(tmp_path)
+ result = cli(["integrations", "prometheus", "add", "-a", "test", "-h", "host", "--username", "user", "--password", "pass", "-f", str(f)], return_type=ReturnType.RAW)
+ assert result.exit_code != 0
+
+@responses.activate
+def test_integrations_prometheus_add_multiple_valid(tmp_path):
+ f = tmp_path / "valid.json"
+ f.write_text('{"configurations": []}')
+ responses.add(responses.PUT, os.getenv("CORTEX_BASE_URL") + "/api/v1/prometheus/configurations", json={}, status=200)
+ cli(["integrations", "prometheus", "add-multiple", "-f", str(f)])
diff --git a/tests/test_integrations_rollbar.py b/tests/test_integrations_rollbar.py
index 9e06ac6..e392a9a 100644
--- a/tests/test_integrations_rollbar.py
+++ b/tests/test_integrations_rollbar.py
@@ -29,6 +29,28 @@ def test_integrations_rollbar_validate():
responses.add(responses.POST, os.getenv("CORTEX_BASE_URL") + "/api/v1/rollbar/configuration/validate", json={}, status=200)
cli(["integrations", "rollbar", "validate"])
+@responses.activate
+def test_integrations_rollbar_add_with_flags():
+ responses.add(responses.POST, os.getenv("CORTEX_BASE_URL") + "/api/v1/rollbar/configuration", json={}, status=200)
+ cli(["integrations", "rollbar", "add", "--access-token", "foo", "--organization-slug", "bar"])
+
+@responses.activate
+def test_integrations_rollbar_add_file_with_flags_error(tmp_path):
+ f = _dummy_file(tmp_path)
+ result = cli(["integrations", "rollbar", "add", "-f", str(f), "--access-token", "foo"], return_type=ReturnType.RAW)
+ assert result.exit_code != 0
+
+@responses.activate
+def test_integrations_rollbar_update_with_flags():
+ responses.add(responses.PUT, os.getenv("CORTEX_BASE_URL") + "/api/v1/rollbar/configuration", json={}, status=200)
+ cli(["integrations", "rollbar", "update", "--access-token", "foo"])
+
+@responses.activate
+def test_integrations_rollbar_update_file_with_flags_error(tmp_path):
+ f = _dummy_file(tmp_path)
+ result = cli(["integrations", "rollbar", "update", "-f", str(f), "--access-token", "foo"], return_type=ReturnType.RAW)
+ assert result.exit_code != 0
+
@responses.activate
def test_integrations_rollbar_delete():
responses.add(responses.DELETE, os.getenv("CORTEX_BASE_URL") + "/api/v1/rollbar/configurations", json={}, status=200)
diff --git a/tests/test_integrations_rootly.py b/tests/test_integrations_rootly.py
index 5cf8f5a..cb870a1 100644
--- a/tests/test_integrations_rootly.py
+++ b/tests/test_integrations_rootly.py
@@ -57,3 +57,16 @@ def test_integrations_rootly_validate():
def test_integrations_rootly_validate_all():
responses.add(responses.POST, os.getenv("CORTEX_BASE_URL") + "/api/v1/rootly/configuration/validate", json={}, status=200)
cli(["integrations", "rootly", "validate-all"])
+
+@responses.activate
+def test_integrations_rootly_add_file_with_flags_error(tmp_path):
+ f = _dummy_file(tmp_path)
+ result = cli(["integrations", "rootly", "add", "-a", "test", "--api-key", "key", "-f", str(f)], return_type=ReturnType.RAW)
+ assert result.exit_code != 0
+
+@responses.activate
+def test_integrations_rootly_add_multiple_valid(tmp_path):
+ f = tmp_path / "valid.json"
+ f.write_text('{"configurations": []}')
+ responses.add(responses.PUT, os.getenv("CORTEX_BASE_URL") + "/api/v1/rootly/configurations", json={}, status=200)
+ cli(["integrations", "rootly", "add-multiple", "-f", str(f)])
diff --git a/tests/test_integrations_semgrep.py b/tests/test_integrations_semgrep.py
index bd49158..723091f 100644
--- a/tests/test_integrations_semgrep.py
+++ b/tests/test_integrations_semgrep.py
@@ -57,3 +57,16 @@ def test_integrations_semgrep_validate():
def test_integrations_semgrep_validate_all():
responses.add(responses.POST, os.getenv("CORTEX_BASE_URL") + "/api/v1/semgrep/configuration/validate", json={}, status=200)
cli(["integrations", "semgrep", "validate-all"])
+
+@responses.activate
+def test_integrations_semgrep_add_file_with_flags_error(tmp_path):
+ f = _dummy_file(tmp_path)
+ result = cli(["integrations", "semgrep", "add", "-a", "test", "--api-key", "key", "-f", str(f)], return_type=ReturnType.RAW)
+ assert result.exit_code != 0
+
+@responses.activate
+def test_integrations_semgrep_add_multiple_valid(tmp_path):
+ f = tmp_path / "valid.json"
+ f.write_text('{"configurations": []}')
+ responses.add(responses.PUT, os.getenv("CORTEX_BASE_URL") + "/api/v1/semgrep/configurations", json={}, status=200)
+ cli(["integrations", "semgrep", "add-multiple", "-f", str(f)])
diff --git a/tests/test_integrations_sentry.py b/tests/test_integrations_sentry.py
index d9e0fb1..6591e1f 100644
--- a/tests/test_integrations_sentry.py
+++ b/tests/test_integrations_sentry.py
@@ -29,6 +29,28 @@ def test_integrations_sentry_validate():
responses.add(responses.POST, os.getenv("CORTEX_BASE_URL") + "/api/v1/sentry/configuration/validate", json={}, status=200)
cli(["integrations", "sentry", "validate"])
+@responses.activate
+def test_integrations_sentry_add_with_flags():
+ responses.add(responses.POST, os.getenv("CORTEX_BASE_URL") + "/api/v1/sentry/configuration", json={}, status=200)
+ cli(["integrations", "sentry", "add", "--auth-token", "foo", "--organization-slug", "bar"])
+
+@responses.activate
+def test_integrations_sentry_add_file_with_flags_error(tmp_path):
+ f = _dummy_file(tmp_path)
+ result = cli(["integrations", "sentry", "add", "-f", str(f), "--auth-token", "foo"], return_type=ReturnType.RAW)
+ assert result.exit_code != 0
+
+@responses.activate
+def test_integrations_sentry_update_with_flags():
+ responses.add(responses.PUT, os.getenv("CORTEX_BASE_URL") + "/api/v1/sentry/configuration", json={}, status=200)
+ cli(["integrations", "sentry", "update", "--auth-token", "foo"])
+
+@responses.activate
+def test_integrations_sentry_update_file_with_flags_error(tmp_path):
+ f = _dummy_file(tmp_path)
+ result = cli(["integrations", "sentry", "update", "-f", str(f), "--auth-token", "foo"], return_type=ReturnType.RAW)
+ assert result.exit_code != 0
+
@responses.activate
def test_integrations_sentry_delete():
responses.add(responses.DELETE, os.getenv("CORTEX_BASE_URL") + "/api/v1/sentry/configurations", json={}, status=200)
diff --git a/tests/test_integrations_servicenow.py b/tests/test_integrations_servicenow.py
index bf94756..08d470c 100644
--- a/tests/test_integrations_servicenow.py
+++ b/tests/test_integrations_servicenow.py
@@ -29,6 +29,28 @@ def test_integrations_servicenow_validate():
responses.add(responses.POST, os.getenv("CORTEX_BASE_URL") + "/api/v1/servicenow/configuration/validate", json={}, status=200)
cli(["integrations", "servicenow", "validate"])
+@responses.activate
+def test_integrations_servicenow_add_with_flags():
+ responses.add(responses.POST, os.getenv("CORTEX_BASE_URL") + "/api/v1/servicenow/configuration", json={}, status=200)
+ cli(["integrations", "servicenow", "add", "--instance-name", "foo", "--password", "bar", "--username", "baz"])
+
+@responses.activate
+def test_integrations_servicenow_add_file_with_flags_error(tmp_path):
+ f = _dummy_file(tmp_path)
+ result = cli(["integrations", "servicenow", "add", "-f", str(f), "--instance-name", "foo"], return_type=ReturnType.RAW)
+ assert result.exit_code != 0
+
+@responses.activate
+def test_integrations_servicenow_update_with_flags():
+ responses.add(responses.PUT, os.getenv("CORTEX_BASE_URL") + "/api/v1/servicenow/configuration", json={}, status=200)
+ cli(["integrations", "servicenow", "update", "--instance-name", "foo"])
+
+@responses.activate
+def test_integrations_servicenow_update_file_with_flags_error(tmp_path):
+ f = _dummy_file(tmp_path)
+ result = cli(["integrations", "servicenow", "update", "-f", str(f), "--instance-name", "foo"], return_type=ReturnType.RAW)
+ assert result.exit_code != 0
+
@responses.activate
def test_integrations_servicenow_delete():
responses.add(responses.DELETE, os.getenv("CORTEX_BASE_URL") + "/api/v1/servicenow/configurations", json={}, status=200)
diff --git a/tests/test_integrations_servicenow_cloud_observability.py b/tests/test_integrations_servicenow_cloud_observability.py
index 59f9b1b..3ad899a 100644
--- a/tests/test_integrations_servicenow_cloud_observability.py
+++ b/tests/test_integrations_servicenow_cloud_observability.py
@@ -29,6 +29,28 @@ def test_integrations_servicenow_cloud_observability_validate():
responses.add(responses.POST, os.getenv("CORTEX_BASE_URL") + "/api/v1/lightstep/configuration/validate", json={}, status=200)
cli(["integrations", "servicenow-cloud-observability", "validate"])
+@responses.activate
+def test_integrations_servicenow_cloud_observability_add_with_flags():
+ responses.add(responses.POST, os.getenv("CORTEX_BASE_URL") + "/api/v1/lightstep/configuration", json={}, status=200)
+ cli(["integrations", "servicenow-cloud-observability", "add", "--auth-token", "foo", "--organization-id", "bar", "--project-id", "baz"])
+
+@responses.activate
+def test_integrations_servicenow_cloud_observability_add_file_with_flags_error(tmp_path):
+ f = _dummy_file(tmp_path)
+ result = cli(["integrations", "servicenow-cloud-observability", "add", "-f", str(f), "--auth-token", "foo"], return_type=ReturnType.RAW)
+ assert result.exit_code != 0
+
+@responses.activate
+def test_integrations_servicenow_cloud_observability_update_with_flags():
+ responses.add(responses.PUT, os.getenv("CORTEX_BASE_URL") + "/api/v1/lightstep/configuration", json={}, status=200)
+ cli(["integrations", "servicenow-cloud-observability", "update", "--auth-token", "foo"])
+
+@responses.activate
+def test_integrations_servicenow_cloud_observability_update_file_with_flags_error(tmp_path):
+ f = _dummy_file(tmp_path)
+ result = cli(["integrations", "servicenow-cloud-observability", "update", "-f", str(f), "--auth-token", "foo"], return_type=ReturnType.RAW)
+ assert result.exit_code != 0
+
@responses.activate
def test_integrations_servicenow_cloud_observability_delete():
responses.add(responses.DELETE, os.getenv("CORTEX_BASE_URL") + "/api/v1/lightstep/configurations", json={}, status=200)
diff --git a/tests/test_integrations_snyk.py b/tests/test_integrations_snyk.py
index 9eeff7d..ad8240f 100644
--- a/tests/test_integrations_snyk.py
+++ b/tests/test_integrations_snyk.py
@@ -29,6 +29,28 @@ def test_integrations_snyk_validate():
responses.add(responses.POST, os.getenv("CORTEX_BASE_URL") + "/api/v1/snyk/configuration/validate", json={}, status=200)
cli(["integrations", "snyk", "validate"])
+@responses.activate
+def test_integrations_snyk_add_with_flags():
+ responses.add(responses.POST, os.getenv("CORTEX_BASE_URL") + "/api/v1/snyk/configuration", json={}, status=200)
+ cli(["integrations", "snyk", "add", "--auth-token", "foo", "--region", "USA"])
+
+@responses.activate
+def test_integrations_snyk_add_file_with_flags_error(tmp_path):
+ f = _dummy_file(tmp_path)
+ result = cli(["integrations", "snyk", "add", "-f", str(f), "--auth-token", "foo"], return_type=ReturnType.RAW)
+ assert result.exit_code != 0
+
+@responses.activate
+def test_integrations_snyk_update_with_flags():
+ responses.add(responses.PUT, os.getenv("CORTEX_BASE_URL") + "/api/v1/snyk/configuration", json={}, status=200)
+ cli(["integrations", "snyk", "update", "--auth-token", "foo"])
+
+@responses.activate
+def test_integrations_snyk_update_file_with_flags_error(tmp_path):
+ f = _dummy_file(tmp_path)
+ result = cli(["integrations", "snyk", "update", "-f", str(f), "--auth-token", "foo"], return_type=ReturnType.RAW)
+ assert result.exit_code != 0
+
@responses.activate
def test_integrations_snyk_delete():
responses.add(responses.DELETE, os.getenv("CORTEX_BASE_URL") + "/api/v1/snyk/configurations", json={}, status=200)
diff --git a/tests/test_integrations_sonarqube.py b/tests/test_integrations_sonarqube.py
index 9195582..dafd6d4 100644
--- a/tests/test_integrations_sonarqube.py
+++ b/tests/test_integrations_sonarqube.py
@@ -78,3 +78,17 @@ def test_integrations_sonarqube_validate():
def test_integrations_sonarqube_validate_all():
responses.add(responses.POST, os.getenv("CORTEX_BASE_URL") + "/api/v1/sonarqube/configuration/validate", json={}, status=200)
cli(["integrations", "sonarqube", "validate-all"])
+
+@responses.activate
+def test_integrations_sonarqube_add_file_with_flags_error(tmp_path):
+ f = tmp_path / "valid.json"
+ f.write_text('{"alias": "test"}')
+ result = cli(["integrations", "sonarqube", "add", "-a", "test", "--api-key", "key", "-f", str(f)], return_type=ReturnType.RAW)
+ assert result.exit_code != 0
+
+@responses.activate
+def test_integrations_sonarqube_add_multiple_valid(tmp_path):
+ f = tmp_path / "valid.json"
+ f.write_text('{"configurations": []}')
+ responses.add(responses.PUT, os.getenv("CORTEX_BASE_URL") + "/api/v1/sonarqube/configurations", json={}, status=200)
+ cli(["integrations", "sonarqube", "add-multiple", "-f", str(f)])
diff --git a/tests/test_integrations_splunk_observability_cloud.py b/tests/test_integrations_splunk_observability_cloud.py
index 8260a72..c9972c8 100644
--- a/tests/test_integrations_splunk_observability_cloud.py
+++ b/tests/test_integrations_splunk_observability_cloud.py
@@ -29,6 +29,28 @@ def test_integrations_splunk_observability_cloud_validate():
responses.add(responses.POST, os.getenv("CORTEX_BASE_URL") + "/api/v1/signalfx/configuration/validate", json={}, status=200)
cli(["integrations", "splunk-observability-cloud", "validate"])
+@responses.activate
+def test_integrations_splunk_observability_cloud_add_with_flags():
+ responses.add(responses.POST, os.getenv("CORTEX_BASE_URL") + "/api/v1/signalfx/configuration", json={}, status=200)
+ cli(["integrations", "splunk-observability-cloud", "add", "--access-token", "foo", "--realm", "bar"])
+
+@responses.activate
+def test_integrations_splunk_observability_cloud_add_file_with_flags_error(tmp_path):
+ f = _dummy_file(tmp_path)
+ result = cli(["integrations", "splunk-observability-cloud", "add", "-f", str(f), "--access-token", "foo"], return_type=ReturnType.RAW)
+ assert result.exit_code != 0
+
+@responses.activate
+def test_integrations_splunk_observability_cloud_update_with_flags():
+ responses.add(responses.PUT, os.getenv("CORTEX_BASE_URL") + "/api/v1/signalfx/configuration", json={}, status=200)
+ cli(["integrations", "splunk-observability-cloud", "update", "--access-token", "foo"])
+
+@responses.activate
+def test_integrations_splunk_observability_cloud_update_file_with_flags_error(tmp_path):
+ f = _dummy_file(tmp_path)
+ result = cli(["integrations", "splunk-observability-cloud", "update", "-f", str(f), "--access-token", "foo"], return_type=ReturnType.RAW)
+ assert result.exit_code != 0
+
@responses.activate
def test_integrations_splunk_observability_cloud_delete():
responses.add(responses.DELETE, os.getenv("CORTEX_BASE_URL") + "/api/v1/signalfx/configurations", json={}, status=200)
diff --git a/tests/test_integrations_splunk_on_call.py b/tests/test_integrations_splunk_on_call.py
index 2a421c1..2d6bd86 100644
--- a/tests/test_integrations_splunk_on_call.py
+++ b/tests/test_integrations_splunk_on_call.py
@@ -29,6 +29,28 @@ def test_integrations_splunk_on_call_validate():
responses.add(responses.POST, os.getenv("CORTEX_BASE_URL") + "/api/v1/victorops/configuration/validate", json={}, status=200)
cli(["integrations", "splunk-on-call", "validate"])
+@responses.activate
+def test_integrations_splunk_on_call_add_with_flags():
+ responses.add(responses.POST, os.getenv("CORTEX_BASE_URL") + "/api/v1/victorops/configuration", json={}, status=200)
+ cli(["integrations", "splunk-on-call", "add", "--api-id", "foo", "--api-key", "bar", "--organization", "baz"])
+
+@responses.activate
+def test_integrations_splunk_on_call_add_file_with_flags_error(tmp_path):
+ f = _dummy_file(tmp_path)
+ result = cli(["integrations", "splunk-on-call", "add", "-f", str(f), "--api-id", "foo"], return_type=ReturnType.RAW)
+ assert result.exit_code != 0
+
+@responses.activate
+def test_integrations_splunk_on_call_update_with_flags():
+ responses.add(responses.PUT, os.getenv("CORTEX_BASE_URL") + "/api/v1/victorops/configuration", json={}, status=200)
+ cli(["integrations", "splunk-on-call", "update", "--api-id", "foo"])
+
+@responses.activate
+def test_integrations_splunk_on_call_update_file_with_flags_error(tmp_path):
+ f = _dummy_file(tmp_path)
+ result = cli(["integrations", "splunk-on-call", "update", "-f", str(f), "--api-id", "foo"], return_type=ReturnType.RAW)
+ assert result.exit_code != 0
+
@responses.activate
def test_integrations_splunk_on_call_delete():
responses.add(responses.DELETE, os.getenv("CORTEX_BASE_URL") + "/api/v1/victorops/configurations", json={}, status=200)
diff --git a/tests/test_integrations_sumologic.py b/tests/test_integrations_sumologic.py
index 2f7db9c..7ef3f4a 100644
--- a/tests/test_integrations_sumologic.py
+++ b/tests/test_integrations_sumologic.py
@@ -29,6 +29,28 @@ def test_integrations_sumologic_validate():
responses.add(responses.POST, os.getenv("CORTEX_BASE_URL") + "/api/v1/sumologic/configuration/validate", json={}, status=200)
cli(["integrations", "sumo-logic", "validate"])
+@responses.activate
+def test_integrations_sumologic_add_with_flags():
+ responses.add(responses.POST, os.getenv("CORTEX_BASE_URL") + "/api/v1/sumologic/configuration", json={}, status=200)
+ cli(["integrations", "sumo-logic", "add", "--access-id", "foo", "--access-key", "bar", "--deployment", "baz"])
+
+@responses.activate
+def test_integrations_sumologic_add_file_with_flags_error(tmp_path):
+ f = _dummy_file(tmp_path)
+ result = cli(["integrations", "sumo-logic", "add", "-f", str(f), "--access-id", "foo"], return_type=ReturnType.RAW)
+ assert result.exit_code != 0
+
+@responses.activate
+def test_integrations_sumologic_update_with_flags():
+ responses.add(responses.PUT, os.getenv("CORTEX_BASE_URL") + "/api/v1/sumologic/configuration", json={}, status=200)
+ cli(["integrations", "sumo-logic", "update", "--access-id", "foo"])
+
+@responses.activate
+def test_integrations_sumologic_update_file_with_flags_error(tmp_path):
+ f = _dummy_file(tmp_path)
+ result = cli(["integrations", "sumo-logic", "update", "-f", str(f), "--access-id", "foo"], return_type=ReturnType.RAW)
+ assert result.exit_code != 0
+
@responses.activate
def test_integrations_sumologic_delete():
responses.add(responses.DELETE, os.getenv("CORTEX_BASE_URL") + "/api/v1/sumologic/configurations", json={}, status=200)
diff --git a/tests/test_integrations_veracode.py b/tests/test_integrations_veracode.py
index 8b5a731..b1379c4 100644
--- a/tests/test_integrations_veracode.py
+++ b/tests/test_integrations_veracode.py
@@ -29,6 +29,28 @@ def test_integrations_veracode_validate():
responses.add(responses.POST, os.getenv("CORTEX_BASE_URL") + "/api/v1/veracode/configuration/validate", json={}, status=200)
cli(["integrations", "veracode", "validate"])
+@responses.activate
+def test_integrations_veracode_add_with_flags():
+ responses.add(responses.POST, os.getenv("CORTEX_BASE_URL") + "/api/v1/veracode/configuration", json={}, status=200)
+ cli(["integrations", "veracode", "add", "--api-key", "foo", "--key-id", "bar", "--region", "baz"])
+
+@responses.activate
+def test_integrations_veracode_add_file_with_flags_error(tmp_path):
+ f = _dummy_file(tmp_path)
+ result = cli(["integrations", "veracode", "add", "-f", str(f), "--api-key", "foo"], return_type=ReturnType.RAW)
+ assert result.exit_code != 0
+
+@responses.activate
+def test_integrations_veracode_update_with_flags():
+ responses.add(responses.PUT, os.getenv("CORTEX_BASE_URL") + "/api/v1/veracode/configuration", json={}, status=200)
+ cli(["integrations", "veracode", "update", "--api-key", "foo"])
+
+@responses.activate
+def test_integrations_veracode_update_file_with_flags_error(tmp_path):
+ f = _dummy_file(tmp_path)
+ result = cli(["integrations", "veracode", "update", "-f", str(f), "--api-key", "foo"], return_type=ReturnType.RAW)
+ assert result.exit_code != 0
+
@responses.activate
def test_integrations_veracode_delete():
responses.add(responses.DELETE, os.getenv("CORTEX_BASE_URL") + "/api/v1/veracode/configurations", json={}, status=200)
diff --git a/tests/test_integrations_wiz.py b/tests/test_integrations_wiz.py
index 886568b..25bb4f1 100644
--- a/tests/test_integrations_wiz.py
+++ b/tests/test_integrations_wiz.py
@@ -29,6 +29,28 @@ def test_integrations_wiz_validate():
responses.add(responses.POST, os.getenv("CORTEX_BASE_URL") + "/api/v1/wiz/configuration/validate", json={}, status=200)
cli(["integrations", "wiz", "validate"])
+@responses.activate
+def test_integrations_wiz_add_with_flags():
+ responses.add(responses.POST, os.getenv("CORTEX_BASE_URL") + "/api/v1/wiz/configuration", json={}, status=200)
+ cli(["integrations", "wiz", "add", "--client-id", "foo", "--client-secret", "bar", "--data-center", "baz", "--identity-provider", "qux"])
+
+@responses.activate
+def test_integrations_wiz_add_file_with_flags_error(tmp_path):
+ f = _dummy_file(tmp_path)
+ result = cli(["integrations", "wiz", "add", "-f", str(f), "--client-id", "foo"], return_type=ReturnType.RAW)
+ assert result.exit_code != 0
+
+@responses.activate
+def test_integrations_wiz_update_with_flags():
+ responses.add(responses.PUT, os.getenv("CORTEX_BASE_URL") + "/api/v1/wiz/configuration", json={}, status=200)
+ cli(["integrations", "wiz", "update", "--client-id", "foo"])
+
+@responses.activate
+def test_integrations_wiz_update_file_with_flags_error(tmp_path):
+ f = _dummy_file(tmp_path)
+ result = cli(["integrations", "wiz", "update", "-f", str(f), "--client-id", "foo"], return_type=ReturnType.RAW)
+ assert result.exit_code != 0
+
@responses.activate
def test_integrations_wiz_delete():
responses.add(responses.DELETE, os.getenv("CORTEX_BASE_URL") + "/api/v1/wiz/configurations", json={}, status=200)
diff --git a/tests/test_integrations_workday.py b/tests/test_integrations_workday.py
index c9957ee..5025c79 100644
--- a/tests/test_integrations_workday.py
+++ b/tests/test_integrations_workday.py
@@ -29,6 +29,28 @@ def test_integrations_workday_validate():
responses.add(responses.POST, os.getenv("CORTEX_BASE_URL") + "/api/v1/workday/configuration/validate", json={}, status=200)
cli(["integrations", "workday", "validate"])
+@responses.activate
+def test_integrations_workday_add_with_flags():
+ responses.add(responses.POST, os.getenv("CORTEX_BASE_URL") + "/api/v1/workday/configuration", json={}, status=200)
+ cli(["integrations", "workday", "add", "--username", "foo", "--password", "bar", "--ownership-report-url", "baz"])
+
+@responses.activate
+def test_integrations_workday_add_file_with_flags_error(tmp_path):
+ f = _dummy_file(tmp_path)
+ result = cli(["integrations", "workday", "add", "-f", str(f), "--username", "foo"], return_type=ReturnType.RAW)
+ assert result.exit_code != 0
+
+@responses.activate
+def test_integrations_workday_update_with_flags():
+ responses.add(responses.PUT, os.getenv("CORTEX_BASE_URL") + "/api/v1/workday/configuration", json={}, status=200)
+ cli(["integrations", "workday", "update", "--username", "foo"])
+
+@responses.activate
+def test_integrations_workday_update_file_with_flags_error(tmp_path):
+ f = _dummy_file(tmp_path)
+ result = cli(["integrations", "workday", "update", "-f", str(f), "--username", "foo"], return_type=ReturnType.RAW)
+ assert result.exit_code != 0
+
@responses.activate
def test_integrations_workday_delete():
responses.add(responses.DELETE, os.getenv("CORTEX_BASE_URL") + "/api/v1/workday/configurations", json={}, status=200)
diff --git a/tests/test_integrations_xmatters.py b/tests/test_integrations_xmatters.py
index 12e23a5..ceee629 100644
--- a/tests/test_integrations_xmatters.py
+++ b/tests/test_integrations_xmatters.py
@@ -29,6 +29,28 @@ def test_integrations_xmatters_validate():
responses.add(responses.POST, os.getenv("CORTEX_BASE_URL") + "/api/v1/xmatters/configuration/validate", json={}, status=200)
cli(["integrations", "xmatters", "validate"])
+@responses.activate
+def test_integrations_xmatters_add_with_flags():
+ responses.add(responses.POST, os.getenv("CORTEX_BASE_URL") + "/api/v1/xmatters/configuration", json={}, status=200)
+ cli(["integrations", "xmatters", "add", "--organization-slug", "foo", "--password", "bar", "--username", "baz"])
+
+@responses.activate
+def test_integrations_xmatters_add_file_with_flags_error(tmp_path):
+ f = _dummy_file(tmp_path)
+ result = cli(["integrations", "xmatters", "add", "-f", str(f), "--organization-slug", "foo"], return_type=ReturnType.RAW)
+ assert result.exit_code != 0
+
+@responses.activate
+def test_integrations_xmatters_update_with_flags():
+ responses.add(responses.PUT, os.getenv("CORTEX_BASE_URL") + "/api/v1/xmatters/configuration", json={}, status=200)
+ cli(["integrations", "xmatters", "update", "--organization-slug", "foo"])
+
+@responses.activate
+def test_integrations_xmatters_update_file_with_flags_error(tmp_path):
+ f = _dummy_file(tmp_path)
+ result = cli(["integrations", "xmatters", "update", "-f", str(f), "--organization-slug", "foo"], return_type=ReturnType.RAW)
+ assert result.exit_code != 0
+
@responses.activate
def test_integrations_xmatters_delete():
responses.add(responses.DELETE, os.getenv("CORTEX_BASE_URL") + "/api/v1/xmatters/configurations", json={}, status=200)
diff --git a/tests/test_scim.py b/tests/test_scim.py
index ed5bda7..354cba6 100644
--- a/tests/test_scim.py
+++ b/tests/test_scim.py
@@ -2,7 +2,7 @@
from urllib.error import HTTPError
import pytest
-@pytest.mark.skip(reason="Disabled until CET-23082 is resolved.")
+#@pytest.mark.skip(reason="Disabled until CET-23082 is resolved.")
def test():
response = cli(["scim", "list"], ReturnType.STDOUT)
diff --git a/tests/test_secrets.py b/tests/test_secrets.py
index 6145c21..18fd56f 100644
--- a/tests/test_secrets.py
+++ b/tests/test_secrets.py
@@ -34,6 +34,13 @@ def test():
response = cli(["secrets", "get", "-t", "cli_test_secret"])
assert response['name'] == 'Updated CLI Test Secret', "Should have updated name"
+ # Verify table output with default columns
+ cli(["secrets", "list", "--table"], return_type=ReturnType.STDOUT)
+
+ # Verify list with explicit page
+ response = cli(["secrets", "list", "-p", "0"])
+ assert 'secrets' in response, "Should return secrets with explicit page"
+
# Delete the secret
cli(["secrets", "delete", "-t", "cli_test_secret"])
diff --git a/tests/test_utils.py b/tests/test_utils.py
new file mode 100644
index 0000000..0becd9d
--- /dev/null
+++ b/tests/test_utils.py
@@ -0,0 +1,224 @@
+import pytest
+import typer
+from unittest.mock import MagicMock
+from cortexapps_cli.utils import (
+ guess_data_key,
+ get_value_at_path,
+ matches_filters,
+ humanize_value,
+ print_output,
+ print_output_with_context,
+)
+
+
+# Task 1: guess_data_key edge cases (Lines 25, 31, 34, 38)
+
+def test_guess_data_key_list_returns_empty_string():
+ assert guess_data_key([1, 2, 3]) == ''
+
+
+def test_guess_data_key_dict_no_list_raises():
+ with pytest.raises(ValueError, match="does not contain a list"):
+ guess_data_key({"key": "value"})
+
+
+def test_guess_data_key_dict_multiple_lists_raises():
+ with pytest.raises(ValueError, match="contains multiple lists"):
+ guess_data_key({"a": [1], "b": [2]})
+
+
+def test_guess_data_key_non_list_non_dict_raises():
+ with pytest.raises(ValueError, match="is not a list or dict"):
+ guess_data_key("a string")
+
+
+# Task 2: get_value_at_path edge cases (Lines 58-62, 64-65)
+
+def test_get_value_at_path_list_index():
+ data = {"items": [{"name": "first"}, {"name": "second"}]}
+ assert get_value_at_path(data, "items.1.name") == "second"
+
+
+def test_get_value_at_path_non_dict_non_list_returns_none():
+ data = {"key": 42}
+ assert get_value_at_path(data, "key.nested") is None
+
+
+def test_get_value_at_path_invalid_list_index_returns_none():
+ data = {"items": [1, 2]}
+ assert get_value_at_path(data, "items.abc") is None
+
+
+def test_get_value_at_path_index_out_of_range_returns_none():
+ data = {"items": [1]}
+ assert get_value_at_path(data, "items.99") is None
+
+
+# Task 3: matches_filters and humanize_value (Lines 80-87, 102, 104)
+
+def test_matches_filters_matching():
+ data = {"name": "foo", "status": "active"}
+ assert matches_filters(data, ["name=foo"]) is True
+
+
+def test_matches_filters_no_match():
+ data = {"name": "foo"}
+ assert matches_filters(data, ["name=bar"]) is False
+
+
+def test_matches_filters_missing_key():
+ data = {"name": "foo"}
+ assert matches_filters(data, ["missing=.*"]) is False
+
+
+def test_matches_filters_multiple_all_match():
+ data = {"name": "foo", "status": "active"}
+ assert matches_filters(data, ["name=foo", "status=active"]) is True
+
+
+def test_matches_filters_multiple_one_fails():
+ data = {"name": "foo", "status": "active"}
+ assert matches_filters(data, ["name=foo", "status=inactive"]) is False
+
+
+def test_humanize_value_list():
+ assert humanize_value(["a", "b", "c"]) == "a, b, c"
+
+
+def test_humanize_value_dict():
+ result = humanize_value({"key": "val"})
+ assert '"key": "val"' in result
+
+
+def test_humanize_value_none():
+ assert humanize_value(None) == ""
+
+
+def test_humanize_value_string():
+ assert humanize_value("hello") == "hello"
+
+
+# Task 4: print_output table/csv/sort/filter paths
+
+def test_print_output_none_format_defaults_to_json(capsys):
+ print_output({"key": "val"}, output_format=None)
+ out = capsys.readouterr().out
+ assert "key" in out
+
+
+def test_print_output_invalid_format_raises():
+ with pytest.raises(ValueError, match="Invalid output format"):
+ print_output({}, output_format="xml")
+
+
+def test_print_output_json_with_columns_raises():
+ with pytest.raises(typer.BadParameter, match="Columns can only be specified"):
+ print_output({"items": [{"a": 1}]}, columns=["a"], output_format="json")
+
+
+def test_print_output_json_with_filters_raises():
+ with pytest.raises(typer.BadParameter, match="Filters can only be specified"):
+ print_output({"items": [{"a": 1}]}, filters=["a=1"], output_format="json")
+
+
+def test_print_output_table_no_columns_raises():
+ with pytest.raises(typer.BadParameter, match="Columns must be specified"):
+ print_output({"items": [{"a": 1}]}, output_format="table")
+
+
+def test_print_output_table_shorthand_column(capsys):
+ data = {"items": [{"name": "foo"}, {"name": "bar"}]}
+ print_output(data, columns=["name"], output_format="table")
+ out = capsys.readouterr().out
+ assert "foo" in out
+ assert "bar" in out
+
+
+def test_print_output_table_invalid_column_format_raises():
+ data = {"items": [{"a": 1}]}
+ with pytest.raises(typer.BadParameter, match="Columns must be in the format"):
+ print_output(data, columns=["!!!invalid"], output_format="table")
+
+
+def test_print_output_table_with_filters(capsys):
+ data = {"items": [{"name": "foo", "status": "active"}, {"name": "bar", "status": "inactive"}]}
+ print_output(data, columns=["Name=name"], filters=["status=active"], output_format="table")
+ out = capsys.readouterr().out
+ assert "foo" in out
+ assert "bar" not in out
+
+
+def test_print_output_table_invalid_filter_raises():
+ data = {"items": [{"a": 1}]}
+ with pytest.raises(typer.BadParameter, match="Filters must be in the format"):
+ print_output(data, columns=["a"], filters=["!!!"], output_format="table")
+
+
+def test_print_output_table_sort_asc(capsys):
+ data = {"items": [{"name": "b"}, {"name": "a"}]}
+ print_output(data, columns=["name"], sort=["name:asc"], output_format="table")
+ out = capsys.readouterr().out
+ assert out.index("a") < out.index("b")
+
+
+def test_print_output_table_sort_desc(capsys):
+ data = {"items": [{"name": "zebra"}, {"name": "apple"}]}
+ print_output(data, columns=["name"], sort=["name:desc"], output_format="table")
+ out = capsys.readouterr().out
+ assert out.index("zebra") < out.index("apple")
+
+
+def test_print_output_table_invalid_sort_raises():
+ data = {"items": [{"a": 1}]}
+ with pytest.raises(typer.BadParameter, match="Sort must be in the format"):
+ print_output(data, columns=["a"], sort=["bad_sort"], output_format="table")
+
+
+def test_print_output_csv(capsys):
+ data = {"items": [{"name": "foo"}, {"name": "bar"}]}
+ print_output(data, columns=["Name=name"], output_format="csv")
+ out = capsys.readouterr().out
+ lines = out.strip().split("\n")
+ assert lines[0] == "Name"
+ assert lines[1] == "foo"
+ assert lines[2] == "bar"
+
+
+def test_print_output_csv_no_headers(capsys):
+ data = {"items": [{"name": "foo"}]}
+ print_output(data, columns=["Name=name"], output_format="csv", no_headers=True)
+ out = capsys.readouterr().out
+ lines = out.strip().split("\n")
+ assert lines[0] == "foo"
+ assert "Name" not in out
+
+
+# Task 5: print_output_with_context (Lines 195, 199)
+
+def test_print_output_with_context_table_and_csv_raises():
+ ctx = MagicMock()
+ ctx.params = {
+ "columns": ["name"],
+ "filters": None,
+ "sort": None,
+ "table_output": True,
+ "csv_output": True,
+ "no_headers": False,
+ }
+ with pytest.raises(typer.BadParameter, match="Only one of --table and --csv"):
+ print_output_with_context(ctx, {"items": [{"name": "foo"}]})
+
+
+def test_print_output_with_context_csv(capsys):
+ ctx = MagicMock()
+ ctx.params = {
+ "columns": ["Name=name"],
+ "filters": None,
+ "sort": None,
+ "table_output": False,
+ "csv_output": True,
+ "no_headers": False,
+ }
+ print_output_with_context(ctx, {"items": [{"name": "foo"}]})
+ out = capsys.readouterr().out
+ assert "foo" in out