or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

index.mdmulti-node-spec.mdsystem-properties.mdtest-conductor.mdtest-configuration.md
tile.json

test-configuration.mddocs/

Test Configuration

The MultiNodeConfig class provides the foundation for configuring multi-node tests, including participant roles, Akka settings, and deployment configurations.

MultiNodeConfig Class

abstract class MultiNodeConfig {
  def commonConfig(config: Config): Unit
  def nodeConfig(roles: RoleName*)(configs: Config*): Unit  
  def role(name: String): RoleName
  def debugConfig(on: Boolean): Config
  def deployOn(role: RoleName, deployment: String): Unit
  def deployOnAll(deployment: String): Unit
  def testTransport(on: Boolean): Unit
}

Configuration Methods

Common Configuration

def commonConfig(config: Config): Unit

Registers a common base configuration that applies to all test participants. This is typically used for shared Akka settings.

Usage Example:

object MyMultiNodeConfig extends MultiNodeConfig {
  commonConfig(ConfigFactory.parseString("""
    akka {
      actor.provider = remote
      loglevel = "INFO"
      remote.artery.canonical.port = 0
    }
  """))
}

Node-Specific Configuration

def nodeConfig(roles: RoleName*)(configs: Config*): Unit

Registers configuration overrides for specific participants. Multiple roles can share the same configuration, and multiple configs are merged with fallback behavior.

Usage Example:

val first = role("first")
val second = role("second")
val third = role("third")

// Configure first node with specific settings
nodeConfig(first)(ConfigFactory.parseString("""
  akka.cluster.roles = ["seed"]
"""))

// Configure second and third nodes with shared settings
nodeConfig(second, third)(ConfigFactory.parseString("""
  akka.cluster.roles = ["worker"]  
"""))

Role Creation

def role(name: String): RoleName

Creates and registers a new role name for test participants. Each role name must be unique within the test configuration.

Usage Example:

object ClusterTestConfig extends MultiNodeConfig {
  val controller = role("controller")
  val worker1 = role("worker1") 
  val worker2 = role("worker2")
  val observer = role("observer")
}

Parameters:

  • name: String - Unique name for the role

Returns: RoleName - Role identifier for use in test execution

Throws: IllegalArgumentException if role name is not unique

Debug Configuration

def debugConfig(on: Boolean): Config

Generates debug configuration for verbose logging of actor and remoting operations.

Usage Example:

commonConfig(debugConfig(on = true).withFallback(baseConfig))

Parameters:

  • on: Boolean - When true, enables debug logging for actors and remoting

Returns: Config - Configuration with debug settings or empty config

Deployment Configuration

Role-Specific Deployment

def deployOn(role: RoleName, deployment: String): Unit

Configures actor deployment for a specific role. Deployment strings use HOCON format and support address replacement tokens.

Usage Example:

deployOn(worker1, """
  /myActor {
    remote = "@worker2@/user/service"
    router = round-robin-pool
    nr-of-instances = 3
  } 
""")

Global Deployment

def deployOnAll(deployment: String): Unit

Configures actor deployment that applies to all roles.

Usage Example:

deployOnAll("""
  /globalService {
    router = consistent-hashing-pool
    nr-of-instances = 5
  }
""")

Network Testing Configuration

Test Transport

def testTransport(on: Boolean): Unit

Enables or disables the test transport mode required for network failure injection features like blackhole, passThrough, and throttle.

Usage Example:

object NetworkTestConfig extends MultiNodeConfig {
  testTransport(on = true)  // Required for failure injection
  
  val node1 = role("node1")
  val node2 = role("node2")
}

class NetworkFailureTest extends MultiNodeSpec(NetworkTestConfig) {
  "network test" must {
    "simulate network partition" in {
      testConductor.blackhole(node1, node2, Direction.Both).await
      // Test behavior during network partition
    }
  }
}

Parameters:

  • on: Boolean - Enable test transport adapters when true

Note: Must be enabled before using blackhole, passThrough, or throttle methods in TestConductor.

Configuration Properties

The MultiNodeConfig provides several read-only properties for accessing configuration state:

// Internal properties (read-only)
private[testkit] def myself: RoleName           // Current node's role
private[akka] def config: Config                // Computed final configuration
private[testkit] def deployments(node: RoleName): immutable.Seq[String]  // Node deployments
private[testkit] def roles: immutable.Seq[RoleName]  // All registered roles

Address Replacement Tokens

Deployment configurations support address replacement tokens that are resolved at runtime:

deployOn(serviceNode, """
  /client {
    remote = "@dataNode@/user/database"
  }
""")

Available Tokens:

  • @roleName@ - Replaced with the actual network address of the specified role

Configuration Inheritance

Configuration follows a layered approach with fallback behavior:

  1. Node-specific config (highest priority)
  2. Common config
  3. Test transport config (if enabled)
  4. Base MultiNodeSpec config
  5. Default Akka config (lowest priority)

Complete Configuration Example

object ComplexMultiNodeConfig extends MultiNodeConfig {
  // Define roles
  val seed = role("seed")
  val worker1 = role("worker1")
  val worker2 = role("worker2") 
  val client = role("client")
  
  // Common configuration for all nodes
  commonConfig(ConfigFactory.parseString("""
    akka {
      actor.provider = remote
      cluster {
        seed-nodes = []
        auto-down-unreachable-after = 10s
      }
      remote.artery.canonical.port = 0
    }
  """))
  
  // Seed node specific configuration
  nodeConfig(seed)(ConfigFactory.parseString("""
    akka.cluster.roles = ["seed"]
  """))
  
  // Worker nodes configuration  
  nodeConfig(worker1, worker2)(ConfigFactory.parseString("""
    akka.cluster.roles = ["worker"]
  """))
  
  // Client node configuration
  nodeConfig(client)(ConfigFactory.parseString("""
    akka.cluster.roles = ["client"]
  """))
  
  // Enable network failure injection
  testTransport(on = true)
  
  // Deploy services on specific nodes
  deployOn(worker1, """
    /workerService {
      router = round-robin-pool
      nr-of-instances = 2
    }
  """)
  
  deployOn(client, """
    /clientService {
      remote = "@worker1@/user/workerService"
    }
  """)
  
  // Global deployment
  deployOnAll("""
    /monitor {
      dispatcher = "akka.actor.default-dispatcher"
    }
  """)
}