diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index 1d58ab1e..b30169ae 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -345,3 +345,40 @@ jobs: [ "$FAILURES" = "0" ] || { echo "::error::failures $FAILURES, clause (d) requires zero"; rc=1; } [ "$ERRORS" = "0" ] || { echo "::error::errors $ERRORS, clause (d) requires zero"; rc=1; } exit $rc + + # --------------------------------------------------------------------------- + # AT-R1, THE HTTP HALF — plan 034 T095 (FR-011's HTTP half; openxFactory + # `specs/034-opendox-standalone-operation/spec.md` § "AT-R1"). Release 1's + # acceptance: on a clean machine with only openDox installed, a plain git + # repository opens, and the wheel, the radar lens and the chat pane are + # served, with chat's "no model configured" state. + # + # ITS OWN JOB, AND WITH NO DATABASE SERVICE, because the harness ASSERTS a + # clean machine: nothing answers on PostgreSQL's port or socket, and no DSN + # is set. The `validate` job's PostgreSQL service (T036) would break that + # precondition, so the two cannot share a job. + # + # NOT A PYTEST RUN. `acceptance/at_r1_http.py` sits outside `tests/` and + # `tests_runtime/`, so `testpaths` never collects it and #1144's F9.1 is + # unchanged. It installs `opendox[local]` into a fresh venv of its own, + # from this checkout and through the dependency lock, runs the documented + # `opendox generate-and-open --local …` over two plain repositories, checks + # what the server answers, stops it, and asserts that no bundled PostgreSQL + # process is left. It exits non-zero on the first failed assertion, naming + # it. So this job installs nothing itself, and the one `pip install` step + # in this file stays the `validate` job's. + # + # NOT A REQUIRED CHECK. Making it one is a ruleset change for the + # repository's owner (`docs/branch-protection.md`); this file cannot. + acceptance: + runs-on: ubuntu-latest + timeout-minutes: 20 + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-python@v5 + with: + python-version: '3.12' + + - name: AT-R1, the HTTP half + run: python3 acceptance/at_r1_http.py diff --git a/acceptance/at_r1_http.py b/acceptance/at_r1_http.py new file mode 100644 index 00000000..684fb5d4 --- /dev/null +++ b/acceptance/at_r1_http.py @@ -0,0 +1,3158 @@ +#!/usr/bin/env python3 +"""AT-R1, the HTTP half: release 1's acceptance, run in CI (plan 034 T095). + +The brief AT-R1 makes checkable (openxFactory +`specs/034-opendox-standalone-operation/spec.md` § "AT-R1"): *"on a clean +machine with only openDox installed, a plain git repo opens, and the wheel, +radar lens and chat panes work, with chat's 'no model configured' state."* +This harness is that test's HTTP half: steps 1-4, and the route answers behind +steps 5-8. The browser half is a Playwright run on the host (T096), whose +verdict `tests/smoke_signals.py` computes. It realizes FR-011's HTTP half. + +WHAT IT IS NOT. It is not a pytest module, and it sits outside `tests/` and +`tests_runtime/`, so `pyproject.toml`'s `testpaths` never collects it and +#1144's F9.1 is unchanged. It installs the product and drives it from +outside, as a user would, so it is no member of this leg's suite, and FR-006's +"no exclusion" still holds. It runs in its own `acceptance` job in +`.github/workflows/validate.yml`, which has NO database service, because the +harness asserts a clean machine and the `validate` job's PostgreSQL service +would break that precondition. Making `acceptance` a required check is a +ruleset change for the repository's owner. + +WHAT IT DOES, in order. Each step is an assertion with an id; the run stops at +the FIRST that fails, prints `AT-R1 HTTP half: FAIL []: ` and exits 1 +(`--keep-going` reports every failed assertion instead, and still exits 1). +A harness that breaks before a verdict exits 2, never 0. + + 1. INSTALLS openDox ALONE into a fresh venv, as `opendox[local]` (R1Q16 + (iii)), from a copy of this checkout's tracked files. The documented + install is `pip install "opendox[local]"`; no release of `opendox` is + published to a package index, so this installs the same distribution + with the same extra from the checkout, `pip install "[local]"`, + which is the substitution the openDox root's README states for the first + line. With `constraints-cpython312-linux.txt` beside it and a CPython + 3.12 on Linux, the install reads that lock, as every install of this + package does (`tests_runtime/test_deploy_shape.py`); the lock pins + versions and adds no package. + 2. ASSERTS THE CLEAN MACHINE, in a fresh `OPENDOX_STATE_DIR` created empty + for this run (and short, so the bundled server's socket path fits): none + of the four siblings (`openxdox`, `ideation_dashboard`, `doc_health`, + `corpus_adapter_openxfactory`) is importable; `omp` (the harness the + product names, `doxbench_bridge.HARNESS_COMMAND`) is not on the PATH; no + identity broker (`openprofiler-broker`, the one that exists) is on the + PATH and no issuer is set; no database answers on PostgreSQL's default + port or on a distribution's socket, and no DSN is set; and neither + repository holds a model binding (`doxbench_binding.bindings_path`). + 3. COPIES BOTH PLAIN REPOSITORIES into fresh `git init`s (AT-R1 step 3): + (a) 5.0's `tests/fixtures/plain-documents`, and (b) three ordinary + Markdown notes with no front matter at all, quickstart.md § 2's. Each gets + a git identity (`git config user.name` and `user.email`), which the served + actor is read from. + 4. RUNS THE DOCUMENTED COMMAND on loopback, once per repository. The command + is the one T007 batch H's 10.3 addendum names and the openDox root's + README documents, `opendox generate-and-open --local …`. The harness fills + the `…` with the verb's own arguments, as quickstart.md § 3 does, and + checks its argv against that line before it runs it. + 5. FETCHES `/` (it must be HTML), `/snapshot.json` (non-empty, and neutral + per F5.3: none of openxFactory's declared governance words in any string + value; and it fills the grouping station, so the chat pane can open, R1Q13 + (a) with (c)) and `/capabilities` (`install.mode == "local"`, and NO + console token: a standalone plane hands its token to no loopback + caller, T104). As quickstart.md § 3 asserts it (T007 batch N), the RAW + payload carries the token neither by name, at any depth, nor by value, + which step 6 checks once the opener has handed the harness the token. + 6. READS THE CONSOLE TOKEN THE WAY THE USER'S BROWSER IS HANDED IT (plan 034 + T104; RULED openxFactory#656 `5963851934`). The start prints the PATH of + a private opener file, `/console/.html`, and + never the token. The file must be this user's regular file, mode 0600, + with one link, in a directory of mode 0700, under a state + directory and ancestors no other user can change (T104's own rules for + that tree), and outside the served repository. Its one LIVE + meta-refresh (none inside a `