Skip to content

fix: testops_multi xdist run_id handoff + mask api token in config log (commons part)#498

Merged
gibiw merged 2 commits into
mainfrom
fix/commons-multi-xdist-and-masker
Jun 3, 2026
Merged

fix: testops_multi xdist run_id handoff + mask api token in config log (commons part)#498
gibiw merged 2 commits into
mainfrom
fix/commons-multi-xdist-and-masker

Conversation

@gibiw

@gibiw gibiw commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

Commons-only half of the two-PR split. Pytest counterpart will land in a separate PR pinning this commons release.

Bundles two fixes in qase-python-commons 5.1.2:


Fix 1 — testops_multi run_id handoff (commons side)

Symptom (reported by customer running pytest -n 2 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 in xdist, silently fell back to the local report reporter, and dropped results from TestOps.

Root cause

The xdist controller produces Dict[project_code, run_id]. The pytest worker boundary called QaseCoreReporter.set_run_id(run_id) with that dict shape, but the inner QaseTestOpsMulti.set_run_id(project_code, run_id) requires two positional arguments — so workers crashed with a TypeError that the core reporter swallowed and routed through fallback.

Fix (in this PR)

  • New QaseTestOpsMulti.set_run_ids(run_ids: Dict[str, Union[str, int]]) — bulk setter that seeds every known project at once, warns on unknown codes.
  • QaseCoreReporter.set_run_id(run_id) now dispatches dict input to set_run_ids (via hasattr so non-multi reporters never see a dict).
  • Existing QaseTestOpsMulti.set_run_id(project_code, run_id) API is unchanged for direct callers.

The pytest-side change (JSON serialisation of the lock file + load_run_from_lock dispatch) lives in the companion pytest PR.


Fix 2 — Mask API token in debug config dump

Symptom

QaseCoreReporter.__init__ logged the full config via str(self.config), which serialised testops.api.token in clear text. Any debug log shared with support or pasted into an issue leaked the customer's API key.

Fix

  • New qase.commons.util.token_masker module mirroring the JS contract from qase-javascript-commons/src/utils/token-masker.ts.
  • Tokens of 8+ chars render as abc****wxyz (first 3 + last 4).
  • Tokens of 7 chars or fewer are fully replaced with *.
  • Falls back to plain str(config) if the serialised form cannot be parsed as JSON, so a bug in the masker can never silence debug output.

Test plan

  • 247 unit tests passed, including:
    • 3 new set_run_ids cases (bulk, string→int, unknown-project warning).
    • 3 new QaseCoreReporter.set_run_id dispatch cases (dict → set_run_ids, scalar → set_run_id, TypeError fallback).
    • 10 new token_masker cases (long/short token edges, defensive non-string input, sanitize roundtrip, unparseable fallback).
  • End-to-end smoke against real Qase API in examples/multiproject/pytest with pytest -n 2:
    • Baseline on main: 4× missing 1 required positional argument errors, 3× full token leak in log, 4 silent fallback JSON files.
    • With this commons fix + pytest counterpart: 0 errors, token shown as 90e****ce5a, all 12 worker results land in DEVX #895 / DEMO #714.

Release

  • Bumped qase-python-commons 5.1.1 → 5.1.2.

Companion PR

  • qase-pytest half (lock-file JSON serialisation + dep pin to qase-python-commons~=5.1.2) ships next.

gibiw added 2 commits June 3, 2026 19:33
…debug log

In testops_multi mode the controller produces a {project_code: run_id}
dict. The xdist worker boundary previously called
QaseTestOpsMulti.set_run_id() with one positional argument, but the
multi setter requires (project_code, run_id), so workers crashed with
"missing 1 required positional argument: 'run_id'" and silently fell
back to local report, dropping results from TestOps.

Add QaseTestOpsMulti.set_run_ids(run_ids: Dict[str, Union[str, int]])
that seeds every known project at once, and route
QaseCoreReporter.set_run_id() to dispatch dict input through it. The
single-project (project_code, run_id) API is unchanged.

Also mask the api token in the QaseCoreReporter debug config dump.
str(self.config) serialised testops.api.token in clear text, so any
debug log shared with support or pasted into an issue leaked the
customer's API key. Add qase.commons.util.token_masker mirroring the
qase-javascript-commons/src/utils/token-masker.ts contract: tokens of
8+ chars render as abc****wxyz, tokens of 7 chars or fewer are fully
hidden, and a serialisation failure falls back to plain str(config) so
the masker can never silence debug output.
@gibiw gibiw merged commit afabf95 into main Jun 3, 2026
37 checks passed
@gibiw gibiw deleted the fix/commons-multi-xdist-and-masker branch June 3, 2026 16:43
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