camunda / camunda/api-test-generator

feat: Python SDK emitter (`python-sdk`) — lower path-analyser scenarios onto camunda-orchestration-sdk

Open
#133 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

enhancement OCA
Dominant language
TypeScript
Stars
0
Forks
3
Avg merge
13h 41m
Merged PRs (30d)
23

Description

Sub-issue of #8. Implement the python-sdk emitter strategy that lowers path-analyser EndpointScenarioCollection objects onto the camunda-orchestration-sdk Python SDK, generating pytest suites instead of raw Playwright HTTP calls.

Prerequisites

  • Emitter interface — landed (path-analyser/src/codegen/emitter.ts, registry.ts, orchestrator.ts, cli-args.ts) — see #5 / #8
  • Reference implementation — PlaywrightEmitter at path-analyser/src/codegen/playwright/emitter.ts (488 lines). Treat this as the canonical lowering reference.
  • Per-step success-status correctness — successStatusByOp flows through RequestStep.expect.status (PR #52 / Bug A). SDK emitter gets this for free.
  • JS SDK emitter (js-sdk) — recommended to land first as it establishes the SdkMappingSource interface and OperationMapJsonSource implementation.

What to build

A new emitter registered under --target=python-sdk at:

path-analyser/src/codegen/python-sdk/
  emitter.ts            # PythonSdkEmitter implements Emitter
  sdk-mapping.ts        # reuses SdkMappingSource interface; OperationMapJsonSource impl with snake_case transform
  materialize-support.ts  # vendors a self-contained Python project skeleton into <outDir>/
  README.md             # emitter id, env vars, supported scenario shapes

The emitter is written in TypeScript (part of this Node.js generator) but its output is .py test files.

SDK surface
  • Package: camunda-orchestration-sdk (PyPI)
  • Client: CamundaAsyncClient (recommended over sync CamundaClient — matches SDK's own integration tests)
  • Method naming: snake_case — transform from operationId camelCase via camelToSnake()
  • Ergonomic helpers: deploy_resources_from_files and others in generated/.../client.py
  • Codegen hook: hooks/post_gen/
  • Existing helper map: examples/operation-map.jsonregion values are already in snake_case method symbol form
Mapping strategy (Option C from #8)
  1. Load the SDK's examples/operation-map.json (see Open Question 1 on checkout layout).
  2. For each operationId, look up operation-map.json[operationId][0].region — this is the preferred Python method symbol.
  3. If no entry exists, derive raw method by converting operationId camelCase → snake_case.
  4. Emit result = await client.<method>(args) inside an async def test_... function.

Reuse the SdkMappingSource interface established by the JS SDK sub-issue; provide a Python-specific OperationMapJsonSource that applies the snake_case naming fallback.

Test framework
  • pytest + pytest-asyncio for async test execution.
  • One conftest.py with a session-scoped client fixture that constructs CamundaAsyncClient from env vars.
  • Each scenario maps to one async def test_<operationId>_<variant>(client) function.
Auth / env vars

The generated suite must support both:

  • Local unauthenticatedCamundaAsyncClient() with no credentials (matches docker-compose.yml setup)
  • OAuth2 / SaaSCAMUNDA_CLIENT_ID, CAMUNDA_CLIENT_SECRET, CAMUNDA_OAUTH_URL per SDK conventions

Document the env-var mapping from the current API_BASE_URL-based setup to SDK-native config.

Emitter behaviour

For each scenario step, the emitter should:

  1. Map RequestStep.operationId → Python method symbol via SdkMappingSource.
  2. Build Python keyword-argument call from RequestStep.body / RequestStep.pathParams / RequestStep.queryParams.
  3. Emit result = await client.<method>(<kwargs>).
  4. Extract response fields into a ctx dict using equivalent Python extraction logic.
  5. Assert response shape using Python assert statements or a Python port of the assertion pattern.
Emitted file structure
<outDir>/
  conftest.py                   # client fixture
  requirements.txt              # camunda-orchestration-sdk, pytest, pytest-asyncio
  pytest.ini / pyproject.toml   # asyncio_mode = auto
  activate_jobs/
    test_activate_jobs.py
  create_process_instance/
    test_create_process_instance.py
  ...

Open questions to resolve (from #8)

  1. SDK-repo checkout layout — same question as the JS sub-issue. Decision made there should be applied consistently here. Options: sparse clone, published PyPI sidecar package, local path env var.
  2. Helper-signature mismatch policy — silent fallback to raw method vs. hard-fail. Align with the decision made in the JS sub-issue.
  3. request-validation parity — should request-validation get a Python SDK variant, or path-analyser-only for now?
  4. Python type model — the SDK likely uses Pydantic models for request/response. Investigate whether scenario body shapes map cleanly onto Pydantic constructors, or whether a dict-based fallback is needed for complex/oneOf shapes.
  5. Assertion libraryassert-json-body is JS-only. Determine the Python equivalent (e.g. jsonschema, pydantic model validation, or plain assert comparisons) for response shape assertions.

Layered test requirements (mirrors Bug A pattern from #8)

Per the AGENTS.md red/green/class-scoped rule and the regression-guard pattern in #8:

  1. Layer-1 fixture — one hand-built EndpointScenarioCollection → emitted Python test assertion (in tests/fixtures/planner/).
  2. Layer-2 contractPythonSdkEmitter.emit() purity test: same input → byte-identical .py output (in tests/codegen/python-sdk-emitter.test.ts).
  3. Layer-3 invariant — class-scoped assertion in tests/regression/bundled-spec-invariants.test.ts:
    • Every URL placeholder in the Python SDK suite is either seeded or extracted by an upstream step (mirrors Bug A invariant).
    • operationId keyset of the emitter's call-shape table matches examples/operation-map.json under CI.

Definition of Done

  • PythonSdkEmitter registered under --target=python-sdk via registerEmitter().
  • npm run testsuite:generate -- --target=python-sdk produces a runnable suite for every endpoint in the pinned bundled spec.
  • materialize-support analogue produces a self-contained project (requirements.txt, pyproject.toml/pytest.ini, conftest.py) that runs pip install -r requirements.txt && pytest without referencing this generator.
  • Layer-3 invariant in tests/regression/bundled-spec-invariants.test.ts covers placeholder binding and per-step success-status correctness for the Python SDK suite.
  • operationId keyset of emitter's mapping matches SDK's examples/operation-map.json under CI.
  • path-analyser/src/codegen/python-sdk/README.md documents: emitter id, expected env vars, supported scenario shapes, how to point the strategy at a checked-out SDK repo.
  • npm run lint, tsc --noEmit, and npm test all pass.

Out of scope

  • Live-cluster CI matrix (separate follow-up).
  • Resolving Bug B (#53) and Bug C (#54) — apply same scope filters as Bug A invariant.
  • JS and C# emitters (tracked in their own sub-issues).
  • Sync CamundaClient variant — async-only for now.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with path-analyser/src/codegen/playwright/emitter.ts and the Emitter registration files, then review the JS SDK sub-issue decisions for mapping and checkout layout. Implement and test the new path-analyser/src/codegen/python-sdk/ files, including the fixture, contract, and regression tests named in the issue. Done means --target=python-sdk produces a self-contained pytest suite and npm run lint, tsc --noEmit, and npm test pass.

Written by the indexing model from the issue text.

Assessment

Tech stack
node.js, playwright, python, typescript
Domain
api, testing, tooling
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
42/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.