Skip to content

test: add MaaS API key expiration tests#1292

Merged
dbasunag merged 4 commits intoopendatahub-io:mainfrom
SB159:test/api-key-expiration
Mar 25, 2026
Merged

test: add MaaS API key expiration tests#1292
dbasunag merged 4 commits intoopendatahub-io:mainfrom
SB159:test/api-key-expiration

Conversation

@SB159
Copy link
Copy Markdown
Contributor

@SB159 SB159 commented Mar 24, 2026

Pull Request

Summary

add MaaS API key expiration tests

Related Issues

  • Fixes:
  • JIRA:

How it has been tested

  • Locally
  • Jenkins

Additional Requirements

  • If this PR introduces a new test image, did you create a PR to mirror it in disconnected environment?
  • If this PR introduces new marker(s)/adds a new component, was relevant ticket created to update relevant Jenkins job?

Summary by CodeRabbit

  • Tests
    • Added an end-to-end test suite for API key expiration policy: creating keys below, at, and beyond the maximum allowed expiration; asserting correct acceptance/rejection and presence/absence of expiration timestamps on retrieval.
    • Added test helpers and a short-expiration (1-hour) fixture to create, validate, retrieve, and revoke keys, plus improved test error handling for creation flows.

Signed-off-by: Swati Mukund Bagal <sbagal@redhat.com>
@github-actions
Copy link
Copy Markdown

The following are automatically added/executed:

  • PR size label.
  • Run pre-commit
  • Run tox
  • Add PR author as the PR assignee
  • Build image based on the PR

Available user actions:

  • To mark a PR as WIP, add /wip in a comment. To remove it from the PR comment /wip cancel to the PR.
  • To block merging of a PR, add /hold in a comment. To un-block merging of PR comment /hold cancel.
  • To mark a PR as approved, add /lgtm in a comment. To remove, add /lgtm cancel.
    lgtm label removed on each new commit push.
  • To mark PR as verified comment /verified to the PR, to un-verify comment /verified cancel to the PR.
    verified label removed on each new commit push.
  • To Cherry-pick a merged PR /cherry-pick <target_branch_name> to the PR. If <target_branch_name> is valid,
    and the current PR is merged, a cherry-picked PR would be created and linked to the current PR.
  • To build and push image to quay, add /build-push-pr-image in a comment. This would create an image with tag
    pr-<pr_number> to quay repository. This image tag, however would be deleted on PR merge or close action.
Supported labels

{'/hold', '/verified', '/lgtm', '/wip', '/cherry-pick', '/build-push-pr-image'}

@SB159 SB159 requested a review from dbasunag March 24, 2026 23:28
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 24, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Central YAML (inherited), Organization UI (inherited)

Review profile: CHILL

Plan: Pro

Run ID: bc21d1ca-e3b5-4892-81c6-58510667f556

📥 Commits

Reviewing files that changed from the base of the PR and between 6c41d8e and 7c78ded.

📒 Files selected for processing (3)
  • tests/model_serving/maas_billing/maas_subscription/conftest.py
  • tests/model_serving/maas_billing/maas_subscription/test_api_key_expiration.py
  • tests/model_serving/maas_billing/maas_subscription/utils.py
🚧 Files skipped from review as they are similar to previous changes (3)
  • tests/model_serving/maas_billing/maas_subscription/conftest.py
  • tests/model_serving/maas_billing/maas_subscription/utils.py
  • tests/model_serving/maas_billing/maas_subscription/test_api_key_expiration.py

📝 Walkthrough

Walkthrough

Adds end-to-end tests and fixtures to validate MAAS API key expiration behavior, plus test utility updates to support optional expiresIn in API key creation and new response-assertion helpers. Tests cover below/at/above expiration limits and retrieval of keys with/without expirations.

Changes

Cohort / File(s) Summary
Test Fixtures
tests/model_serving/maas_billing/maas_subscription/conftest.py
Adds a function-scoped pytest fixture short_expiration_api_key_id that creates and yields an API key ID with expires_in="1h" via the existing helper.
Expiration Test Suite
tests/model_serving/maas_billing/maas_subscription/test_api_key_expiration.py
New test module TestAPIKeyExpiration with parametrized E2E tests: create keys within/at/exceeding a 30-day limit (assert success or 400 with limit message), and GET checks for keys created without expiration (expect no expiresAt) and with short expiration (expect non-null expiration).
Test Utilities
tests/model_serving/maas_billing/maas_subscription/utils.py
create_api_key() accepts `expires_in: str

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Security notes (actionable):

  • Review handling and logging of API key material in tests to avoid persisting secrets or printing keys to logs (information exposure: CWE-200).
  • Ensure test helpers and fixtures do not leave active keys in environments; confirm revocation paths always run (incomplete cleanup could lead to leaked credentials: CWE-532).
🚥 Pre-merge checks | ✅ 1 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description is largely incomplete. While it includes a summary and testing checklist, critical sections lack substantive content: Related Issues are not filled in, and the summary is minimal (only one sentence). Provide concrete details in the Summary section explaining what expiration tests were added and why. Fill in Related Issues/JIRA with actual ticket references if applicable.
✅ Passed checks (1 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the primary change: adding MaaS API key expiration tests.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (1)
tests/model_serving/maas_billing/maas_subscription/conftest.py (1)

52-93: Remove dead commented-out code.

40+ lines of commented code adds noise. If this fix is still needed, track it in a ticket; otherwise delete.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/model_serving/maas_billing/maas_subscription/conftest.py` around lines
52 - 93, Remove the large block of dead commented-out code related to the
temporary maas-controller patch (the fixture maas_controller_pr498_image and its
body referencing Deployment, ResourceEditor, MAAS_DB_NAMESPACE, LOGGER); simply
delete the entire commented section so the file no longer contains the 40+ lines
of noise—if the workaround is still required, add a reference to a tracking
ticket in a short comment instead of leaving the full commented implementation.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@tests/model_serving/maas_billing/maas_subscription/test_api_key_expiration.py`:
- Around line 111-131: The test test_create_key_without_expiration is missing an
assertion for the retrieved expiresAt value: after calling get_api_key and
assigning expires_at = get_body.get("expiresAt"), add an assertion to verify the
key has no expiration (e.g., assert expires_at is None or assert "expiresAt" not
in get_body) so the test fails when an expiresAt is present; update the
assertion message to include get_resp.status_code and get_resp.text as context
if desired.
- Around line 29-52: The tests test_create_key_within_expiration_limit and
test_create_key_at_expiration_limit create API keys via create_api_key but never
revoke them; update each test to perform cleanup by calling the existing
revoke_api_key (or switch to using the fixture create_and_yield_api_key_id) to
ensure keys are removed after assertions. Specifically, capture the created key
id from create_body.get("key") (or return value from
create_and_yield_api_key_id), call revoke_api_key with base_url, ocp_user_token,
and request_session_http in a finally block or use a fixture teardown to
guarantee revocation even on assertion failure, and remove any lingering
LOGGER-only behavior so the test always revokes the created key.
- Around line 133-155: In test_create_key_with_short_expiration update the
GET-response field checks to use the API's canonical key name: replace all uses
of "expirationDate" on get_body (the assertion and formatted failure message)
and the LOGGER.info call with "expiresAt" so the assertion reads
get_body.get("expiresAt") and the log prints get_body["expiresAt"]; the change
should be made within the test_create_key_with_short_expiration function where
get_api_key returns get_body and LOGGER.info is called.

---

Nitpick comments:
In `@tests/model_serving/maas_billing/maas_subscription/conftest.py`:
- Around line 52-93: Remove the large block of dead commented-out code related
to the temporary maas-controller patch (the fixture maas_controller_pr498_image
and its body referencing Deployment, ResourceEditor, MAAS_DB_NAMESPACE, LOGGER);
simply delete the entire commented section so the file no longer contains the
40+ lines of noise—if the workaround is still required, add a reference to a
tracking ticket in a short comment instead of leaving the full commented
implementation.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Central YAML (inherited), Organization UI (inherited)

Review profile: CHILL

Plan: Pro

Run ID: f8c2789e-e6ef-4b24-8388-0f4a6b42e001

📥 Commits

Reviewing files that changed from the base of the PR and between a84ef19 and ee3aef0.

📒 Files selected for processing (3)
  • tests/model_serving/maas_billing/maas_subscription/conftest.py
  • tests/model_serving/maas_billing/maas_subscription/test_api_key_expiration.py
  • tests/model_serving/maas_billing/maas_subscription/utils.py

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (3)
tests/model_serving/maas_billing/maas_subscription/test_api_key_expiration.py (3)

150-153: ⚠️ Potential issue | 🟠 Major

Use expiresAt consistently in assertion and logging.

Line [150]-Line [153] checks expirationDate, while this suite/docstring expects expiresAt. This mismatch can fail valid responses and hide real regressions.

Suggested fix
-        assert get_body.get("expirationDate"), (
-            f"Expected non-null 'expirationDate' for 1h key, got: {get_body.get('expirationDate')!r}"
+        assert get_body.get("expiresAt"), (
+            f"Expected non-null 'expiresAt' for 1h key, got: {get_body.get('expiresAt')!r}"
         )
-        LOGGER.info(f"[expiration] 1h key expirationDate={get_body['expirationDate']}")
+        LOGGER.info(f"[expiration] 1h key expiresAt={get_body['expiresAt']}")

As per coding guidelines, prioritize “Bug-prone patterns and error handling gaps”.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@tests/model_serving/maas_billing/maas_subscription/test_api_key_expiration.py`
around lines 150 - 153, The test currently asserts and logs the wrong key name
'expirationDate' — update the assertion and the LOGGER.info call in the test
(where get_body is used) to check and log the 'expiresAt' field instead (i.e.,
use get_body.get("expiresAt") in the assert message and get_body["expiresAt"] in
the LOGGER.info) so the test and docstring/schema remain consistent with the
expected response key.

29-52: ⚠️ Potential issue | 🟠 Major

Add guaranteed teardown for keys created in both limit-success tests.

Line [39] and Line [64] create real API keys but never revoke them. This leaves persistent artifacts and introduces cross-test flakiness/quota pressure. Wrap assertions in try/finally and revoke by id in finally.

Suggested fix
 from tests.model_serving.maas_billing.maas_subscription.utils import (
     assert_api_key_created_ok,
     create_api_key,
     get_api_key,
+    revoke_api_key,
 )
@@
     def test_create_key_within_expiration_limit(
@@
-        create_resp, create_body = create_api_key(
-            base_url=base_url,
-            ocp_user_token=ocp_token_for_actor,
-            request_session_http=request_session_http,
-            api_key_name=key_name,
-            expires_in=f"{expires_in_hours}h",
-            raise_on_error=False,
-        )
-        assert_api_key_created_ok(create_resp, create_body, required_fields=("key", "expiresAt"))
-        LOGGER.info(
-            f"[expiration] Created key within limit: expires_in={expires_in_hours}h, "
-            f"expiresAt={create_body.get('expiresAt')}"
-        )
+        create_resp, create_body = create_api_key(
+            base_url=base_url,
+            ocp_user_token=ocp_token_for_actor,
+            request_session_http=request_session_http,
+            api_key_name=key_name,
+            expires_in=f"{expires_in_hours}h",
+            raise_on_error=False,
+        )
+        key_id = create_body.get("id")
+        try:
+            assert_api_key_created_ok(create_resp, create_body, required_fields=("key", "expiresAt"))
+            LOGGER.info(
+                f"[expiration] Created key within limit: expires_in={expires_in_hours}h, "
+                f"expiresAt={create_body.get('expiresAt')}"
+            )
+        finally:
+            if key_id:
+                revoke_api_key(
+                    request_session_http=request_session_http,
+                    base_url=base_url,
+                    key_id=key_id,
+                    ocp_user_token=ocp_token_for_actor,
+                )
@@
     def test_create_key_at_expiration_limit(
@@
-        create_resp, create_body = create_api_key(
-            base_url=base_url,
-            ocp_user_token=ocp_token_for_actor,
-            request_session_http=request_session_http,
-            api_key_name=key_name,
-            expires_in=f"{expires_in_hours}h",
-            raise_on_error=False,
-        )
-        assert_api_key_created_ok(create_resp, create_body, required_fields=("key", "expiresAt"))
-        LOGGER.info(
-            f"[expiration] Created key at limit: expires_in={expires_in_hours}h "
-            f"({MAAS_API_KEY_MAX_EXPIRATION_DAYS} days)"
-        )
+        create_resp, create_body = create_api_key(
+            base_url=base_url,
+            ocp_user_token=ocp_token_for_actor,
+            request_session_http=request_session_http,
+            api_key_name=key_name,
+            expires_in=f"{expires_in_hours}h",
+            raise_on_error=False,
+        )
+        key_id = create_body.get("id")
+        try:
+            assert_api_key_created_ok(create_resp, create_body, required_fields=("key", "expiresAt"))
+            LOGGER.info(
+                f"[expiration] Created key at limit: expires_in={expires_in_hours}h "
+                f"({MAAS_API_KEY_MAX_EXPIRATION_DAYS} days)"
+            )
+        finally:
+            if key_id:
+                revoke_api_key(
+                    request_session_http=request_session_http,
+                    base_url=base_url,
+                    key_id=key_id,
+                    ocp_user_token=ocp_token_for_actor,
+                )

As per coding guidelines, prioritize “Bug-prone patterns and error handling gaps”.

Also applies to: 54-77

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@tests/model_serving/maas_billing/maas_subscription/test_api_key_expiration.py`
around lines 29 - 52, The test test_create_key_within_expiration_limit creates a
real API key via create_api_key and never revokes it; wrap the assertion and
subsequent logic in a try/finally so the created key is always cleaned up:
capture the new key's id from create_body.get("id") (or create_body["id"]) after
create_api_key, perform assertions inside try, and in finally call the existing
revoke/delete helper (e.g., revoke_api_key or the HTTP DELETE using
request_session_http and base_url with ocp_token_for_actor) to revoke the key by
id; apply the same try/finally teardown pattern to the other similar test that
creates a key (the test in the 54-77 range).

111-131: ⚠️ Potential issue | 🟠 Major

Assert the no-expiration contract explicitly.

Line [128] reads expiresAt but the test never validates it. This test currently passes even when expiration is unexpectedly set.

Suggested fix
         expires_at = get_body.get("expiresAt")
+        assert expires_at is None, (
+            f"Expected 'expiresAt' to be None/absent for key without expiration, "
+            f"got {expires_at!r}; status={get_resp.status_code}, body={get_resp.text[:200]}"
+        )
         LOGGER.info(f"[expiration] Key without expiration field: expiresAt={expires_at!r}")

As per coding guidelines, prioritize “Bug-prone patterns and error handling gaps”.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@tests/model_serving/maas_billing/maas_subscription/test_api_key_expiration.py`
around lines 111 - 131, The test test_create_key_without_expiration currently
reads expires_at from the GET response but never asserts it; update the test to
explicitly assert the no-expiration contract by checking the GET response body
from get_api_key (refer to get_api_key, test_create_key_without_expiration, and
the expires_at local variable) and assert that expires_at is either None or the
"expiresAt" key is absent, with a clear failure message so the test fails if an
unexpected expiration is present.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In
`@tests/model_serving/maas_billing/maas_subscription/test_api_key_expiration.py`:
- Around line 150-153: The test currently asserts and logs the wrong key name
'expirationDate' — update the assertion and the LOGGER.info call in the test
(where get_body is used) to check and log the 'expiresAt' field instead (i.e.,
use get_body.get("expiresAt") in the assert message and get_body["expiresAt"] in
the LOGGER.info) so the test and docstring/schema remain consistent with the
expected response key.
- Around line 29-52: The test test_create_key_within_expiration_limit creates a
real API key via create_api_key and never revokes it; wrap the assertion and
subsequent logic in a try/finally so the created key is always cleaned up:
capture the new key's id from create_body.get("id") (or create_body["id"]) after
create_api_key, perform assertions inside try, and in finally call the existing
revoke/delete helper (e.g., revoke_api_key or the HTTP DELETE using
request_session_http and base_url with ocp_token_for_actor) to revoke the key by
id; apply the same try/finally teardown pattern to the other similar test that
creates a key (the test in the 54-77 range).
- Around line 111-131: The test test_create_key_without_expiration currently
reads expires_at from the GET response but never asserts it; update the test to
explicitly assert the no-expiration contract by checking the GET response body
from get_api_key (refer to get_api_key, test_create_key_without_expiration, and
the expires_at local variable) and assert that expires_at is either None or the
"expiresAt" key is absent, with a clear failure message so the test fails if an
unexpected expiration is present.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Central YAML (inherited), Organization UI (inherited)

Review profile: CHILL

Plan: Pro

Run ID: 94bbe35c-da7e-48dc-86d8-b2a77f481556

📥 Commits

Reviewing files that changed from the base of the PR and between ee3aef0 and 6c41d8e.

📒 Files selected for processing (1)
  • tests/model_serving/maas_billing/maas_subscription/test_api_key_expiration.py

Signed-off-by: Swati Mukund Bagal <sbagal@redhat.com>
@mwaykole
Copy link
Copy Markdown
Member

/lgtm

@dbasunag dbasunag merged commit 9ef3f9d into opendatahub-io:main Mar 25, 2026
9 checks passed
@github-actions
Copy link
Copy Markdown

Status of building tag latest: success.
Status of pushing tag latest to image registry: success.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants