-
Notifications
You must be signed in to change notification settings - Fork 8
test: reproducing tests for all 25 high/pre-mainnet issues (red while live, green when fixed) #1129
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
Bojan131
wants to merge
23
commits into
main
Choose a base branch
from
test/issue-liveness-suite
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from 1 commit
Commits
Show all changes
23 commits
Select commit
Hold shift + click to select a range
1ba6797
test: add issue-liveness regression suite (14 confirmed-live issues)
Bojan131 7917fb6
test: reproducing tests for all 25 HIGH / pre-mainnet issues
Bojan131 aa23cef
Merge remote-tracking branch 'origin/main' into test/issue-liveness-s…
Bojan131 2d5d74a
test(issue-liveness): real CI repros for #462 #936 #1013 #1078 #1091;…
Bojan131 1d20474
test(issue-liveness): CI root-cause repro for #1124 (host-mode drops …
Bojan131 f26bbde
test(issue-liveness): address Codex review on PR #1129; remove the doc
Bojan131 635dc2b
test(issue-liveness): gate CI repros behind RUN_ISSUE_LIVENESS; fix #…
Bojan131 144e30b
chore(turbo): pass RUN_ISSUE_LIVENESS through to test tasks
Bojan131 96758c1
test(issue-liveness): address Codex round-3 on PR #1129
Bojan131 d33cf88
test(issue-liveness): address Codex round-4 (env parsing, evm-module …
Bojan131 6389eb4
test(issue-liveness): address Codex round-5 (devnet publisher probe +…
Bojan131 ac8b652
test(issue-liveness): address Codex round-6 (downgrade #1124, tighten…
Bojan131 f522d89
test(issue-liveness): address Codex round-7 (devnet false-positive + …
Bojan131 f64a69e
test(issue-liveness): address Codex round-8 (snapshot-isolate #1091, …
Bojan131 da1a87d
chore: restore localhost_contracts.json to main (deploy artifact churn)
Bojan131 ed5761f
ci: run issue-liveness repros in CI (red = live bug) + fix stale Shar…
Bojan131 a266a81
test(evm): fix stale EpochStorage version assertion (10.0.2 → 10.0.3)
Bojan131 597e56a
test: run issue regression tests in the standard package lanes + devn…
Bojan131 d5e07dd
Merge remote-tracking branch 'origin/fix/high-pre-mainnet-issues' int…
Bojan131 f8fd474
test: fix two stale liveness tests whose fixes already landed (#184/#…
Bojan131 fea04c4
test: fix #1095 devnet liveness assertion (false negative vs real impl)
Bojan131 85ceac0
Merge remote-tracking branch 'origin/main' into test/issue-liveness-s…
Bojan131 19bd01d
test(#1091): refresh grindable-seed repro for the PR #1226 1-arg prev…
Bojan131 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,212 @@ | ||
| /** | ||
| * Multi-node issue-liveness regression suite (live devnet). | ||
| * | ||
| * Reproduces confirmed-live cross-node bugs from the rc.17 QA sweep. Each is an | ||
| * `it.fails` repro: the assertion of CORRECT behaviour fails today (bug live), | ||
| * so the suite is green while the bugs persist and turns RED when one is fixed — | ||
| * signalling that the linked GitHub issue can close. | ||
| * | ||
| * #705 / #923 — assertion lifecycle metadata (`dkg:state`, the `_meta` | ||
| * lifecycle record) is written ONLY on the authoring node and is | ||
| * not replicated, so a peer that received the SWM data cannot | ||
| * resolve the assertion's lifecycle state / descriptor. | ||
| * https://github.com/OriginTrail/dkg/issues/705 | ||
| * https://github.com/OriginTrail/dkg/issues/923 | ||
| * | ||
| * #872 — imported Markdown SOURCE bytes are not replicated to peers of a | ||
| * public/open CG; a peer that synced the structural triples cannot | ||
| * fetch the original document via import-artifact/read-markdown. | ||
| * https://github.com/OriginTrail/dkg/issues/872 | ||
| * | ||
| * Preconditions: | ||
| * ./scripts/devnet.sh clean && ./scripts/devnet.sh start 6 | ||
| * node devnet/_bootstrap/bootstrap.cjs | ||
| * Run: pnpm test:devnet:issue-liveness | ||
| */ | ||
| import { describe, it, expect, beforeAll } from 'vitest'; | ||
| import { readFileSync, existsSync } from 'node:fs'; | ||
| import { join, resolve } from 'node:path'; | ||
| import * as http from 'node:http'; | ||
|
|
||
| const REPO_ROOT = resolve(__dirname, '../..'); | ||
| const DEVNET_DIR = join(REPO_ROOT, '.devnet'); | ||
| const sleep = (ms: number) => new Promise((r) => setTimeout(r, ms)); | ||
|
|
||
| interface DevnetNode { | ||
| num: number; | ||
| apiPort: number; | ||
| home: string; | ||
| authToken: string; | ||
| } | ||
|
|
||
| function readNode(num: number): DevnetNode { | ||
| const home = join(DEVNET_DIR, `node${num}`); | ||
| if (!existsSync(home)) { | ||
| throw new Error(`Devnet node${num} home missing — run ./scripts/devnet.sh start 6 first`); | ||
| } | ||
| const config = JSON.parse(readFileSync(join(home, 'config.json'), 'utf8')); | ||
| let authToken = ''; | ||
| if (existsSync(join(home, 'auth.token'))) { | ||
| authToken = | ||
| readFileSync(join(home, 'auth.token'), 'utf8') | ||
| .split('\n') | ||
| .map((l) => l.trim()) | ||
| .find((l) => l.length > 0 && !l.startsWith('#')) ?? ''; | ||
| } | ||
| return { num, apiPort: config.apiPort, home, authToken }; | ||
| } | ||
|
|
||
| function request( | ||
| method: 'GET' | 'POST', | ||
| url: string, | ||
| token: string, | ||
| body?: unknown, | ||
| contentType = 'application/json', | ||
| ): Promise<{ status: number; body: any }> { | ||
| return new Promise((resolveP, rejectP) => { | ||
| const u = new URL(url); | ||
| const data = | ||
| body === undefined ? '' : contentType === 'application/json' ? JSON.stringify(body) : (body as string); | ||
| const req = http.request( | ||
| { | ||
| host: u.hostname, | ||
| port: u.port, | ||
| method, | ||
| path: u.pathname + u.search, | ||
| headers: { | ||
| Authorization: `Bearer ${token}`, | ||
| ...(data ? { 'Content-Type': contentType, 'Content-Length': Buffer.byteLength(data) } : {}), | ||
| }, | ||
| }, | ||
| (res) => { | ||
| let buf = ''; | ||
| res.on('data', (c) => (buf += c)); | ||
| res.on('end', () => { | ||
| try { | ||
| resolveP({ status: res.statusCode ?? 0, body: JSON.parse(buf) }); | ||
| } catch { | ||
| resolveP({ status: res.statusCode ?? 0, body: buf }); | ||
| } | ||
| }); | ||
| }, | ||
| ); | ||
| req.on('error', rejectP); | ||
| if (data) req.write(data); | ||
| req.end(); | ||
| }); | ||
| } | ||
|
|
||
| const api = (n: DevnetNode) => `http://127.0.0.1:${n.apiPort}`; | ||
| const get = (n: DevnetNode, p: string) => request('GET', api(n) + p, n.authToken); | ||
| const post = (n: DevnetNode, p: string, b: unknown) => request('POST', api(n) + p, n.authToken, b); | ||
|
|
||
| const STAMP = Date.now(); | ||
| const CG = `issue-liveness-${STAMP}`; | ||
| const KA = `liveness-ka-${STAMP}`; | ||
| const ENTITY = `https://example.org/liveness/${STAMP}`; | ||
|
|
||
| let node1: DevnetNode; | ||
| let node2: DevnetNode; | ||
| let assertionUri = ''; | ||
| let importFileHash = ''; | ||
| let importAssertionUri = ''; | ||
|
|
||
| describe('multi-node issue liveness', () => { | ||
| beforeAll(async () => { | ||
| node1 = readNode(1); | ||
| node2 = readNode(2); | ||
|
|
||
| // node1: public CG, registered on-chain. | ||
| await post(node1, '/api/context-graph/create', { id: CG, name: `Liveness ${STAMP}`, accessPolicy: 0 }); | ||
| await post(node1, '/api/context-graph/register', { id: CG }); | ||
|
|
||
| // node1: create → write → finalize → share a named assertion. | ||
| await post(node1, '/api/knowledge-assets', { contextGraphId: CG, name: KA }); | ||
| await post(node1, `/api/knowledge-assets/${KA}/wm/write`, { | ||
| contextGraphId: CG, | ||
| quads: [{ subject: ENTITY, predicate: 'https://schema.org/name', object: '"LivenessEntity"' }], | ||
| }); | ||
| await post(node1, `/api/knowledge-assets/${KA}/wm/finalize`, { contextGraphId: CG }); | ||
| const share = await post(node1, `/api/knowledge-assets/${KA}/swm/share`, { contextGraphId: CG, entities: 'all' }); | ||
| expect(share.status).toBe(200); | ||
|
|
||
| // node1: import a Markdown file + promote (for #872). | ||
| const md = `# Liveness Doc ${STAMP}\n\nSection A — replicated bytes check.\n`; | ||
| const boundary = '----dkgLiveness' + STAMP; | ||
| const multipart = | ||
| `--${boundary}\r\nContent-Disposition: form-data; name="contextGraphId"\r\n\r\n${CG}\r\n` + | ||
| `--${boundary}\r\nContent-Disposition: form-data; name="file"; filename="liveness-${STAMP}.md"\r\n` + | ||
| `Content-Type: text/markdown\r\n\r\n${md}\r\n--${boundary}--\r\n`; | ||
| const imp = await request( | ||
| 'POST', | ||
| `${api(node1)}/api/knowledge-assets/liveness-import-${STAMP}/wm/import-file`, | ||
| node1.authToken, | ||
| multipart, | ||
| `multipart/form-data; boundary=${boundary}`, | ||
| ); | ||
| if (imp.status === 200 && imp.body) { | ||
| importFileHash = imp.body.fileHash ?? ''; | ||
| importAssertionUri = imp.body.assertionUri ?? ''; | ||
| await post(node1, `/api/knowledge-assets/liveness-import-${STAMP}/swm/share`, { | ||
| contextGraphId: CG, | ||
| entities: 'all', | ||
| }); | ||
| } | ||
|
|
||
| assertionUri = `did:dkg:context-graph:${CG}/assertion/`; | ||
|
|
||
| // node2: subscribe + give catch-up time to replicate the SWM data. | ||
| await post(node2, '/api/context-graph/subscribe', { contextGraphId: CG }); | ||
| for (let i = 0; i < 24; i++) { | ||
| const r = await post(node2, '/api/query', { | ||
| sparql: `SELECT ?o WHERE { ?s <https://schema.org/name> ?o }`, | ||
| contextGraphId: CG, | ||
| view: 'shared-working-memory', | ||
| }); | ||
| const rows = r.body?.result?.bindings?.length ?? 0; | ||
| if (rows > 0) break; | ||
| await sleep(5000); | ||
| } | ||
| }, 240_000); | ||
|
|
||
| it('CONTROL: node2 replicated the SWM data for the peer-authored assertion', async () => { | ||
| const r = await post(node2, '/api/query', { | ||
| sparql: `SELECT ?o WHERE { ?s <https://schema.org/name> ?o }`, | ||
| contextGraphId: CG, | ||
| view: 'shared-working-memory', | ||
| }); | ||
| const names = (r.body?.result?.bindings ?? []).map((b: any) => b.o); | ||
| expect(names.some((n: string) => String(n).includes('LivenessEntity'))).toBe(true); | ||
| }); | ||
|
|
||
| it.fails( | ||
| 'GH #705/#923: node2 can resolve lifecycle state for the peer-authored assertion', | ||
| async () => { | ||
| // The lifecycle record lives only in node1's non-replicated `_meta`, so | ||
| // node2 sees zero lifecycle rows for the assertion it received via SWM. | ||
| const r = await post(node2, '/api/query', { | ||
| sparql: | ||
| 'PREFIX dkg: <http://dkg.io/ontology/> SELECT ?a ?state WHERE { ?a a dkg:Assertion ; dkg:state ?state }', | ||
|
Bojan131 marked this conversation as resolved.
Outdated
|
||
| contextGraphId: CG, | ||
| graphSuffix: '_meta', | ||
| }); | ||
| const rows = r.body?.result?.bindings?.length ?? 0; | ||
| expect(rows).toBeGreaterThan(0); | ||
| }, | ||
| ); | ||
|
|
||
| it.fails( | ||
| 'GH #872: node2 (public-CG peer) can fetch the imported Markdown source bytes', | ||
| async () => { | ||
| // Skip cleanly if the import didn't land (keeps the assertion meaningful). | ||
| expect(importFileHash).not.toBe(''); | ||
|
Bojan131 marked this conversation as resolved.
Outdated
Bojan131 marked this conversation as resolved.
Outdated
|
||
| const r = await post(node2, '/api/knowledge-assets/import-artifact/read-markdown', { | ||
| contextGraphId: CG, | ||
| assertionUri: importAssertionUri, | ||
| fileHash: importFileHash, | ||
| }); | ||
| expect(r.status).toBe(200); | ||
| expect(String(r.body?.markdown ?? r.body)).toContain('Liveness Doc'); | ||
| }, | ||
| ); | ||
| }); | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| { | ||
| "name": "@origintrail-official/dkg-devnet-issue-liveness", | ||
| "version": "0.0.0", | ||
| "private": true, | ||
| "type": "module", | ||
| "scripts": { | ||
| "test:devnet": "vitest run --config vitest.config.ts" | ||
| }, | ||
| "dependencies": { | ||
| "ethers": "^6.16.0" | ||
| }, | ||
| "devDependencies": { | ||
| "vitest": "^4.0.18" | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,28 @@ | ||
| import { defineConfig } from 'vitest/config'; | ||
| import { resolve } from 'node:path'; | ||
|
|
||
| /** | ||
| * Multi-node issue-liveness regression suite against a live devnet. | ||
| * | ||
| * Encodes confirmed-live cross-node bugs from the rc.17 QA sweep as `it.fails` | ||
| * repros — each fails today (bug live) and flips RED when fixed, signalling the | ||
| * linked GitHub issue can close. Manual-run (needs a live devnet), like the | ||
| * sibling devnet suites. | ||
| * | ||
| * Preconditions: | ||
| * pnpm run build | ||
| * ./scripts/devnet.sh clean && ./scripts/devnet.sh start 6 | ||
| * node devnet/_bootstrap/bootstrap.cjs | ||
| * | ||
| * Run: pnpm test:devnet:issue-liveness | ||
| */ | ||
| export default defineConfig({ | ||
| test: { | ||
| include: [resolve(__dirname, 'automated.test.ts')], | ||
|
Bojan131 marked this conversation as resolved.
Outdated
|
||
| testTimeout: 240_000, | ||
| hookTimeout: 240_000, | ||
| pool: 'forks', | ||
| fileParallelism: false, | ||
| reporters: ['verbose'], | ||
| }, | ||
| }); | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,74 @@ | ||
| # Issue-liveness regression tests | ||
|
|
||
| A suite that encodes confirmed-live GitHub issues as runnable tests, so we can | ||
| (a) prove which issues are still real and (b) get a self-closing signal when one | ||
| gets fixed. | ||
|
|
||
| ## How it works (the `it.fails` / `test.fixme` convention) | ||
|
|
||
| Each test asserts the **correct** behaviour the issue asks for. While the bug is | ||
| live, that assertion throws — so the test is wrapped in vitest's `it.fails` | ||
| (`test.fixme` for Playwright). That means: | ||
|
|
||
| - **Bug live →** assertion fails → `it.fails` reports **pass** → CI stays green. | ||
| - **Bug fixed →** assertion passes → `it.fails` reports **fail** ("expected to | ||
| fail but passed") → CI goes **red**, telling the fixer to remove `.fails`, | ||
| make it a plain `it(...)`, and close the issue. | ||
|
|
||
| So a red `it.fails` is the cue that an issue can be closed. Every test names its | ||
| issue in the title and links it in the file header. | ||
|
|
||
| All tests were written against — and confirmed failing-as-expected on — a real | ||
| node / live devnet during the 2026-06-11 QA sweep. Zero mocks for chain or | ||
| network behaviour. | ||
|
|
||
| ## What's covered (14 issues) | ||
|
|
||
| ### Tier 1 — single-node, runs in the normal `turbo test` CI lanes | ||
|
|
||
| | Issue | Test file | Asserts (correct behaviour) | | ||
| |---|---|---| | ||
| | [#1125](https://github.com/OriginTrail/dkg/issues/1125) | `packages/cli/test/skill-md-dynamic-section.test.ts` | served skill.md has no literal `(dynamic)` placeholders | | ||
| | [#675](https://github.com/OriginTrail/dkg/issues/675) | `packages/query/test/subgraph-view-scoping.test.ts` | WM-view query includes sub-graph data | | ||
| | [#184](https://github.com/OriginTrail/dkg/issues/184) | `packages/query/test/subgraph-view-scoping.test.ts` | `view` + `subGraphName` scopes instead of throwing | | ||
| | [#416](https://github.com/OriginTrail/dkg/issues/416) | `packages/core/test/escape-rdf-literal-control-chars.test.ts` | escaper UCHAR-encodes NUL/VT/DEL control bytes | | ||
| | [#709](https://github.com/OriginTrail/dkg/issues/709) | `packages/epcis/test/event-type-container-filter.test.ts` | events query excludes the `EPCISDocument` container | | ||
| | [#15](https://github.com/OriginTrail/dkg/issues/15) | `packages/cli/test/rdf-parser-jsonld.test.ts` | `.jsonld` with `@context` parses (or isn't advertised) | | ||
| | [#787](https://github.com/OriginTrail/dkg/issues/787) | `packages/cli/test/issue-liveness-daemon-routes.test.ts` | SWM write of string quads → 4xx, not 500 | | ||
| | [#306](https://github.com/OriginTrail/dkg/issues/306) | `packages/cli/test/issue-liveness-daemon-routes.test.ts` | KA wm/write of string quads → 4xx, not 500 | | ||
| | [#158](https://github.com/OriginTrail/dkg/issues/158) | `packages/cli/test/issue-liveness-daemon-routes.test.ts` | ccl/eval not-found (real CG) → 4xx, not 500 | | ||
| | [#309](https://github.com/OriginTrail/dkg/issues/309) | `packages/cli/test/issue-liveness-daemon-routes.test.ts` | `/api/status` exposes `defaultAgentAddress` | | ||
| | [#757](https://github.com/OriginTrail/dkg/issues/757) | `packages/cli/test/issue-liveness-daemon-routes.test.ts` | non-curator token is 403'd from `/join-requests` | | ||
|
|
||
| The daemon-route file spins one real edge daemon against the shared Hardhat node | ||
| (zero chain mocks), same harness as `daemon-http-behavior-extra.test.ts`. | ||
|
|
||
| ### Tier 2 — multi-node, manual-run devnet suite | ||
|
|
||
| `devnet/issue-liveness/automated.test.ts` (run: `pnpm test:devnet:issue-liveness` | ||
| after `./scripts/devnet.sh start 6` + bootstrap). A `CONTROL` test proves the SWM | ||
| data actually replicated to the peer, so the `it.fails` repros can't pass for the | ||
| wrong reason. | ||
|
|
||
| | Issue | Asserts (correct behaviour) | | ||
| |---|---| | ||
| | [#705](https://github.com/OriginTrail/dkg/issues/705) / [#923](https://github.com/OriginTrail/dkg/issues/923) | a peer can resolve lifecycle state for a peer-authored assertion | | ||
| | [#872](https://github.com/OriginTrail/dkg/issues/872) | a public-CG peer can fetch imported Markdown source bytes | | ||
|
|
||
| ## Deferred (confirmed live, automated test intentionally NOT written) | ||
|
|
||
| Writing a test for these would either fabricate signal or need fixtures too heavy | ||
| for CI. They stay tracked as live issues; a test should come with the fix PR. | ||
|
|
||
| - **#614** (conviction-NFT sweep into a closed epoch) — money-path **contract** | ||
| bug, "Steps to reproduce: N/A". Needs a contracts engineer to model the exact | ||
| billing-window math; a speculative hardhat test risks passing for the wrong | ||
| reason. | ||
| - **#1091** (grindable RandomSampling seed) — un-grindability is a **design | ||
| property** (commit-reveal / VRF), not a single-assertion unit fact. | ||
| - **#1112 / #1113 / #1015** (UI count caps) — need a >50k-triple fixture to | ||
| exercise the display cap; too heavy for the Playwright lane. | ||
| - **#966** (no single-root publish UI path on a multi-root CG) — needs a | ||
| multi-root SWM UI fixture; reachable but low value (low/post-mainnet). | ||
| - **#467 / #703 / #998** — environment-specific (markitdown install fidelity, | ||
| live OpenClaw runtime) that a CI box can't fake. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.