diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md new file mode 100644 index 00000000..7c7c7905 --- /dev/null +++ b/.github/copilot-instructions.md @@ -0,0 +1,9 @@ +# Copilot Instructions + +This repository is `mpt-api-python-client` — a Python API client for the SoftwareONE +Marketplace Platform API. + +Read `AGENTS.md` in the repository root for the full documentation index, reading order, +key commands, and project structure. + +Quick validation: `make check && make test` diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 00000000..8252d651 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,63 @@ +# AGENTS.md + +This file is the AI assistant entry point for `mpt-api-python-client`. + +## Repository Purpose + +Python API client for the SoftwareONE Marketplace Platform (MPT) API. Provides synchronous +(`MPTClient`) and asynchronous (`AsyncMPTClient`) clients built on httpx, with typed +resource services, mixin-based HTTP operations, and an RQL query builder. + +## Documentation Reading Order + +1. `README.md` — project overview and quick start +2. `docs/architecture.md` — layered architecture, directory structure, key abstractions +3. `docs/testing.md` — test structure, tooling, conventions, how to run tests +4. `docs/contributing.md` — development workflow, coding conventions, linting setup +5. `docs/local-development.md` — Docker setup, Make targets, environment variables + +## Library Usage + +See `docs/PROJECT_DESCRIPTION.md` for installation and usage examples (sync and async). + +## API Reference + +The upstream MPT API is described by the OpenAPI spec: +https://api.s1.show/public/v1/openapi.json + +Use this to understand available endpoints, request/response schemas, and field names. + +## Key Commands + +| Command | Purpose | +|---|---| +| `make build` | Build the Docker development environment | +| `make test` | Run unit tests | +| `make check` | Run all linting and type checks | +| `make check-all` | Run checks + tests | +| `make format` | Auto-format code | + +## Project Structure + +``` +mpt_api_client/ +├── mpt_client.py # MPTClient / AsyncMPTClient entry points +├── http/ # HTTP transport, services, mixins +├── resources/ # API domain modules (catalog, commerce, billing, …) +├── models/ # Response model classes +├── rql/ # RQL query builder +└── exceptions.py # Error hierarchy +``` + +## Shared Standards + +This repository follows shared engineering standards from +[mpt-extension-skills](https://github.com/softwareone-platform/mpt-extension-skills): + +- `standards/python-style-guide.md` +- `standards/testing-standard.md` +- `standards/contributing-standard.md` +- `standards/pull-request-guidelines.md` + + + diff --git a/README.md b/README.md index 0fc1214f..883fba71 100644 --- a/README.md +++ b/README.md @@ -5,121 +5,54 @@ # mpt-api-python-client -A Python client for interacting with the MPT API. +Python API client for the SoftwareONE Marketplace Platform (MPT) API. -## Documentation - -📚 **[Complete Usage Guide](docs/PROJECT_DESCRIPTION.md)** - -## Getting started - -### Prerequisites - -- Docker and Docker Compose plugin (`docker compose` CLI) -- `make` -- [CodeRabbit CLI](https://www.coderabbit.ai/cli) (optional. Used for running review check locally) - -### Make targets overview - -Common development workflows are wrapped in the `Makefile`. Run `make help` to see the list of available commands. - -### How the Makefile works - -The project uses a modular Makefile structure that organizes commands into logical groups: -- **Main Makefile** (`Makefile`): Entry point that automatically includes all `.mk` files from the `make/` directory -- **Modular includes** (`make/*.mk`): Commands are organized by category: - - `common.mk` - Core development commands (build, test, format, etc.) - - `repo.mk` - Repository management and dependency commands - - `migrations.mk` - Database migration commands (Only available in extension repositories) - - `external_tools.mk` - Integration with external tools - - -You can extend the Makefile with your own custom commands creating a `local.mk` file inside make folder. This file is -automatically ignored by git, so your personal commands won't affect other developers or appear in version control. - - -### Setup - -Follow these steps to set up the development environment: - -#### 1. Clone the repository - -```bash -git clone -``` -```bash -cd mpt-api-python-client -``` - -#### 2. Create environment configuration +Provides synchronous (`MPTClient`) and asynchronous (`AsyncMPTClient`) clients built on +[httpx](https://www.python-httpx.org/), with typed resource services, mixin-based HTTP +operations, and an RQL query builder. -Copy the sample environment file and update it with your values: - -```bash -cp .env.sample .env -``` - -Edit the `.env` file with your actual configuration values. See the [Configuration](#configuration) section for details on available variables. - -#### 3. Build the Docker images - -Build the development environment: +## Quick Start ```bash +cp .env.sample .env # configure MPT_API_BASE_URL and MPT_API_TOKEN make build -``` - -This will create the Docker images with all required dependencies and the virtualenv. - -#### 4. Verify the setup - -Run the test suite to ensure everything is configured correctly: - -```bash make test ``` -You're now ready to start developing! See [Running the client](#running-the-client) for next steps. +## Usage +📚 **[Installation & Usage Guide](docs/PROJECT_DESCRIPTION.md)** -## Running the client +```python +from mpt_api_client import MPTClient -Before running, ensure your `.env` file is populated. +client = MPTClient() # reads MPT_API_TOKEN and MPT_API_BASE_URL from environment -```bash -make run +for product in client.catalog.products.iterate(): + print(product.name) ``` -## Developer utilities +## Documentation + +| Document | Description | +|---|---| +| [Architecture](docs/architecture.md) | Layered architecture, directory structure, key abstractions | +| [Contributing](docs/contributing.md) | Development workflow, coding conventions, linting setup | +| [Testing](docs/testing.md) | Test structure, tooling, conventions | +| [Local Development](docs/local-development.md) | Docker setup, Make targets, environment variables | +| [Usage Guide](docs/PROJECT_DESCRIPTION.md) | Installation, sync and async usage examples | +| [MPT OpenAPI Spec](https://api.s1.show/public/v1/openapi.json) | Upstream API contract (endpoints, schemas) | -Useful helper targets during development: +## Key Commands ```bash -make bash # open a bash shell in the app container -make check # run ruff, flake8, and lockfile checks -make check-all # run checks and tests -make format # auto-format code and imports -make review # check the code in the cli by running CodeRabbit +make build # build Docker development environment +make test # run unit tests +make check # run all quality checks (ruff, flake8, mypy) +make check-all # run checks + tests +make format # auto-format code +make bash # open a shell in the container +make run # start an IPython session ``` -## Configuration - -The following environment variables are typically set in `.env`. Docker Compose reads them when using the Make targets described above. - -### Application - -| Environment Variable | Default | Example | Description | -|---------------------------------|---------|-------------------------------------------|-------------------------------------------------------------------------------------------| -| `MPT_API_BASE_URL` | - | `https://portal.softwareone.com/mpt` | SoftwareONE Marketplace API URL | -| `MPT_API_TOKEN` | - | eyJhbGciOiJSUzI1N... | SoftwareONE Marketplace API Token | - -### E2E - -| Environment Variable | Default | Example | Description | -|----------------------------|---------|--------------------------------------|----------------------------------------------| -| `MPT_API_TOKEN_CLIENT` | - | eyJhbGciOiJSUzI1N... | SoftwareONE Marketplace API Client Token | -| `MPT_API_TOKEN_OPERATIONS` | - | eyJhbGciOiJSUzI1N... | SoftwareONE Marketplace API Operations Token | -| `MPT_API_TOKEN_VENDOR` | - | eyJhbGciOiJSUzI1N... | SoftwareONE Marketplace API Vendor Token | -| `RP_API_KEY` | - | pytest_XXXXXXXXXXXXXX | ReportPortal API key | -| `RP_ENDPOINT` | - | `https://reportportal.example.com` | ReportPortal endpoint | -| `RP_LAUNCH` | - | `dev-env` | ReportPortal launch | +Run `make help` to see all available commands. diff --git a/docs/architecture.md b/docs/architecture.md new file mode 100644 index 00000000..26668fad --- /dev/null +++ b/docs/architecture.md @@ -0,0 +1,197 @@ +# Architecture + +This document describes the internal architecture of `mpt-api-python-client`. + +## Overview + +`mpt-api-python-client` is a Python API client that provides a typed, fluent interface for the +SoftwareONE Marketplace Platform (MPT) REST API. It supports both synchronous and asynchronous +usage and is built on top of [httpx](https://www.python-httpx.org/). + +**API Reference:** The full upstream API contract is described by the +[MPT OpenAPI Spec](https://api.s1.show/public/v1/openapi.json). +The client mirrors this spec's resource structure. + +The client exposes every MPT API domain (catalog, commerce, billing, etc.) as a resource group, +where each resource is a service object composed from reusable HTTP operation mixins. + +## Directory Structure + +``` +mpt_api_client/ +├── __init__.py # Public API: MPTClient, AsyncMPTClient, RQLQuery +├── mpt_client.py # Client entry points +├── constants.py # Shared constants (content types) +├── exceptions.py # Error hierarchy (MPTError, MPTHttpError, MPTAPIError) +│ +├── http/ # HTTP transport layer +│ ├── client.py # Sync HTTPClient (httpx.Client) +│ ├── async_client.py # Async AsyncHTTPClient (httpx.AsyncClient) +│ ├── base_service.py # ServiceBase — shared service logic +│ ├── service.py # Service — sync service (extends ServiceBase) +│ ├── async_service.py # AsyncService — async service (extends ServiceBase) +│ ├── query_state.py # Query parameter accumulation +│ ├── client_utils.py # URL validation helpers +│ ├── types.py # Type aliases (Response, HeaderTypes, etc.) +│ └── mixins/ # Composable HTTP operation mixins +│ ├── collection_mixin.py +│ ├── create_mixin.py +│ ├── create_file_mixin.py +│ ├── update_mixin.py +│ ├── update_file_mixin.py +│ ├── delete_mixin.py +│ ├── get_mixin.py +│ ├── enable_mixin.py +│ ├── disable_mixin.py +│ ├── download_file_mixin.py +│ ├── file_operations_mixin.py +│ ├── queryable_mixin.py +│ └── resource_mixins.py +│ +├── models/ # Response models +│ ├── model.py # Model base class (camelCase ↔ snake_case mapping) +│ ├── collection.py # Collection[Model] — paginated result set +│ ├── meta.py # Meta / Pagination metadata +│ └── file_model.py # FileModel for binary responses +│ +├── resources/ # API domain modules +│ ├── accounts/ # Account, Users, Buyers, Sellers, API Tokens, … +│ ├── audit/ # Audit records, Event types +│ ├── billing/ # Invoices, Ledgers, Journals, Statements, Credit memos, … +│ ├── catalog/ # Products, Listings, Price lists, Authorizations, … +│ ├── commerce/ # Agreements, Orders, Subscriptions, Assets +│ ├── helpdesk/ # Cases, Chats, Queues, Forms, … +│ └── notifications/ # Messages, Batches, Subscribers, … +│ +└── rql/ # RQL query builder + ├── query_builder.py # RQLQuery, RQLProperty, RQLValue + └── constants.py # RQL operator constants +``` + +## Layered Architecture + +The client is organized into four layers: + +``` +┌─────────────────────────────────────────────┐ +│ MPTClient / AsyncMPTClient │ Entry point +├─────────────────────────────────────────────┤ +│ Resource Groups (domains) │ catalog, commerce, billing, … +├─────────────────────────────────────────────┤ +│ Service + Mixins (HTTP operations) │ get, create, update, delete, iterate, … +├─────────────────────────────────────────────┤ +│ HTTPClient / AsyncHTTPClient │ httpx transport +└─────────────────────────────────────────────┘ +``` + +### 1. Client Layer — `mpt_client.py` + +`MPTClient` (sync) and `AsyncMPTClient` (async) are the public entry points. + +Each client holds an HTTP client instance and exposes domain-specific resource groups as +properties: + +```python +client = MPTClient.from_config(api_token="...", base_url="...") +client.catalog # → Catalog +client.commerce # → Commerce +client.billing # → Billing +client.accounts # → Accounts +client.audit # → Audit +client.helpdesk # → Helpdesk +client.notifications # → Notifications +``` + +### 2. Resource Groups — `resources/` + +Each resource group (e.g. `Catalog`, `Commerce`) is a plain class that groups related service +objects. For example, `Catalog` exposes `products`, `listings`, `price_lists`, +`authorizations`, `pricing_policies`, `items`, and `units_of_measure`. + +Resource groups pass the HTTP client down to each service. + +### 3. Service Layer — `http/service.py`, `http/async_service.py` + +`Service` and `AsyncService` extend `ServiceBase` and represent a single REST resource +endpoint (e.g. `/catalog/products`). + +Services are composed using **mixins** that add HTTP operations: + +| Mixin | Operation | +|---|---| +| `CollectionMixin` | `iterate()` — paginated listing | +| `GetMixin` | `get(id)` — retrieve single resource | +| `CreateMixin` | `create(data)` — create resource | +| `UpdateMixin` | `update(id, data)` — update resource | +| `DeleteMixin` | `delete(id)` — delete resource | +| `CreateFileMixin` | create with file upload | +| `UpdateFileMixin` | update with file upload | +| `DownloadFileMixin` | download binary content | +| `EnableMixin` / `DisableMixin` | enable/disable actions | +| `QueryableMixin` | `filter()`, `order_by()`, `select()` — RQL query chaining | + +Example service definition: + +```python +class ProductsService( + Service[Model], + CollectionMixin, + GetMixin, + CreateFileMixin, + UpdateFileMixin, + DeleteMixin, +): + _endpoint = "/catalog/products" + _model_class = Model +``` + +### 4. HTTP Transport — `http/client.py`, `http/async_client.py` + +`HTTPClient` and `AsyncHTTPClient` wrap `httpx.Client` / `httpx.AsyncClient` with: + +- automatic Bearer token authentication +- base URL resolution +- retry transport (configurable) +- error transformation into `MPTHttpError` / `MPTAPIError` +- multipart file upload support + +Configuration is read from constructor arguments or environment variables +(`MPT_API_TOKEN`, `MPT_API_BASE_URL`). + +## Cross-Cutting Concerns + +### RQL Query Builder — `rql/` + +`RQLQuery` provides a fluent, type-safe builder for +[Resource Query Language](https://doc.mpt.softwareone.com) filter expressions: + +```python +from mpt_api_client import RQLQuery + +query = RQLQuery(status="active", product__id="PRD-123") +``` + +The query builder is used by `QueryableMixin.filter()` to chain filters immutably. + +### Model Layer — `models/` + +`Model` is a lightweight base class that: + +- converts API responses from `camelCase` to `snake_case` attribute access +- supports nested model parsing +- provides `to_dict()` serialization back to `camelCase` + +`Collection[Model]` wraps paginated API responses with metadata (`Meta`, `Pagination`). + +### Error Handling — `exceptions.py` + +All API errors are wrapped in a hierarchy: + +``` +MPTError +├── MPTHttpError # generic HTTP error (status_code, message, body) +│ └── MPTAPIError # structured API error (payload, title, detail, trace_id) +``` + + + diff --git a/docs/contributing.md b/docs/contributing.md new file mode 100644 index 00000000..f7df14f1 --- /dev/null +++ b/docs/contributing.md @@ -0,0 +1,81 @@ +# Contributing + +This document describes the development workflow and coding conventions for +`mpt-api-python-client`. + +## Development Workflow + +1. Create a feature branch from `main`. +2. Make your changes. +3. Run `make check` to validate code quality. +4. Run `make test` to run the test suite. +5. Open a pull request targeting `main`. + +## Coding Conventions + +### Python Style + +- **Python 3.12+** — use modern syntax (type parameter syntax `[T]`, `type` statements, `|` unions). +- **Type annotations** — all public functions, methods, and module-level variables must be fully typed. +- **Docstrings** — use [Google style](https://google.github.io/styleguide/pyguide.html#38-comments-and-docstrings) for all public classes and functions. +- **Line length** — 100 characters maximum. +- **Quotes** — double quotes for strings. +- **Imports** — sorted by `isort` (via `ruff`), one import per line for `from` imports. + +### Linting and Formatting + +The project uses a multi-tool linting setup. All checks run inside Docker via `make check`. + +| Tool | Purpose | +|---|---| +| `ruff` | Linting and import sorting (primary linter) | +| `ruff format` | Code formatting | +| `flake8` + `wemake-python-styleguide` | Additional complexity and style checks | +| `flake8-aaa` | Enforce Arrange-Act-Assert pattern in tests | +| `mypy` | Static type checking (strict mode) | + +Run formatting: + +```bash +make format # auto-format code with ruff +``` + +Run all quality checks: + +```bash +make check # ruff format --check, ruff check, flake8, mypy, uv lock --check +``` + +### Adding Dependencies + +```bash +make uv-add pkg= # add a production dependency +make uv-add-dev pkg= # add a dev dependency +make uv-upgrade # upgrade all packages +make uv-upgrade pkg= # upgrade a specific package +``` + +All dependency changes rebuild the Docker image automatically. + +### Code Organization + +When adding new API resources: + +1. Create a new module under `mpt_api_client/resources//`. +2. Define sync and async service classes extending `Service` / `AsyncService` with appropriate mixins. +3. Register the service in the domain's group class (e.g. `Catalog`, `Commerce`). +4. Expose the domain group in `MPTClient` / `AsyncMPTClient` if it is a new domain. +5. Add corresponding unit tests under `tests/unit/resources//`. + +## Shared Standards + +This repository follows shared engineering standards maintained in +[mpt-extension-skills](https://github.com/softwareone-platform/mpt-extension-skills): + +- → `standards/python-style-guide.md` +- → `standards/contributing-standard.md` +- → `standards/pull-request-guidelines.md` + +Repository-specific conventions are documented in this file. Where this document is silent, +the shared standards apply. + diff --git a/docs/local-development.md b/docs/local-development.md new file mode 100644 index 00000000..33aa05b8 --- /dev/null +++ b/docs/local-development.md @@ -0,0 +1,107 @@ +# Local Development + +This document describes how to set up and run `mpt-api-python-client` locally. + +## Prerequisites + +- **Docker** and **Docker Compose** plugin (`docker compose` CLI) +- **Make** +- [CodeRabbit CLI](https://www.coderabbit.ai/cli) (optional — used for running review checks locally) + +## Setup + +### 1. Clone the repository + +```bash +git clone +cd mpt-api-python-client +``` + +### 2. Create environment configuration + +```bash +cp .env.sample .env +``` + +Edit `.env` with your actual values. See [Environment Variables](#environment-variables) below. + +### 3. Build the Docker images + +```bash +make build +``` + +This creates the Docker images with all required dependencies and the virtual environment. + +### 4. Verify the setup + +```bash +make test +``` + +## Running the Client + +Start an interactive IPython session with the client available: + +```bash +make run +``` + +Ensure your `.env` file is populated with valid `MPT_API_BASE_URL` and `MPT_API_TOKEN` values. + +## Make Targets + +Common development workflows are wrapped in the Makefile. Run `make help` to see all available +commands. + +| Command | Description | +|---|---| +| `make build` | Build Docker images | +| `make test` | Run unit tests | +| `make test args="tests/e2e"` | Run end-to-end tests | +| `make check` | Run all quality checks (ruff, flake8, mypy, lockfile) | +| `make check-all` | Run quality checks + unit tests | +| `make format` | Auto-format code and imports | +| `make bash` | Open a shell in the app container | +| `make run` | Start an IPython session | +| `make uv-add pkg=` | Add a production dependency | +| `make uv-add-dev pkg=` | Add a dev dependency | +| `make uv-upgrade` | Upgrade all dependencies | + +### How the Makefile Works + +The project uses a modular Makefile structure: + +- **`Makefile`** — entry point that includes all `.mk` files from `make/`. +- **`make/common.mk`** — core commands (build, test, format, check). +- **`make/local.mk`** — personal commands, ignored by git. Create this file to add your own + targets without affecting other developers. + +## Environment Variables + +### Application + +| Variable | Default | Example | Description | +|---|---|---|---| +| `MPT_API_BASE_URL` | — | `https://portal.softwareone.com/mpt` | SoftwareONE Marketplace API URL | +| `MPT_API_TOKEN` | — | `eyJhbGciOiJSUzI1N...` | SoftwareONE Marketplace API Token | + +### End-to-End Testing + +| Variable | Default | Example | Description | +|---|---|---|---| +| `MPT_API_TOKEN_CLIENT` | — | `eyJhbGciOiJSUzI1N...` | Client API token | +| `MPT_API_TOKEN_OPERATIONS` | — | `eyJhbGciOiJSUzI1N...` | Operations API token | +| `MPT_API_TOKEN_VENDOR` | — | `eyJhbGciOiJSUzI1N...` | Vendor API token | +| `RP_API_KEY` | — | `pytest_XXXX` | ReportPortal API key | +| `RP_ENDPOINT` | — | `https://reportportal.example.com` | ReportPortal endpoint | +| `RP_LAUNCH` | — | `dev-env` | ReportPortal launch name | + +## Docker + +The development environment runs entirely inside Docker: + +- **Base image**: `ghcr.io/astral-sh/uv:python3.12-bookworm-slim` +- **Package manager**: [uv](https://docs.astral.sh/uv/) +- **Services**: defined in `compose.yaml` — a single `app` service that mounts the project directory. + diff --git a/docs/testing.md b/docs/testing.md new file mode 100644 index 00000000..6a731c43 --- /dev/null +++ b/docs/testing.md @@ -0,0 +1,128 @@ +# Testing + +This document describes the testing strategy, tooling, and conventions for +`mpt-api-python-client`. + +## Test Structure + +``` +tests/ +├── unit/ # Fast, isolated unit tests (default target) +│ ├── conftest.py # Shared fixtures (http_client, async_http_client, DummyModel) +│ ├── http/ # Tests for HTTP transport, services, and mixins +│ ├── models/ # Tests for Model, Collection, Meta +│ ├── resources/ # Tests for each resource domain (accounts, catalog, …) +│ ├── rql/ # Tests for the RQL query builder +│ ├── test_constants.py +│ ├── test_exceptions.py +│ └── test_mpt_client.py +│ +└── e2e/ # End-to-end tests against a live MPT API environment + ├── conftest.py # E2E fixtures (mpt_vendor, mpt_client, mpt_operations) + ├── accounts/ + ├── audit/ + ├── billing/ + ├── catalog/ + ├── commerce/ + ├── helpdesk/ + └── notifications/ +``` + +## Running Tests + +All test commands run inside Docker via the Makefile. + +```bash +make test # run unit tests (default) +make test args="tests/e2e" # run end-to-end tests +make test args="tests/unit/http" # run a specific test directory +make test args="-k test_create" # run tests matching a pattern +make check-all # run quality checks + unit tests +``` + +## Tooling + +| Tool | Purpose | +|---|---| +| `pytest` | Test runner | +| `pytest-asyncio` | Async test support (auto mode) | +| `respx` | Mock `httpx.AsyncClient` responses | +| `responses` | Mock `httpx.Client` responses (sync) | +| `pytest-mock` | `mocker` fixture for patching | +| `freezegun` | Freeze time in tests | +| `pyfakefs` | Fake filesystem for file operation tests | +| `pytest-cov` | Coverage reporting | +| `pytest-xdist` | Parallel test execution | +| `pytest-randomly` | Randomize test order | +| `flake8-aaa` | Enforce Arrange-Act-Assert pattern | + +## Writing Tests + +### Conventions + +- **Function-based tests** — use plain `test_` functions, not test classes. +- **Arrange-Act-Assert (AAA)** — structure every test with the AAA pattern. The `flake8-aaa` + plugin enforces this automatically. +- **Fixture-based setup** — use `pytest` fixtures for shared setup (HTTP clients, mock data). +- **No docstrings required** — test function names should be descriptive enough. +- **Magic numbers allowed** — `PLR2004` and `WPS432` are suppressed in test files. + +### Unit Test Example + +```python +import pytest +import respx +from httpx import Response + +def test_get_product(async_http_client): + # Arrange + respx.get("/catalog/products/PRD-001").mock( + return_value=Response(200, json={"id": "PRD-001", "name": "Test"}) + ) + + # Act + result = async_http_client.request("GET", "/catalog/products/PRD-001") + + # Assert + assert result.status_code == 200 +``` + +### End-to-End Tests + +E2E tests run against a live MPT API. They require the following environment variables: + +| Variable | Description | +|---|---| +| `MPT_API_BASE_URL` | MPT API base URL | +| `MPT_API_TOKEN_VENDOR` | Vendor API token | +| `MPT_API_TOKEN_CLIENT` | Client API token | +| `MPT_API_TOKEN_OPERATIONS` | Operations API token | + +Optional ReportPortal integration: + +| Variable | Description | +|---|---| +| `RP_API_KEY` | ReportPortal API key | +| `RP_ENDPOINT` | ReportPortal endpoint URL | +| `RP_LAUNCH` | ReportPortal launch name | + +E2E test configuration is defined in `e2e_config.test.json`. + +## Coverage + +Coverage is collected automatically during test runs via `pytest-cov`. + +- Source: `mpt_api_client/` +- Reports: terminal (missing lines) + XML (`coverage.xml`) +- Branch coverage is enabled +- `__init__.py` files are excluded from coverage + +Coverage results are reported to SonarCloud via `sonar-project.properties`. + +## Shared Standards + +This repository follows the shared testing standard from +[mpt-extension-skills](https://github.com/softwareone-platform/mpt-extension-skills): + +- → `standards/testing-standard.md` +