@gate-forge/pack-playwright 0.7.1 → 0.8.0
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.
- package/README.md +31 -0
- package/dist/constants.d.ts +13 -140
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +13 -148
- package/dist/constants.js.map +1 -1
- package/dist/cypress/plugin.d.ts +53 -0
- package/dist/cypress/plugin.d.ts.map +1 -0
- package/dist/cypress/plugin.js +369 -0
- package/dist/cypress/plugin.js.map +1 -0
- package/dist/cypress/spec-scan.d.ts +39 -0
- package/dist/cypress/spec-scan.d.ts.map +1 -0
- package/dist/cypress/spec-scan.js +301 -0
- package/dist/cypress/spec-scan.js.map +1 -0
- package/dist/diagnosis.d.ts +39 -0
- package/dist/diagnosis.d.ts.map +1 -0
- package/dist/diagnosis.js +63 -0
- package/dist/diagnosis.js.map +1 -0
- package/dist/discovery/adapter-projection.d.ts +13 -0
- package/dist/discovery/adapter-projection.d.ts.map +1 -0
- package/dist/discovery/adapter-projection.js +84 -0
- package/dist/discovery/adapter-projection.js.map +1 -0
- package/dist/discovery/config-locations.d.ts +5 -0
- package/dist/discovery/config-locations.d.ts.map +1 -0
- package/dist/discovery/config-locations.js +19 -0
- package/dist/discovery/config-locations.js.map +1 -0
- package/dist/discovery/cypress-runner-adapter.d.ts +112 -0
- package/dist/discovery/cypress-runner-adapter.d.ts.map +1 -0
- package/dist/discovery/cypress-runner-adapter.js +501 -0
- package/dist/discovery/cypress-runner-adapter.js.map +1 -0
- package/dist/discovery/discover.d.ts +53 -1
- package/dist/discovery/discover.d.ts.map +1 -1
- package/dist/discovery/discover.js +100 -3
- package/dist/discovery/discover.js.map +1 -1
- package/dist/discovery/git-ignore.d.ts +13 -0
- package/dist/discovery/git-ignore.d.ts.map +1 -0
- package/dist/discovery/git-ignore.js +62 -0
- package/dist/discovery/git-ignore.js.map +1 -0
- package/dist/discovery/index.d.ts +9 -0
- package/dist/discovery/index.d.ts.map +1 -1
- package/dist/discovery/index.js +9 -0
- package/dist/discovery/index.js.map +1 -1
- package/dist/discovery/playwright-runner-adapter.d.ts +108 -0
- package/dist/discovery/playwright-runner-adapter.d.ts.map +1 -0
- package/dist/discovery/playwright-runner-adapter.js +185 -0
- package/dist/discovery/playwright-runner-adapter.js.map +1 -0
- package/dist/discovery/prepare-barrier.d.ts +522 -0
- package/dist/discovery/prepare-barrier.d.ts.map +1 -0
- package/dist/discovery/prepare-barrier.js +739 -0
- package/dist/discovery/prepare-barrier.js.map +1 -0
- package/dist/discovery/pytest-adapter.d.ts.map +1 -1
- package/dist/discovery/pytest-adapter.js +6 -2
- package/dist/discovery/pytest-adapter.js.map +1 -1
- package/dist/discovery/pytest-runner-adapter.d.ts +124 -0
- package/dist/discovery/pytest-runner-adapter.d.ts.map +1 -0
- package/dist/discovery/pytest-runner-adapter.js +454 -0
- package/dist/discovery/pytest-runner-adapter.js.map +1 -0
- package/dist/discovery/reconcile.d.ts +80 -11
- package/dist/discovery/reconcile.d.ts.map +1 -1
- package/dist/discovery/reconcile.js +395 -78
- package/dist/discovery/reconcile.js.map +1 -1
- package/dist/discovery/runner-env.d.ts +88 -2
- package/dist/discovery/runner-env.d.ts.map +1 -1
- package/dist/discovery/runner-env.js +142 -4
- package/dist/discovery/runner-env.js.map +1 -1
- package/dist/discovery/static-discovery.d.ts +38 -3
- package/dist/discovery/static-discovery.d.ts.map +1 -1
- package/dist/discovery/static-discovery.js +454 -15
- package/dist/discovery/static-discovery.js.map +1 -1
- package/dist/discovery/supervised-run.d.ts +199 -10
- package/dist/discovery/supervised-run.d.ts.map +1 -1
- package/dist/discovery/supervised-run.js +350 -39
- package/dist/discovery/supervised-run.js.map +1 -1
- package/dist/discovery/trusted-config.d.ts +180 -6
- package/dist/discovery/trusted-config.d.ts.map +1 -1
- package/dist/discovery/trusted-config.js +279 -10
- package/dist/discovery/trusted-config.js.map +1 -1
- package/dist/discovery/vitest-runner-adapter.d.ts +172 -0
- package/dist/discovery/vitest-runner-adapter.d.ts.map +1 -0
- package/dist/discovery/vitest-runner-adapter.js +607 -0
- package/dist/discovery/vitest-runner-adapter.js.map +1 -0
- package/dist/fixture/consumer-runner.d.ts +4 -0
- package/dist/fixture/consumer-runner.d.ts.map +1 -0
- package/dist/fixture/consumer-runner.js +38 -0
- package/dist/fixture/consumer-runner.js.map +1 -0
- package/dist/fixture/evidence.d.ts +27 -0
- package/dist/fixture/evidence.d.ts.map +1 -1
- package/dist/fixture/evidence.js +12 -0
- package/dist/fixture/evidence.js.map +1 -1
- package/dist/fixture/fixture.d.ts +2 -1
- package/dist/fixture/fixture.d.ts.map +1 -1
- package/dist/fixture/fixture.js +1 -17
- package/dist/fixture/fixture.js.map +1 -1
- package/dist/fixture/witness-client.d.ts +6 -174
- package/dist/fixture/witness-client.d.ts.map +1 -1
- package/dist/fixture/witness-client.js +6 -267
- package/dist/fixture/witness-client.js.map +1 -1
- package/dist/index.d.ts +9 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -4
- package/dist/index.js.map +1 -1
- package/dist/json.d.ts +6 -12
- package/dist/json.d.ts.map +1 -1
- package/dist/json.js +6 -17
- package/dist/json.js.map +1 -1
- package/dist/reporter/project-graph-reporter.d.ts +92 -0
- package/dist/reporter/project-graph-reporter.d.ts.map +1 -0
- package/dist/reporter/project-graph-reporter.js +92 -0
- package/dist/reporter/project-graph-reporter.js.map +1 -0
- package/dist/reporter/reporter.cjs.map +1 -1
- package/dist/reporter/reporter.d.ts +93 -4
- package/dist/reporter/reporter.d.ts.map +1 -1
- package/dist/reporter/reporter.js +174 -18
- package/dist/reporter/reporter.js.map +1 -1
- package/dist/runner-claims.d.ts +13 -0
- package/dist/runner-claims.d.ts.map +1 -0
- package/dist/runner-claims.js +46 -0
- package/dist/runner-claims.js.map +1 -0
- package/dist/runner-resolution.d.ts +3 -0
- package/dist/runner-resolution.d.ts.map +1 -0
- package/dist/runner-resolution.js +14 -0
- package/dist/runner-resolution.js.map +1 -0
- package/dist/supervisor/client.d.ts +23 -1
- package/dist/supervisor/client.d.ts.map +1 -1
- package/dist/supervisor/client.js +49 -0
- package/dist/supervisor/client.js.map +1 -1
- package/dist/supervisor/drain.d.ts +65 -0
- package/dist/supervisor/drain.d.ts.map +1 -1
- package/dist/supervisor/drain.js +353 -9
- package/dist/supervisor/drain.js.map +1 -1
- package/dist/supervisor/index.d.ts +7 -3
- package/dist/supervisor/index.d.ts.map +1 -1
- package/dist/supervisor/index.js +7 -3
- package/dist/supervisor/index.js.map +1 -1
- package/dist/supervisor/spool.d.ts +65 -2
- package/dist/supervisor/spool.d.ts.map +1 -1
- package/dist/supervisor/spool.js +47 -0
- package/dist/supervisor/spool.js.map +1 -1
- package/dist/surface.d.ts +6 -207
- package/dist/surface.d.ts.map +1 -1
- package/dist/surface.js +6 -264
- package/dist/surface.js.map +1 -1
- package/dist/vitest/index.d.ts +23 -0
- package/dist/vitest/index.d.ts.map +1 -0
- package/dist/vitest/index.js +356 -0
- package/dist/vitest/index.js.map +1 -0
- package/dist/vitest/reporter.d.ts +83 -0
- package/dist/vitest/reporter.d.ts.map +1 -0
- package/dist/vitest/reporter.js +211 -0
- package/dist/vitest/reporter.js.map +1 -0
- package/dist/vitest/worker-slot.d.ts +36 -0
- package/dist/vitest/worker-slot.d.ts.map +1 -0
- package/dist/vitest/worker-slot.js +51 -0
- package/dist/vitest/worker-slot.js.map +1 -0
- package/dist/witness/adapter-registry.d.ts +6 -50
- package/dist/witness/adapter-registry.d.ts.map +1 -1
- package/dist/witness/adapter-registry.js +6 -257
- package/dist/witness/adapter-registry.js.map +1 -1
- package/dist/witness/behavior-request.d.ts +6 -76
- package/dist/witness/behavior-request.d.ts.map +1 -1
- package/dist/witness/behavior-request.js +6 -273
- package/dist/witness/behavior-request.js.map +1 -1
- package/dist/witness/behavior.d.ts +6 -48
- package/dist/witness/behavior.d.ts.map +1 -1
- package/dist/witness/behavior.js +6 -114
- package/dist/witness/behavior.js.map +1 -1
- package/dist/witness/bin.d.ts +6 -17
- package/dist/witness/bin.d.ts.map +1 -1
- package/dist/witness/bin.js +6 -168
- package/dist/witness/bin.js.map +1 -1
- package/dist/witness/browser.d.ts +14 -206
- package/dist/witness/browser.d.ts.map +1 -1
- package/dist/witness/browser.js +15 -654
- package/dist/witness/browser.js.map +1 -1
- package/dist/witness/classifications.d.ts +6 -38
- package/dist/witness/classifications.d.ts.map +1 -1
- package/dist/witness/classifications.js +6 -83
- package/dist/witness/classifications.js.map +1 -1
- package/dist/witness/env-attestation.d.ts +6 -94
- package/dist/witness/env-attestation.d.ts.map +1 -1
- package/dist/witness/env-attestation.js +6 -198
- package/dist/witness/env-attestation.js.map +1 -1
- package/dist/witness/fixture-provider.d.ts +6 -82
- package/dist/witness/fixture-provider.d.ts.map +1 -1
- package/dist/witness/fixture-provider.js +6 -105
- package/dist/witness/fixture-provider.js.map +1 -1
- package/dist/witness/loopback-pins.d.ts +6 -75
- package/dist/witness/loopback-pins.d.ts.map +1 -1
- package/dist/witness/loopback-pins.js +6 -240
- package/dist/witness/loopback-pins.js.map +1 -1
- package/dist/witness/server.d.ts +8 -26
- package/dist/witness/server.d.ts.map +1 -1
- package/dist/witness/server.js +24 -3761
- package/dist/witness/server.js.map +1 -1
- package/dist/witness/types.d.ts +6 -882
- package/dist/witness/types.d.ts.map +1 -1
- package/dist/witness/types.js +9 -1
- package/dist/witness/types.js.map +1 -1
- package/examples/overlay-proof.spec.js +75 -0
- package/package.json +8 -2
- package/python/__pycache__/gateforge_persistence_intents.cpython-312.pyc +0 -0
- package/python/__pycache__/gateforge_persistence_intents.cpython-314.pyc +0 -0
- package/python/gateforge_pytest_plugin.py +462 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/witness/types.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/witness/types.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,cAAc,mCAAmC,CAAC"}
|
package/dist/witness/types.js
CHANGED
|
@@ -1,2 +1,10 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Compatibility re-export: `witness/types` now lives in the runner-neutral
|
|
3
|
+
* `@gate-forge/witness` package (plan 2026-09-25 phase 1).
|
|
4
|
+
*
|
|
5
|
+
* The move is physical, not behavioural: this module forwards every
|
|
6
|
+
* export unchanged so `@gate-forge/pack-playwright`'s published import
|
|
7
|
+
* paths — which consumers pin — keep resolving to the same values.
|
|
8
|
+
*/
|
|
9
|
+
export * from '@gate-forge/witness/witness/types';
|
|
2
10
|
//# sourceMappingURL=types.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/witness/types.ts"],"names":[],"mappings":""}
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/witness/types.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,cAAc,mCAAmC,CAAC"}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
// A complete, self-contained overlay proof test. Copy it into your own
|
|
2
|
+
// `tests/e2e/gateforge/<resource>.<op>.spec.js` and change the surface —
|
|
3
|
+
// the fixture calls never change.
|
|
4
|
+
//
|
|
5
|
+
// The shape is always the same:
|
|
6
|
+
//
|
|
7
|
+
// 1. `gateforgeTest.extend({ surface })` — YOUR half: the selectors
|
|
8
|
+
// that read YOUR rendered UI. The pack ships no selectors.
|
|
9
|
+
// 2. `annotation: { type: 'gateforge', description: '<obligation key>' }`
|
|
10
|
+
// — the claim this test proves. Copy the exact key from
|
|
11
|
+
// `gateforge next` or the report; a test that names no obligation
|
|
12
|
+
// grades nothing.
|
|
13
|
+
// 3. `evidence.ui.*` — the UI action, inside a witness-recorded
|
|
14
|
+
// interval, with the created identity read back from the rendered
|
|
15
|
+
// list (never from suite-declared data).
|
|
16
|
+
// 4. `evidence.visible.confirm(receipt)` — the result a user sees.
|
|
17
|
+
// 5. `evidence.http.observe(...)` — the transport exchange the browser
|
|
18
|
+
// made inside that same interval.
|
|
19
|
+
// 6. `evidence.persistence.verify(receipt)` — the engine observes the
|
|
20
|
+
// server's own echo on the SAME entity; `fieldsMatch` is the
|
|
21
|
+
// postcondition, graded by the engine, not by this test.
|
|
22
|
+
// 7. `evidence.finalize()` — closes the evidence bundle.
|
|
23
|
+
//
|
|
24
|
+
// Every `evidence` call is a witnessed channel. A plain `page.click` is
|
|
25
|
+
// not evidence: nothing then proves what the server stored.
|
|
26
|
+
import { test as gateforgeTest, expect, SURFACE_DESCRIPTOR_VERSION } from '@gate-forge/pack-playwright';
|
|
27
|
+
|
|
28
|
+
// Replace these selectors with your own rendered UI. Every section is
|
|
29
|
+
// required (`validateSurface` refuses an incomplete surface); `{id}`
|
|
30
|
+
// templates are substituted with the entity id the UI action produced.
|
|
31
|
+
const surface = Object.freeze({
|
|
32
|
+
schemaVersion: SURFACE_DESCRIPTOR_VERSION,
|
|
33
|
+
list: {
|
|
34
|
+
path: '/',
|
|
35
|
+
readySelector: 'h1:has-text("Contacts")',
|
|
36
|
+
rowSelector: 'tbody tr',
|
|
37
|
+
idCellIndex: 0,
|
|
38
|
+
fieldCellIndexes: { name: 1, status: 2 },
|
|
39
|
+
},
|
|
40
|
+
create: {
|
|
41
|
+
formPath: '/contacts/new',
|
|
42
|
+
formReadySelector: 'form[action="/contacts"]',
|
|
43
|
+
fields: { name: 'input[name="name"]' },
|
|
44
|
+
submitSelector: 'button[type="submit"]',
|
|
45
|
+
},
|
|
46
|
+
edit: {
|
|
47
|
+
linkSelector: 'a[href$="/edit"]',
|
|
48
|
+
formReadySelectorTemplate: 'form[action="/contacts/{id}"]',
|
|
49
|
+
fields: { name: 'input[name="name"]' },
|
|
50
|
+
saveSelectorTemplate: 'form[action="/contacts/{id}"] button:not([formaction])',
|
|
51
|
+
},
|
|
52
|
+
archive: {
|
|
53
|
+
controlSelectorTemplate: 'form[action="/contacts/{id}/archive"] button',
|
|
54
|
+
},
|
|
55
|
+
status: {
|
|
56
|
+
field: 'status',
|
|
57
|
+
createdValue: 'active',
|
|
58
|
+
archivedValue: 'archived',
|
|
59
|
+
},
|
|
60
|
+
afterAction: { path: '/' },
|
|
61
|
+
deleteFields: { status: 'archived' },
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
const test = gateforgeTest.extend({ surface });
|
|
65
|
+
|
|
66
|
+
test('creates a contact through the rendered UI (persistence:create)', {
|
|
67
|
+
annotation: { type: 'gateforge', description: 'tenant.contacts:persistence:create' },
|
|
68
|
+
}, async ({ evidence }) => {
|
|
69
|
+
const receipt = await evidence.ui.create({ fields: { name: 'Ada' } });
|
|
70
|
+
await evidence.visible.confirm(receipt);
|
|
71
|
+
await evidence.http.observe({ method: 'POST', path: '/contacts' });
|
|
72
|
+
const outcome = await evidence.persistence.verify(receipt);
|
|
73
|
+
expect(outcome.verdictRelevant.fieldsMatch, JSON.stringify(outcome.verdictRelevant)).toBe(true);
|
|
74
|
+
await evidence.finalize();
|
|
75
|
+
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gate-forge/pack-playwright",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"gateforgeCompatibility": {
|
|
5
5
|
"supervisedFixtureProtocol": "gateforge.supervised-playwright.v1"
|
|
6
6
|
},
|
|
@@ -27,6 +27,10 @@
|
|
|
27
27
|
"types": "./dist/fixture/fixture.d.ts",
|
|
28
28
|
"import": "./dist/fixture/fixture.js"
|
|
29
29
|
},
|
|
30
|
+
"./vitest": {
|
|
31
|
+
"types": "./dist/vitest/index.d.ts",
|
|
32
|
+
"import": "./dist/vitest/index.js"
|
|
33
|
+
},
|
|
30
34
|
"./witness": {
|
|
31
35
|
"types": "./dist/witness/server.d.ts",
|
|
32
36
|
"import": "./dist/witness/server.js"
|
|
@@ -39,6 +43,7 @@
|
|
|
39
43
|
"files": [
|
|
40
44
|
"bin",
|
|
41
45
|
"dist",
|
|
46
|
+
"examples",
|
|
42
47
|
"python"
|
|
43
48
|
],
|
|
44
49
|
"scripts": {
|
|
@@ -47,7 +52,8 @@
|
|
|
47
52
|
"test": "vitest run"
|
|
48
53
|
},
|
|
49
54
|
"dependencies": {
|
|
50
|
-
"@gate-forge/core": "^0.
|
|
55
|
+
"@gate-forge/core": "^0.8.0",
|
|
56
|
+
"@gate-forge/witness": "^0.8.0",
|
|
51
57
|
"playwright": "1.58.2",
|
|
52
58
|
"typescript": "^5.9.2",
|
|
53
59
|
"yaml": "^2.8.1"
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,462 @@
|
|
|
1
|
+
"""Gateforge pytest plugin: per-test witness sessions + proxied ``httpx``.
|
|
2
|
+
|
|
3
|
+
Plan 2026-09-25 phase 3. The plugin gives pytest the SAME supervised
|
|
4
|
+
evidence channel the Playwright pack gives the browser:
|
|
5
|
+
|
|
6
|
+
- Lifecycle: each test writes ``testBegin`` / ``testEnd`` events (test
|
|
7
|
+
identity, outcome, attempt) to the run's lifecycle spool. The trusted
|
|
8
|
+
CLI drain reads the spool and performs the witness's SUPERVISOR calls
|
|
9
|
+
(session open/seal); this process holds no supervisor rights at all.
|
|
10
|
+
- Tagging: the ``gateforge_session`` fixture resolves the
|
|
11
|
+
supervisor-issued session credential for the RUNNING test by the exact
|
|
12
|
+
``(workerIndex, testId)`` pair (only an OPEN supervisor session
|
|
13
|
+
answers). The ``gateforge_http`` fixture hands the test an
|
|
14
|
+
``httpx.Client`` whose transport rewrites the app origin onto the
|
|
15
|
+
per-test session proxy origin, so every exchange is attributed to THAT
|
|
16
|
+
session — the same ``session-proxy`` channel the Playwright fixture
|
|
17
|
+
uses.
|
|
18
|
+
|
|
19
|
+
The plugin is INERT without supervised wiring: when the run-scoped
|
|
20
|
+
``GATEFORGE_*`` variables are absent (plain ``pytest``, collection,
|
|
21
|
+
advisory diagnostics) every hook is a no-op, so enumeration and
|
|
22
|
+
non-witnessed runs behave exactly as without the plugin. A test that
|
|
23
|
+
REQUESTS the fixtures without wiring fails loudly — never silently
|
|
24
|
+
passes unwitnessed.
|
|
25
|
+
|
|
26
|
+
Claims: mapped obligation ids come from the CLI-written
|
|
27
|
+
``claim-injections.json`` in the run-state dir, keyed by the
|
|
28
|
+
reconciliation key ``<file>#<title path joined by '>'>``. The document
|
|
29
|
+
is read-only here; a broken or absent document contributes nothing.
|
|
30
|
+
|
|
31
|
+
Env contract (all four required for activation):
|
|
32
|
+
- ``GATEFORGE_WITNESS_URL`` witness base URL,
|
|
33
|
+
- ``GATEFORGE_RUN_TOKEN`` the run's submission credential,
|
|
34
|
+
- ``GATEFORGE_STATE_DIR`` absolute run-state dir (spool + claims),
|
|
35
|
+
- ``GATEFORGE_RUN_ID`` the run identity both sides agree on.
|
|
36
|
+
|
|
37
|
+
Optional: ``GATEFORGE_APP_BASE_URL`` (the app origin the transport
|
|
38
|
+
rewrites; without it ``gateforge_http`` uses the session proxy as base).
|
|
39
|
+
|
|
40
|
+
Requires pytest >= 7 (hook wrappers) and, for ``gateforge_http``,
|
|
41
|
+
``httpx`` importable in the test environment.
|
|
42
|
+
"""
|
|
43
|
+
|
|
44
|
+
import json
|
|
45
|
+
import os
|
|
46
|
+
import re
|
|
47
|
+
import time
|
|
48
|
+
import urllib.error
|
|
49
|
+
import urllib.request
|
|
50
|
+
|
|
51
|
+
import pytest
|
|
52
|
+
|
|
53
|
+
WITNESS_URL_ENV = "GATEFORGE_WITNESS_URL"
|
|
54
|
+
RUN_TOKEN_ENV = "GATEFORGE_RUN_TOKEN"
|
|
55
|
+
STATE_DIR_ENV = "GATEFORGE_STATE_DIR"
|
|
56
|
+
RUN_ID_ENV = "GATEFORGE_RUN_ID"
|
|
57
|
+
APP_BASE_URL_ENV = "GATEFORGE_APP_BASE_URL"
|
|
58
|
+
|
|
59
|
+
RUN_HEADER = "x-gateforge-run"
|
|
60
|
+
CLAIM_INJECTIONS_FILE = "claim-injections.json"
|
|
61
|
+
|
|
62
|
+
SESSION_RESOLVE_TIMEOUT_SECONDS = 5.0
|
|
63
|
+
SESSION_RESOLVE_POLL_SECONDS = 0.05
|
|
64
|
+
SPOOL_POLL_SETTLE_SECONDS = 0.05
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def _wired():
|
|
68
|
+
"""Whether the run-scoped supervised wiring is present.
|
|
69
|
+
|
|
70
|
+
Returns:
|
|
71
|
+
bool: True exactly when every activation variable is set and
|
|
72
|
+
non-empty; False means the plugin stays inert.
|
|
73
|
+
"""
|
|
74
|
+
return all(os.environ.get(name) for name in (WITNESS_URL_ENV, RUN_TOKEN_ENV, STATE_DIR_ENV, RUN_ID_ENV))
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def _spool_file():
|
|
78
|
+
"""The run's lifecycle spool path.
|
|
79
|
+
|
|
80
|
+
Returns:
|
|
81
|
+
str: absolute ``<stateDir>/spool/<runId>/events.jsonl`` path.
|
|
82
|
+
"""
|
|
83
|
+
return os.path.join(
|
|
84
|
+
os.environ[STATE_DIR_ENV], "spool", os.environ[RUN_ID_ENV], "events.jsonl"
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def _append_event(event):
|
|
89
|
+
"""Append one lifecycle event line to the spool (runner side).
|
|
90
|
+
|
|
91
|
+
The line is one JSON object terminated by a newline (the format is
|
|
92
|
+
NUL-safe by construction: ``json.dumps`` escapes control characters).
|
|
93
|
+
A failed append never crashes the run: the drain then simply never
|
|
94
|
+
opens that session and the witness rejects submissions fail-closed.
|
|
95
|
+
|
|
96
|
+
Args:
|
|
97
|
+
event: dict with the spool event fields (kind, testId,
|
|
98
|
+
workerIndex, file, titlePath, project, and for testEnd also
|
|
99
|
+
outcome + attempt; claims when mapped).
|
|
100
|
+
|
|
101
|
+
Returns:
|
|
102
|
+
None.
|
|
103
|
+
"""
|
|
104
|
+
try:
|
|
105
|
+
path = _spool_file()
|
|
106
|
+
os.makedirs(os.path.dirname(path), exist_ok=True)
|
|
107
|
+
with open(path, "a", encoding="utf-8") as handle:
|
|
108
|
+
handle.write(json.dumps(event, separators=(",", ":")) + "\n")
|
|
109
|
+
except OSError as error:
|
|
110
|
+
print("[gateforge] cannot append to the lifecycle spool: %s" % error)
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def _worker_index(config):
|
|
114
|
+
"""The worker slot this process runs in (pytest-xdist aware).
|
|
115
|
+
|
|
116
|
+
Args:
|
|
117
|
+
config: pytest config object (``workerinput`` exists only under
|
|
118
|
+
xdist workers).
|
|
119
|
+
|
|
120
|
+
Returns:
|
|
121
|
+
int: non-negative worker index; 0 for a non-distributed run.
|
|
122
|
+
"""
|
|
123
|
+
worker_input = getattr(config, "workerinput", None)
|
|
124
|
+
if not isinstance(worker_input, dict):
|
|
125
|
+
return 0
|
|
126
|
+
raw = str(worker_input.get("workerid", "gw0"))
|
|
127
|
+
digits = "".join(character for character in raw if character.isdigit())
|
|
128
|
+
return int(digits) if digits else 0
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def _identity(item):
|
|
132
|
+
"""The reconciliation identity of one test item.
|
|
133
|
+
|
|
134
|
+
Args:
|
|
135
|
+
item: the pytest item running.
|
|
136
|
+
|
|
137
|
+
Returns:
|
|
138
|
+
tuple: ``(file, title_path, key)`` where ``file`` is the
|
|
139
|
+
cwd-relative posix path of the test module, ``title_path`` is
|
|
140
|
+
the node id's segment list after the file part, and ``key`` is
|
|
141
|
+
the ``<file>#<title path joined by '>'>`` reconciliation key the
|
|
142
|
+
CLI's claim-injections document is keyed by.
|
|
143
|
+
"""
|
|
144
|
+
node_file = item.nodeid.split("::")[0]
|
|
145
|
+
path = getattr(item, "path", None)
|
|
146
|
+
if path is not None:
|
|
147
|
+
node_file = os.path.relpath(str(path), os.getcwd()).replace(os.sep, "/")
|
|
148
|
+
title_path = item.nodeid.split("::")[1:]
|
|
149
|
+
key = "%s#%s" % (node_file, ">".join(title_path))
|
|
150
|
+
return node_file, title_path, key
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
def _claims_for(key):
|
|
154
|
+
"""Mapped obligation ids for one reconciliation key.
|
|
155
|
+
|
|
156
|
+
Args:
|
|
157
|
+
key: the ``<file>#<title path>`` reconciliation key.
|
|
158
|
+
|
|
159
|
+
Returns:
|
|
160
|
+
list[str]: sorted, de-duplicated obligation ids; empty when the
|
|
161
|
+
claims document is absent, unmapped, or malformed (declarations
|
|
162
|
+
never redirect evidence on a broken document).
|
|
163
|
+
"""
|
|
164
|
+
path = os.path.join(os.environ[STATE_DIR_ENV], CLAIM_INJECTIONS_FILE)
|
|
165
|
+
try:
|
|
166
|
+
with open(path, encoding="utf-8") as handle:
|
|
167
|
+
document = json.load(handle)
|
|
168
|
+
except (OSError, ValueError):
|
|
169
|
+
return []
|
|
170
|
+
if not isinstance(document, dict):
|
|
171
|
+
return []
|
|
172
|
+
injections = document.get("injections")
|
|
173
|
+
if not isinstance(injections, dict):
|
|
174
|
+
return []
|
|
175
|
+
raw = injections.get(key)
|
|
176
|
+
if not isinstance(raw, list):
|
|
177
|
+
return []
|
|
178
|
+
claims = sorted({claim for claim in raw if isinstance(claim, str) and claim})
|
|
179
|
+
return claims
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
def _resolve_session(test_id, worker_index):
|
|
183
|
+
"""Resolve the supervisor-issued session credential for one test.
|
|
184
|
+
|
|
185
|
+
Polls ``POST /sessions/resolve`` briefly: the drain opens the
|
|
186
|
+
session asynchronously after the ``testBegin`` spool event lands.
|
|
187
|
+
Only an OPEN supervisor session answers; a never-opened or sealed
|
|
188
|
+
session 404s and keeps polling until the bound.
|
|
189
|
+
|
|
190
|
+
Args:
|
|
191
|
+
test_id: the runner test id the spool event carried.
|
|
192
|
+
worker_index: this process's worker slot.
|
|
193
|
+
|
|
194
|
+
Returns:
|
|
195
|
+
dict: the session credential (sessionId, sessionToken, proxyUrl,
|
|
196
|
+
claims, ...).
|
|
197
|
+
|
|
198
|
+
Raises:
|
|
199
|
+
RuntimeError: when no open session answers within the bound —
|
|
200
|
+
fail closed, never a silently unwitnessed "pass".
|
|
201
|
+
"""
|
|
202
|
+
request = json.dumps({"testId": test_id, "workerIndex": worker_index}).encode("utf-8")
|
|
203
|
+
deadline = time.monotonic() + SESSION_RESOLVE_TIMEOUT_SECONDS
|
|
204
|
+
last_detail = "no answer"
|
|
205
|
+
while time.monotonic() < deadline:
|
|
206
|
+
call = urllib.request.Request(
|
|
207
|
+
os.environ[WITNESS_URL_ENV].rstrip("/") + "/sessions/resolve",
|
|
208
|
+
data=request,
|
|
209
|
+
headers={"content-type": "application/json", RUN_HEADER: os.environ[RUN_TOKEN_ENV]},
|
|
210
|
+
method="POST",
|
|
211
|
+
)
|
|
212
|
+
try:
|
|
213
|
+
with urllib.request.urlopen(call, timeout=5) as response:
|
|
214
|
+
body = json.loads(response.read().decode("utf-8"))
|
|
215
|
+
if isinstance(body, dict) and body.get("sessionId"):
|
|
216
|
+
return body
|
|
217
|
+
last_detail = "malformed resolve response"
|
|
218
|
+
except urllib.error.HTTPError as error:
|
|
219
|
+
if error.code != 404:
|
|
220
|
+
last_detail = "HTTP %s from /sessions/resolve" % error.code
|
|
221
|
+
except (OSError, ValueError) as error:
|
|
222
|
+
last_detail = str(error)
|
|
223
|
+
time.sleep(SESSION_RESOLVE_POLL_SECONDS)
|
|
224
|
+
raise RuntimeError(
|
|
225
|
+
"gateforge: no open witness session answered for test '%s' (worker %s) within %.0fs (%s) "
|
|
226
|
+
"— run under the supervised window so the drain opens a session per started test"
|
|
227
|
+
% (test_id, worker_index, SESSION_RESOLVE_TIMEOUT_SECONDS, last_detail)
|
|
228
|
+
)
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
class GateforgeSession:
|
|
232
|
+
"""One test's supervisor-issued session credential.
|
|
233
|
+
|
|
234
|
+
Attributes:
|
|
235
|
+
session_id: the witness session id.
|
|
236
|
+
session_token: the per-session submission token.
|
|
237
|
+
proxy_url: the per-test session proxy origin (None when the run
|
|
238
|
+
wired no observation proxy).
|
|
239
|
+
app_base_url: the app origin the proxy fronts (may be empty).
|
|
240
|
+
"""
|
|
241
|
+
|
|
242
|
+
def __init__(self, credential, app_base_url):
|
|
243
|
+
self.session_id = str(credential.get("sessionId", ""))
|
|
244
|
+
self.session_token = str(credential.get("sessionToken", ""))
|
|
245
|
+
proxy_url = credential.get("proxyUrl")
|
|
246
|
+
self.proxy_url = str(proxy_url) if isinstance(proxy_url, str) and proxy_url else None
|
|
247
|
+
self.app_base_url = app_base_url or ""
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
def _proxied_client(session):
|
|
251
|
+
"""Build the session-proxied ``httpx.Client`` for one test.
|
|
252
|
+
|
|
253
|
+
The transport rewrites requests addressed to the app origin onto the
|
|
254
|
+
per-test session proxy origin (same loopback discipline as the
|
|
255
|
+
Playwright fixture's page routing), preserving the path, so the
|
|
256
|
+
exchange is observed under THIS test's session. Without an app base
|
|
257
|
+
URL the client addresses the session proxy directly (relative paths).
|
|
258
|
+
|
|
259
|
+
Args:
|
|
260
|
+
session: the resolved GateforgeSession.
|
|
261
|
+
|
|
262
|
+
Returns:
|
|
263
|
+
httpx.Client: the client the test drives (caller closes it).
|
|
264
|
+
"""
|
|
265
|
+
import httpx
|
|
266
|
+
from urllib.parse import urlsplit
|
|
267
|
+
|
|
268
|
+
if session.proxy_url is None:
|
|
269
|
+
raise RuntimeError(
|
|
270
|
+
"gateforge: the witness issued no per-session proxy for this run — the run must wire "
|
|
271
|
+
"an observation proxy (proxyTarget) so httpx traffic can be attributed to the session"
|
|
272
|
+
)
|
|
273
|
+
inner = httpx.HTTPTransport()
|
|
274
|
+
app = urlsplit(session.app_base_url) if session.app_base_url else None
|
|
275
|
+
proxy = urlsplit(session.proxy_url)
|
|
276
|
+
|
|
277
|
+
class _SessionProxyTransport(httpx.BaseTransport):
|
|
278
|
+
"""Rewrites app-origin requests onto the session proxy origin."""
|
|
279
|
+
|
|
280
|
+
def handle_request(self, request):
|
|
281
|
+
if (
|
|
282
|
+
app is not None
|
|
283
|
+
and request.url.host == app.hostname
|
|
284
|
+
and (request.url.port or (443 if request.url.scheme == "https" else 80))
|
|
285
|
+
== (app.port or (443 if app.scheme == "https" else 80))
|
|
286
|
+
):
|
|
287
|
+
request.url = request.url.copy_with(host=proxy.hostname, port=proxy.port)
|
|
288
|
+
return inner.handle_request(request)
|
|
289
|
+
|
|
290
|
+
base_url = session.app_base_url or session.proxy_url
|
|
291
|
+
return httpx.Client(base_url=base_url, transport=_SessionProxyTransport(), timeout=30.0)
|
|
292
|
+
|
|
293
|
+
|
|
294
|
+
def _failure_diagnosis(reports):
|
|
295
|
+
"""The additive diagnosis of one failed test, from pytest's own report.
|
|
296
|
+
|
|
297
|
+
Args:
|
|
298
|
+
reports: the stashed phase reports of one test.
|
|
299
|
+
|
|
300
|
+
Returns:
|
|
301
|
+
dict: ``errorMessage`` (the longrepr's first line) and
|
|
302
|
+
``stackFrames`` (at most five ``file:line`` entries), or an empty
|
|
303
|
+
dict when pytest reported no usable text.
|
|
304
|
+
"""
|
|
305
|
+
for report in reports:
|
|
306
|
+
longrepr = getattr(report, "longrepr", None)
|
|
307
|
+
if longrepr is None:
|
|
308
|
+
continue
|
|
309
|
+
text = str(longrepr)
|
|
310
|
+
message = text.splitlines()[0].strip() if text else ""
|
|
311
|
+
frames = []
|
|
312
|
+
for line in text.splitlines()[1:]:
|
|
313
|
+
match = re.search(r"([\w./-]+\.py):(\d+)", line)
|
|
314
|
+
if match is None:
|
|
315
|
+
continue
|
|
316
|
+
frame = "%s:%s" % (match.group(1), match.group(2))
|
|
317
|
+
if frame not in frames:
|
|
318
|
+
frames.append(frame)
|
|
319
|
+
if len(frames) == 5:
|
|
320
|
+
break
|
|
321
|
+
if message or frames:
|
|
322
|
+
return {"errorMessage": message, "stackFrames": frames}
|
|
323
|
+
return {}
|
|
324
|
+
|
|
325
|
+
|
|
326
|
+
@pytest.hookimpl(wrapper=True)
|
|
327
|
+
def pytest_runtest_protocol(item, nextitem):
|
|
328
|
+
"""Spool ``testBegin`` before, and ``testEnd`` after, one wired test.
|
|
329
|
+
|
|
330
|
+
Args:
|
|
331
|
+
item: the test item about to run.
|
|
332
|
+
nextitem: the next item (pytest protocol argument, unused).
|
|
333
|
+
|
|
334
|
+
Returns:
|
|
335
|
+
The wrapped protocol result, unchanged.
|
|
336
|
+
"""
|
|
337
|
+
if not _wired():
|
|
338
|
+
result = yield
|
|
339
|
+
return result
|
|
340
|
+
worker = _worker_index(item.config)
|
|
341
|
+
node_file, title_path, key = _identity(item)
|
|
342
|
+
# The reconciliation key IS the test identity: sidecar claims, the
|
|
343
|
+
# verifier's record→claim join, and the vitest/cypress reporters all
|
|
344
|
+
# name a test `<file>#<title path>`. The pytest node id
|
|
345
|
+
# (`file::test`) is the same test in pytest's own spelling, so a
|
|
346
|
+
# record stamped with it can never join its claim and the obligation
|
|
347
|
+
# stays blocking no matter what the witness observed.
|
|
348
|
+
event = {
|
|
349
|
+
"kind": "testBegin",
|
|
350
|
+
"testId": key,
|
|
351
|
+
"nodeId": item.nodeid,
|
|
352
|
+
"workerIndex": worker,
|
|
353
|
+
"file": node_file,
|
|
354
|
+
"titlePath": title_path,
|
|
355
|
+
"project": None,
|
|
356
|
+
}
|
|
357
|
+
claims = _claims_for(key)
|
|
358
|
+
if claims:
|
|
359
|
+
event["claims"] = claims
|
|
360
|
+
_append_event(event)
|
|
361
|
+
try:
|
|
362
|
+
result = yield
|
|
363
|
+
finally:
|
|
364
|
+
reports = item.__dict__.get("gateforge_reports", [])
|
|
365
|
+
failed = any(report.failed for report in reports)
|
|
366
|
+
skipped = any(report.skipped for report in reports) and not failed
|
|
367
|
+
call_reports = [report for report in reports if report.when == "call"]
|
|
368
|
+
if skipped:
|
|
369
|
+
outcome = "skipped"
|
|
370
|
+
elif failed or not call_reports or not all(report.passed for report in reports if report.when != "teardown"):
|
|
371
|
+
outcome = "failed"
|
|
372
|
+
else:
|
|
373
|
+
outcome = "passed"
|
|
374
|
+
end_event = {
|
|
375
|
+
"kind": "testEnd",
|
|
376
|
+
"testId": key,
|
|
377
|
+
"nodeId": item.nodeid,
|
|
378
|
+
"workerIndex": worker,
|
|
379
|
+
"file": node_file,
|
|
380
|
+
"titlePath": title_path,
|
|
381
|
+
"project": None,
|
|
382
|
+
"outcome": outcome,
|
|
383
|
+
"attempt": max(1, len(call_reports)),
|
|
384
|
+
}
|
|
385
|
+
if outcome == "failed":
|
|
386
|
+
# Additive diagnosis for the CI progress stream and the
|
|
387
|
+
# Gateforge-owned failures artifact: pytest's own longrepr
|
|
388
|
+
# first line plus its `file:line` frames. The raw report is
|
|
389
|
+
# never copied, and the CLI screens the text before it is
|
|
390
|
+
# printed or written.
|
|
391
|
+
end_event.update(_failure_diagnosis(reports))
|
|
392
|
+
_append_event(end_event)
|
|
393
|
+
return result
|
|
394
|
+
|
|
395
|
+
|
|
396
|
+
@pytest.hookimpl(wrapper=True)
|
|
397
|
+
def pytest_runtest_makereport(item, call):
|
|
398
|
+
"""Stash each phase report so the protocol hook can grade the test.
|
|
399
|
+
|
|
400
|
+
Args:
|
|
401
|
+
item: the test item the report belongs to.
|
|
402
|
+
call: the test phase (setup/call/teardown) being reported.
|
|
403
|
+
|
|
404
|
+
Returns:
|
|
405
|
+
The wrapped report, unchanged.
|
|
406
|
+
"""
|
|
407
|
+
report = yield
|
|
408
|
+
item.__dict__.setdefault("gateforge_reports", []).append(report)
|
|
409
|
+
return report
|
|
410
|
+
|
|
411
|
+
|
|
412
|
+
@pytest.fixture
|
|
413
|
+
def gateforge_session(request):
|
|
414
|
+
"""One test's supervisor-issued witness session.
|
|
415
|
+
|
|
416
|
+
Returns:
|
|
417
|
+
GateforgeSession: the resolved credential for the RUNNING test.
|
|
418
|
+
|
|
419
|
+
Raises:
|
|
420
|
+
pytest.fail: when the supervised wiring is absent or no open
|
|
421
|
+
session answers — a fixture user can never silently pass
|
|
422
|
+
unwitnessed.
|
|
423
|
+
"""
|
|
424
|
+
if not _wired():
|
|
425
|
+
pytest.fail(
|
|
426
|
+
"gateforge: the gateforge_session fixture requires the supervised wiring "
|
|
427
|
+
"(GATEFORGE_WITNESS_URL / GATEFORGE_RUN_TOKEN / GATEFORGE_STATE_DIR / GATEFORGE_RUN_ID) — "
|
|
428
|
+
"run under 'gateforge test-gates' or the PytestRunnerAdapter"
|
|
429
|
+
)
|
|
430
|
+
# The SAME reconciliation key the lifecycle hook announced: the
|
|
431
|
+
# supervisor opened the session under that id, so resolving by pytest's
|
|
432
|
+
# own node id would never find it.
|
|
433
|
+
_node_file, _title_path, key = _identity(request.node)
|
|
434
|
+
return GateforgeSession(
|
|
435
|
+
_resolve_session(key, _worker_index(request.config)),
|
|
436
|
+
os.environ.get(APP_BASE_URL_ENV, ""),
|
|
437
|
+
)
|
|
438
|
+
|
|
439
|
+
|
|
440
|
+
@pytest.fixture
|
|
441
|
+
def gateforge_http(gateforge_session):
|
|
442
|
+
"""An ``httpx.Client`` routed through THIS test's session proxy.
|
|
443
|
+
|
|
444
|
+
Requests to the app origin (or relative requests, when no app base
|
|
445
|
+
URL is wired) travel the per-test session proxy, so the witness
|
|
446
|
+
observes every exchange under this test's session.
|
|
447
|
+
|
|
448
|
+
Args:
|
|
449
|
+
gateforge_session: the resolved session credential fixture.
|
|
450
|
+
|
|
451
|
+
Yields:
|
|
452
|
+
httpx.Client: the proxied client (closed on teardown).
|
|
453
|
+
|
|
454
|
+
Raises:
|
|
455
|
+
RuntimeError: when the run wired no observation proxy — traffic
|
|
456
|
+
would bypass the session channel, which is never silent.
|
|
457
|
+
"""
|
|
458
|
+
client = _proxied_client(gateforge_session)
|
|
459
|
+
try:
|
|
460
|
+
yield client
|
|
461
|
+
finally:
|
|
462
|
+
client.close()
|