tenzir-test 0.15.0__tar.gz → 1.0.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (153) hide show
  1. {tenzir_test-0.15.0 → tenzir_test-1.0.1}/PKG-INFO +1 -1
  2. tenzir_test-1.0.1/changelog/releases/v1.0.0/entries/auto-detect-tenzir-binary-with-uvx-fallback.md +11 -0
  3. tenzir_test-1.0.1/changelog/releases/v1.0.0/manifest.yaml +8 -0
  4. tenzir_test-1.0.1/changelog/releases/v1.0.0/notes.md +9 -0
  5. tenzir_test-1.0.1/changelog/releases/v1.0.1/entries/tuple-unpacking-in-diff-runner-for-multi-word-commands.md +11 -0
  6. tenzir_test-1.0.1/changelog/releases/v1.0.1/manifest.yaml +5 -0
  7. tenzir_test-1.0.1/changelog/releases/v1.0.1/notes.md +9 -0
  8. {tenzir_test-0.15.0 → tenzir_test-1.0.1}/example-project/README.md +5 -3
  9. {tenzir_test-0.15.0 → tenzir_test-1.0.1}/pyproject.toml +1 -1
  10. {tenzir_test-0.15.0 → tenzir_test-1.0.1}/src/tenzir_test/_python_runner.py +7 -2
  11. {tenzir_test-0.15.0 → tenzir_test-1.0.1}/src/tenzir_test/cli.py +0 -16
  12. tenzir_test-1.0.1/src/tenzir_test/config.py +70 -0
  13. {tenzir_test-0.15.0 → tenzir_test-1.0.1}/src/tenzir_test/engine/state.py +2 -3
  14. {tenzir_test-0.15.0 → tenzir_test-1.0.1}/src/tenzir_test/fixtures/__init__.py +4 -4
  15. {tenzir_test-0.15.0 → tenzir_test-1.0.1}/src/tenzir_test/fixtures/node.py +13 -4
  16. {tenzir_test-0.15.0 → tenzir_test-1.0.1}/src/tenzir_test/run.py +7 -17
  17. {tenzir_test-0.15.0 → tenzir_test-1.0.1}/src/tenzir_test/runners/custom_python_fixture_runner.py +3 -2
  18. {tenzir_test-0.15.0 → tenzir_test-1.0.1}/src/tenzir_test/runners/diff_runner.py +1 -1
  19. tenzir_test-1.0.1/tests/test_config.py +106 -0
  20. {tenzir_test-0.15.0 → tenzir_test-1.0.1}/tests/test_python_runner.py +2 -2
  21. {tenzir_test-0.15.0 → tenzir_test-1.0.1}/tests/test_run.py +10 -14
  22. {tenzir_test-0.15.0 → tenzir_test-1.0.1}/tests/test_run_config.py +9 -8
  23. tenzir_test-0.15.0/src/tenzir_test/config.py +0 -57
  24. tenzir_test-0.15.0/tests/test_config.py +0 -42
  25. {tenzir_test-0.15.0 → tenzir_test-1.0.1}/.gitignore +0 -0
  26. {tenzir_test-0.15.0 → tenzir_test-1.0.1}/LICENSE +0 -0
  27. {tenzir_test-0.15.0 → tenzir_test-1.0.1}/README.md +0 -0
  28. {tenzir_test-0.15.0 → tenzir_test-1.0.1}/changelog/config.yaml +0 -0
  29. {tenzir_test-0.15.0 → tenzir_test-1.0.1}/changelog/releases/v0.11.0/entries/01-expose-tenzir-test-as-a-library.md +0 -0
  30. {tenzir_test-0.15.0 → tenzir_test-1.0.1}/changelog/releases/v0.11.0/manifest.yaml +0 -0
  31. {tenzir_test-0.15.0 → tenzir_test-1.0.1}/changelog/releases/v0.11.0/notes.md +0 -0
  32. {tenzir_test-0.15.0 → tenzir_test-1.0.1}/changelog/releases/v0.12.0/entries/01-honor-no-color-for-python-api-consumers.md +0 -0
  33. {tenzir_test-0.15.0 → tenzir_test-1.0.1}/changelog/releases/v0.12.0/manifest.yaml +0 -0
  34. {tenzir_test-0.15.0 → tenzir_test-1.0.1}/changelog/releases/v0.12.0/notes.md +0 -0
  35. {tenzir_test-0.15.0 → tenzir_test-1.0.1}/changelog/releases/v0.13.0/entries/01-improve-diagnostics-when-tenzir-node-fails-to-start.md +0 -0
  36. {tenzir_test-0.15.0 → tenzir_test-1.0.1}/changelog/releases/v0.13.0/entries/01-library-mode-and-package-dirs.md +0 -0
  37. {tenzir_test-0.15.0 → tenzir_test-1.0.1}/changelog/releases/v0.13.0/manifest.yaml +0 -0
  38. {tenzir_test-0.15.0 → tenzir_test-1.0.1}/changelog/releases/v0.13.0/notes.md +0 -0
  39. {tenzir_test-0.15.0 → tenzir_test-1.0.1}/changelog/releases/v0.13.1/entries/strip-root-paths-from-diff-runner-output.md +0 -0
  40. {tenzir_test-0.15.0 → tenzir_test-1.0.1}/changelog/releases/v0.13.1/manifest.yaml +0 -0
  41. {tenzir_test-0.15.0 → tenzir_test-1.0.1}/changelog/releases/v0.13.1/notes.md +0 -0
  42. {tenzir_test-0.15.0 → tenzir_test-1.0.1}/changelog/releases/v0.14.0/entries/inline-test-inputs-and-local-inputs-directories.md +0 -0
  43. {tenzir_test-0.15.0 → tenzir_test-1.0.1}/changelog/releases/v0.14.0/manifest.yaml +0 -0
  44. {tenzir_test-0.15.0 → tenzir_test-1.0.1}/changelog/releases/v0.14.0/notes.md +0 -0
  45. {tenzir_test-0.15.0 → tenzir_test-1.0.1}/changelog/releases/v0.15.0/entries/verbose-output-flag-for-test-results.md +0 -0
  46. {tenzir_test-0.15.0 → tenzir_test-1.0.1}/changelog/releases/v0.15.0/manifest.yaml +0 -0
  47. {tenzir_test-0.15.0 → tenzir_test-1.0.1}/changelog/releases/v0.15.0/notes.md +0 -0
  48. {tenzir_test-0.15.0 → tenzir_test-1.0.1}/example-library/README.md +0 -0
  49. {tenzir_test-0.15.0 → tenzir_test-1.0.1}/example-library/bar/operators/double.tql +0 -0
  50. {tenzir_test-0.15.0 → tenzir_test-1.0.1}/example-library/bar/package.yaml +0 -0
  51. {tenzir_test-0.15.0 → tenzir_test-1.0.1}/example-library/bar/test.yaml +0 -0
  52. {tenzir_test-0.15.0 → tenzir_test-1.0.1}/example-library/bar/tests/use-foo.tql +0 -0
  53. {tenzir_test-0.15.0 → tenzir_test-1.0.1}/example-library/bar/tests/use-foo.txt +0 -0
  54. {tenzir_test-0.15.0 → tenzir_test-1.0.1}/example-library/foo/operators/increment.tql +0 -0
  55. {tenzir_test-0.15.0 → tenzir_test-1.0.1}/example-library/foo/package.yaml +0 -0
  56. {tenzir_test-0.15.0 → tenzir_test-1.0.1}/example-library/foo/test.yaml +0 -0
  57. {tenzir_test-0.15.0 → tenzir_test-1.0.1}/example-library/foo/tests/use-bar.tql +0 -0
  58. {tenzir_test-0.15.0 → tenzir_test-1.0.1}/example-library/foo/tests/use-bar.txt +0 -0
  59. {tenzir_test-0.15.0 → tenzir_test-1.0.1}/example-package/operators/deeply/nested/twist.tql +0 -0
  60. {tenzir_test-0.15.0 → tenzir_test-1.0.1}/example-package/operators/filter.tql +0 -0
  61. {tenzir_test-0.15.0 → tenzir_test-1.0.1}/example-package/operators/parse.tql +0 -0
  62. {tenzir_test-0.15.0 → tenzir_test-1.0.1}/example-package/package.yaml +0 -0
  63. {tenzir_test-0.15.0 → tenzir_test-1.0.1}/example-package/tests/context/01-context-list.tql +0 -0
  64. {tenzir_test-0.15.0 → tenzir_test-1.0.1}/example-package/tests/context/01-context-list.txt +0 -0
  65. {tenzir_test-0.15.0 → tenzir_test-1.0.1}/example-package/tests/context/02-context-update.tql +0 -0
  66. {tenzir_test-0.15.0 → tenzir_test-1.0.1}/example-package/tests/context/02-context-update.txt +0 -0
  67. {tenzir_test-0.15.0 → tenzir_test-1.0.1}/example-package/tests/context/03-context-inspect.tql +0 -0
  68. {tenzir_test-0.15.0 → tenzir_test-1.0.1}/example-package/tests/context/03-context-inspect.txt +0 -0
  69. {tenzir_test-0.15.0 → tenzir_test-1.0.1}/example-package/tests/context/test.yaml +0 -0
  70. {tenzir_test-0.15.0 → tenzir_test-1.0.1}/example-package/tests/inputs/sample.log +0 -0
  71. {tenzir_test-0.15.0 → tenzir_test-1.0.1}/example-package/tests/ops.tql +0 -0
  72. {tenzir_test-0.15.0 → tenzir_test-1.0.1}/example-package/tests/ops.txt +0 -0
  73. {tenzir_test-0.15.0 → tenzir_test-1.0.1}/example-package/tests/twist.tql +0 -0
  74. {tenzir_test-0.15.0 → tenzir_test-1.0.1}/example-package/tests/twist.txt +0 -0
  75. {tenzir_test-0.15.0 → tenzir_test-1.0.1}/example-project/fixtures/README.md +0 -0
  76. {tenzir_test-0.15.0 → tenzir_test-1.0.1}/example-project/fixtures/__init__.py +0 -0
  77. {tenzir_test-0.15.0 → tenzir_test-1.0.1}/example-project/fixtures/http.py +0 -0
  78. {tenzir_test-0.15.0 → tenzir_test-1.0.1}/example-project/fixtures/server.py +0 -0
  79. {tenzir_test-0.15.0 → tenzir_test-1.0.1}/example-project/inputs/events.ndjson +0 -0
  80. {tenzir_test-0.15.0 → tenzir_test-1.0.1}/example-project/runners/__init__.py +0 -0
  81. {tenzir_test-0.15.0 → tenzir_test-1.0.1}/example-project/runners/xxd.py +0 -0
  82. {tenzir_test-0.15.0 → tenzir_test-1.0.1}/example-project/tests/context/01-context-create.tql +0 -0
  83. {tenzir_test-0.15.0 → tenzir_test-1.0.1}/example-project/tests/context/01-context-create.txt +0 -0
  84. {tenzir_test-0.15.0 → tenzir_test-1.0.1}/example-project/tests/context/02-context-update.tql +0 -0
  85. {tenzir_test-0.15.0 → tenzir_test-1.0.1}/example-project/tests/context/02-context-update.txt +0 -0
  86. {tenzir_test-0.15.0 → tenzir_test-1.0.1}/example-project/tests/context/03-context-inspect.tql +0 -0
  87. {tenzir_test-0.15.0 → tenzir_test-1.0.1}/example-project/tests/context/03-context-inspect.txt +0 -0
  88. {tenzir_test-0.15.0 → tenzir_test-1.0.1}/example-project/tests/context/test.yaml +0 -0
  89. {tenzir_test-0.15.0 → tenzir_test-1.0.1}/example-project/tests/fail.tql +0 -0
  90. {tenzir_test-0.15.0 → tenzir_test-1.0.1}/example-project/tests/fail.txt +0 -0
  91. {tenzir_test-0.15.0 → tenzir_test-1.0.1}/example-project/tests/hex/hello.txt +0 -0
  92. {tenzir_test-0.15.0 → tenzir_test-1.0.1}/example-project/tests/hex/hello.xxd +0 -0
  93. {tenzir_test-0.15.0 → tenzir_test-1.0.1}/example-project/tests/http-fixture.tql +0 -0
  94. {tenzir_test-0.15.0 → tenzir_test-1.0.1}/example-project/tests/http-fixture.txt +0 -0
  95. {tenzir_test-0.15.0 → tenzir_test-1.0.1}/example-project/tests/lazy.tql +0 -0
  96. {tenzir_test-0.15.0 → tenzir_test-1.0.1}/example-project/tests/lazy.txt +0 -0
  97. {tenzir_test-0.15.0 → tenzir_test-1.0.1}/example-project/tests/local-inputs/inputs/local-data.ndjson +0 -0
  98. {tenzir_test-0.15.0 → tenzir_test-1.0.1}/example-project/tests/local-inputs/read-local.tql +0 -0
  99. {tenzir_test-0.15.0 → tenzir_test-1.0.1}/example-project/tests/local-inputs/read-local.txt +0 -0
  100. {tenzir_test-0.15.0 → tenzir_test-1.0.1}/example-project/tests/node-fixture.tql +0 -0
  101. {tenzir_test-0.15.0 → tenzir_test-1.0.1}/example-project/tests/node-fixture.txt +0 -0
  102. {tenzir_test-0.15.0 → tenzir_test-1.0.1}/example-project/tests/python/executor-only/sum.py +0 -0
  103. {tenzir_test-0.15.0 → tenzir_test-1.0.1}/example-project/tests/python/executor-only/sum.txt +0 -0
  104. {tenzir_test-0.15.0 → tenzir_test-1.0.1}/example-project/tests/python/executor-with-http-fixture/request.py +0 -0
  105. {tenzir_test-0.15.0 → tenzir_test-1.0.1}/example-project/tests/python/executor-with-http-fixture/request.txt +0 -0
  106. {tenzir_test-0.15.0 → tenzir_test-1.0.1}/example-project/tests/python/executor-with-http-fixture/test.yaml +0 -0
  107. {tenzir_test-0.15.0 → tenzir_test-1.0.1}/example-project/tests/python/executor-with-node-fixture/context-manager.py +0 -0
  108. {tenzir_test-0.15.0 → tenzir_test-1.0.1}/example-project/tests/python/executor-with-node-fixture/context-manager.txt +0 -0
  109. {tenzir_test-0.15.0 → tenzir_test-1.0.1}/example-project/tests/python/fixture-driving/manual_control.py +0 -0
  110. {tenzir_test-0.15.0 → tenzir_test-1.0.1}/example-project/tests/python/fixture-driving/manual_control.txt +0 -0
  111. {tenzir_test-0.15.0 → tenzir_test-1.0.1}/example-project/tests/python/pure-python/flaky_coin.py +0 -0
  112. {tenzir_test-0.15.0 → tenzir_test-1.0.1}/example-project/tests/python/pure-python/flaky_coin.txt +0 -0
  113. {tenzir_test-0.15.0 → tenzir_test-1.0.1}/example-project/tests/python/pure-python/hello_world.py +0 -0
  114. {tenzir_test-0.15.0 → tenzir_test-1.0.1}/example-project/tests/python/pure-python/hello_world.txt +0 -0
  115. {tenzir_test-0.15.0 → tenzir_test-1.0.1}/example-project/tests/read-inline-input.input +0 -0
  116. {tenzir_test-0.15.0 → tenzir_test-1.0.1}/example-project/tests/read-inline-input.tql +0 -0
  117. {tenzir_test-0.15.0 → tenzir_test-1.0.1}/example-project/tests/read-inline-input.txt +0 -0
  118. {tenzir_test-0.15.0 → tenzir_test-1.0.1}/example-project/tests/read-inputs.tql +0 -0
  119. {tenzir_test-0.15.0 → tenzir_test-1.0.1}/example-project/tests/read-inputs.txt +0 -0
  120. {tenzir_test-0.15.0 → tenzir_test-1.0.1}/example-project/tests/shell/exit-code-test.sh +0 -0
  121. {tenzir_test-0.15.0 → tenzir_test-1.0.1}/example-project/tests/shell/exit-code-test.txt +0 -0
  122. {tenzir_test-0.15.0 → tenzir_test-1.0.1}/example-project/tests/shell/http-fixture-check.sh +0 -0
  123. {tenzir_test-0.15.0 → tenzir_test-1.0.1}/example-project/tests/shell/http-fixture-check.txt +0 -0
  124. {tenzir_test-0.15.0 → tenzir_test-1.0.1}/example-project/tests/shell/tmp-dir.sh +0 -0
  125. {tenzir_test-0.15.0 → tenzir_test-1.0.1}/example-project/tests/shell/tmp-dir.txt +0 -0
  126. {tenzir_test-0.15.0 → tenzir_test-1.0.1}/example-satellite/README.md +0 -0
  127. {tenzir_test-0.15.0 → tenzir_test-1.0.1}/example-satellite/fixtures.py +0 -0
  128. {tenzir_test-0.15.0 → tenzir_test-1.0.1}/example-satellite/inputs/input.txt +0 -0
  129. {tenzir_test-0.15.0 → tenzir_test-1.0.1}/example-satellite/tests/hex/reuse.txt +0 -0
  130. {tenzir_test-0.15.0 → tenzir_test-1.0.1}/example-satellite/tests/hex/reuse.xxd +0 -0
  131. {tenzir_test-0.15.0 → tenzir_test-1.0.1}/example-satellite/tests/satellite/input.sh +0 -0
  132. {tenzir_test-0.15.0 → tenzir_test-1.0.1}/example-satellite/tests/satellite/input.txt +0 -0
  133. {tenzir_test-0.15.0 → tenzir_test-1.0.1}/example-satellite/tests/satellite/marker.sh +0 -0
  134. {tenzir_test-0.15.0 → tenzir_test-1.0.1}/example-satellite/tests/satellite/marker.txt +0 -0
  135. {tenzir_test-0.15.0 → tenzir_test-1.0.1}/example-satellite/tests/satellite/test.yaml +0 -0
  136. {tenzir_test-0.15.0 → tenzir_test-1.0.1}/src/tenzir_test/__init__.py +0 -0
  137. {tenzir_test-0.15.0 → tenzir_test-1.0.1}/src/tenzir_test/engine/__init__.py +0 -0
  138. {tenzir_test-0.15.0 → tenzir_test-1.0.1}/src/tenzir_test/engine/operations.py +0 -0
  139. {tenzir_test-0.15.0 → tenzir_test-1.0.1}/src/tenzir_test/engine/registry.py +0 -0
  140. {tenzir_test-0.15.0 → tenzir_test-1.0.1}/src/tenzir_test/engine/worker.py +0 -0
  141. {tenzir_test-0.15.0 → tenzir_test-1.0.1}/src/tenzir_test/packages.py +0 -0
  142. {tenzir_test-0.15.0 → tenzir_test-1.0.1}/src/tenzir_test/py.typed +0 -0
  143. {tenzir_test-0.15.0 → tenzir_test-1.0.1}/src/tenzir_test/runners/__init__.py +0 -0
  144. {tenzir_test-0.15.0 → tenzir_test-1.0.1}/src/tenzir_test/runners/_utils.py +0 -0
  145. {tenzir_test-0.15.0 → tenzir_test-1.0.1}/src/tenzir_test/runners/ext_runner.py +0 -0
  146. {tenzir_test-0.15.0 → tenzir_test-1.0.1}/src/tenzir_test/runners/runner.py +0 -0
  147. {tenzir_test-0.15.0 → tenzir_test-1.0.1}/src/tenzir_test/runners/shell_runner.py +0 -0
  148. {tenzir_test-0.15.0 → tenzir_test-1.0.1}/src/tenzir_test/runners/tenzir_runner.py +0 -0
  149. {tenzir_test-0.15.0 → tenzir_test-1.0.1}/src/tenzir_test/runners/tql_runner.py +0 -0
  150. {tenzir_test-0.15.0 → tenzir_test-1.0.1}/tests/test_cli.py +0 -0
  151. {tenzir_test-0.15.0 → tenzir_test-1.0.1}/tests/test_engine_operations.py +0 -0
  152. {tenzir_test-0.15.0 → tenzir_test-1.0.1}/tests/test_runner_registry.py +0 -0
  153. {tenzir_test-0.15.0 → tenzir_test-1.0.1}/tests/test_shell_runner.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: tenzir-test
3
- Version: 0.15.0
3
+ Version: 1.0.1
4
4
  Summary: Reusable test execution framework extracted from the Tenzir repository.
5
5
  Project-URL: Homepage, https://github.com/tenzir/test
6
6
  Project-URL: Repository, https://github.com/tenzir/test
@@ -0,0 +1,11 @@
1
+ ---
2
+ title: Auto-detect Tenzir binary with uvx fallback
3
+ type: breaking
4
+ authors:
5
+ - mavam
6
+ - claude
7
+ pr: 7
8
+ created: 2026-01-23T13:05:39.988216Z
9
+ ---
10
+
11
+ Binary detection now follows a consistent precedence order: environment variable `TENZIR_BINARY` takes priority, followed by a PATH lookup, with `uvx` as the final fallback. The same logic applies to `TENZIR_NODE_BINARY`. Environment variables now support multi-part commands like `TENZIR_BINARY="uvx tenzir"`, giving you full control over how binaries are invoked. The `--tenzir-binary` and `--tenzir-node-binary` CLI flags have been removed in favor of environment variables, which provide more flexibility and clarity.
@@ -0,0 +1,8 @@
1
+ created: 2026-01-23
2
+ title: Automatic Binary Detection
3
+ intro: >-
4
+ This release introduces automatic binary detection with a clear precedence order:
5
+ environment variables take priority, followed by PATH lookup, with `uvx` as fallback.
6
+ Environment variables now support multi-part commands for full control over binary
7
+ invocation. The `--tenzir-binary` and `--tenzir-node-binary` CLI flags have been
8
+ removed in favor of environment variables.
@@ -0,0 +1,9 @@
1
+ This release introduces automatic binary detection with a clear precedence order: environment variables take priority, followed by PATH lookup, with `uvx` as fallback. Environment variables now support multi-part commands for full control over binary invocation. The `--tenzir-binary` and `--tenzir-node-binary` CLI flags have been removed in favor of environment variables.
2
+
3
+ ## 💥 Breaking changes
4
+
5
+ ### Auto-detect Tenzir binary with uvx fallback
6
+
7
+ Binary detection now follows a consistent precedence order: environment variable `TENZIR_BINARY` takes priority, followed by a PATH lookup, with `uvx` as the final fallback. The same logic applies to `TENZIR_NODE_BINARY`. Environment variables now support multi-part commands like `TENZIR_BINARY="uvx tenzir"`, giving you full control over how binaries are invoked. The `--tenzir-binary` and `--tenzir-node-binary` CLI flags have been removed in favor of environment variables, which provide more flexibility and clarity.
8
+
9
+ *By @mavam and @claude in #7.*
@@ -0,0 +1,11 @@
1
+ ---
2
+ title: Tuple unpacking in diff runner for multi-word commands
3
+ type: bugfix
4
+ authors:
5
+ - mavam
6
+ - claude
7
+ pr: 8
8
+ created: 2026-01-23T17:18:03.224393Z
9
+ ---
10
+
11
+ Fixed an error that occurred when running diff tests with multi-word Tenzir commands like `uvx tenzir`.
@@ -0,0 +1,5 @@
1
+ created: 2026-01-23
2
+ title: Multi-Word Command Fix
3
+ intro: >-
4
+ This release fixes an issue with diff tests failing when using multi-word Tenzir
5
+ commands.
@@ -0,0 +1,9 @@
1
+ This release fixes an issue with diff tests failing when using multi-word Tenzir commands.
2
+
3
+ ## 🐞 Bug fixes
4
+
5
+ ### Tuple unpacking in diff runner for multi-word commands
6
+
7
+ Fixed an error that occurred when running diff tests with multi-word Tenzir commands like `uvx tenzir`.
8
+
9
+ *By @mavam and @claude in #8.*
@@ -7,10 +7,12 @@ pieces fit together.
7
7
 
8
8
  ## Prerequisites
9
9
 
10
- - A `tenzir` binary on your `PATH`, or an explicit `TENZIR_BINARY`.
11
- - `tenzir-node` for scenarios that talk to a running node.
12
10
  - Python 3.12+ with [`uv`](https://docs.astral.sh/uv/) (used in the commands
13
11
  below).
12
+ - A `tenzir` binary on your `PATH`. If not found, the harness falls back to
13
+ `uvx tenzir` when `uv` is installed. Set `TENZIR_BINARY` to override.
14
+ - `tenzir-node` for scenarios that talk to a running node. Same fallback logic
15
+ applies via `TENZIR_NODE_BINARY`.
14
16
 
15
17
  ## Layout
16
18
 
@@ -83,7 +85,7 @@ example-project/
83
85
  - **Satellite demo** (`../example-satellite/`): a self-contained project that
84
86
  reuses the root fixtures **and** the `xxd` runner while adding its own
85
87
  `satellite_marker` fixture. Invoke `uvx tenzir-test --root example-project
86
- example-satellite` to run both projects in one go.
88
+ example-satellite` to run both projects in one go.
87
89
 
88
90
  Every scenario keeps its expected output in a neighbouring `.txt` file. Run with
89
91
  `--update` after deliberate behaviour changes to refresh the baselines.
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "tenzir-test"
3
- version = "0.15.0"
3
+ version = "1.0.1"
4
4
  description = "Reusable test execution framework extracted from the Tenzir repository."
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.12"
@@ -3,6 +3,7 @@ from __future__ import annotations
3
3
  import json
4
4
  import os
5
5
  import runpy
6
+ import shlex
6
7
  import sys
7
8
  from contextvars import Token
8
9
  from pathlib import Path
@@ -49,14 +50,18 @@ def _push_context_from_env() -> Token[Any] | None:
49
50
  config_args_raw = payload.get("config_args", ())
50
51
  config_args = tuple(str(arg) for arg in config_args_raw)
51
52
  env = dict(os.environ)
53
+ tenzir_binary_str = env.get("TENZIR_PYTHON_FIXTURE_BINARY") or env.get("TENZIR_BINARY")
54
+ tenzir_node_binary_str = env.get("TENZIR_NODE_BINARY")
52
55
  context = _fixtures.FixtureContext(
53
56
  test=test_path,
54
57
  config=config,
55
58
  coverage=coverage,
56
59
  env=env,
57
60
  config_args=config_args,
58
- tenzir_binary=env.get("TENZIR_PYTHON_FIXTURE_BINARY") or env.get("TENZIR_BINARY"),
59
- tenzir_node_binary=env.get("TENZIR_NODE_BINARY"),
61
+ tenzir_binary=tuple(shlex.split(tenzir_binary_str)) if tenzir_binary_str else None,
62
+ tenzir_node_binary=tuple(shlex.split(tenzir_node_binary_str))
63
+ if tenzir_node_binary_str
64
+ else None,
60
65
  )
61
66
  token = _fixtures.push_context(context)
62
67
  return cast(Token[Any], token)
@@ -39,18 +39,6 @@ def _normalize_exit_code(value: object) -> int:
39
39
  ),
40
40
  help="Project root to scan for tests.",
41
41
  )
42
- @click.option(
43
- "tenzir_binary",
44
- "--tenzir-binary",
45
- type=click.Path(path_type=Path, dir_okay=False, writable=False, resolve_path=False),
46
- help="Path to the tenzir executable.",
47
- )
48
- @click.option(
49
- "tenzir_node_binary",
50
- "--tenzir-node-binary",
51
- type=click.Path(path_type=Path, dir_okay=False, writable=False, resolve_path=False),
52
- help="Path to the tenzir-node executable.",
53
- )
54
42
  @click.option(
55
43
  "package_dirs",
56
44
  "--package-dirs",
@@ -150,8 +138,6 @@ def cli(
150
138
  ctx: click.Context,
151
139
  *,
152
140
  root: Path | None,
153
- tenzir_binary: Path | None,
154
- tenzir_node_binary: Path | None,
155
141
  package_dirs: tuple[str, ...],
156
142
  tests: tuple[Path, ...],
157
143
  update: bool,
@@ -186,8 +172,6 @@ def cli(
186
172
  try:
187
173
  result = runtime.run_cli(
188
174
  root=root,
189
- tenzir_binary=tenzir_binary,
190
- tenzir_node_binary=tenzir_node_binary,
191
175
  package_dirs=package_paths,
192
176
  tests=list(tests),
193
177
  update=update,
@@ -0,0 +1,70 @@
1
+ from __future__ import annotations
2
+
3
+ import os
4
+ import shlex
5
+ import shutil
6
+ from dataclasses import dataclass
7
+ from pathlib import Path
8
+ from typing import Mapping
9
+
10
+
11
+ @dataclass(slots=True)
12
+ class Settings:
13
+ """Configuration values steering how the harness discovers binaries and data."""
14
+
15
+ root: Path
16
+ tenzir_binary: tuple[str, ...] | None
17
+ tenzir_node_binary: tuple[str, ...] | None
18
+
19
+ @property
20
+ def inputs_dir(self) -> Path:
21
+ direct = self.root / "inputs"
22
+ if direct.exists():
23
+ return direct
24
+ nested = self.root / "tests" / "inputs"
25
+ if nested.exists():
26
+ return nested
27
+ return direct
28
+
29
+
30
+ def _resolve_binary(
31
+ env_var: str | None,
32
+ binary_name: str,
33
+ ) -> tuple[str, ...] | None:
34
+ """Resolve a binary with fallback to uvx."""
35
+ if env_var:
36
+ try:
37
+ parts = tuple(shlex.split(env_var))
38
+ except ValueError as e:
39
+ raise ValueError(
40
+ f"Invalid shell syntax in environment variable for {binary_name}: {e}"
41
+ ) from e
42
+ if not parts:
43
+ raise ValueError(f"Empty command in environment variable for {binary_name}")
44
+ return parts
45
+ which_result = shutil.which(binary_name)
46
+ if which_result:
47
+ return (which_result,)
48
+ if shutil.which("uvx"):
49
+ if binary_name == "tenzir-node":
50
+ return ("uvx", "--from", "tenzir", "tenzir-node")
51
+ return ("uvx", binary_name)
52
+ return None
53
+
54
+
55
+ def discover_settings(
56
+ *,
57
+ root: Path | None = None,
58
+ env: Mapping[str, str] | None = None,
59
+ ) -> Settings:
60
+ """Produce harness settings by combining CLI overrides with environment defaults."""
61
+
62
+ environment = dict(os.environ if env is None else env)
63
+
64
+ chosen_root = root or environment.get("TENZIR_TEST_ROOT") or Path.cwd()
65
+ root_path = Path(chosen_root).resolve()
66
+
67
+ tenzir_path = _resolve_binary(environment.get("TENZIR_BINARY"), "tenzir")
68
+ node_path = _resolve_binary(environment.get("TENZIR_NODE_BINARY"), "tenzir-node")
69
+
70
+ return Settings(root=root_path, tenzir_binary=tenzir_path, tenzir_node_binary=node_path)
@@ -1,13 +1,12 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  from pathlib import Path
4
- from typing import Optional
5
4
 
6
5
  from ..config import Settings
7
6
  from tenzir_test import run
8
7
 
9
- TENZIR_BINARY: Optional[str] = run.TENZIR_BINARY
10
- TENZIR_NODE_BINARY: Optional[str] = run.TENZIR_NODE_BINARY
8
+ TENZIR_BINARY: tuple[str, ...] | None = run.TENZIR_BINARY
9
+ TENZIR_NODE_BINARY: tuple[str, ...] | None = run.TENZIR_NODE_BINARY
11
10
  ROOT: Path = run.ROOT
12
11
  INPUTS_DIR: Path = run.INPUTS_DIR
13
12
 
@@ -42,8 +42,8 @@ class FixtureContext:
42
42
  coverage: bool
43
43
  env: dict[str, str]
44
44
  config_args: Sequence[str]
45
- tenzir_binary: str | None
46
- tenzir_node_binary: str | None
45
+ tenzir_binary: tuple[str, ...] | None
46
+ tenzir_node_binary: tuple[str, ...] | None
47
47
 
48
48
 
49
49
  _CONTEXT: ContextVar[FixtureContext | None] = ContextVar(
@@ -57,7 +57,7 @@ class Executor:
57
57
  def __init__(self, env: Mapping[str, str] | None = None) -> None:
58
58
  source = env or os.environ
59
59
  try:
60
- self.binary: str = source["TENZIR_NODE_CLIENT_BINARY"]
60
+ self.binary: tuple[str, ...] = tuple(shlex.split(source["TENZIR_NODE_CLIENT_BINARY"]))
61
61
  except KeyError as exc: # pragma: no cover - defensive guard
62
62
  raise RuntimeError("TENZIR_NODE_CLIENT_BINARY is not configured") from exc
63
63
  self.endpoint: str | None = source.get("TENZIR_NODE_CLIENT_ENDPOINT")
@@ -72,7 +72,7 @@ class Executor:
72
72
  self, source: str, desired_timeout: float | None = None, mirror: bool = False
73
73
  ) -> subprocess.CompletedProcess[bytes]:
74
74
  cmd = [
75
- self.binary,
75
+ *self.binary,
76
76
  "--bare-mode",
77
77
  "--console-verbosity=warning",
78
78
  "--multi",
@@ -165,9 +165,13 @@ def node() -> Iterator[dict[str, str]]:
165
165
  if context is None:
166
166
  raise RuntimeError("node fixture requires an active test context")
167
167
 
168
- node_binary = context.tenzir_node_binary or context.env.get("TENZIR_NODE_BINARY")
168
+ node_binary: tuple[str, ...] | None = context.tenzir_node_binary
169
169
  if not node_binary:
170
- raise RuntimeError("TENZIR_NODE_BINARY must be configured for the node fixture")
170
+ raise RuntimeError(
171
+ "tenzir-node binary not available. The harness checks: "
172
+ "TENZIR_NODE_BINARY env var, PATH lookup, uvx fallback. "
173
+ "Ensure tenzir-node is installed, uv is available, or set TENZIR_NODE_BINARY."
174
+ )
171
175
 
172
176
  env = context.env.copy()
173
177
  # Extract and filter config arguments: we handle --config and --package-dirs separately.
@@ -240,7 +244,7 @@ def node() -> Iterator[dict[str, str]]:
240
244
  test_root = context.test.parent
241
245
 
242
246
  node_cmd = [
243
- node_binary,
247
+ *node_binary,
244
248
  "--bare-mode",
245
249
  "--console-verbosity=warning",
246
250
  f"--state-directory={state_dir}",
@@ -292,9 +296,14 @@ def node() -> Iterator[dict[str, str]]:
292
296
  )
293
297
  raise RuntimeError(f"failed to obtain endpoint from tenzir-node ({detail})")
294
298
 
299
+ client_binary: str | None = None
300
+ if context.tenzir_binary:
301
+ client_binary = shlex.join(context.tenzir_binary)
302
+ else:
303
+ client_binary = env.get("TENZIR_BINARY")
295
304
  fixture_env = {
296
305
  "TENZIR_NODE_CLIENT_ENDPOINT": endpoint,
297
- "TENZIR_NODE_CLIENT_BINARY": context.tenzir_binary or env.get("TENZIR_BINARY"),
306
+ "TENZIR_NODE_CLIENT_BINARY": client_binary,
298
307
  "TENZIR_NODE_CLIENT_TIMEOUT": str(context.config["timeout"]),
299
308
  "TENZIR_NODE_STATE_DIRECTORY": str(state_dir),
300
309
  "TENZIR_NODE_CACHE_DIRECTORY": str(cache_dir),
@@ -176,8 +176,8 @@ def detect_execution_mode(root: Path) -> tuple[ExecutionMode, Path | None]:
176
176
 
177
177
 
178
178
  _settings: Settings | None = None
179
- TENZIR_BINARY: str | None = None
180
- TENZIR_NODE_BINARY: str | None = None
179
+ TENZIR_BINARY: tuple[str, ...] | None = None
180
+ TENZIR_NODE_BINARY: tuple[str, ...] | None = None
181
181
  ROOT: Path = Path.cwd()
182
182
  INPUTS_DIR: Path = ROOT / "inputs"
183
183
  EXECUTION_MODE: ExecutionMode = ExecutionMode.PROJECT
@@ -1866,9 +1866,9 @@ def get_test_env_and_config_args(
1866
1866
  if node_config_file.exists():
1867
1867
  env["TENZIR_NODE_CONFIG"] = str(node_config_file)
1868
1868
  if TENZIR_BINARY:
1869
- env["TENZIR_BINARY"] = TENZIR_BINARY
1869
+ env["TENZIR_BINARY"] = shlex.join(TENZIR_BINARY)
1870
1870
  if TENZIR_NODE_BINARY:
1871
- env["TENZIR_NODE_BINARY"] = TENZIR_NODE_BINARY
1871
+ env["TENZIR_NODE_BINARY"] = shlex.join(TENZIR_NODE_BINARY)
1872
1872
  env["TENZIR_TEST_ROOT"] = str(ROOT)
1873
1873
  tmp_dir = _create_test_tmp_dir(test)
1874
1874
  env[TEST_TMP_ENV_VAR] = str(tmp_dir)
@@ -2711,7 +2711,7 @@ def get_version() -> str:
2711
2711
  return (
2712
2712
  subprocess.check_output(
2713
2713
  [
2714
- TENZIR_BINARY,
2714
+ *TENZIR_BINARY,
2715
2715
  "--bare-mode",
2716
2716
  "--console-verbosity=warning",
2717
2717
  "version | select version | write_lines",
@@ -2949,7 +2949,7 @@ def run_simple_test(
2949
2949
  if not TENZIR_BINARY:
2950
2950
  raise RuntimeError("TENZIR_BINARY must be configured before running tests")
2951
2951
  cmd: list[str] = [
2952
- TENZIR_BINARY,
2952
+ *TENZIR_BINARY,
2953
2953
  "--bare-mode",
2954
2954
  "--console-verbosity=warning",
2955
2955
  "--multi",
@@ -3359,8 +3359,6 @@ def collect_all_tests(directory: Path) -> Iterator[Path]:
3359
3359
  def run_cli(
3360
3360
  *,
3361
3361
  root: Path | None,
3362
- tenzir_binary: Path | None,
3363
- tenzir_node_binary: Path | None,
3364
3362
  package_dirs: Sequence[Path] | None = None,
3365
3363
  tests: Sequence[Path],
3366
3364
  update: bool,
@@ -3448,11 +3446,7 @@ def run_cli(
3448
3446
  print(f"{INFO} ignoring --update in passthrough mode")
3449
3447
  update = False
3450
3448
 
3451
- settings = discover_settings(
3452
- root=root,
3453
- tenzir_binary=tenzir_binary,
3454
- tenzir_node_binary=tenzir_node_binary,
3455
- )
3449
+ settings = discover_settings(root=root)
3456
3450
  apply_settings(settings)
3457
3451
  _set_cli_packages(list(package_dirs or []))
3458
3452
  selected_tests = list(tests)
@@ -3796,8 +3790,6 @@ def run_cli(
3796
3790
  def execute(
3797
3791
  *,
3798
3792
  root: Path | None = None,
3799
- tenzir_binary: Path | None = None,
3800
- tenzir_node_binary: Path | None = None,
3801
3793
  tests: Sequence[Path] = (),
3802
3794
  update: bool = False,
3803
3795
  debug: bool = False,
@@ -3825,8 +3817,6 @@ def execute(
3825
3817
  resolved_jobs = jobs if jobs is not None else get_default_jobs()
3826
3818
  return run_cli(
3827
3819
  root=root,
3828
- tenzir_binary=tenzir_binary,
3829
- tenzir_node_binary=tenzir_node_binary,
3830
3820
  tests=list(tests),
3831
3821
  update=update,
3832
3822
  debug=debug,
@@ -2,6 +2,7 @@ from __future__ import annotations
2
2
 
3
3
  import json
4
4
  import os
5
+ import shlex
5
6
  import subprocess
6
7
  import sys
7
8
  import typing
@@ -99,9 +100,9 @@ class CustomPythonFixture(ExtRunner):
99
100
  endpoint = None
100
101
  new_pythonpath = os.pathsep.join(pythonpath_entries)
101
102
  env["PYTHONPATH"] = new_pythonpath
102
- env["TENZIR_NODE_CLIENT_BINARY"] = binary
103
+ env["TENZIR_NODE_CLIENT_BINARY"] = shlex.join(binary)
103
104
  env["TENZIR_NODE_CLIENT_TIMEOUT"] = str(timeout)
104
- env.setdefault("TENZIR_PYTHON_FIXTURE_BINARY", binary)
105
+ env.setdefault("TENZIR_PYTHON_FIXTURE_BINARY", shlex.join(binary))
105
106
  env["TENZIR_PYTHON_FIXTURE_TIMEOUT"] = str(timeout)
106
107
  if node_requested and endpoint:
107
108
  env["TENZIR_PYTHON_FIXTURE_ENDPOINT"] = endpoint
@@ -66,7 +66,7 @@ class DiffRunner(TqlRunner):
66
66
  binary = run_mod.TENZIR_BINARY
67
67
  if not binary:
68
68
  raise RuntimeError("TENZIR_BINARY must be configured for diff runners")
69
- base_cmd: list[str] = [binary, *config_args]
69
+ base_cmd: list[str] = [*binary, *config_args]
70
70
 
71
71
  if coverage:
72
72
  coverage_dir = env.get(
@@ -0,0 +1,106 @@
1
+ from __future__ import annotations
2
+
3
+ from pathlib import Path
4
+
5
+ import pytest
6
+
7
+ from tenzir_test import config
8
+
9
+
10
+ def test_discover_settings_defaults(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> None:
11
+ monkeypatch.chdir(tmp_path)
12
+ monkeypatch.setattr(config.shutil, "which", lambda name: f"/tmp/{name}")
13
+
14
+ settings = config.discover_settings(env={})
15
+
16
+ assert settings.root == tmp_path.resolve()
17
+ assert settings.inputs_dir == tmp_path / "inputs"
18
+ assert settings.tenzir_binary == ("/tmp/tenzir",)
19
+ assert settings.tenzir_node_binary == ("/tmp/tenzir-node",)
20
+
21
+
22
+ def test_discover_settings_env_overrides(tmp_path: Path) -> None:
23
+ env = {
24
+ "TENZIR_TEST_ROOT": str(tmp_path / "suite"),
25
+ "TENZIR_BINARY": "/custom/tenzir",
26
+ "TENZIR_NODE_BINARY": "/custom/tenzir-node",
27
+ }
28
+
29
+ settings = config.discover_settings(env=env)
30
+
31
+ assert settings.root == (tmp_path / "suite").resolve()
32
+ assert settings.tenzir_binary == ("/custom/tenzir",)
33
+ assert settings.tenzir_node_binary == ("/custom/tenzir-node",)
34
+
35
+
36
+ def test_discover_settings_env_multipart(tmp_path: Path) -> None:
37
+ """Environment variables can specify multi-part commands like 'uvx tenzir'."""
38
+ env = {
39
+ "TENZIR_TEST_ROOT": str(tmp_path / "suite"),
40
+ "TENZIR_BINARY": "uvx tenzir",
41
+ "TENZIR_NODE_BINARY": "uvx tenzir-node",
42
+ }
43
+
44
+ settings = config.discover_settings(env=env)
45
+
46
+ assert settings.tenzir_binary == ("uvx", "tenzir")
47
+ assert settings.tenzir_node_binary == ("uvx", "tenzir-node")
48
+
49
+
50
+ def test_discover_settings_uvx_fallback(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> None:
51
+ """When tenzir is not found but uvx is available, fall back to uvx."""
52
+ monkeypatch.chdir(tmp_path)
53
+
54
+ def mock_which(name: str) -> str | None:
55
+ if name == "uvx":
56
+ return "/usr/local/bin/uvx"
57
+ return None
58
+
59
+ monkeypatch.setattr(config.shutil, "which", mock_which)
60
+
61
+ settings = config.discover_settings(env={})
62
+
63
+ assert settings.tenzir_binary == ("uvx", "tenzir")
64
+ assert settings.tenzir_node_binary == ("uvx", "--from", "tenzir", "tenzir-node")
65
+
66
+
67
+ def test_discover_settings_no_binary(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> None:
68
+ """When no binary is found and uvx is unavailable, return None."""
69
+ monkeypatch.chdir(tmp_path)
70
+ monkeypatch.setattr(config.shutil, "which", lambda name: None)
71
+
72
+ settings = config.discover_settings(env={})
73
+
74
+ assert settings.tenzir_binary is None
75
+ assert settings.tenzir_node_binary is None
76
+
77
+
78
+ def test_settings_inputs_dir_nested(tmp_path: Path) -> None:
79
+ nested = tmp_path / "tests" / "inputs"
80
+ nested.mkdir(parents=True)
81
+
82
+ settings = config.discover_settings(root=tmp_path)
83
+
84
+ assert settings.inputs_dir == nested
85
+
86
+
87
+ def test_discover_settings_malformed_binary(tmp_path: Path) -> None:
88
+ """Malformed shell syntax in binary env var raises ValueError."""
89
+ env = {
90
+ "TENZIR_TEST_ROOT": str(tmp_path),
91
+ "TENZIR_BINARY": "unclosed 'quote",
92
+ }
93
+
94
+ with pytest.raises(ValueError, match="Invalid shell syntax"):
95
+ config.discover_settings(env=env)
96
+
97
+
98
+ def test_discover_settings_empty_binary(tmp_path: Path) -> None:
99
+ """Empty string after parsing raises ValueError."""
100
+ env = {
101
+ "TENZIR_TEST_ROOT": str(tmp_path),
102
+ "TENZIR_BINARY": " ", # Whitespace-only parses to empty
103
+ }
104
+
105
+ with pytest.raises(ValueError, match="Empty command"):
106
+ config.discover_settings(env=env)
@@ -21,7 +21,7 @@ def python_fixture_root(tmp_path: Path) -> Path:
21
21
  )
22
22
  test_settings = config.Settings(
23
23
  root=tmp_path,
24
- tenzir_binary=run.TENZIR_BINARY or "/usr/bin/tenzir",
24
+ tenzir_binary=run.TENZIR_BINARY or ("/usr/bin/tenzir",),
25
25
  tenzir_node_binary=run.TENZIR_NODE_BINARY,
26
26
  )
27
27
  run.apply_settings(test_settings)
@@ -267,7 +267,7 @@ def test_executor_from_env() -> None:
267
267
  "TENZIR_NODE_CLIENT_TIMEOUT": "5",
268
268
  }
269
269
  executor = fixtures.Executor.from_env(env)
270
- assert executor.binary == "/usr/bin/tenzir-node"
270
+ assert executor.binary == ("/usr/bin/tenzir-node",)
271
271
  assert executor.endpoint == "localhost:0"
272
272
 
273
273
 
@@ -520,8 +520,8 @@ def test_node_fixture_uses_explicit_node_config(tmp_path, monkeypatch):
520
520
  coverage=False,
521
521
  env=env,
522
522
  config_args=("--config=/default/tenzir.yaml", "--package-dirs=/pkg"),
523
- tenzir_binary="/usr/bin/tenzir",
524
- tenzir_node_binary="/usr/bin/tenzir-node",
523
+ tenzir_binary=("/usr/bin/tenzir",),
524
+ tenzir_node_binary=("/usr/bin/tenzir-node",),
525
525
  )
526
526
 
527
527
  token = fixture_api.push_context(context)
@@ -571,8 +571,8 @@ def test_node_fixture_skips_config_when_unset(monkeypatch):
571
571
  coverage=False,
572
572
  env=env,
573
573
  config_args=("--config=/default/tenzir.yaml", "--package-dirs=/pkg"),
574
- tenzir_binary="/usr/bin/tenzir",
575
- tenzir_node_binary="/usr/bin/tenzir-node",
574
+ tenzir_binary=("/usr/bin/tenzir",),
575
+ tenzir_node_binary=("/usr/bin/tenzir-node",),
576
576
  )
577
577
 
578
578
  token = fixture_api.push_context(context)
@@ -620,8 +620,8 @@ def test_node_fixture_adds_package_dirs_from_env(monkeypatch):
620
620
  coverage=False,
621
621
  env=env,
622
622
  config_args=("--config=/default/tenzir.yaml",),
623
- tenzir_binary="/usr/bin/tenzir",
624
- tenzir_node_binary="/usr/bin/tenzir-node",
623
+ tenzir_binary=("/usr/bin/tenzir",),
624
+ tenzir_node_binary=("/usr/bin/tenzir-node",),
625
625
  )
626
626
 
627
627
  token = fixture_api.push_context(context)
@@ -676,8 +676,8 @@ def test_node_fixture_deduplicates_package_dirs(monkeypatch):
676
676
  coverage=False,
677
677
  env=env,
678
678
  config_args=("--package-dirs=/pkg,/other",),
679
- tenzir_binary="/usr/bin/tenzir",
680
- tenzir_node_binary="/usr/bin/tenzir-node",
679
+ tenzir_binary=("/usr/bin/tenzir",),
680
+ tenzir_node_binary=("/usr/bin/tenzir-node",),
681
681
  )
682
682
 
683
683
  token = fixture_api.push_context(context)
@@ -709,8 +709,8 @@ def test_node_fixture_in_suite_receives_package_dirs(monkeypatch: pytest.MonkeyP
709
709
  run.apply_settings(
710
710
  config.Settings(
711
711
  root=package_root,
712
- tenzir_binary="tenzir",
713
- tenzir_node_binary="tenzir-node",
712
+ tenzir_binary=("tenzir",),
713
+ tenzir_node_binary=("tenzir-node",),
714
714
  )
715
715
  )
716
716
  run._clear_directory_config_cache()
@@ -934,8 +934,6 @@ def test_cli_rejects_partial_suite_selection(
934
934
  with pytest.raises(run.HarnessError) as exc_info:
935
935
  run.run_cli(
936
936
  root=project_root,
937
- tenzir_binary=None,
938
- tenzir_node_binary=None,
939
937
  tests=[test_file],
940
938
  update=False,
941
939
  debug=False,
@@ -961,8 +959,6 @@ def test_cli_rejects_partial_suite_selection(
961
959
  with pytest.raises(run.HarnessError) as exc_info:
962
960
  run.run_cli(
963
961
  root=project_root,
964
- tenzir_binary=None,
965
- tenzir_node_binary=None,
966
962
  tests=[suite_subdir],
967
963
  update=False,
968
964
  debug=False,