EventsTesting Events

Testing Events

This page shows two ways to run integration tests: against a Causet Cloud test fork (available today, via the CLI) and against a self-hosted local Docker stack (team contributors with private source — see Installation). The CLI/fork examples below work on Cloud today.

Causet does not have a unit test framework for rules — testing happens at the integration level against a running stack. The compiler provides the first line of validation.


The Compiler as First Test

Before any test runs, the Causet compiler validates your application:

  • Event types are declared before being referenced in actions, projections, or listeners.
  • Action emit operations reference valid event types.
  • Projection source_events reference valid event types.
  • derive expression field references exist in the referenced event’s payload.
  • Reserved field names (type, ts, entity_id) are not declared in payloads.
  • Expression syntax is valid.
  • No circular dependencies in state or event references.

Run the compiler as part of CI:

causet build compile --runtime . --out dist/
# Exit 0 = valid. Exit non-zero = compiler errors with locations.

A passing compile gives you schema correctness without running a single test.


Integration Test Pattern

The standard testing pattern for Causet:

  1. Create (or reuse) a disposable test fork on Causet Cloud.
  2. Deploy your application IR to that fork.
  3. Submit intents.
  4. Wait for projections.
  5. Assert projection rows via query.
  6. Delete the fork.

Step 1: Create a test fork

causet fork create test-run-001 --parent main

Step 2: Deploy to the test fork

causet build compile --runtime . --out dist/
causet deploy --fork test-run-001 --runtime . --full

Step 3: Submit an intent

causet intent FOLLOW_ARTIST \
  --fork test-run-001 \
  --stream user_stream \
  --entity user-test-001 \
  --payload '{"user_id":"user-test-001","artist_id":"artist-test-001"}'

Step 4: Assert entity/ledger state

causet inspect entity user-test-001 --fork test-run-001 --stream user_stream
causet inspect timeline --fork test-run-001 --stream user_stream --entity user-test-001

Step 5: Wait for projection, then query

Projections are async. Poll the query until the row appears (typically well under a second):

until causet query artist_followers --fork test-run-001 --param user_id=user-test-001 \
  --json | grep -q "user-test-001"; do sleep 0.2; done
 
causet query artist_followers --fork test-run-001 --param user_id=user-test-001
# Expected: {"rows": [{"user_id": "user-test-001", "artist_id": "artist-test-001", "followed_at": ...}]}

Step 6: Clean up

causet fork delete test-run-001

Testing Preflight Rejection

Submit an intent that should be rejected by preflight:

causet intent FOLLOW_ARTIST \
  --fork test-run-001 \
  --stream user_stream \
  --entity user-test-002 \
  --payload '{"user_id":"user-test-002","artist_id":"user-test-002"}'
 
# Expected response:
# {
#   "status": "REJECTED",
#   "code": "CANNOT_FOLLOW_SELF",
#   "message": "A user cannot follow themselves."
# }

Assert no event was written by checking the timeline is unchanged:

causet inspect timeline --fork test-run-001 --stream user_stream --entity user-test-002
# Expected: empty — no ARTIST_FOLLOWED event

Testing Projection Failure

Inject an event that will fail the projection handler (e.g. by temporarily breaking the projection definition and deploying it):

  1. Introduce a derive expression error in your projection DSL.
  2. Compile and deploy the broken IR to the test fork.
  3. Submit an intent that triggers the broken projection.
  4. Confirm the query no longer returns the expected row (the event committed, but the projection failed to materialize it).
⚠️

A dedicated DLQ/failure inspection CLI (causet dlq, causet failures) is proposed and not shipped today — see Projection Failures. For now, detect projection failures by comparing query results against the entity/ledger state from causet inspect.


Testing Multi-Step Flows

For multi-step flows (submit chains, see Your First Workflow), submit the sequence of intents and verify state transitions with causet inspect entity after each step:

causet intent SAVE_SHOW_DRAFT --fork test-run-001 --stream artist_stream --entity artist-1 --payload '...'
causet inspect entity artist-1 --fork test-run-001 --stream artist_stream
# Expect status: "draft", then after the auto-submitted PUBLISH_SHOW commits:
causet inspect entity artist-1 --fork test-run-001 --stream artist_stream
# Expect status: "published"

Testing Multiple Events in Sequence

Test idempotency and state accumulation:

# Follow
causet intent FOLLOW_ARTIST --fork test-run-001 --stream user_stream --entity u1 --payload '{"user_id":"u1","artist_id":"a1"}'
# Unfollow
causet intent UNFOLLOW_ARTIST --fork test-run-001 --stream user_stream --entity u1 --payload '{"user_id":"u1","artist_id":"a1"}'
# Follow again
causet intent FOLLOW_ARTIST --fork test-run-001 --stream user_stream --entity u1 --payload '{"user_id":"u1","artist_id":"a1"}'
 
# Assert: artist_followers should have one row (not three)
causet query artist_followers --fork test-run-001 --param user_id=u1
# Expected: {"rows": [{"user_id": "u1", "artist_id": "a1", ...}]}

Isolation Between Tests

Use a unique fork per test run (or test suite) to avoid cross-test contamination — each fork gets its own isolated schema:

FORK_ID="test-$(date +%s)"
causet fork create "$FORK_ID" --parent main
causet deploy --fork "$FORK_ID" --runtime . --full
# run tests against --fork "$FORK_ID"
causet fork delete "$FORK_ID"

No Unit Test Framework

There is currently no framework for unit-testing individual rules in isolation (without a deployed IR). Rule evaluation requires a loaded IR and an entity snapshot — the same dependencies as a running fork.

The recommended approach is fast integration tests against a disposable Causet Cloud fork, as shown above. Once the self-hosted runtime ships (see Installation), the same pattern will also work against a local Docker stack.