PTHMINT-117: terminals and terminal-groups APIs and tests#56
Conversation
Introduce Terminal and TerminalGroup API surface: TerminalManager and TerminalGroupManager, request/response models (CreateTerminalRequest, Terminal), and listing/creation flows. Expose new SDK getters (get_terminal_manager, get_terminal_group_manager). Add example scripts for creating and listing terminals and listing terminals by group, plus dedicated example E2E fixtures that selectively skip tests when required env vars are missing. Include comprehensive unit and E2E tests covering serialization, option filtering, auth scopes, endpoint URLs, and response mapping. Also adjust shared e2e conftest validation signature to accept env name for clearer error messages.
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #56 +/- ##
==========================================
+ Coverage 92.83% 92.96% +0.12%
==========================================
Files 164 172 +8
Lines 2889 3027 +138
==========================================
+ Hits 2682 2814 +132
- Misses 207 213 +6 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
Add a pytest_collection_modifyitems hook in tests/multisafepay/e2e/conftest.py to skip all E2E tests when the E2E_API_KEY environment variable is not set, avoiding hard errors from real API calls during fixture setup. Also introduce EXAMPLE_E2E_NODE_PREFIX in tests/multisafepay/e2e/examples/conftest.py and restrict the example conftest's collection logic to only operate on example E2E tests.
There was a problem hiding this comment.
Pull request overview
Adds POS terminal (/json/terminals) and terminal-group (/json/terminal-groups/{id}/terminals) support to the SDK, including managers, request/response models, examples, and unit + E2E tests. The new managers route reads through the partner_affiliate auth scope of ScopedCredentialResolver, while terminal creation uses the default scope.
Changes:
- New
TerminalManager(list + create) andTerminalGroupManager(list by group),CreateTerminalRequestandTerminalmodels, plus SDK getters. - Example scripts and dedicated E2E tests with a separate
examples/conftest.pythat wires a terminals-specific SDK (custom base URL, partner key) and resolves a group id from a terminal id. - Generalized
_validate_e2e_base_urlto take the env var name for clearer error messages, plus README/.env.example updates.
Reviewed changes
Copilot reviewed 23 out of 24 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| src/multisafepay/api/paths/terminals/terminal_manager.py | New manager for listing/creating terminals |
| src/multisafepay/api/paths/terminals/request/create_terminal_request.py | Builder request model with provider whitelist |
| src/multisafepay/api/paths/terminals/response/terminal.py | New Terminal response model |
| src/multisafepay/api/paths/terminals/{init,request/init,response/init}.py | Package exports for terminals |
| src/multisafepay/api/paths/terminal_groups/terminal_group_manager.py | New manager for listing terminals by group |
| src/multisafepay/api/paths/terminal_groups/init.py | Package export |
| src/multisafepay/sdk.py | Exposes get_terminal_manager and get_terminal_group_manager |
| tests/multisafepay/unit/test_unit_sdk.py | Asserts new SDK getters return the right managers |
| tests/multisafepay/unit/api/path/terminals/... | Unit tests for terminal manager, request, response |
| tests/multisafepay/unit/api/path/terminal_groups/... | Unit tests for terminal group manager |
| tests/multisafepay/e2e/conftest.py | _validate_e2e_base_url now takes the env name |
| tests/multisafepay/e2e/examples/conftest.py | New fixtures + selective skip for terminal example E2E tests |
| tests/multisafepay/e2e/examples/terminal_manager/test_get_terminals.py | E2E for get_terminals example |
| tests/multisafepay/e2e/examples/terminal_group_manager/test_get_terminals_by_group.py | E2E for get_terminals_by_group example |
| examples/terminal_manager/{create,get_terminals}.py | Example usage for terminal create/list |
| examples/terminal_group_manager/get_terminals_by_group.py | Example usage for terminal-group listing |
| README.md | Docs for terminal managers and E2E setup |
| .env.example | Trailing newline fix |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| def pytest_collection_modifyitems( | ||
| config: pytest.Config, # noqa: ARG001 | ||
| items: list[pytest.Item], | ||
| ) -> None: | ||
| """Skip example E2E tests when the required credentials are missing.""" | ||
| has_default_e2e = bool(os.getenv(DEFAULT_E2E_API_KEY_ENV, "").strip()) | ||
| has_terminal_e2e = _has_terminal_e2e_env() | ||
|
|
||
| if has_default_e2e and has_terminal_e2e: | ||
| return | ||
|
|
||
| default_skip = pytest.mark.skip( | ||
| reason=f"E2E tests require {DEFAULT_E2E_API_KEY_ENV} (not set)", | ||
| ) | ||
| terminal_skip = pytest.mark.skip( | ||
| reason=( | ||
| "Terminal endpoint E2E tests require API_KEY, " | ||
| "MSP_SDK_CUSTOM_BASE_URL, and E2E_CLOUD_POS_TERMINAL_ID " | ||
| "(not set)" | ||
| ), | ||
| ) | ||
| for item in items: | ||
| if not item.nodeid.startswith(EXAMPLE_E2E_NODE_PREFIX): | ||
| continue | ||
|
|
||
| if item.nodeid.startswith(TERMINAL_E2E_NODE_PREFIXES): | ||
| if has_terminal_e2e: | ||
| continue | ||
| item.add_marker(terminal_skip) | ||
| continue | ||
|
|
||
| if not has_default_e2e: | ||
| item.add_marker(default_skip) |
| body={ | ||
| "success": True, | ||
| "data": { | ||
| "terminal_id": "T-001", | ||
| "name": "Demo POS Terminal", | ||
| "group_id": 42, | ||
| "active": True, | ||
| "status": "active", | ||
| "provider": "CTAP", | ||
| }, | ||
| }, | ||
| ) | ||
|
|
||
|
|
||
| def _build_listing_api_response() -> ApiResponse: | ||
| return ApiResponse( | ||
| headers={}, | ||
| status_code=200, | ||
| body={ | ||
| "success": True, | ||
| "data": [ | ||
| { | ||
| "terminal_id": "T-001", | ||
| "name": "Terminal 1", | ||
| "group_id": 42, | ||
| "active": True, | ||
| "status": "active", | ||
| "provider": "CTAP", | ||
| }, | ||
| ], |
Use partner-affiliate credentials when creating terminals by passing AuthScope(ScopedCredentialResolver.AUTH_SCOPE_PARTNER_AFFILIATE) to create_post_request. Change CreateTerminalRequest.add_group_id to accept an int and update unit tests accordingly. Update tests and response handling to expect 'id' instead of 'terminal_id'. Add a timestamp to the example terminal name to avoid name collisions. Adjust e2e conftest skip logic by introducing TERMINAL_EXAMPLE_NODE_PREFIXES so terminal/example tests can opt into their own credential detection.
| # Optional: set when you want to skip automatic terminal-group lookup | ||
| export CLOUD_POS_TERMINAL_GROUP_ID="<terminal_group_id>" |
| def _has_terminal_e2e_env() -> bool: | ||
| return bool( | ||
| _get_first_env(TERMINAL_DEFAULT_API_KEY_ENV) | ||
| and _get_first_env(TERMINAL_CUSTOM_BASE_URL_ENV) | ||
| and _get_first_env(TERMINAL_E2E_TERMINAL_ID_ENV), | ||
| ) |
This pull request introduces a comprehensive implementation for POS terminal and terminal group management in the SDK, along with example scripts and supporting models. The changes include new managers, request and response models, and integration into the main SDK interface.
SDK Feature Additions:
TerminalManagerandTerminalGroupManagerclasses to provide API operations for POS terminals and terminal groups, including listing and creation endpoints. [1] [2] [3] [4]Sdkclass, exposingget_terminal_managerandget_terminal_group_managermethods for easy access. [1] [2]Request and Response Models:
CreateTerminalRequestfor creating POS terminals, with validation for allowed providers and builder-style methods for setting attributes. [1] [2]Terminalresponse model to represent terminal data returned by the API, including a static method for instantiation from dictionaries. [1] [2]Examples and Usage:
Other Improvements:
_validate_e2e_base_urlnow takes the environment variable name for clearer error messages.