Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/CODEOWNERS
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@
# the repo. Unless a later match takes precedence,
# @global-owner1 and @global-owner2 will be requested for
# review when someone opens a pull request.
* @jeff-schnitter
* @jeff-schnitter @Cantaley
9 changes: 0 additions & 9 deletions DEVELOPER.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,8 @@ Available recipes:
test testname # Run a single test, ie: just test tests/test_catalog.py
test-all # Run all tests
test-import # Run import test, a pre-requisite for any tests that rely on test data.
test-parallel # Run tests that can run in parallel
test-serial # Run tests that have to run sequentially
```

## Sequential and parallel tests
Most tests can run in a parallel but a handful have dependencies that can impact other tests,
for example changing the scope of CORTEX_API_KEY, so they have to run sequentially.

The `test-all` target runs all the tests that can be run in parallel, followed by those that
have to run sequentially.

# Commit messages
The CLI uses [git-changelog](https://pypi.org/project/git-changelog/) to dynamically generate
the [HISTORY.md](./HISTORY.md) file.
Expand Down
8 changes: 8 additions & 0 deletions HISTORY.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,14 @@ 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).

<!-- insertion marker -->
## [1.0.5](https://github.com/cortexapps/cli/releases/tag/1.0.5) - 2025-08-25

<small>[Compare with 1.0.4](https://github.com/cortexapps/cli/compare/1.0.4...1.0.5)</small>

### Fixed

- fix: correct end endpoint for adding multiple configurations ([8e325bb](https://github.com/cortexapps/cli/commit/8e325bbfd71a38f9d6ac4439276ad7eef8e34fff) by Jeff Schnitter).

## [1.0.4](https://github.com/cortexapps/cli/releases/tag/1.0.4) - 2025-08-01

<small>[Compare with 1.0.3](https://github.com/cortexapps/cli/compare/1.0.3...1.0.4)</small>
Expand Down
15 changes: 4 additions & 11 deletions Justfile
Original file line number Diff line number Diff line change
Expand Up @@ -12,24 +12,17 @@ _setup:
@if [ -f .coverage ]; then rm .coverage; fi

# Run all tests
test-all: _setup test-parallel test-serial

# Run tests that can run in parallel
test-parallel: test-import
{{pytest}} -n auto -m "not setup and not serial" --html=report-parallel.html --self-contained-html --cov=cortexapps_cli --cov-append --cov-report term-missing tests
test-all: _setup test-import
{{pytest}} -n auto -m "not setup" --html=report.html --self-contained-html --cov=cortexapps_cli --cov-append --cov-report term-missing tests

# Run all tests serially - helpful to see if any tests seem to be hanging
_test-all-individual: test-import
{{pytest}} --html=report-all-invidual.html --self-contained-html --cov=cortexapps_cli --cov-append --cov-report term-missing tests

# Run tests that have to run sequentially
test-serial: test-import
{{pytest}} -n auto -m "serial" --html=report-serial.html --self-contained-html --cov=cortexapps_cli --cov-append --cov-report term-missing tests

# Run import test, a pre-requisite for any tests that rely on test data.
test-import:
{{pytest}} tests/test_import.py --cov=cortexapps_cli --cov-report=
{{pytest}} tests/test_import.py --cov=cortexapps_cli --cov-report=

# Run a single test, ie: just test tests/test_catalog.py
test testname:
{{pytest}} {{testname}}
{{pytest}} -n auto {{testname}}
17 changes: 9 additions & 8 deletions cortexapps_cli/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@
def global_callback(
ctx: typer.Context,
api_key: str = typer.Option(None, "--api-key", "-k", help="API key", envvar="CORTEX_API_KEY"),
url: str = typer.Option("https://api.getcortexapp.com", "--url", "-u", help="Base URL for the API", envvar="CORTEX_BASE_URL"),
url: str = typer.Option(None, "--url", "-u", help="Base URL for the API", envvar="CORTEX_BASE_URL"),
config_file: str = typer.Option(os.path.join(os.path.expanduser('~'), '.cortex', 'config'), "--config", "-c", help="Config file path", envvar="CORTEX_CONFIG"),
tenant: str = typer.Option("default", "--tenant", "-t", help="Tenant alias", envvar="CORTEX_TENANT_ALIAS"),
log_level: Annotated[str, typer.Option("--log-level", "-l", help="Set the logging level")] = "INFO"
Expand Down Expand Up @@ -109,16 +109,17 @@ def global_callback(
else:
# config file found
# if api_key is provided, use that in preference to the config file
config = configparser.ConfigParser()
config.read(config_file)
if tenant not in config:
raise typer.BadParameter(f"Tenant {tenant} not found in config file")
if not api_key:
config = configparser.ConfigParser()
config.read(config_file)
if tenant not in config:
raise typer.BadParameter(f"Tenant {tenant} not found in config file")
api_key = config[tenant]["api_key"]
if url not in config[tenant]:
url = url
if not url:
if 'base_url' in config[tenant].keys():
url = config[tenant]['base_url']
else:
url = config[tenant]["base_url"]
url = "https://api.getcortexapp.com"

# strip any quotes or spaces from the api_key and url
api_key = api_key.strip('"\' ')
Expand Down
4 changes: 2 additions & 2 deletions tests/test_catalog_archive_entity.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from tests.helpers.utils import *

def test():
cli(["catalog", "archive", "-t", "archive-entity"])
cli(["catalog", "archive", "-t", "cli-test-archive-entity"])

response = cli(["catalog", "details", "-t", "archive-entity"])
response = cli(["catalog", "details", "-t", "cli-test-archive-entity"])
assert response['isArchived'] == True, "isArchived attribute should be true"
58 changes: 37 additions & 21 deletions tests/test_config_file.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,27 +3,12 @@
Tests for the cortex CLI config file
"""

# These tests are all marked to run in serial order because they make modifications to the
# cortex config file and/or CORTEX_API_KEY value and would potentially impact other tests
# that are running in parallel (with poetry run pytest -n auto), so they are run separately.

# Additionally, order is VERY IMPORTANT in this file because of the way CORTEX_API key is
# deleted, set to invalid values, etc. Moving test order could impact the overall success
# of pytest. Tread carefully here.

import io
import os
import pytest
import sys
from string import Template

# Requires user input, so use monkeypatch to set it.
@pytest.fixture(scope="session")
def delete_cortex_api_key():
if "CORTEX_API_KEY" in os.environ:
del os.environ['CORTEX_API_KEY']

@pytest.mark.serial
def test_config_file_api_key_quotes(tmp_path):
cortex_api_key = os.getenv('CORTEX_API_KEY')
f = tmp_path / "cortex_config_api_key_quotes"
Expand All @@ -35,22 +20,53 @@ def test_config_file_api_key_quotes(tmp_path):
f.write_text(content)
cli(["-c", str(f), "entity-types", "list"])

@pytest.mark.serial
def test_config_file_create(monkeypatch, tmp_path):
monkeypatch.setattr('sys.stdin', io.StringIO('y'))
f = tmp_path / "test-config.txt"
response = cli(["-c", str(f), "-k", os.getenv('CORTEX_API_KEY'), "scorecards", "list"])
assert any(scorecard['tag'] == 'cli-test-scorecard' for scorecard in response['scorecards']), "Should find scorecard with tag cli-test-scorecard"

@pytest.mark.serial
def test_config_file_bad_api_key(monkeypatch, tmp_path, delete_cortex_api_key):
def test_config_file_bad_api_key(monkeypatch, tmp_path):
monkeypatch.delenv("CORTEX_API_KEY")
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"

@pytest.mark.serial
def test_environment_variable_invalid_key():
os.environ["CORTEX_API_KEY"] = "invalidKey"
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"

def test_config_file_bad_url(monkeypatch, tmp_path):
monkeypatch.delenv("CORTEX_BASE_URL")
cortex_api_key = os.getenv('CORTEX_API_KEY')
f = tmp_path / "cortex_config_api_key_quotes"
template = Template("""
[default]
api_key = "${cortex_api_key}"

[mySection]
api_key = "${cortex_api_key}"
base_url = https://bogus.url
""")
content = template.substitute(cortex_api_key=cortex_api_key)
f.write_text(content)
response = cli(["-c", str(f), "-l", "DEBUG", "-t", "mySection", "entity-types", "list"], return_type=ReturnType.RAW)
assert "Max retries exceeded with url" in str(response), "should get max retries error"

def test_config_file_base_url_env_var(monkeypatch, tmp_path):
cortex_api_key = os.getenv('CORTEX_API_KEY')
f = tmp_path / "cortex_config_api_key_quotes"
template = Template("""
[default]
api_key = "${cortex_api_key}"

[mySection]
api_key = "${cortex_api_key}"
base_url = https://bogus.url
""")
content = template.substitute(cortex_api_key=cortex_api_key)
f.write_text(content)
monkeypatch.setenv("CORTEX_BASE_URL", "https://api.getcortexapp.com")
cli(["-c", str(f), "-t", "mySection", "entity-types", "list"])