Grove.Testing (Grove v0.1.1)
View SourceTesting utilities for Grove CRDTs.
This module provides helpers for testing CRDT behavior, especially convergence under concurrent operations.
Usage
Add to your test file:
import Grove.TestingExample
test "replicas converge after sync" do
# Create 3 replicas of a GCounter
[r1, r2, r3] = create_replicas(GCounter, 3)
# Each replica increments
r1 = GCounter.increment(r1, 5)
r2 = GCounter.increment(r2, 3)
r3 = GCounter.increment(r3, 7)
# Sync all replicas
[r1, r2, r3] = sync_all([r1, r2, r3])
# Assert convergence
assert_convergent([r1, r2, r3])
assert GCounter.value(r1) == 15
end
Summary
Functions
Applies a list of operations to a CRDT.
Asserts that all replicas have converged to the same value.
Applies a list of operations atomically using Grove.batch/2.
Creates count replicas of the given CRDT module.
Applies a function to a specific replica in a list.
Generates random operations for fuzz testing.
Returns the values of all replicas for inspection.
Syncs two replicas bidirectionally.
Syncs all replicas with each other.
Functions
Applies a list of operations to a CRDT.
Example
ops = [{:increment, [5]}, {:increment, [3]}]
counter = apply_operations(counter, ops)
@spec assert_convergent([term()]) :: :ok
Asserts that all replicas have converged to the same value.
Raises ExUnit.AssertionError if values differ.
Example
assert_convergent([r1, r2, r3])
Applies a list of operations atomically using Grove.batch/2.
Like apply_operations/2 but with rollback semantics on error.
Example
{:ok, set} = batch_operations(set, [
{:add, ["a"]},
{:add, ["b"]}
])
@spec create_replicas(module(), pos_integer(), keyword()) :: [term()]
Creates count replicas of the given CRDT module.
Each replica gets a unique actor ID (:replica_1, :replica_2, etc).
Options
:actor_prefix- Prefix for actor IDs (default::replica)
Example
[r1, r2, r3] = create_replicas(GCounter, 3)
[a, b] = create_replicas(ORSet, 2, actor_prefix: :node)
@spec mutate_replica([term()], non_neg_integer(), (term() -> term())) :: [term()]
Applies a function to a specific replica in a list.
Returns the updated list with the modified replica.
Example
replicas = mutate_replica(replicas, 0, &GCounter.increment(&1, 5))
@spec random_operations(module(), pos_integer(), keyword()) :: [{atom(), [term()]}]
Generates random operations for fuzz testing.
Returns a list of {operation, args} tuples suitable for the given CRDT.
Supported CRDTs
Grove.Counter.GCounter-:incrementoperationsGrove.Counter.PNCounter-:incrementand:decrementoperationsGrove.Set.ORSet-:addand:removeoperationsGrove.Set.DVVSet-:addand:removeoperations
Example
ops = random_operations(GCounter, 10)
# => [{:increment, [5]}, {:increment, [3]}, ...]
Returns the values of all replicas for inspection.
Useful for debugging convergence issues.
Example
values = replica_values([r1, r2, r3])
# => [10, 10, 10]
Syncs two replicas bidirectionally.
Merges replica1 into replica2 and vice versa, returning both updated replicas.
Example
{r1, r2} = sync(r1, r2)
Syncs all replicas with each other.
Each replica receives updates from all other replicas. Returns the list of synced replicas in the same order.
Example
[r1, r2, r3] = sync_all([r1, r2, r3])