From df88c86ddd7a0e9318a3a04c2c5369f2f2ee9408 Mon Sep 17 00:00:00 2001 From: Nanook Claw Date: Mon, 15 Jun 2026 20:31:45 +0000 Subject: [PATCH] fix: allow CLI help without config --- .../connectors/connectors_cli.py | 27 ++++++++---- .../tests/test_connectors_cli.py | 42 ++++++++++++++++++- 2 files changed, 61 insertions(+), 8 deletions(-) diff --git a/app/connectors_service/connectors/connectors_cli.py b/app/connectors_service/connectors/connectors_cli.py index ffccc9aba..03b37cafc 100644 --- a/app/connectors_service/connectors/connectors_cli.py +++ b/app/connectors_service/connectors/connectors_cli.py @@ -45,6 +45,25 @@ def load_config(ctx, config): raise FileNotFoundError(msg) +class LazyConfig: + def __init__(self, ctx, config): + self.ctx = ctx + self.config = config + self.value = None + + def __getitem__(self, key): + if self.value is None: + try: + self.value = load_config(self.ctx, self.config) + except FileNotFoundError as e: + click.echo( + f"{e} Run `connectors login` first, or make sure that the config is either present at the default location ({CONFIG_FILE_PATH}) or it's passed via the '-c' or '--config' option." + ) + self.ctx.exit(1) + + return self.value[key] + + # Main group @click.group( invoke_without_command=True, @@ -60,13 +79,7 @@ def cli(ctx, config): return ctx.ensure_object(dict) - try: - ctx.obj["config"] = load_config(ctx, config) - except FileNotFoundError as e: - click.echo( - f"{e} Make sure that the config is either present at the default location ({CONFIG_FILE_PATH}) or it's passed via the '-c' or '--config' option." - ) - ctx.exit(1) + ctx.obj["config"] = LazyConfig(ctx, config) @click.command(help="Authenticate Connectors CLI with an Elasticsearch instance") diff --git a/app/connectors_service/tests/test_connectors_cli.py b/app/connectors_service/tests/test_connectors_cli.py index 7f7de65bc..6fc266047 100644 --- a/app/connectors_service/tests/test_connectors_cli.py +++ b/app/connectors_service/tests/test_connectors_cli.py @@ -22,12 +22,21 @@ @pytest.fixture(autouse=True) -def mock_cli_config(): +def mock_cli_config(request): + if "use_real_cli_config" in request.fixturenames: + yield + return + with patch("connectors.connectors_cli.load_config") as mock: mock.return_value = {"elasticsearch": {"host": "http://localhost:9211/"}} yield mock +@pytest.fixture +def use_real_cli_config(): + pass + + @pytest.fixture(autouse=True) def mock_connector_es_client(): with patch("connectors.cli.connector.CLIClient") as mock: @@ -655,6 +664,37 @@ def test_job_help_page_without_subcommands(): assert "Commands:" in result.output +def test_job_help_page_without_config_file(tmp_path, use_real_cli_config): + runner = CliRunner() + with runner.isolated_filesystem(temp_dir=tmp_path): + result = runner.invoke(cli, ["job", "--help"]) + + assert result.exit_code == 0 + assert "Usage:" in result.output + assert "Commands:" in result.output + assert f"{CONFIG_FILE_PATH} was not found" not in result.output + + +def test_job_subcommand_help_page_without_config_file(tmp_path, use_real_cli_config): + runner = CliRunner() + with runner.isolated_filesystem(temp_dir=tmp_path): + result = runner.invoke(cli, ["job", "list", "--help"]) + + assert result.exit_code == 0 + assert "Usage:" in result.output + assert f"{CONFIG_FILE_PATH} was not found" not in result.output + + +def test_command_without_config_file_suggests_login(tmp_path, use_real_cli_config): + runner = CliRunner() + with runner.isolated_filesystem(temp_dir=tmp_path): + result = runner.invoke(cli, ["job", "list", "test-connector-id"]) + + assert result.exit_code == 1 + assert f"{CONFIG_FILE_PATH} was not found" in result.output + assert "connectors login" in result.output + + @patch("click.confirm", MagicMock(return_value=True)) def test_job_cancel(): runner = CliRunner()