feat: add create-config subcommand to generate default config (#577)

This commit is contained in:
Jens Bergmann
2025-07-13 13:09:40 +02:00
committed by GitHub
parent 526592047e
commit c425193b10
6 changed files with 111 additions and 46 deletions

View File

@@ -18,6 +18,16 @@ from kleinanzeigen_bot.utils import i18n
from tests.conftest import DummyBrowser, DummyPage, SmokeKleinanzeigenBot
def run_cli_subcommand(args:list[str], cwd:str | None = None) -> subprocess.CompletedProcess[str]:
"""
Run the kleinanzeigen-bot CLI as a subprocess with the given arguments.
Returns the CompletedProcess object.
"""
cli_module = "kleinanzeigen_bot.__main__"
cmd = [sys.executable, "-m", cli_module] + args
return subprocess.run(cmd, check = False, capture_output = True, text = True, cwd = cwd) # noqa: S603
@pytest.mark.smoke
def test_app_starts(smoke_bot:SmokeKleinanzeigenBot) -> None:
"""Smoke: Bot can be instantiated and started without error."""
@@ -97,7 +107,15 @@ def test_dummy_browser_session() -> None:
@pytest.mark.smoke
def test_cli_entrypoint_help_runs() -> None:
"""Smoke: CLI entry point runs with --help and exits cleanly (subprocess)."""
cli_module = "kleinanzeigen_bot.__main__"
result = subprocess.run([sys.executable, "-m", cli_module, "--help"], check = False, capture_output = True, text = True) # noqa: S603
result = run_cli_subcommand(["--help"])
assert result.returncode in {0, 1}, f"CLI exited with unexpected code: {result.returncode}\nstdout: {result.stdout}\nstderr: {result.stderr}"
assert "Usage" in result.stdout or "usage" in result.stdout or "help" in result.stdout.lower(), f"No help text in CLI output: {result.stdout}"
@pytest.mark.smoke
def test_cli_create_config_creates_file(tmp_path:Path) -> None:
"""Smoke: CLI 'create-config' creates a config.yaml file in the current directory."""
result = run_cli_subcommand(["create-config"], cwd = str(tmp_path))
config_path = tmp_path / "config.yaml"
assert result.returncode == 0, f"CLI exited with code {result.returncode}\nstdout: {result.stdout}\nstderr: {result.stderr}"
assert config_path.exists(), "config.yaml was not created by create-config command"

View File

@@ -2,12 +2,12 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
# SPDX-ArtifactOfProjectHomePage: https://github.com/Second-Hand-Friends/kleinanzeigen-bot/
import gc, pytest # isort: skip
import pathlib
from kleinanzeigen_bot import KleinanzeigenBot
class TestKleinanzeigenBot:
@pytest.fixture
def bot(self) -> KleinanzeigenBot:
return KleinanzeigenBot()
@@ -26,6 +26,37 @@ class TestKleinanzeigenBot:
assert bot.ads_selector == "all"
assert bot.keep_old_ads
def test_parse_args_create_config(self, bot:KleinanzeigenBot) -> None:
"""Test parsing of create-config command"""
bot.parse_args(["app", "create-config"])
assert bot.command == "create-config"
def test_create_default_config_logs_error_if_exists(self, tmp_path:pathlib.Path, bot:KleinanzeigenBot, caplog:pytest.LogCaptureFixture) -> None:
"""Test that create_default_config logs an error if the config file already exists."""
config_path = tmp_path / "config.yaml"
config_path.write_text("dummy: value")
bot.config_file_path = str(config_path)
with caplog.at_level("ERROR"):
bot.create_default_config()
assert any("already exists" in m for m in caplog.messages)
def test_create_default_config_creates_file(self, tmp_path:pathlib.Path, bot:KleinanzeigenBot) -> None:
"""Test that create_default_config creates a config file if it does not exist."""
config_path = tmp_path / "config.yaml"
bot.config_file_path = str(config_path)
assert not config_path.exists()
bot.create_default_config()
assert config_path.exists()
content = config_path.read_text()
assert "username: changeme" in content
def test_load_config_handles_missing_file(self, tmp_path:pathlib.Path, bot:KleinanzeigenBot) -> None:
"""Test that load_config creates a default config file if missing. No info log is expected anymore."""
config_path = tmp_path / "config.yaml"
bot.config_file_path = str(config_path)
bot.load_config()
assert config_path.exists()
def test_get_version(self, bot:KleinanzeigenBot) -> None:
"""Test version retrieval"""
version = bot.get_version()

View File

@@ -291,17 +291,12 @@ class TestKleinanzeigenBotConfiguration:
test_bot:KleinanzeigenBot,
test_data_dir:str
) -> None:
"""Verify that loading a missing config file creates default config."""
"""Verify that loading a missing config file creates default config. No info log is expected anymore."""
config_path = Path(test_data_dir) / "missing_config.yaml"
config_path.unlink(missing_ok = True)
test_bot.config_file_path = str(config_path)
with patch.object(LOG, "warning") as mock_warning:
test_bot.load_config()
mock_warning.assert_called_once()
assert config_path.exists()
assert test_bot.config.login.username == "changeme" # noqa: S105 placeholder for default config, not a real username
assert test_bot.config.login.password == "changeme" # noqa: S105 placeholder for default config, not a real password
test_bot.load_config()
assert config_path.exists()
def test_load_config_validates_required_fields(self, test_bot:KleinanzeigenBot, test_data_dir:str) -> None:
"""Verify that config validation checks required fields."""