ContractScalaCheckCommands

scalus.testing.ContractScalaCheckCommands
See theContractScalaCheckCommands companion object
class ContractScalaCheckCommands[S](initialEmulator: ImmutableEmulator, step: ContractStepVariations[S], timeout: FiniteDuration = ...)(checkInvariants: (BlockchainReader, S) => Future[Prop] = ...)(using ec: ExecutionContext) extends Commands

Bridges ContractStepVariations with ScalaCheck's stateful testing framework.

This adapter enables property-based testing of smart contract interactions by:

==Two Emulators Pattern==

Per ScalaCheck Commands pattern, we maintain two separate emulators:

  • '''State''' (CommandsState): The abstract model - wraps ImmutableEmulator + contract state S + RNG seed. Used for genCommand, nextState, and preCondition.
  • '''Sut''' (scalus.cardano.node.Emulator): The system under test - the existing mutable Emulator class. Used for run (actual execution) and postCondition verification.

This separation allows ScalaCheck to detect discrepancies between model and implementation.

==Slot Advancement==

Slot advancement is controlled by overriding ContractStepVariations.slotDelays in the step. This returns slot delays that are included as StepAction.Wait actions alongside transaction submissions.

==JVM-Only==

This class uses Await.result for synchronous execution and is JVM-only. For cross-platform testing, use ScenarioExplorer instead.

==Example Usage==

val auctionBidStep = new ContractStepVariations[AuctionState] {
 def extractState(reader: BlockchainReader)(using EC) = ???
 def makeBaseTx(reader: BlockchainReader, state: AuctionState)(using EC) = ???
 def variations = TxVariations.standard.default(...)
 override def slotDelays(state: AuctionState) = Seq(10L, 100L)
}

// Without invariant checking
val emulator = ImmutableEmulator.withAddresses(Seq(Alice.address, Bob.address))
val commands = ContractScalaCheckCommands(emulator, auctionBidStep)()
commands.property().check()

// With invariant checking
val commandsWithInvariants = ContractScalaCheckCommands(emulator, auctionBidStep) {
 (reader, state) => Future {
   Prop(state.balance >= Coin.zero) && Prop(state.highestBidder.isDefined)
 }
}

Type parameters

S

the contract state type

Value parameters

checkInvariants

async function to check invariants after successful transactions

ec

execution context for async operations

initialEmulator

the starting emulator state with funded addresses

step

the contract step variations to test

timeout

timeout for async operations (default: 30 seconds)

Attributes

Companion
object
Graph
Supertypes
trait Commands
class Object
trait Matchable
class Any

Members list

Type members

Classlikes

case class AdvanceSlotCommand(slots: Long) extends Command

Command to advance the slot in the emulator.

Command to advance the slot in the emulator.

Attributes

Supertypes
trait Serializable
trait Product
trait Equals
trait Command
class Object
trait Matchable
class Any
Show all
case class SubmitTxCommand(tx: Transaction) extends Command

Command to submit a transaction to the emulator.

Command to submit a transaction to the emulator.

Attributes

Supertypes
trait Serializable
trait Product
trait Equals
trait Command
class Object
trait Matchable
class Any
Show all

Inherited classlikes

trait Command

A type representing the commands that can run in the system under test. This type should be immutable and implement the equality operator properly.

A type representing the commands that can run in the system under test. This type should be immutable and implement the equality operator properly.

Attributes

Inherited from:
Commands
Supertypes
class Object
trait Matchable
class Any
Known subtypes
case object NoOp extends Command

A command that doesn't do anything

A command that doesn't do anything

Attributes

Inherited from:
Commands
Supertypes
trait Singleton
trait Product
trait Mirror
trait Serializable
trait Product
trait Equals
trait Command
class Object
trait Matchable
class Any
Show all
trait SuccessCommand extends Command

A command that never should throw an exception on execution.

A command that never should throw an exception on execution.

Attributes

Inherited from:
Commands
Supertypes
trait Command
class Object
trait Matchable
class Any
trait UnitCommand extends Command

A command that doesn't return a result, only succeeds or fails.

A command that doesn't return a result, only succeeds or fails.

Attributes

Inherited from:
Commands
Supertypes
trait Command
class Object
trait Matchable
class Any

Types

override type State = CommandsState[S]

The abstract state type. Must be immutable. The State type should model the state of the system under test (SUT). It should only contain details needed for specifying our pre- and post-conditions, and for creating Sut instances.

The abstract state type. Must be immutable. The State type should model the state of the system under test (SUT). It should only contain details needed for specifying our pre- and post-conditions, and for creating Sut instances.

Attributes

override type Sut = Emulator

A type representing one instance of the system under test (SUT). The Sut type should be a proxy to the actual system under test and is therefore, by definition, a mutable type. It is used by the Command.run method to execute commands in the system under test. It should be possible to have any number of co-existing instances of the Sut type, as long as canCreateNewSut isn't violated, and each Sut instance should be a proxy to a distinct SUT instance. There should be no dependencies between the Sut instances, as they might be used in parallel by ScalaCheck. Sut instances are created by newSut and destroyed by destroySut. newSut and destroySut might be called at any time by ScalaCheck, as long as canCreateNewSut isn't violated.

A type representing one instance of the system under test (SUT). The Sut type should be a proxy to the actual system under test and is therefore, by definition, a mutable type. It is used by the Command.run method to execute commands in the system under test. It should be possible to have any number of co-existing instances of the Sut type, as long as canCreateNewSut isn't violated, and each Sut instance should be a proxy to a distinct SUT instance. There should be no dependencies between the Sut instances, as they might be used in parallel by ScalaCheck. Sut instances are created by newSut and destroyed by destroySut. newSut and destroySut might be called at any time by ScalaCheck, as long as canCreateNewSut isn't violated.

Attributes

Value members

Concrete methods

override def canCreateNewSut(newState: State, initSuts: Iterable[State], runningSuts: Iterable[Sut]): Boolean

Check if a new Sut can be created.

Check if a new Sut can be created.

Always returns true since Emulator has no resource constraints.

Attributes

Definition Classes
Commands
override def destroySut(sut: Sut): Unit

Clean up system under test (no-op for Emulator).

Clean up system under test (no-op for Emulator).

Attributes

Definition Classes
Commands
override def genCommand(state: State): Gen[Command]

Generate a command based on current abstract state.

Generate a command based on current abstract state.

Uses ContractStepVariations.allActions to generate actions (transactions + slot delays), then randomly selects one.

Attributes

Definition Classes
Commands
override def genInitialState: Gen[State]

Generate initial abstract state.

Generate initial abstract state.

Extracts the contract state from the initial emulator and creates a CommandsState.

Attributes

Definition Classes
Commands
override def initialPreCondition(state: State): Boolean

Check if initial state is valid for testing.

Check if initial state is valid for testing.

Always returns true since we construct valid initial states.

Attributes

Definition Classes
Commands
override def newSut(state: State): Sut

Create a new system under test from abstract state.

Create a new system under test from abstract state.

Converts the ImmutableEmulator to a mutable scalus.cardano.node.Emulator.

Attributes

Definition Classes
Commands

Inherited methods

def commandSequence(head: Command, snd: Command, rest: Command*): Command

A command that runs a sequence of other commands. All commands (and their post conditions) are executed even if some command fails. Note that you probably can't use this method if you're testing in parallel (threadCount larger than 1). This is because ScalaCheck regards each command as atomic, even if the command is a sequence of other commands.

A command that runs a sequence of other commands. All commands (and their post conditions) are executed even if some command fails. Note that you probably can't use this method if you're testing in parallel (threadCount larger than 1). This is because ScalaCheck regards each command as atomic, even if the command is a sequence of other commands.

Attributes

Inherited from:
Commands
final def property(threadCount: Int = ..., maxParComb: Int = ...): Prop

A property that can be used to test this Commands specification.

A property that can be used to test this Commands specification.

The parameter threadCount specifies the number of commands that might be executed in parallel. Defaults to one, which means the commands will only be run serially for the same Sut instance. Distinct Sut instances might still receive commands in parallel, if the Test.Parameters.workers parameter is larger than one. Setting threadCount higher than one enables ScalaCheck to reveal thread-related issues in your system under test.

When setting threadCount larger than one, ScalaCheck must evaluate all possible command interleavings (and the end State instances they produce), since parallel command execution is non-deterministic. ScalaCheck tries out all possible end states with the Command.postCondition function of the very last command executed (there is always exactly one command executed after all parallel command executions). If it fails to find an end state that satisfies the postcondition, the test fails. However, the number of possible end states grows rapidly with increasing values of threadCount. Therefore, the lengths of the parallel command sequences are limited so that the number of possible end states don't exceed maxParComb. The default value of maxParComb is 1000000.

Attributes

Inherited from:
Commands
def shrinkState: Shrink[State]

Override this to provide a custom Shrinker for your internal State. By default no shrinking is done for State.

Override this to provide a custom Shrinker for your internal State. By default no shrinking is done for State.

Attributes

Inherited from:
Commands