Files
gitea-agent/harness/tests/README.md
T

363 lines
21 KiB
Markdown

# Canonical harness tests
## Installation-root prerequisite
Before executing the suite, provide `HARNESS_ROOT` in the process environment.
It names the installation root containing `harness/` and `repositories/`, not
the `harness/` directory itself. For the current installation, configure the
current agent's `$HERMES_HOME/.env` with:
```dotenv
HARNESS_ROOT=/opt/data/gitea-agent
```
The tests consume the environment supplied by Hermes; they do not open or
source `.env` files, search other profiles, or implement a second configuration
loader. When running outside Hermes, supply the variable explicitly:
```sh
export HARNESS_ROOT=/opt/data/gitea-agent
python3 -B "$HARNESS_ROOT/harness/tests/run.py"
```
On another system, replace the value with that installation's absolute root.
The managed-lock subprocess contention test requires a nonempty, existing
absolute directory and compares its resolved location with the installation
containing the test file. Missing, empty, relative, nonexistent, non-directory,
or different-installation values raise an explicit `HARNESS_ROOT` error before
creating a lock or starting the contention subprocess. There is no fallback to
`Path.cwd()` or to the test-file-derived root. Only the validated import root
is passed as a subprocess argument; no `.env` contents are read or serialized.
This test-only prerequisite does not change the production lock implementation
or the canonical runner's source-bound collection behavior; `--collect-only`
does not require the variable.
An external-cwd regression runs only the contention test from a temporary
working directory, with `PYTHONPATH` removed and an explicit fixture
`HARNESS_ROOT`, using isolated Python and bounded subprocess timeouts. Invalid
configuration coverage checks that neither lock acquisition nor subprocess
execution occurs. It does not recursively invoke the entire suite.
## Running the canonical suite
The canonical runner is anchored to its own source tree and works from any
current directory. From the repository root:
```sh
python3 -B harness/tests/run.py
python3 -B harness/tests/run.py --collect-only
```
From outside the repository, use the absolute command:
```sh
python3 -B /opt/data/gitea-agent/harness/tests/run.py --collect-only
```
That absolute command is intentionally literal and may be run from `/` or any
other working directory; do not replace it with a relative path. The canonical
source paths are `/opt/data/gitea-agent/harness/tests/run.py` and the four role
trees under `/opt/data/gitea-agent/harness/tests/{common,spec,impl,review}`.
This document is owned by the canonical harness tests and must be updated in
place when their supported discovery contract or command paths change.
Broker fixture contract: in-process broker fixtures bind and connect through
`/proc/self/fd/{parent_fd}/{socket_name}`, matching the production broker. Long
UTF-8 roots and socket paths are supported; the fixture must not shorten them or
reject them merely because the conventional Unix-domain path encoding exceeds
107 bytes. Fixture setup failures clean up only resources they own, preserve
foreign/replaced paths, close every client and directory descriptor, and retain
unauthenticated-token rejection behavior.
Rework readback contract: the impl role's canonical `rework_comment.py snapshot`
and `verify-live` commands collect authenticated GET-only provider state through
`rework_readback.py` and `rework_readback_cli.py`. Focused coverage lives in
the provider/cache/bridge modules under `harness/tests/impl/rework/` and
`harness/tests/impl/rework/test_rework_readback_transport.py`; run them with the
`harness/tests/impl/rework/test_rework_comment.py` regressions. They exercise real
renderer/verifier integration and a local HTTP fixture, not live Gitea access.
Provider/baseline fingerprints, exact identity/endpoints, complete pagination,
create/reuse deltas, metadata causality and failed-receipt preservation are
fail-closed. The workflow contract and canonical runtime artifact paths are in
`harness/agents/impl/skills/gitea-issue-rework/references/readback.md`.
Initial input-gate coverage is in the `test_rework_input_gate_*.py` modules
under `harness/tests/impl/rework/`.
It exercises the real public CLI and canonical spec renderer, mixed-role/permutation
and same-role supersession decisions, precise timestamp/tie handling, marker/state/
source binding, complete raw validation/drift, immutable capture and atomic output
races, and new/legacy output budgets. Synthetic transport is GET-only; these
regressions do not claim live Gitea or native deployment execution.
The review native adapter's registered-worktree fixture accepts only exact
`gitea-impl-agent` or read-only legacy `impl` creator-role literals. It preserves
`state.json` bytes/mode/mtime/inode and rejects malformed role values without
relaxing the Issue lease, Git, target, or security guards.
It validates all `test*.py` files recursively under exactly these required
roles: `common`, `spec`, `impl`, and `review`. `harness/tests`, every role, and
every nested test package must have an `__init__.py`; every role and every
individual test source file must contain at least one statically inventoried
test. Files matching `test*.py` outside a role are rejected.
Before executing tests, the runner independently inventories source with the
Python AST, collects with standard `unittest`, and requires exact qualified
ID-set equality. It first preserves the loader's raw ID list so duplicate
loader results cannot disappear through set conversion. It rejects missing or
extra loader IDs, import failures and `_FailedTest` entries, duplicate class or
method declarations, zero-test files/roles, module/package `load_tests`,
unsupported conditional discovery declarations, unsupported bases, cyclic
local inheritance, free-function tests, missing package markers, and raw-byte
source/package-manifest drift during collection. The package manifest covers
required, nested, and root markers; it is rescanned after collection so added
or removed files/markers and initializer changes fail closed.
As the first admission step, the runner rejects every symbolic link anywhere
under `harness/tests` (including test sources, package markers, role
directories, and nested directories) with a canonical exit-2 diagnostic. This
boundary is checked before source bytes are read or test modules are imported;
it prevents source-tree path escape through links. It is not a sandbox: code
that is admitted and imported may still perform ordinary external filesystem,
process, network, or other side effects.
`--collect-only` emits JSON containing `collection_only: true`, per-role test
counts, sorted exact unittest IDs, and source SHA-256 hashes. Collection-only
success is not runtime success: normal unittest assertion failures and errors
remain visible and return a nonzero status. The runner intentionally accepts
standard `unittest.TestCase`, aliases, local mixins/inheritance, inherited
`test*` methods, and normal decorators such as `@unittest.skip`.
The inventory is deliberately conservative, not a Python semantic evaluator or
security sandbox. Test classes/functions declared inside module/class control
flow are unsupported and rejected; helper functions/classes nested inside an
ordinary test method are runtime fixture code and are not inspected as
canonical discovery declarations. Tests supplied solely by dynamic imports,
metaclasses, or custom discovery protocols are unsupported: their collected
IDs must not substitute for or extend the statically declared inventory.
Imports used by ordinary fixture helpers are not prohibited. The harness does
not claim to prevent side effects performed by imported test code; it detects
source drift and validates collection before execution.
The regression tests use bounded subprocess timeouts and clean test-owned
`TemporaryDirectory` trees under `/opt/data/cache/scratch/canonical-tests-main/fix2/`.
Run them with:
```sh
python3 -B -m unittest harness.tests.common.runner.test_canonical_test_runner -v
```
## Domain ownership and verification levels
The canonical output of this documentation is `harness/tests/README.md` in this
repository. Update this file in place rather than publishing a parallel test
inventory document. The canonical runner remains `harness/tests/run.py`;
there is no alternate discovery framework or hand-maintained source-hash list.
- `common/runner`: source admission and exact discovery/exit-status contracts.
- `common/locking`: primitive flock and authenticated broker lifecycle remain
separate modules; neither substitutes for the other.
- `common/repository`: prefetch, runtime/verification roots, and Git source binding.
- `common/delegation`: evidence, adoption, and real-Git consumer integration.
- `common/evidence`: workflow evidence admission and conditional pytest recipe.
- `common/access`: local credential protocol recipe, never real credentials.
- `common/contracts`: cross-role documentation wiring, not live workflow proof.
- `impl/delivery`: coordinator, initial delivery, reconciliation, and response.
- `impl/rework`: intake, decision, rendering, provider readback, publication,
and separately authorized correction.
- `impl/evidence`: manifest/schema, filesystem/Git admission, and bounded
command/process-group execution (distinct verification boundaries).
- `impl/contracts`: documentation wiring and separately named comparator
execution extracted from the actual canonical documentation.
- `review/native`: native publication/plan, transport, lease/worktree admission,
and durable resume; GET/POST loopback fixtures are not live Gitea publication.
- `review/journal`: binding/presence safety, raw-byte legacy import, and actual
subprocess crash/locking/race tests. Archive import is not fresh readback.
- `review/evidence`: execution of the actual native evidence recipe.
- `review/contracts`: policy-specific classes consolidated in one documentation
module; string/link checks do not certify production behavior.
- `spec/support`: result codec, cache admission, publication/readback, and AC checker.
- `spec/projection`: source-Issue reference and requested-reviewer projection
contracts remain independent modules with their own strict input matrices.
- `spec/contracts`: documentation contracts plus the separately named
`test_actual_collector.py` executable collector regression. That regression
calls the real collector; it is not a local-model documentation example.
### Documentation regression boundary
General workflow guidance is not an exact-prose regression interface. Do not
pin advisory sentences or generic review/reporting guidance with substring
assertions. Keep executable behavior and explicit technical contracts: CLI
options, schema fields, canonical paths/links, recipe structure, and required
procedure ordering. In mixed tests, remove only the prose assertions rather
than deleting the executable or structural checks.
The earlier `20261015T232331Z-final` traceability below is historical evidence
for that source snapshot, not an index of the current tree. Subsequent removal
runs under `logs/test-removal/` preserve their own baseline, exact dispositions,
source hashes and observed outcomes; never relabel old outcomes as current.
The general-guidance cleanup is recorded in
`logs/test-removal/general-guidance-20261016T054315Z/`.
### Shared fixture boundaries
`_support/` holds ordinary data/transport/resource builders only. It must not
contain or re-export TestCase classes, imported test bases, generated tests, or
`load_tests`. Root derivation is centralized in `_support/paths.py`; helpers do
not use `HARNESS_ROOT` or cwd to select canonical test sources. Resource builders
own explicit cleanup. Meaningful expected results, no-call/no-write/no-retry,
identity/source/stage and mode/inode/bytes assertions stay in the tests. Import
fixture helpers rather than importing another test module or manually invoking
another TestCase's `setUp()`/`tearDown()`.
### Focused execution and evidence limits
From the repository root, for example:
```sh
HARNESS_ROOT=/opt/data/gitea-agent python3 -B -m unittest discover -s harness/tests/common -t .
HARNESS_ROOT=/opt/data/gitea-agent python3 -B -m unittest discover -s harness/tests/impl -t .
HARNESS_ROOT=/opt/data/gitea-agent python3 -B -m unittest discover -s harness/tests/review -t .
HARNESS_ROOT=/opt/data/gitea-agent python3 -B -m unittest discover -s harness/tests/spec -t .
```
These are focused runtime checks, not substitutes for the canonical admission
and full execution command above. Compilation is not test execution;
`--collect-only` is not runtime success. Source hashes and counts are emitted
from the actual current source tree, never manually adjusted to admit a move.
- Documentation contracts read canonical documents (and required workflow
references through `_support/contracts.py`); they emit assertions, not a live
deployment/publication receipt.
- Unit tests call actual helpers with bounded synthetic inputs; they certify
their stated input/side-effect boundaries, not provider behavior in general.
- Git/HTTP integration tests create local temporary repositories or loopback
servers. They exercise real Git or transport code, not authenticated Gitea.
- Process tests exercise actual child processes/crash/locks and preserve durable
state assertions. In-process fakes or archive replay do not replace these.
- Evidence recipe tests execute the canonical recipe in test-owned temporary
roots. Their manifests/logs are local synthetic evidence, not product delivery.
- Two pytest recipe execution cases skip when this interpreter has no admitted
pytest installation (`admitted pytest interpreter required`). The documentation
case still runs. No dependency is installed, skip removed, or warning/failure
summary suppressed merely to obtain a green run.
### Test-to-artifact scope
Artifact scope is local and test-owned unless explicitly stated otherwise.
Temporary files, synthetic repositories, loopback traffic and child-process
journals are cleaned up by fixtures; they are not retained delivery evidence.
The retained canonical runtime log records each exact test ID and outcome.
The following groups specify what each module's tests may produce or inspect;
a passing group does not certify every stronger evidence layer listed elsewhere.
- `common/access/test_git_credential_recipe.py`: local helper/askpass scripts,
permissions and protocol stdout; no stored or real provider credentials.
- `common/contracts/test_*.py`: canonical Markdown sections, workflow links and
documentation assertions; no publication receipt or workflow execution.
- `common/delegation/test_delegation_evidence*.py`: test-owned declarations,
source snapshots, bundle hashes and verifier decisions. Adoption tests add
fresh admission results; consumer unit tests add adapter-call records.
`test_delegation_consumers_integration.py` adds actual temporary Git evidence,
CLI stdout/stderr and fake consumer effects, not live Gitea writes.
- `common/evidence/test_workflow_guard.py`: local contract/test inventories,
snapshots, change decisions and fault inputs. `test_pytest_evidence_recipe.py`
inspects the canonical recipe and conditionally creates actual pytest
logs/summaries; skipped execution produces no recipe execution evidence.
- `common/locking/test_managed_lock.py`: local lock files/descriptors and
contention subprocess results. `test_managed_lock_broker.py`: private socket,
lease/token/lifecycle state and process results; not a remote Issue lock.
- `common/repository/test_repository_prefetch.py`: temporary local Git/cache
fixtures. Root tests produce local ownership/path/cleanup state;
`test_test_inventory_binding.py` produces source-bound inventories and binding
decisions, not semantic test-coverage proof.
- `common/runner/test_canonical_test_runner.py`: temporary source trees,
canonical collection JSON/diagnostics and subprocess exit codes, including
negative admission inputs. This is runner behavior, not product delivery.
- `impl/contracts/test_impl_architecture_wiring.py`: canonical document/link
assertions only. `test_impl_architecture_comparator.py`: comparator code
extracted from that document, synthetic inputs and comparator decisions.
- `impl/delivery/test_impl_operation.py`, `test_implementation_delivery.py`
and `test_implementation_operation_integration.py`: local operation plans,
evidence/adoption records and adapter call/effect ordering.
`test_delivery_response.py` and `test_delivery_reconciliation.py` add raised
HTTP errors or loopback response/reconciliation fixtures and completion
decisions; neither authorizes retrying an uncertain remote write.
- `impl/evidence/test_evidence_manifest_schema.py`: plan/manifest JSON,
command-admission diagnostics and bounded command outputs.
`test_evidence_manifest_filesystem_git.py`: real temporary Git identities,
artifact bytes/paths and manifest validation.
`test_evidence_command_deadlines.py`: actual child/process-group deadline
behavior and local command logs; separate from Git topology validation.
- `impl/rework/test_rework_input_gate_*.py`: schema inputs, CLI output files,
role/timestamp decisions and counter/publication-phase diagnostics using
canonical production entry points and synthetic provider data.
- `impl/rework/test_rework_readback_provider.py`: provider-shaped snapshots,
pagination and normalization decisions. Cache tests add strict-cache records;
bridge/transport tests add renderer/CLI or loopback HTTP artifacts. These are
not authenticated fresh Gitea readbacks.
- `impl/rework/test_rework_comment.py`, `test_rework_correction.py` and
`test_rework_publication.py`: local rendered comments, correction/publication
plans, synthetic readback/receipt and no-write/no-retry call records.
- `review/contracts/test_review_document_contracts.py`: actual policy document
assertions only; no native review publication or observability run receipt.
- `review/evidence/test_native_evidence_recipe.py`: actual recipe execution in
temporary roots, evidence plans/manifests/logs and archive-path rejection;
not a product delivery manifest or live provider verification.
- `review/journal/test_journal_safety.py` and
`test_journal_filesystem_safety.py`: private binding/presence records,
byte/mode/path integrity and corruption decisions.
`test_legacy_journal_contract.py`: source raw bytes, registration receipt,
imported legacy archive and zero-POST assertions; imported archives do not
establish fresh verification. `test_publication_processes.py`: actual child
crashes, lock contention and durable POST-counter/journal state.
- `review/native/test_native_publication.py`: synthetic plans/snapshots,
journal/receipt and fake API call counts. Lease tests add local worktree/lease
admission state; resume tests add durable pending-intent/readback failures;
transport tests add local HTTP request/response and adapter artifacts.
- `spec/contracts/test_priority_review_contracts.py`: actual canonical documents
and links, not execution of local-model examples.
`test_actual_collector.py`: actual collector callback arguments/results.
- `spec/projection/test_requested_reviewer.py` and
`test_source_issue_reference.py`: distinct snapshot/projection inputs,
production comparison results, CLI stdout/stderr and unchanged-file checks.
- `spec/support/test_codec.py`: rendered/parsed result bytes and CLI decisions;
`test_cache.py`: private cache/path/ownership admission;
`test_publication.py`: synthetic exact readback/publication comparisons;
`test_ac_checker.py`: actual classification CLI/input digest decisions and
canonical documentation assertions. None is live authenticated publication.
Exact per-test IDs, baseline predecessors, source anchors, artifact scope,
verification layers and actual execution outcomes are retained in the run's
`traceability.json`; `inventory-summary.json` distinguishes documentation,
production behavior and overlapping process/integration evidence rather than
adding incompatible counts. For this cleanup the run directory is
`logs/regression-test-implementation/20261015T232331Z-final/`.
Rebuild that index after source/test-ID changes; fail closed on an unmapped ID,
missing outcome or source-hash mismatch. Primary classifications come from
source assertions/calls and fixture ownership. Detailed Git/HTTP/process/archive
labels are conservative source-confirmed subsets, not exhaustive dynamic
coverage counts; indirect helper calls may add work beyond those labels. Layer
counts overlap, and a declared execution layer is not executed evidence when
its recorded outcome is skipped.
The final raw collection/runtime inputs are preserved in that run directory's
`acceptance-final/` subdirectory. After reviewing any classification changes,
rebuild the exact join with:
```sh
python3 -B logs/regression-test-implementation/20261015T232331Z-final/refine_classification.py
python3 -B logs/regression-test-implementation/20261015T232331Z-final/build_traceability.py
```
These are run-specific evidence tools, not a replacement canonical runner.
Do not infer coverage from a filename, a successful collection or the mere
existence of an artifact.
The cleanup's original-to-current exact ID dispositions and run-specific counts,
verification-level inventory, collection JSON, fault-injection controls and full
execution logs are preserved under
`logs/regression-test-implementation/20261015/`. The original 659-ID baseline
under `logs/regression-test-audit/20261015/` is immutable. Counts in run evidence
refer to actual methods (subTest branches are not additional tests); domain and
verification-level labels do not make one test a substitute for another.