Skip to content

MPT-20436: Added endpoints and e2e tests for program certificates#319

Open
robcsegal wants to merge 1 commit intomainfrom
MPT-20436-add-endpoints-and-e-2-e-tests-for-program-certificates
Open

MPT-20436: Added endpoints and e2e tests for program certificates#319
robcsegal wants to merge 1 commit intomainfrom
MPT-20436-add-endpoints-and-e-2-e-tests-for-program-certificates

Conversation

@robcsegal
Copy link
Copy Markdown
Contributor

@robcsegal robcsegal commented Apr 22, 2026

This pull request adds support for program certificates in the API client, including synchronous and asynchronous service classes and comprehensive end-to-end and unit test coverage.

Program Certificate Service Implementation:

  • Added Certificate model and CertificateService / AsyncCertificateService classes in mpt_api_client/resources/program/certificates.py, providing CRUD operations via ManagedResourceMixin and CollectionMixin, along with a terminate action method for both sync and async services.

Integration with Program Module:

  • Registered certificates as a property on Program and AsyncProgram in mpt_api_client/resources/program/program.py, exposing the new service alongside the existing enrollments service.

Testing Enhancements:

  • Added end-to-end tests for both sync and async certificate flows in tests/e2e/program/certificate/test_sync_certificate.py and test_async_certificate.py, covering creation, retrieval, listing, updating, termination, and error handling.
  • Created test fixtures for certificate data in tests/e2e/program/certificate/conftest.py.
  • Added unit tests for CertificateService, AsyncCertificateService, and the Program / AsyncProgram integration in tests/unit/resources/program/test_certificates.py and tests/unit/resources/program/test_program.py.

Configuration Updates:

  • Updated e2e_config.test.json with program.certificate.id for E2E testing.

Closes MPT-20436

@robcsegal robcsegal requested a review from a team as a code owner April 22, 2026 18:18
@robcsegal robcsegal requested review from albertsola and jentyk April 22, 2026 18:18
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 22, 2026

📝 Walkthrough

Walkthrough

This PR introduces certificate management functionality to the MPT API client. It adds a new Certificate model and service classes (both sync and async variants) to manage program certificates, integrates them into the existing Program resource, and provides comprehensive unit and end-to-end test coverage for the new functionality.

Changes

Cohort / File(s) Summary
Configuration
e2e_config.test.json
Added a new configuration entry "program.certificate.id": "CER-9646-2171-8417" for E2E testing.
Certificate Resource Module
mpt_api_client/resources/program/certificates.py
New module implementing Certificate model with 12 typed attributes, CertificateServiceConfig with endpoint /public/v1/program/certificates, and both CertificateService and AsyncCertificateService classes. Each service includes a terminate(resource_id, resource_data=None) method that posts to /{resource_id}/terminate.
Program Resource Integration
mpt_api_client/resources/program/program.py
Added certificates property to Program and AsyncProgram classes that instantiates and returns the corresponding certificate service using the shared http_client.
E2E Test Infrastructure
tests/e2e/program/certificate/conftest.py
Added four pytest fixtures: certificate_id, invalid_certificate_id, certificate_data, and terminated_certificate_data_factory to support certificate E2E tests.
E2E Tests
tests/e2e/program/certificate/test_async_certificate.py, test_sync_certificate.py
Added comprehensive E2E test suites covering retrieval by valid/invalid ID, listing with pagination, filtering, creation, updating, and termination of certificates. Both modules include shared fixtures for certificate lifecycle management with automatic cleanup.
Unit Tests
tests/unit/resources/program/test_certificates.py, test_program.py
Added unit test coverage for Certificate model validation (primitive, nested, and optional fields), service method mocking, and updated program service property tests to validate certificate service integration.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Jira Issue Key In Title ✅ Passed The PR title contains the Jira issue key MPT-20436 in the correct MPT-XXXX format.
Test Coverage Required ✅ Passed PR modifies code files and includes comprehensive test coverage with unit tests, async/sync E2E tests, and fixtures.
Single Commit Required ✅ Passed The PR contains exactly one commit (2f156fc) with message 'Added endpoints and e2e tests for program certificates' encompassing all changes.

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


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

@sonarqubecloud
Copy link
Copy Markdown

Copy link
Copy Markdown

@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.

🧹 Nitpick comments (3)
tests/e2e/program/certificate/test_sync_certificate.py (1)

22-25: Consider asserting resource identity/field changes, not only existence.

Upgrading these checks will make failures more actionable and reduce false positives in E2E coverage.

Example assertion upgrades
 def test_get_certificate_by_id(mpt_vendor, certificate_id):
     result = mpt_vendor.program.certificates.get(certificate_id)

-    assert result is not None
+    assert result is not None
+    assert result.id == certificate_id
...
 def test_update_certificate(mpt_client, created_certificate):
@@
-    assert result is not None
+    assert result is not None
+    assert result.id == created_certificate.id
+    assert result.name == updated_name
...
 def test_terminate_certificate(
@@
-    assert result is not None
+    assert result is not None
+    assert result.id == created_certificate.id

Also applies to: 61-67, 70-79

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

In `@tests/e2e/program/certificate/test_sync_certificate.py` around lines 22 - 25,
The test currently only asserts non-None for test_get_certificate_by_id; change
it to assert concrete fields/identity from the retrieved object (e.g., that
result.id == certificate_id and other important fields like result.status,
result.owner or result.updated_at match expected values or types) so failures
are actionable; update the same pattern in the other certificate tests
referenced (the blocks around lines 61-67 and 70-79) that call
mpt_vendor.program.certificates.get/create/list to validate key fields and
identities rather than only existence.
tests/unit/resources/program/test_certificates.py (1)

51-69: Prefer semantic JSON assertions over raw-byte equality in request checks.

The current byte-level check is brittle to harmless serialization changes. Compare decoded JSON payloads instead.

Proposed refactor
+import json
...
-    request_expected_content = b'{"id":"CER-123","status":"updated"}'
...
-        assert request.content == request_expected_content
+        assert json.loads(request.content) == input_status
...
-    request_expected_content = b'{"id":"CER-123","status":"updated"}'
...
-        assert request.content == request_expected_content
+        assert json.loads(request.content) == input_status

Also applies to: 80-98

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

In `@tests/unit/resources/program/test_certificates.py` around lines 51 - 69,
Replace the brittle byte-level assertion of request.content with a semantic JSON
comparison: decode the request body (e.g., via json.loads(request.content) or
request.json()) and assert the resulting dict equals the expected payload dict
(use a variable like request_expected_data instead of request_expected_content).
Update the assertion in the certificate_service.terminate test (replace assert
request.content == request_expected_content) and make the same change for the
similar assertions referenced at the other occurrence (lines around 80-98) to
compare decoded JSON dicts instead of raw bytes.
tests/e2e/program/certificate/test_async_certificate.py (1)

26-30: Consider strengthening assertions beyond non-null checks.

These tests would catch regressions earlier if they validated returned identifiers/updated fields/status transitions instead of only existence.

Example assertion upgrades
 async def test_get_certificate_by_id(async_mpt_vendor, certificate_id):
     result = await async_mpt_vendor.program.certificates.get(certificate_id)

-    assert result is not None
+    assert result is not None
+    assert result.id == certificate_id
...
 async def test_update_certificate(async_mpt_client, created_certificate):
@@
-    assert result is not None
+    assert result is not None
+    assert result.id == created_certificate.id
+    assert result.name == updated_name
...
 async def test_terminate_certificate(
@@
-    assert result is not None
+    assert result is not None
+    assert result.id == created_certificate.id

Also applies to: 65-71, 74-83

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

In `@tests/e2e/program/certificate/test_async_certificate.py` around lines 26 -
30, The test test_get_certificate_by_id currently only asserts non-null; update
it to validate key certificate fields to catch regressions: assert that
result.id equals the input certificate_id, assert expected status (e.g.,
"issued" or the known default/state transition for this fixture), and assert
important fields like result.owner_id or result.updated_at/created_at are
present and well-formed (not None/empty). Apply the same pattern to the other
certificate tests referenced (the blocks around lines 65-71 and 74-83) by
replacing bare existence checks with explicit assertions of identifiers, status
transitions, and at least one changed/updated field to ensure the API returns
correct data. Ensure you reference the async_mpt_vendor.program.certificates.get
call and the certificate_id fixture when adding these assertions.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@tests/e2e/program/certificate/test_async_certificate.py`:
- Around line 26-30: The test test_get_certificate_by_id currently only asserts
non-null; update it to validate key certificate fields to catch regressions:
assert that result.id equals the input certificate_id, assert expected status
(e.g., "issued" or the known default/state transition for this fixture), and
assert important fields like result.owner_id or result.updated_at/created_at are
present and well-formed (not None/empty). Apply the same pattern to the other
certificate tests referenced (the blocks around lines 65-71 and 74-83) by
replacing bare existence checks with explicit assertions of identifiers, status
transitions, and at least one changed/updated field to ensure the API returns
correct data. Ensure you reference the async_mpt_vendor.program.certificates.get
call and the certificate_id fixture when adding these assertions.

In `@tests/e2e/program/certificate/test_sync_certificate.py`:
- Around line 22-25: The test currently only asserts non-None for
test_get_certificate_by_id; change it to assert concrete fields/identity from
the retrieved object (e.g., that result.id == certificate_id and other important
fields like result.status, result.owner or result.updated_at match expected
values or types) so failures are actionable; update the same pattern in the
other certificate tests referenced (the blocks around lines 61-67 and 70-79)
that call mpt_vendor.program.certificates.get/create/list to validate key fields
and identities rather than only existence.

In `@tests/unit/resources/program/test_certificates.py`:
- Around line 51-69: Replace the brittle byte-level assertion of request.content
with a semantic JSON comparison: decode the request body (e.g., via
json.loads(request.content) or request.json()) and assert the resulting dict
equals the expected payload dict (use a variable like request_expected_data
instead of request_expected_content). Update the assertion in the
certificate_service.terminate test (replace assert request.content ==
request_expected_content) and make the same change for the similar assertions
referenced at the other occurrence (lines around 80-98) to compare decoded JSON
dicts instead of raw bytes.

ℹ️ Review info
⚙️ Run configuration

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

Review profile: CHILL

Plan: Pro

Run ID: d5571e80-e555-40b7-be6a-5314e4367d28

📥 Commits

Reviewing files that changed from the base of the PR and between 28ccd6f and 2f156fc.

📒 Files selected for processing (8)
  • e2e_config.test.json
  • mpt_api_client/resources/program/certificates.py
  • mpt_api_client/resources/program/program.py
  • tests/e2e/program/certificate/conftest.py
  • tests/e2e/program/certificate/test_async_certificate.py
  • tests/e2e/program/certificate/test_sync_certificate.py
  • tests/unit/resources/program/test_certificates.py
  • tests/unit/resources/program/test_program.py

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant