|
6 | 6 | """Unit tests for pull_labs_poller (no network, no AWS).""" |
7 | 7 |
|
8 | 8 | import json |
| 9 | +import logging |
9 | 10 | import os |
10 | 11 | import tempfile |
11 | 12 | import urllib.error |
|
28 | 29 | _GET = "kernel_ci_cloud_labs.pull_labs_poller._http_get_json" |
29 | 30 | _PUT = "kernel_ci_cloud_labs.pull_labs_poller._http_put_json" |
30 | 31 |
|
31 | | -# Capture the real validator at import time so a specific test can restore it |
32 | | -# after the autouse fixture has stubbed it out. |
| 32 | +# Capture the real validators at import time so a specific test can call them |
| 33 | +# after the autouse fixtures have stubbed them out. |
33 | 34 | _REAL_VALIDATE_DEFAULT_EXECUTOR_DEPS = poller_mod._validate_default_executor_deps |
| 35 | +_REAL_VALIDATE_API_TOKEN = poller_mod._validate_api_token |
34 | 36 |
|
35 | 37 |
|
36 | 38 | # --------------------------------------------------------------------------- |
@@ -60,6 +62,16 @@ def _skip_default_executor_deps_check(monkeypatch): |
60 | 62 | monkeypatch.setattr(poller_mod, "_validate_default_executor_deps", lambda: None) |
61 | 63 |
|
62 | 64 |
|
| 65 | +@pytest.fixture(autouse=True) |
| 66 | +def _skip_api_token_check(monkeypatch): |
| 67 | + """Bypass the startup /whoami token preflight (no network in unit tests). |
| 68 | +
|
| 69 | + Dedicated tests call the real _validate_api_token via the captured |
| 70 | + reference with _http_get_json patched. |
| 71 | + """ |
| 72 | + monkeypatch.setattr(poller_mod, "_validate_api_token", lambda *a, **k: None) |
| 73 | + |
| 74 | + |
63 | 75 | def _minimal_kc(**overrides): |
64 | 76 | base = { |
65 | 77 | "api_base_uri": "https://api.example/latest", |
@@ -554,3 +566,55 @@ def _fail_if_called(): |
554 | 566 | # Custom executor — validator must be skipped, no SystemExit. |
555 | 567 | PullLabsPoller(_minimal_kc(), job_executor=lambda cfg: ([], None)) |
556 | 568 | assert called["validator"] is False |
| 569 | + |
| 570 | + |
| 571 | +# --------------------------------------------------------------------------- |
| 572 | +# Startup /whoami token preflight |
| 573 | +# --------------------------------------------------------------------------- |
| 574 | + |
| 575 | + |
| 576 | +class TestValidateApiToken: |
| 577 | + """_validate_api_token() -- never fatal, logs token validity and groups.""" |
| 578 | + |
| 579 | + URI = "https://api.example/latest" |
| 580 | + RUNTIME = "pull-labs-aws-ec2" |
| 581 | + |
| 582 | + def test_no_token_warns(self, caplog): |
| 583 | + with caplog.at_level(logging.WARNING): |
| 584 | + _REAL_VALIDATE_API_TOKEN(self.URI, None, self.RUNTIME) |
| 585 | + assert "No kernelci-api token" in caplog.text |
| 586 | + |
| 587 | + def test_401_logs_error(self, caplog): |
| 588 | + err = urllib.error.HTTPError(self.URI, 401, "Unauthorized", {}, None) |
| 589 | + with patch(_GET, side_effect=err), caplog.at_level(logging.ERROR): |
| 590 | + _REAL_VALIDATE_API_TOKEN(self.URI, "bad-token", self.RUNTIME) |
| 591 | + assert "rejected" in caplog.text |
| 592 | + |
| 593 | + def test_network_error_is_not_fatal(self): |
| 594 | + # A transient API error must not raise -- it cannot block startup. |
| 595 | + with patch(_GET, side_effect=urllib.error.URLError("boom")): |
| 596 | + _REAL_VALIDATE_API_TOKEN(self.URI, "t", self.RUNTIME) |
| 597 | + |
| 598 | + def test_valid_token_with_editor_group(self, caplog): |
| 599 | + whoami = { |
| 600 | + "username": "pullbot", |
| 601 | + "groups": [{"name": "runtime:pull-labs-aws-ec2:node-editor"}], |
| 602 | + } |
| 603 | + with patch(_GET, return_value=whoami), caplog.at_level(logging.INFO): |
| 604 | + _REAL_VALIDATE_API_TOKEN(self.URI, "t", self.RUNTIME) |
| 605 | + assert "token OK" in caplog.text |
| 606 | + assert "cannot edit" not in caplog.text |
| 607 | + |
| 608 | + def test_superuser_token_ok(self, caplog): |
| 609 | + whoami = {"username": "root", "is_superuser": True, "groups": []} |
| 610 | + with patch(_GET, return_value=whoami), caplog.at_level(logging.INFO): |
| 611 | + _REAL_VALIDATE_API_TOKEN(self.URI, "t", self.RUNTIME) |
| 612 | + assert "cannot edit" not in caplog.text |
| 613 | + |
| 614 | + def test_valid_token_without_editor_group_warns(self, caplog): |
| 615 | + whoami = {"username": "pullbot", "groups": [{"name": "some-other-group"}]} |
| 616 | + with patch(_GET, return_value=whoami), caplog.at_level(logging.WARNING): |
| 617 | + _REAL_VALIDATE_API_TOKEN(self.URI, "t", self.RUNTIME) |
| 618 | + assert "cannot edit job nodes" in caplog.text |
| 619 | + # The required group is named in the hint. |
| 620 | + assert "runtime:pull-labs-aws-ec2:node-editor" in caplog.text |
0 commit comments