Skip to content

Commit d72f2b3

Browse files
author
smkc
committed
fix(ci): restore 100% coverage and ignore lighthouse in main openapi check
1 parent bff593d commit d72f2b3

6 files changed

Lines changed: 136 additions & 2 deletions

File tree

tests/cli/integration/test_lighthouse.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
from ksef_client.cli.config import paths
99
from ksef_client.cli.errors import CliError
1010
from ksef_client.cli.exit_codes import ExitCode
11+
from ksef_client.exceptions import KsefHttpError, KsefRateLimitError
1112

1213

1314
def _json_output(text: str) -> dict:
@@ -68,6 +69,38 @@ def test_lighthouse_status_validation_error_exit_code(runner, monkeypatch) -> No
6869
assert result.exit_code == int(ExitCode.VALIDATION_ERROR)
6970

7071

72+
def test_lighthouse_status_rate_limit_error_exit_code(runner, monkeypatch) -> None:
73+
monkeypatch.setattr(
74+
lighthouse_cmd,
75+
"get_lighthouse_status",
76+
lambda **kwargs: (_ for _ in ()).throw(
77+
KsefRateLimitError(status_code=429, message="Too Many Requests")
78+
),
79+
)
80+
result = runner.invoke(app, ["lighthouse", "status"])
81+
assert result.exit_code == int(ExitCode.RETRY_EXHAUSTED)
82+
83+
84+
def test_lighthouse_status_api_error_exit_code(runner, monkeypatch) -> None:
85+
monkeypatch.setattr(
86+
lighthouse_cmd,
87+
"get_lighthouse_status",
88+
lambda **kwargs: (_ for _ in ()).throw(KsefHttpError(status_code=500, message="boom")),
89+
)
90+
result = runner.invoke(app, ["lighthouse", "status"])
91+
assert result.exit_code == int(ExitCode.API_ERROR)
92+
93+
94+
def test_lighthouse_status_unexpected_error_exit_code(runner, monkeypatch) -> None:
95+
monkeypatch.setattr(
96+
lighthouse_cmd,
97+
"get_lighthouse_status",
98+
lambda **kwargs: (_ for _ in ()).throw(RuntimeError("unexpected")),
99+
)
100+
result = runner.invoke(app, ["lighthouse", "status"])
101+
assert result.exit_code == int(ExitCode.CONFIG_ERROR)
102+
103+
71104
def test_lighthouse_status_works_without_active_profile(runner, monkeypatch, tmp_path) -> None:
72105
_write_unconfigured_config(monkeypatch, tmp_path)
73106
seen: dict[str, object] = {}
@@ -101,6 +134,16 @@ def _fake_status(**kwargs):
101134
assert seen["lighthouse_base_url"] == "https://api-latarnia.ksef.mf.gov.pl"
102135

103136

137+
def test_lighthouse_messages_unexpected_error_exit_code(runner, monkeypatch) -> None:
138+
monkeypatch.setattr(
139+
lighthouse_cmd,
140+
"get_lighthouse_messages",
141+
lambda **kwargs: (_ for _ in ()).throw(RuntimeError("unexpected")),
142+
)
143+
result = runner.invoke(app, ["lighthouse", "messages"])
144+
assert result.exit_code == int(ExitCode.CONFIG_ERROR)
145+
146+
104147
def _write_unconfigured_config(monkeypatch, tmp_path: Path) -> Path:
105148
config_path = tmp_path / "config.json"
106149
config_path.write_text(

tests/cli/unit/test_auth_manager.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,28 @@ def test_resolve_lighthouse_base_url_fallbacks_to_lighthouse_test(monkeypatch) -
247247
assert manager.resolve_lighthouse_base_url(None) == KsefLighthouseEnvironment.TEST.value
248248

249249

250+
def test_resolve_lighthouse_base_url_invalid_profile_base_fallback(monkeypatch) -> None:
251+
monkeypatch.setattr(
252+
manager,
253+
"load_config",
254+
lambda: CliConfig(
255+
active_profile="demo",
256+
profiles={
257+
"demo": ProfileConfig(
258+
name="demo",
259+
env="DEMO",
260+
base_url="https://unknown.example",
261+
context_type="nip",
262+
context_value="123",
263+
)
264+
},
265+
),
266+
)
267+
assert manager.resolve_lighthouse_base_url(None, profile="demo") == (
268+
KsefLighthouseEnvironment.TEST.value
269+
)
270+
271+
250272
def test_refresh_access_token_missing_token_in_response(monkeypatch) -> None:
251273
class _FakeClientNoToken:
252274
def __init__(self) -> None:

tests/test_client.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,15 @@ def test_sync_client_context(self):
2020
close_mock.assert_called_once()
2121
lighthouse_close_mock.assert_called_once()
2222

23+
def test_sync_client_unknown_lighthouse_mapping_fallbacks_to_empty_base(self):
24+
options = KsefClientOptions(base_url="https://unknown.example")
25+
client = KsefClient(options)
26+
with patch.object(client._http, "close", Mock()), patch.object(
27+
client._lighthouse_http, "close", Mock()
28+
):
29+
self.assertEqual(client.lighthouse._base_url, "")
30+
client.close()
31+
2332

2433
class AsyncClientTests(unittest.IsolatedAsyncioTestCase):
2534
async def test_async_client_context(self):
@@ -40,6 +49,15 @@ async def test_async_client_context(self):
4049
aclose_mock.assert_called_once()
4150
lighthouse_aclose_mock.assert_called_once()
4251

52+
async def test_async_client_unknown_lighthouse_mapping_fallbacks_to_empty_base(self):
53+
options = KsefClientOptions(base_url="https://unknown.example")
54+
client = AsyncKsefClient(options)
55+
with patch.object(client._http, "aclose", AsyncMock()), patch.object(
56+
client._lighthouse_http, "aclose", AsyncMock()
57+
):
58+
self.assertEqual(client.lighthouse._base_url, "")
59+
await client.aclose()
60+
4361

4462
if __name__ == "__main__":
4563
unittest.main()

tests/test_clients.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,18 @@ def test_lighthouse_client(self):
317317
"https://api-latarnia-test.ksef.mf.gov.pl/messages",
318318
)
319319

320+
def test_lighthouse_client_handles_invalid_payload_and_missing_base_url(self):
321+
client = LighthouseClient(self.http, "https://api-latarnia-test.ksef.mf.gov.pl")
322+
with patch.object(client, "_request_json", Mock(side_effect=[None, {"unexpected": True}])):
323+
status = client.get_status()
324+
messages = client.get_messages()
325+
self.assertEqual(status.status.value, "AVAILABLE")
326+
self.assertEqual(messages, [])
327+
328+
missing_base_client = LighthouseClient(self.http, "")
329+
with self.assertRaises(ValueError):
330+
missing_base_client.get_status()
331+
320332

321333
class AsyncClientsTests(unittest.IsolatedAsyncioTestCase):
322334
async def test_async_clients(self):
@@ -605,6 +617,24 @@ async def test_async_clients(self):
605617
"https://api-latarnia-test.ksef.mf.gov.pl/messages",
606618
)
607619

620+
async def test_async_lighthouse_handles_invalid_payload_and_missing_base_url(self):
621+
response = HttpResponse(200, httpx.Headers({}), b"{}")
622+
http = DummyAsyncHttp(response)
623+
lighthouse = AsyncLighthouseClient(http, "https://api-latarnia-test.ksef.mf.gov.pl")
624+
with patch.object(
625+
lighthouse,
626+
"_request_json",
627+
AsyncMock(side_effect=[None, {"unexpected": True}]),
628+
):
629+
status = await lighthouse.get_status()
630+
messages = await lighthouse.get_messages()
631+
self.assertEqual(status.status.value, "AVAILABLE")
632+
self.assertEqual(messages, [])
633+
634+
missing_base = AsyncLighthouseClient(http, "")
635+
with self.assertRaises(ValueError):
636+
await missing_base.get_messages()
637+
608638

609639
if __name__ == "__main__":
610640
unittest.main()

tests/test_config.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,12 @@ def test_resolve_lighthouse_base_url(self):
7575
KsefLighthouseEnvironment.PROD.value,
7676
)
7777

78+
options = KsefClientOptions(base_url=KsefLighthouseEnvironment.TEST.value)
79+
self.assertEqual(
80+
options.resolve_lighthouse_base_url(),
81+
KsefLighthouseEnvironment.TEST.value,
82+
)
83+
7884
options = KsefClientOptions(base_url="https://example.com")
7985
with self.assertRaises(ValueError):
8086
options.resolve_lighthouse_base_url()

tools/check_coverage.py

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -221,9 +221,15 @@ def _extract_expected_status(self, node: ast.Call) -> set[int]:
221221
return set()
222222

223223

224-
def get_implemented_endpoints_deep(source_dir: Path) -> list[ImplementedEndpoint]:
224+
def get_implemented_endpoints_deep(
225+
source_dir: Path,
226+
excluded_file_names: set[str] | None = None,
227+
) -> list[ImplementedEndpoint]:
225228
endpoints = []
229+
excluded = excluded_file_names or set()
226230
for py_file in source_dir.rglob("*.py"):
231+
if py_file.name in excluded:
232+
continue
227233
try:
228234
visitor = AdvancedClientVisitor(str(py_file))
229235
tree = ast.parse(py_file.read_text(encoding="utf-8"))
@@ -249,13 +255,22 @@ def main():
249255
parser.add_argument(
250256
"--src", required=True, type=Path, help="Path to source directory containing clients"
251257
)
258+
parser.add_argument(
259+
"--exclude-files",
260+
nargs="*",
261+
default=["lighthouse.py"],
262+
help="Client file names to exclude from this OpenAPI coverage check.",
263+
)
252264
args = parser.parse_args()
253265

254266
# 1. Load OpenAPI Specs
255267
openapi_specs = get_openapi_specs(args.openapi)
256268

257269
# 2. Analyze Code
258-
implemented_eps = get_implemented_endpoints_deep(args.src)
270+
implemented_eps = get_implemented_endpoints_deep(
271+
args.src,
272+
excluded_file_names=set(args.exclude_files),
273+
)
259274

260275
# 3. Compare
261276
openapi_keys = set(openapi_specs.keys())

0 commit comments

Comments
 (0)