Repository navigation
Testing Best Practices and Guidelines
- Introduction
- Project Structure
- Core Components
- Architecture Overview
- Detailed Component Analysis
- Dependency Analysis
- Performance Considerations
- Troubleshooting Guide
- Conclusion
- Appendices
This document establishes testing best practices and guidelines for the Sigvara project across Solidity, TypeScript, and JavaScript layers. It codifies naming conventions, test organization, fixture management, shared utilities, security-focused testing, maintainability rules, coverage expectations, CI strategies, performance benchmarking, assertion patterns, debugging techniques, test data lifecycle, environment variable handling, and CI/CD integration. The guidance is grounded in the repository’s existing test suites and configuration.
The repository uses a layered testing approach:
- Solidity tests under
test/using Foundry (unit, fuzz, invariant, E2E). - Oracle Node.js tests under
oracle/using Node’s built-in test runner. - TypeScript SDK tests under
packages/sdk/test/using Vitest.
As of 3 November 2026 the contract suite has 740 tests (each of the 21 invariants counts once), the oracle suite 999 and the SDK suite 300. Without a fork URL, 728 of the contract tests run and 12 skip, and 8 of the SDK tests, the integration tests, skip without a live deployment.
graph TB
subgraph "Solidity"
T1["test/E2E.t.sol"]
T2["test/SigvaraIdentity.t.sol"]
F["foundry.toml"]
end
subgraph "Oracle JS"
O1["oracle/adversarial.test.js"]
OP["oracle/package.json"]
end
subgraph "SDK TS"
S1["packages/sdk/test/integration.test.ts"]
VC["packages/sdk/vitest.config.ts"]
end
F --> T1
F --> T2
OP --> O1
VC --> S1
Diagram sources
- foundry.toml
- E2E.t.sol
- SigvaraIdentity.t.sol
- adversarial.test.js:1-255
- package.json
- integration.test.ts:1-129
- vitest.config.ts:1-8
Section sources
- Foundry test harness with default and CI profiles, fuzz runs, and RPC endpoints.
- Five invariant campaigns,
StakingInvariantTest,OracleBondInvariantTest,ReputationInvariantTest,EpochFeesInvariantTestandIdentityInvariantTest, with 21 invariants between them. - End-to-end Solidity suite covering full agent lifecycle, slash dispute, reputation challenge, unbonding, and multi-agent isolation.
- Unit suite for identity contract including operator transfer, key rotation, status transitions, storage layout pinning, and access control.
- Oracle adversarial scoring tests validating economic resistance to wash rings, sybil payers, decay, and flagging.
- SDK integration tests that optionally run against a live chain when environment variables are present.
Section sources
- foundry.toml:23-27
- E2E.t.sol:13-109
- SigvaraIdentity.t.sol:27-50
- adversarial.test.js:1-29
- integration.test.ts:1-41
Testing architecture spans three layers with clear separation of concerns:
- Solidity layer: deterministic in-memory EVM execution, fuzzing, and invariant checks.
- Oracle layer: pure function and pipeline validation with adversarial inputs.
- SDK layer: optional live-chain integration guarded by environment variables.
graph TB
A["Foundry Tests<br/>Unit/Fuzz/Invariant/E2E"]
B["Oracle Tests<br/>Adversarial Scoring"]
C["SDK Tests<br/>Vitest Integration"]
D["Contracts<br/>Identity/Staking/Reputation"]
E["Chain<br/>Arc Testnet (optional)"]
A --> D
B --> D
C --> E
C -.-> D
- Naming conventions:
- Test files mirror contracts:
SigvaraIdentity.t.sol,SigvaraStaking.t.sol, etc. - Test functions use descriptive
test_prefixes and state changes:test_updateStatus_reverts_returningToActiveWithoutCollateral. - Fuzz tests use
testFuzz_prefix:testFuzz_computeDidHash_unique.
- Test files mirror contracts:
- Organization:
- Shared helpers under
test/helpers/RegistrationHelper.sol. - E2E suite in
test/E2E.t.solorchestrates multiple contracts and roles. - Invariant tests target staking/identity safety properties.
- Shared helpers under
- Fixture management:
- Contracts deployed in
setUp()with proxies and role wiring. - Mock token
MockSVRused for stake flows. -
SigvaraReader.t.sol,GatedMarketplace.t.sol,InflowGuard.t.sol,OperatorCustody.t.solandSmartWalletAgent.t.solsharetest/helpers/StackFixture.sol, which deploys the three registries behind proxies wired the way the deploy scripts wire them, a funded operator, and aSigvaraReaderpointed at them.
- Contracts deployed in
- Assertions:
- Explicit revert selectors via
abi.encodeWithSelector. - State assertions on identity status, stake balances, and reputation totals.
- Explicit revert selectors via
- Security focus:
- Collateral checks before reactivation.
- Slash immutability and terminal state enforcement.
- Operator transfer integrity and index consistency.
flowchart TD
Start(["Test Entry"]) --> Setup["Deploy Proxies + Roles"]
Setup --> Act["Call Contract Function"]
Act --> AssertState{"Assert State"}
AssertState --> |Pass| Next["Next Scenario"]
AssertState --> |Fail| RevertCheck["Verify Expected Revert"]
RevertCheck --> Next
Next --> End(["Exit"])
Diagram sources
Section sources
- E2E.t.sol:13-109
- SigvaraIdentity.t.sol:52-139
- SigvaraIdentity.t.sol:145-466
- SigvaraIdentity.t.sol:499-645
- SigvaraIdentity.t.sol:655-850
- SigvaraIdentity.t.sol:896-918
- Purpose: Validate scoring resilience against wash rings, sybil payers, time decay, and flagging.
- Approach: Feed attacker-crafted payment sets through real scoring pipeline without stubs.
- Key scenarios:
- Single-payer cap enforcement.
- Wash ring ceiling bounds.
- Zero-scored counterparties cannot inflate caps or propagation.
- Tenure rewards elapsed activity; abandoned farms decay.
- Flag damage bounded to community factor.
sequenceDiagram
participant T as "adversarial.test.js"
participant P as "payments module"
participant S as "scoring.computeScore"
T->>P : build events (wash ring / dust / flags)
P-->>T : grouped payments + metrics
T->>S : computeScore(measuredInputs)
S-->>T : score breakdown
T->>T : assert caps, decay, bounds
Diagram sources
- adversarial.test.js:45-79
- adversarial.test.js:96-150
- adversarial.test.js:157-194
- adversarial.test.js:200-219
- adversarial.test.js:225-255
Section sources
- adversarial.test.js:1-29
- adversarial.test.js:96-150
- adversarial.test.js:157-194
- adversarial.test.js:200-255
- Optional execution: Skips when required environment variables are absent.
- Live-chain operations: Registers an agent, verifies signatures, builds DID documents, and checks reputation thresholds.
- Environment variables:
-
SIGVARA_RPC_URL,SIGVARA_CHAIN_ID,SIGVARA_IDENTITY_ADDRESS,SIGVARA_REPUTATION_ADDRESS,SIGVARA_STAKING_ADDRESS,SIGVARA_OPERATOR_PRIVATE_KEY.
-
sequenceDiagram
participant V as "integration.test.ts"
participant E as "ethers provider/wallet"
participant I as "Identity contract"
participant R as "Reputation contract"
V->>V : read env vars
V->>E : connect to RPC
V->>I : registerAgent(...)
V->>R : verifySignature(...)
V->>R : meetsThreshold(...)
V-->>V : assert outcomes
Diagram sources
Section sources
- Foundry configuration defines source/output paths, remappings, optimizer settings, the EVM version (
cancun, set explicitly so a Foundry upgrade cannot change what is compiled), fuzz runs (1,000 by default, 5,000 on the CI profile), and RPC endpoints. - Oracle package declares Node test script and dependencies.
- SDK package configures Vitest globals and Node environment.
graph LR
FT["foundry.toml"] --> SU["Solidity Suite"]
OPJ["oracle/package.json"] --> OT["Oracle Tests"]
VC["packages/sdk/vitest.config.ts"] --> ST["SDK Tests"]
Diagram sources
Section sources
- Use Foundry’s CI fuzz profile for denser runs:
FOUNDRY_PROFILE=ci forge test. - Keep unit tests fast and isolated; reserve E2E and integration tests for critical flows.
- For oracle scoring, prefer adversarial scenarios over heavy network calls; they validate economic constraints deterministically.
- Avoid unnecessary chain reads in SDK tests; gate integration tests behind environment variables.
- Solidity:
- Use verbose output for E2E:
forge test --match-contract E2EIntegrationTest -vvv. - Check expected revert selectors match contract errors.
- Verify proxy initialization and role grants in
setUp(). - Three suites fork Arc testnet.
SlashDrillFork.t.solandSlashDrillAdversarialFork.t.solfork it on every run, so they need thearc_testnetendpoint infoundry.tomlto answer.SplitAuthorityFork.t.solskips its twelve tests unless you pass--fork-url https://rpc.testnet.arc.io.
- Use verbose output for E2E:
- Oracle:
- Confirm configuration values like
PAYMENT_VERIFICATIONand asset addresses. - Inspect console logs from adversarial scenarios to understand score ceilings.
- Confirm configuration values like
- SDK:
- Ensure all required environment variables are set for integration tests.
- Handle “AlreadyRegistered” errors gracefully during setup.
Section sources
Sigvara’s testing strategy combines deterministic Solidity unit/fuzz/invariant suites, adversarial oracle scoring validation, and optional live-chain SDK integration. Following the naming, organization, fixture, and security guidelines here will keep tests reliable, maintainable, and aligned with mainnet readiness goals.
- Solidity:
- Test file:
<ContractName>.t.sol. - Test function:
test_<action>_<expected_behavior>ortestFuzz_<property>_<uniqueness>.
- Test file:
- Oracle JS:
- Test file:
<module>.test.js. - Test names: descriptive strings describing scenario and bound.
- Test file:
- SDK TS:
- Test file:
<feature>.test.ts. - Test names: describe blocks per feature; it blocks describe behavior.
- Test file:
Section sources
- E2E.t.sol:116-214
- SigvaraIdentity.t.sol:52-139
- adversarial.test.js:96-150
- integration.test.ts:73-129
- Separate unit, E2E, and invariant tests by purpose.
- Place shared helpers under
test/helpers/. - Keep oracle tests focused on pure logic and adversarial inputs.
- Gate SDK integration tests behind environment variables.
Section sources
- Deploy proxies and wire roles in
setUp(). - Use mock tokens for stake flows.
- Centralize registration helpers to avoid duplication.
Section sources
- Validate collateral gates before reactivation.
- Enforce slash immutability and terminal states.
- Run adversarial scoring tests to bound wash rings and sybil inflation.
- Prepare for audits by documenting attack surfaces and invariants.
Section sources
- Prefer explicit revert selectors in assertions.
- Keep tests deterministic; avoid flaky sleeps where possible.
- Isolate multi-agent scenarios to prevent cross-test interference.
Section sources
- Run the contract suite with
forge test. It runs offline except for the three fork suites described in the Troubleshooting Guide. Fetch the Solidity dependencies first withsh scripts/fetch-deps.sh. - Increase fuzz intensity with
FOUNDRY_PROFILE=ci forge test. - Execute oracle tests with
cd oracle && node --test. - Execute SDK tests with
cd packages/sdk && npx vitest run.
Section sources
- Use adversarial scenarios to measure scoring ceilings and decay rates.
- Track tenure growth across spans to ensure time-based factors resist buying.
- Monitor community factor impact under flagging to bound blast radius.
Section sources
- Clear scenario titles: “one payer cannot buy more than maxPerPayer points”.
- Bounds assertions: “ring reached <= 60 total”.
- Selector-based reverts:
expectRevert(abi.encodeWithSelector(...)).
Section sources
- Verbose Forge output for E2E flows.
- Console logging in adversarial tests to inspect score breakdowns.
- Conditional skipping of integration tests when environment variables are missing.
Section sources
- Generate deterministic keys and addresses with helper utilities.
- Use mock tokens for stake flows and slashing distributions.
- Manage time with warp to simulate challenge windows and unbonding periods.
Section sources
- Oracle: configuration loaded via module-level config reader.
- SDK: integration tests gated by environment variables; skip if absent.
Section sources
- Default Foundry profile for local runs; CI profile for fuzz intensity.
- Node test script for oracle suite.
- Vitest command for SDK suite.
-
.github/workflows/ci.ymlruns on every push tomainand every pull request, with read-only repository permissions. It builds the contracts (forge build --sizes) and tests them on theciprofile with 5,000 fuzz runs, includingSlashDrillFork. After the build it compares the deployed testnet contracts with it (scripts/check-bytecode.mjs, which reports rather than fails a mismatch once main's contract sources have moved past the commit the deployment file records). It builds and tests the SDK, runs the oracle tests and the simulation plan tests, and runs the repository checks:scripts/check-docs.mjs,scripts/check-authz.mjs,scripts/check-headers.mjs,scripts/check-evidence-fixture.mjs,scripts/check-outcome-vectors.mjs,scripts/check-secrets.mjs(after its self-test) andscripts/check-before-renounce.mjs --self-test. It also runs Slither, which fails the build on High findings only.
Section sources
- Agent Registration and Lifecycle
- Contract Overview and Architecture
- Economic Security Model and Incentives
- Evidence Verification and Merkle Proofs
- Key Management and Rotation
- Maturity Mechanism and Score Release
- Proposal Workflow and Challenge System
- Score Storage and Data Model
- SigvaraIdentity Contract
- SigvaraOracleBond Contract
- SigvaraReputation Contract
- SigvaraStaking Contract
- Slashing Lifecycle and Dispute Resolution
- Stake Management Functions
- Status Management and Control
- Token and Fee Contracts
- Unbonding Period and Withdrawal Security
- Anti-Manipulation and Security Measures
- Community Flag Processing
- Evidence Sources and Verification
- External Trust Integration
- Merkle Proof System
- Oracle Score Computation
- Payment-Backed Attestations
- Scoring Model and Formulas
- Agent Creation and Key Management
- Agent Operations
- Agent Registration Workflow
- Challenge-Response Authentication
- Gate Access Control
- Integration Patterns and Examples
- SDK Overview and Setup
- Verifier Functions