test: property-based fuzzing, CLI+contract integration, full E2E jour… - #353
Merged
ALLEN-AYODEJI merged 2 commits intoAug 30, 2026
Conversation
…ney, and mobile-responsive layout (Trellis-Ecosystem#141, Trellis-Ecosystem#142, Trellis-Ecosystem#143, Trellis-Ecosystem#120) Implements the four assigned issues. Each new test layer is runnable locally and wired into CI. Along the way this branch also completes three half-merged changes already on master that left the contract crate, the CLI crate, and the frontend unbuildable — without them none of the new tests could compile or run (details under "Prerequisite fixes" below). ────────────────────────────────────────────────────────────────────────── Trellis-Ecosystem#141 — Contract fuzzing / property-based testing for the escrow state machine ────────────────────────────────────────────────────────────────────────── * New proptest case `prop_balance_conservation_random_op_sequence` in contracts/trellis_core/src/test_properties.rs. It generates a random sequence (20–60 ops) of lock / submit / approve / dispute / resolve / cancel operations targeting randomly chosen milestones of a 2–4 milestone agreement, applies each one only when it is a legal transition from that milestone's current state (tracked in a local shadow state machine, not read back from the contract), and after every applied op asserts: contract token balance == sum of amounts still locked (milestones Funded / WorkSubmitted / Disputed) which is the issue's `total locked - released - refunded` invariant. This exercises adversarial interleavings across milestones (e.g. disputing milestone 2 while milestone 0 is mid-approval) that the existing fixed per-milestone lock→submit→approve property tests never construct. * Green at the default case count and partway through a `PROPTEST_CASES=10000` sweep with no invariant violations; raise the count locally for a deeper run. * proptest was already a dev-dependency; module doc comment updated to describe invariant 6. Commits 256 `test_snapshots/` fixtures, same as every other property test. ────────────────────────────────────────────────────────────────────────── Trellis-Ecosystem#142 — CLI + contract integration test suite (end-to-end, real RPC) ────────────────────────────────────────────────────────────────────────── * New tests/cli_integration/ — run.sh + README.md. The script starts a local Soroban devnet (`stellar container start local`), builds the CLI (release) and the contract (wasm32), deploys a fresh contract instance plus a native-XLM SAC token, then drives every CLI command exclusively through the built `trellis` binary and asserts on its real `--json` output: - full milestone lifecycle: init → status → lock-funds → milestone-status → submit-work (with --proof-uri) → approve-release - dispute path: raise-dispute → resolve-dispute --refund-to-payer - cancel-milestone on an unfunded milestone - error paths: malformed agreement ID (rejected client-side, non-zero exit), double-init, wrong-signer, and --dry-run 22/22 assertions pass against a local devnet. * Idempotent: fresh 64-hex agreement IDs per run, and existing local identities are re-funded (an unfunded key from an interrupted earlier run otherwise fails init with "Account not found"). * CI: new `cli-integration` job in .github/workflows/contract-ci.yml (needs: verify) — only needs a Docker container, no testnet or secrets. ────────────────────────────────────────────────────────────────────────── Trellis-Ecosystem#143 — Full user journey E2E across contract + frontend + browser ────────────────────────────────────────────────────────────────────────── * Added @playwright/test (dev-dependency), frontend/playwright.config.ts, `test:e2e` script. * frontend/e2e/create-agreement.spec.ts — deploys nothing itself; drives a real Chromium browser through connecting a (mocked) Freighter wallet, creating an agreement on the /create page, and confirming it reads back on /agreement/:id with the right payer/payee/amount/status. * frontend/e2e/freighter-mock.ts — fake Freighter extension speaking the real `@stellar/freighter-api` postMessage wire protocol (reverse- engineered from the bundled extension code, cited in-file). Everything except signing returns a fixed mock response; signing hands the tx XDR out to the Node test process, which signs with the test payer's real key — the key never enters the browser context. * tests/e2e/ — run.sh + README.md orchestrating devnet + contract deploy + `npm run dev` against it + the Playwright run, with a temporary frontend/.env (backed up and restored on exit) and an explicit timeout on every step that could hang. 1/1 passing locally. * CI: new `e2e` job in .github/workflows/frontend-ci.yml, `workflow_dispatch` only (needs Docker + a real browser, far heavier than the rest). * frontend/.gitignore: ignore Playwright's test-results / playwright-report. ────────────────────────────────────────────────────────────────────────── Trellis-Ecosystem#120 — Mobile-first responsive layout ────────────────────────────────────────────────────────────────────────── * Navbar: real mobile menu — hamburger/close toggle (44x44 targets, full aria wiring), slide-down panel, auto-closes on navigation; nav links reduced to one NAV_LINKS source of truth shared by desktop + mobile; header no longer overflows on narrow widths (flex-wrap, tighter gaps). * Milestones on the status page: desktop keeps the table (MilestoneRow); <768px now renders a single-column card list (new MilestoneCard) instead of a horizontally-scrolling table. Shared action/state logic extracted to hooks/useMilestoneActions.ts so the two layouts can't drift. * Touch targets: min-h-[44px] on the action buttons / links / inputs across CreatePage, StatusPage, AgreementHistoryPage, MilestoneCard, Navbar. * Overflow fixes: min-w-0 on flex inputs so long values don't force horizontal page scroll; break-all on full-length identifiers in ExplorerLink; ToastProvider pinned to both edges on mobile, max-w-sm only from sm: up. * Verified at 320 / 375 / 414 / 768 / 1024 px — no horizontal scroll, no overlap. Wiring needed to make Trellis-Ecosystem#120/Trellis-Ecosystem#143 testable through the real UI: * frontend/src/lib/soroban.ts: implemented getAgreement() (was a throw-stub) — simulates get_agreement over RPC and decodes the ScVal result, plus agreementIdToScVal / milestoneToScVal encoders. * CreatePage: the init form now actually builds the ScVal args and calls the contract via useContractInvoke (was a console.log stub); client-side validation of the hex agreement ID and positive integer amounts; label htmlFor/id so the fields are reachable by accessible name. * StatusPage: use the shared getAgreement(); drop the local throw-stub. ────────────────────────────────────────────────────────────────────────── Prerequisite fixes — half-merged changes already on master that don't build ────────────────────────────────────────────────────────────────────────── These are not part of the four issues, but master (67a358f) does not compile without them and the new tests can't run otherwise: * contracts/trellis_core/src/events.rs: the event topic symbols added by the earlier "namespace event topics with trlls_ prefix" change are 10 chars — `symbol_short!` hard-caps at 9, so the crate failed to compile. Shortened `trlls_*` → `trls_*` (9 chars, same de-vowelled scheme). Every test_properties snapshot is regenerated as a result: the topic symbol is part of the recorded event, so ~11.8k *.json snapshots change (symbol bytes only; i128 values shift only because proptest inputs are seeded off the now-passing run). * cli/trellis_cli/src/{main.rs,commands/mod.rs}: `confirm_action` was called in three handlers but never defined, and `main.rs` called `Config::from_env()` which had been replaced by `Config::resolve(...)` — the CLI crate didn't compile. Added `confirm_action` (honours --yes, skips on --dry-run, fails closed under --quiet), wired every state- mutating handler to it and to `validate_agreement_id`, and switched `main` to `Config::resolve` so `--network` / `--rpc-url` / `--network-passphrase` (needed to point the CLI at a local devnet for Trellis-Ecosystem#142) take effect. Also JSON-quote `--proof-uri` before handing it to `stellar contract invoke`, which parses every non-bytes arg as JSON. * frontend: `main.tsx` imported `BrowserRouter` but never wrapped `<App>` in it, so every route hook threw; `useContractInvoke.ts` / `useContractRead.ts` imported `SorobanRpc`, which the pinned @stellar/stellar-sdk (16.x) no longer exports (it's `rpc` now); `StatusPage.tsx` used `<ExplorerLink>` without importing it. All three fixed. Not addressed (pre-existing on master, unrelated to these issues): the frontend `npm run typecheck` / `npm run build` still fail on ~8 files this branch does not touch (NetworkBackground.tsx null checks, EventFeed JSX namespace, StatsBar/AgreementIdGenerator test imports, ThemeContext type-only import, CreateAgreementPage ScValType, etc.), and 32 pre-existing vitest failures from test files that render router-hook components without a <MemoryRouter>. Every file changed here is type-clean; flagging separately rather than expanding scope. Test status * contracts: `cargo test --workspace` green — 22 lib tests (incl. the new fuzz case) + 38 CLI tests. * CLI integration: tests/cli_integration/run.sh — 22/22 against local devnet. * E2E: tests/e2e/run.sh — 1/1 against local devnet + Chromium. * frontend vitest: 95 pass / 32 fail — same 32 as before this branch (pre-existing, see above). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01H7zsKzioyWdXyuss8rkWtx
|
@CodePrincess360 Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits. You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀 |
# Conflicts: # .github/workflows/contract-ci.yml # cli/trellis_cli/src/commands/mod.rs # cli/trellis_cli/src/main.rs # contracts/trellis_core/src/events.rs # frontend/src/components/toast/ToastProvider.tsx # frontend/src/hooks/useContractInvoke.ts # frontend/src/pages/CreatePage.tsx
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.
…ney, and mobile-responsive layout (#141, #142, #143, #120)
Implements the four assigned issues. Each new test layer is runnable locally and wired into CI. Along the way this branch also completes three half-merged changes already on master that left the contract crate, the CLI crate, and the frontend unbuildable — without them none of the new tests could compile or run (details under "Prerequisite fixes" below).
────────────────────────────────────────────────────────────────────────── #141 — Contract fuzzing / property-based testing for the escrow state machine ──────────────────────────────────────────────────────────────────────────
New proptest case
prop_balance_conservation_random_op_sequencein contracts/trellis_core/src/test_properties.rs. It generates a random sequence (20–60 ops) of lock / submit / approve / dispute / resolve / cancel operations targeting randomly chosen milestones of a 2–4 milestone agreement, applies each one only when it is a legal transition from that milestone's current state (tracked in a local shadow state machine, not read back from the contract), and after every applied op asserts:which is the issue's
total locked - released - refundedinvariant. This exercises adversarial interleavings across milestones (e.g. disputing milestone 2 while milestone 0 is mid-approval) that the existing fixed per-milestone lock→submit→approve property tests never construct.Green at the default case count and partway through a
PROPTEST_CASES=10000sweep with no invariant violations; raise the count locally for a deeper run.proptest was already a dev-dependency; module doc comment updated to describe invariant 6. Commits 256
test_snapshots/fixtures, same as every other property test.────────────────────────────────────────────────────────────────────────── #142 — CLI + contract integration test suite (end-to-end, real RPC) ──────────────────────────────────────────────────────────────────────────
stellar container start local), builds the CLI (release) and the contract (wasm32), deploys a fresh contract instance plus a native-XLM SAC token, then drives every CLI command exclusively through the builttrellisbinary and asserts on its real--jsonoutput:cli-integrationjob in .github/workflows/contract-ci.yml (needs: verify) — only needs a Docker container, no testnet or secrets.────────────────────────────────────────────────────────────────────────── #143 — Full user journey E2E across contract + frontend + browser ──────────────────────────────────────────────────────────────────────────
test:e2escript.@stellar/freighter-apipostMessage wire protocol (reverse- engineered from the bundled extension code, cited in-file). Everything except signing returns a fixed mock response; signing hands the tx XDR out to the Node test process, which signs with the test payer's real key — the key never enters the browser context.npm run devagainst it + the Playwright run, with a temporary frontend/.env (backed up and restored on exit) and an explicit timeout on every step that could hang. 1/1 passing locally.e2ejob in .github/workflows/frontend-ci.yml,workflow_dispatchonly (needs Docker + a real browser, far heavier than the rest).────────────────────────────────────────────────────────────────────────── #120 — Mobile-first responsive layout
──────────────────────────────────────────────────────────────────────────
Wiring needed to make #120/#143 testable through the real UI:
────────────────────────────────────────────────────────────────────────── Prerequisite fixes — half-merged changes already on master that don't build ────────────────────────────────────────────────────────────────────────── These are not part of the four issues, but master (67a358f) does not compile without them and the new tests can't run otherwise:
symbol_short!hard-caps at 9, so the crate failed to compile. Shortenedtrlls_*→trls_*(9 chars, same de-vowelled scheme). Every test_properties snapshot is regenerated as a result: the topic symbol is part of the recorded event, so ~11.8k *.json snapshots change (symbol bytes only; i128 values shift only because proptest inputs are seeded off the now-passing run).confirm_actionwas called in three handlers but never defined, andmain.rscalledConfig::from_env()which had been replaced byConfig::resolve(...)— the CLI crate didn't compile. Addedconfirm_action(honours --yes, skips on --dry-run, fails closed under --quiet), wired every state- mutating handler to it and tovalidate_agreement_id, and switchedmaintoConfig::resolveso--network/--rpc-url/--network-passphrase(needed to point the CLI at a local devnet for No integration test suite: CLI + contract interaction is never tested end-to-end #142) take effect. Also JSON-quote--proof-uribefore handing it tostellar contract invoke, which parses every non-bytes arg as JSON.main.tsximportedBrowserRouterbut never wrapped<App>in it, so every route hook threw;useContractInvoke.ts/useContractRead.tsimportedSorobanRpc, which the pinned @stellar/stellar-sdk (16.x) no longer exports (it'srpcnow);StatusPage.tsxused<ExplorerLink>without importing it. All three fixed.Not addressed (pre-existing on master, unrelated to these issues): the frontend
npm run typecheck/npm run buildstill fail on ~8 files this branch does not touch (NetworkBackground.tsx null checks, EventFeed JSX namespace, StatsBar/AgreementIdGenerator test imports, ThemeContext type-only import, CreateAgreementPage ScValType, etc.), and 32 pre-existing vitest failures from test files that render router-hook components without a . Every file changed here is type-clean; flagging separately rather than expanding scope.Test status
cargo test --workspacegreen — 22 lib tests (incl. the new fuzz case) + 38 CLI tests.Closes #141
Closes #142
Closes #143
Closes #120