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
50 changes: 50 additions & 0 deletions tests/cli/integration/test_init_command.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,3 +76,53 @@ def test_init_non_interactive_requires_context_fields(runner, monkeypatch, tmp_p
],
)
assert result.exit_code == 2


def test_init_interactive_respects_provided_options_and_keeps_active_profile(
runner, monkeypatch, tmp_path
) -> None:
config_path = tmp_path / "config.json"
monkeypatch.setattr(paths, "config_file", lambda: config_path)

seed_result = runner.invoke(
app,
[
"--json",
"init",
"--name",
"seed",
"--env",
"DEMO",
"--context-type",
"nip",
"--context-value",
"111",
"--non-interactive",
"--set-active",
],
)
assert seed_result.exit_code == 0

result = runner.invoke(
app,
[
"--json",
"init",
"--name",
"second",
"--env",
"TEST",
"--base-url",
"https://example.test",
"--context-type",
"nip",
"--context-value",
"222",
],
input="second\n",
)
assert result.exit_code == 0
payload = _json_output(result.stdout)
assert payload["ok"] is True
assert payload["profile"] == "second"
assert payload["data"]["active_profile"] == "seed"
115 changes: 112 additions & 3 deletions tests/cli/unit/test_auth_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,15 @@ def test_status_and_logout(monkeypatch) -> None:


def test_select_certificate_and_require_non_empty_errors() -> None:
cert = manager._select_certificate(
[
{"usage": ["KsefTokenEncryption"], "certificate": ""},
{"usage": ["KsefTokenEncryption"], "certificate": "CERT"},
],
"KsefTokenEncryption",
)
assert cert == "CERT"

with pytest.raises(CliError) as cert_error:
manager._select_certificate([], "KsefTokenEncryption")
assert cert_error.value.code == ExitCode.API_ERROR
Expand Down Expand Up @@ -214,10 +223,31 @@ def test_resolve_base_url_uses_profile_when_missing(monkeypatch) -> None:
assert manager.resolve_base_url(None, profile="demo") == "https://profile.example"


def test_resolve_base_url_falls_back_when_profile_base_url_empty(monkeypatch) -> None:
monkeypatch.setattr(
manager,
"load_config",
lambda: CliConfig(
active_profile="demo",
profiles={
"demo": ProfileConfig(
name="demo",
env="DEMO",
base_url=" ",
context_type="nip",
context_value="123",
)
},
),
)
assert manager.resolve_base_url(None, profile="demo") == manager.KsefEnvironment.DEMO.value


def test_resolve_lighthouse_base_url_prefers_explicit_value() -> None:
assert manager.resolve_lighthouse_base_url(" https://api-latarnia-test.ksef.mf.gov.pl/ ") == (
"https://api-latarnia-test.ksef.mf.gov.pl/"
).strip()
assert (
manager.resolve_lighthouse_base_url(" https://api-latarnia-test.ksef.mf.gov.pl/ ")
== ("https://api-latarnia-test.ksef.mf.gov.pl/").strip()
)


def test_resolve_lighthouse_base_url_uses_profile_mapping(monkeypatch) -> None:
Expand Down Expand Up @@ -269,6 +299,28 @@ def test_resolve_lighthouse_base_url_invalid_profile_base_fallback(monkeypatch)
)


def test_resolve_lighthouse_base_url_fallback_when_profile_base_url_empty(monkeypatch) -> None:
monkeypatch.setattr(
manager,
"load_config",
lambda: CliConfig(
active_profile="demo",
profiles={
"demo": ProfileConfig(
name="demo",
env="DEMO",
base_url=" ",
context_type="nip",
context_value="123",
)
},
),
)
assert manager.resolve_lighthouse_base_url(None, profile="demo") == (
KsefLighthouseEnvironment.TEST.value
)


def test_refresh_access_token_missing_token_in_response(monkeypatch) -> None:
class _FakeClientNoToken:
def __init__(self) -> None:
Expand Down Expand Up @@ -492,3 +544,60 @@ def test_login_with_xades_loader_errors_are_mapped(monkeypatch) -> None:
save=False,
)
assert exc.value.code == ExitCode.VALIDATION_ERROR


def test_login_with_token_without_save(monkeypatch) -> None:
monkeypatch.setattr(manager, "create_client", lambda base_url: _FakeClient())
monkeypatch.setattr(manager, "AuthCoordinator", _FakeAuthCoordinator)
monkeypatch.setattr(
manager,
"save_tokens",
lambda *args, **kwargs: (_ for _ in ()).throw(
AssertionError("save_tokens should not be called")
),
)
monkeypatch.setattr(
manager,
"set_cached_metadata",
lambda *args, **kwargs: (_ for _ in ()).throw(
AssertionError("set_cached_metadata should not be called")
),
)

result = manager.login_with_token(
profile="demo",
base_url="https://api-demo.ksef.mf.gov.pl",
token="TOKEN",
context_type="nip",
context_value="5265877635",
poll_interval=0.0,
max_attempts=1,
save=False,
)
assert result["saved"] is False


def test_refresh_access_token_success_without_save(monkeypatch) -> None:
monkeypatch.setattr(manager, "get_tokens", lambda profile: ("acc", "ref"))
monkeypatch.setattr(manager, "create_client", lambda base_url: _FakeClient())
monkeypatch.setattr(
manager,
"save_tokens",
lambda *args, **kwargs: (_ for _ in ()).throw(
AssertionError("save_tokens should not be called")
),
)
monkeypatch.setattr(
manager,
"set_cached_metadata",
lambda *args, **kwargs: (_ for _ in ()).throw(
AssertionError("set_cached_metadata should not be called")
),
)

result = manager.refresh_access_token(
profile="demo",
base_url="https://api-demo.ksef.mf.gov.pl",
save=False,
)
assert result["saved"] is False
36 changes: 36 additions & 0 deletions tests/cli/unit/test_config_loader_profiles.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,18 @@ def test_loader_profile_parser_skips_invalid_entries(monkeypatch, tmp_path) -> N
assert loader.load_config().profiles == {}


def test_loader_accepts_non_dict_profiles_key(monkeypatch, tmp_path) -> None:
config_path = tmp_path / "config.json"
monkeypatch.setattr(paths, "config_file", lambda: config_path)
config_path.write_text(
json.dumps({"active_profile": "demo", "profiles": []}),
encoding="utf-8",
)
loaded = loader.load_config()
assert loaded.profiles == {}
assert loaded.active_profile is None


def test_loader_skips_non_string_profile_names(monkeypatch, tmp_path) -> None:
config_path = tmp_path / "config.json"
monkeypatch.setattr(paths, "config_file", lambda: config_path)
Expand Down Expand Up @@ -293,3 +305,27 @@ def test_profiles_upsert_and_active_fallback() -> None:

profiles.delete_profile(config, name="one")
assert config.active_profile == "two"


def test_profiles_delete_non_active_profile_keeps_active() -> None:
config = CliConfig(
active_profile="one",
profiles={
"one": ProfileConfig(
name="one",
env="DEMO",
base_url="https://api-demo.ksef.mf.gov.pl",
context_type="nip",
context_value="1",
),
"two": ProfileConfig(
name="two",
env="TEST",
base_url="https://api-test.ksef.mf.gov.pl",
context_type="nip",
context_value="2",
),
},
)
profiles.delete_profile(config, name="two")
assert config.active_profile == "one"
12 changes: 12 additions & 0 deletions tests/cli/unit/test_core_coverage.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,18 @@ def _fake_entrypoint() -> None:
assert called["value"] is True


def test_cli_main_module_import_does_not_invoke_entrypoint(monkeypatch) -> None:
called = {"value": False}

def _fake_entrypoint() -> None:
called["value"] = True

monkeypatch.setattr(app_module, "app_entrypoint", _fake_entrypoint)
main_module = importlib.import_module("ksef_client.cli.__main__")
importlib.reload(main_module)
assert called["value"] is False


def test_config_loader_and_profiles(monkeypatch, tmp_path: Path) -> None:
monkeypatch.setattr(paths, "config_file", lambda: tmp_path / "config.json")
cfg = loader.load_config()
Expand Down
8 changes: 8 additions & 0 deletions tests/cli/unit/test_output_human.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,14 @@ def test_human_renderer_success_skips_raw_response_payload(capsys) -> None:
assert "raw" not in out


def test_human_renderer_success_without_data(capsys) -> None:
renderer = HumanRenderer(no_color=True)
renderer.success(command="send.status", profile="demo", data=None)
out = capsys.readouterr().out
assert "OK" in out
assert "send.status" in out


def test_human_renderer_error_prints_hint(capsys) -> None:
renderer = HumanRenderer(no_color=True)
renderer.error(
Expand Down
Loading
Loading