Docs/Testing

Testing Guide

Test your AutoLoop contracts locally with Anvil and the full E2E test suite.


Unit Tests (Foundry)

AutoLoop contracts ship with Foundry tests. To run them:

cd autoloop
forge test

The test suite covers:

Test FileWhat It Tests
AutoLoop.t.solRegistration, funding, execution, fee calculation
AutoLoopVRF.t.solVRF proof generation, verification, random word derivation
AutoLoopSecurity.t.solAccess control, unauthorized callers, balance checks
HybridGame.t.solHybrid VRF mode, needsVRF flag toggling

Run a specific test:

forge test --match-test testProgressLoop -vvvv

Testing Your Own Contract

Create a test file in test/:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

import "forge-std/Test.sol";
import "../src/AutoLoop.sol";
import "../src/AutoLoopRegistrar.sol";
import "../src/AutoLoopRegistry.sol";
import "../src/sample/NumberGoUp.sol";

contract MyContractTest is Test {
    AutoLoop autoLoop;
    AutoLoopRegistrar registrar;
    AutoLoopRegistry registry;
    NumberGoUp game;

    address controller = address(0xC0);
    address owner = address(0xA1);

    function setUp() public {
        // Deploy protocol
        registry = new AutoLoopRegistry();
        autoLoop = new AutoLoop(address(registry));
        registrar = new AutoLoopRegistrar(address(registry));

        // Deploy your contract
        game = new NumberGoUp(30); // 30s interval

        // Register
        vm.prank(owner);
        registrar.registerAutoLoop{value: 0.1 ether}(address(game));

        // Register controller
        autoLoop.registerController(controller);
    }

    function testShouldProgress() public {
        // Warp time forward past interval
        vm.warp(block.timestamp + 31);
        assertTrue(game.shouldProgressLoop());
    }

    function testProgressLoop() public {
        vm.warp(block.timestamp + 31);

        uint256 before = game.number();
        vm.prank(controller);
        autoLoop.performUpdate(address(game));

        assertEq(game.number(), before + 1);
    }
}

Run it:

forge test --match-contract MyContractTest -vvvv

Local E2E Testing with Anvil

The full end-to-end test verifies the entire stack: contracts, registration, funding, worker execution, and health monitoring.

Prerequisites

  • Foundry (anvil, forge, cast)
  • Node.js >= 20
  • jq (for JSON parsing)

Running the E2E Test

./test-anvil-e2e.sh

This script:

  1. Starts Anvil with 2-second block times on port 8545
  2. Deploys all contracts (AutoLoop, Registry, Registrar)
  3. Deploys NumberGoUp — a sample contract that increments a counter every 30 seconds
  4. Registers the contract on-chain and deposits 0.1 ETH
  5. Registers a controller using Anvil's well-known test key
  6. Starts a worker pointed at the local chain
  7. Verifies the health endpoint returns correct stats
  8. Polls NumberGoUp.number() for up to 90 seconds, expecting it to increment
  9. Cleans up — kills Anvil and worker processes

Expected output:

[PASS] Anvil started
[PASS] Contracts deployed
[PASS] NumberGoUp deployed at 0x...
[PASS] Contract registered and funded
[PASS] Controller registered
[PASS] Worker started, health OK
[PASS] NumberGoUp.number() incremented: 0 → 1

Manual Local Testing

If you prefer to run each step manually:

# Terminal 1: Start Anvil
anvil --block-time 2

# Terminal 2: Deploy contracts
cd autoloop
forge script script/Deploy.s.sol --rpc-url http://127.0.0.1:8545 --broadcast

# Terminal 3: Start worker
cd autoloop-worker
NETWORK=anvil RPC_URL=http://127.0.0.1:8545 \
  PRIVATE_KEY=0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 \
  npm start

Then use cast to interact:

# Check if loop should progress
cast call $CONTRACT_ADDR "shouldProgressLoop()" --rpc-url http://127.0.0.1:8545

# Check current number
cast call $CONTRACT_ADDR "number()" --rpc-url http://127.0.0.1:8545

Testing VRF Contracts

VRF contracts require additional setup — the controller's public key must be registered:

# Derive pkX, pkY from private key (Node.js)
node -e "
const { secp256k1 } = require('@noble/curves/secp256k1');
const pk = secp256k1.getPublicKey('YOUR_PRIVATE_KEY_HEX', false);
const x = '0x' + Buffer.from(pk.slice(1, 33)).toString('hex');
const y = '0x' + Buffer.from(pk.slice(33, 65)).toString('hex');
console.log('pkX:', x);
console.log('pkY:', y);
"

# Register the key on-chain
cast send $VRF_CONTRACT "registerControllerKey(address,uint256,uint256)" \
  $CONTROLLER_ADDR $PKX $PKY \
  --rpc-url http://127.0.0.1:8545 \
  --private-key $DEPLOYER_KEY

The worker will then automatically generate ECVRF proofs when executing VRF-enabled contracts.

Gas Profiling

Use forge test --gas-report to see gas consumption:

forge test --gas-report

Key gas benchmarks:

OperationExpected Gas
performUpdate (Standard)~90,000
performVRFUpdate (Full VRF)~240,000
performVRFUpdate (Hybrid, no VRF tick)~95,000
performVRFUpdate (Hybrid, VRF tick)~240,000
registerAutoLoop~150,000
registerController~50,000