Skip to content

fix: testops_multi run_id handoff for pytest-xdist workers (pytest part)#499

Merged
gibiw merged 4 commits into
mainfrom
fix/pytest-multi-xdist-runid
Jun 4, 2026
Merged

fix: testops_multi run_id handoff for pytest-xdist workers (pytest part)#499
gibiw merged 4 commits into
mainfrom
fix/pytest-multi-xdist-runid

Conversation

@gibiw

@gibiw gibiw commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

Pytest-only half of the two-PR split. Companion to #498 (commons).

Important

Do not merge this PR until #498 is merged and qase-python-commons 5.1.2 is published to PyPI. This PR pins qase-python-commons~=5.1.2 and will not install otherwise.


Symptom (customer report)

Running pytest tests/examples/test_login.py -n 2 -s in testops_multi mode:

[Qase][...][info] Failed to set run id
[Qase][...][error] QaseTestOpsMulti.set_run_id() missing 1 required positional argument: 'run_id'
[Qase][...][warning] No run_id for project DW, skipping send

Workers crashed, silently fell back to the local report reporter, and dropped results from TestOps.

Root cause (pytest side)

  1. QasePytestPlugin.pytest_sessionstart (controller branch) wrote the run id via str(self.run_id). In testops_multi mode self.run_id is a dict, so the lock file held "{'DW': 1553}" — not round-trippable.
  2. QasePytestPlugin.load_run_from_lock (worker branch) read that string back and called self.reporter.set_run_id(self.run_id) with one positional argument, which crashed inside the commons multi reporter.

Fix in this PR

  • pytest_sessionstart now writes the run id via json.dumps, so the dict shape survives the controller → worker boundary.
  • load_run_from_lock parses with json.loads; the result is a dict in multi mode and a scalar (string) in single mode. Falls back to treating the raw read as a string if the file is in the legacy non-JSON format, so older lock files still load.
  • The parsed value is handed to QaseCoreReporter.set_run_id, which in commons 5.1.2 dispatches dict input to QaseTestOpsMulti.set_run_ids() (the companion change in fix: testops_multi xdist run_id handoff + mask api token in config log (commons part) #498).

Dependency pin

Bumped qase-python-commons minimum from ~=5.1.1 to ~=5.1.2 in pyproject.toml. Without 5.1.2, the dict reaches the commons reporter without a matching set_run_ids() method and the fix is silently dead.

Test plan

  • 115 unit tests passed, including 5 new lock-file round-trip cases:
    • Controller writes dict run id as JSON.
    • Controller writes scalar run id as JSON.
    • Worker loads dict run id and seeds the reporter with the dict.
    • Worker loads scalar run id as a string (legacy contract preserved).
    • Worker tolerates a legacy non-JSON lock file.
  • 10 integration tests passed.
  • End-to-end smoke against real Qase API in examples/multiproject/pytest with pytest -n 2:

Release

  • Bumped qase-pytest 8.3.0 → 8.3.1.
  • Pinned qase-python-commons~=5.1.2 so new installs always pull the matching commons release.

Companion PR

gibiw added 4 commits June 3, 2026 19:35
In testops_multi mode, the xdist controller's reporter produces a
{project_code: run_id} dict, but pytest_sessionstart wrote it via
str(self.run_id) and load_run_from_lock read it back as a single
positional string. Workers then crashed in
QaseTestOpsMulti.set_run_id() with
"missing 1 required positional argument: 'run_id'", silently fell
through the QaseCoreReporter fallback path, and dropped every result
into local JSON files instead of TestOps.

Serialise the run id via json.dumps in the controller and parse it
back with json.loads in load_run_from_lock; if the lock file is in the
legacy non-JSON shape (a bare numeric string from a previous run),
fall back to treating it as a scalar so single-project workers keep
working. Pass the parsed value through to
QaseCoreReporter.set_run_id(), which in commons 5.1.2 dispatches dict
input to QaseTestOpsMulti.set_run_ids() so every project is seeded in
each worker.
The xdist fix in plugin.py only works when paired with
QaseTestOpsMulti.set_run_ids() and the dict-aware
QaseCoreReporter.set_run_id() dispatch added in qase-python-commons
5.1.2. Pin the minimum so new installs of qase-pytest 8.3.1 never end
up with the older commons release where workers would silently fall
back to the local report.
The 3-day PIP_UPLOADED_PRIOR_TO quarantine in the matrix test job
blocked cross-package PRs that depended on a freshly-published
qase-python-commons release: pytest pinning qase-python-commons~=5.1.2
could not be installed for up to 72 hours after the commons release
landed on PyPI, even though the package is published from this same
repository's pipeline.

Prefetch qase-python-commons, qase-api-client and qase-api-v2-client
into a find-links cache with PIP_UPLOADED_PRIOR_TO unset, then export
PIP_FIND_LINKS so subsequent pip invocations - including those tox
spawns inside its venv - resolve those packages from the local cache.
PIP_UPLOADED_PRIOR_TO operates on PyPI metadata (upload_time_iso_8601),
which local wheels do not carry, so the find-links candidates are
honoured even when the index-side filter would have removed them.

External dependencies remain subject to the cutoff so the supply-chain
hardening for third-party packages is unchanged.
@gibiw gibiw merged commit b363de7 into main Jun 4, 2026
37 checks passed
@gibiw gibiw deleted the fix/pytest-multi-xdist-runid branch June 4, 2026 06:57
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