Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Testing

The SDK provides a test framework for writing and running AssemblyScript tests against your JAM service, with configurable ecalli host call mocks.

Architecture

Testing involves two layers that work together:

┌─────────────────────────────────────┐
│  AssemblyScript test code (WASM)    │
│                                     │
│  ┌───────────┐  ┌────────────────┐  │
│  │ Your test │  │ SDK test utils │  │
│  │ assertions│  │ TestGas, etc.  │  │
│  └───────────┘  └───────┬────────┘  │
│                         │           │
│         @external("ecalli", ...)    │
└─────────────────────────┼───────────┘
                          │ WASM imports
┌─────────────────────────┼───────────┐
│  sdk-ecalli-mocks (Node.js)         │
│                                     │
│  Stub implementations of ecalli     │
│  host calls + configuration state   │
└─────────────────────────────────────┘
  • sdk-ecalli-mocks/ — A TypeScript (Node.js) package that provides stub implementations of all 27 ecalli host calls (general 0-5 + 100, refine 6-13, accumulate 14-26). These stubs satisfy the WASM imports at test time and hold configurable state (gas value, storage map, preimage data, etc.).

  • sdk/test/test-ecalli/ — AssemblyScript wrapper classes (TestGas, TestFetch, TestLookup, TestStorage, TestEcalli) that bridge to the JS-side stubs via @external("ecalli", ...) WASM imports. These give your AS test code a high-level API for configuring stub behavior.

Writing Tests

Test structure

Tests use the test() helper and Assert class from the SDK:

import { Assert, Test, test } from "@fluffylabs/as-lan/test";

export const TESTS: Test[] = [
  test("my feature works", () => {
    const assert = Assert.create();
    assert.isEqual(1 + 1, 2, "basic math");
    return assert;
  }),
];

Each test function returns an Assert instance. Use assert.isEqual(actual, expected, msg) to add assertions — any failure is recorded and reported after the test completes.

Test runner

Each service needs a test-run.ts entry point that registers test suites:

import { TestSuite, runTestSuites } from "@fluffylabs/as-lan/test";
import * as myTests from "./index.test";

export function runAllTests(): void {
  runTestSuites([TestSuite.create(myTests.TESTS, "my-service.ts")]);
}

And a bin/test.js that boots the WASM and runs:

import { setMemory } from "ecalli";
import { memory, runAllTests } from "../build/test.js";

setMemory(memory);
runAllTests();

Build and run

npm test   # compiles test target and runs bin/test.js

This compiles your test-run entry point to WASM (with the test target from asconfig.json), then executes it in Node.js with the ecalli stubs providing host call implementations.

Configuring Ecalli Mocks

By default the stubs provide sensible test values (e.g. gas() returns 1_000_000, lookup() returns "test-preimage", read()/write() use an in-memory Map). You can override these from within your AS test code.

TestGas

Set the value returned by the gas() ecalli:

import { TestGas } from "@fluffylabs/as-lan/test";

TestGas.set(500);  // gas() will now return 500

TestFetch

Set fixed data returned by the fetch() ecalli (overrides the default kind-dependent pattern):

import { TestFetch } from "@fluffylabs/as-lan/test";

const data = new Uint8Array(4);
data[0] = 0xde; data[1] = 0xad; data[2] = 0xbe; data[3] = 0xef;
TestFetch.setData(data);

TestLookup

Set the preimage returned by the lookup() ecalli:

import { TestLookup } from "@fluffylabs/as-lan/test";

const preimage = new Uint8Array(3);
preimage[0] = 1; preimage[1] = 2; preimage[2] = 3;
TestLookup.setPreimage(preimage);

TestStorage

Pre-populate or delete entries in the read()/write() stub storage:

import { BytesBlob } from "@fluffylabs/as-lan";
import { TestStorage } from "@fluffylabs/as-lan/test";

// Pre-populate a key
const key = BytesBlob.wrap(String.UTF8.encode("counter"));
const value = BytesBlob.wrap(new Uint8Array(8));
TestStorage.set(key, value);

// Delete a key
TestStorage.set(key, null);

TestEcalli

Reset all configuration to defaults and clear storage:

import { TestEcalli } from "@fluffylabs/as-lan/test";

TestEcalli.reset();

Default Stub Behavior

General (0-5, 100):

EcalliDefault
gas()Returns 1_000_000
fetch()Writes a 16-byte kind-dependent pattern, returns 16
lookup()Writes "test-preimage" (13 bytes), returns 13
read()Reads from in-memory Map; returns NONE (-1) if key missing
write()Writes to in-memory Map; returns previous value length or NONE
info()Returns a 96-byte structure (code_hash=0xAA..., balance=1000)
log()Prints [LEVEL] target: message to console

Refine (6-13):

EcalliDefault
historical_lookup()Writes "test-historical" (15 bytes), returns 15
export_()Returns incrementing segment index (0, 1, 2, …)
machine()Returns incrementing machine ID (0, 1, 2, …)
peek()Returns OK (0)
poke()Returns OK (0)
pages()Returns OK (0)
invoke()Returns HALT (0), writes r8 = 0
expunge()Returns OK (0)

Accumulate (14-26):

EcalliDefault
bless()Returns OK (0)
assign()Returns OK (0)
designate()Returns OK (0)
checkpoint()Returns remaining gas (delegates to gas() mock)
new_service()Returns incrementing service ID (256, 257, …)
upgrade()Returns OK (0)
transfer()Returns OK (0)
eject()Returns OK (0)
query()Returns NONE (-1), writes r8 = 0
solicit()Returns OK (0)
forget()Returns OK (0)
yield_result()Returns OK (0)
provide()Returns OK (0)

See the fibonacci and ecalli-test examples for usage examples.