Skip to content

Chaos Engineering

Chaos engineering allows you to simulate various SMTP failure scenarios and network issues during email testing. This helps verify your application handles email delivery failures gracefully.

Chaos features must be enabled on the gateway server. Check availability:

ServerInfo serverInfo = client.getServerInfo();
if (serverInfo.isChaosEnabled()) {
System.out.println("Chaos features available");
} else {
System.out.println("Chaos features disabled on this server");
}
ChaosConfig chaos = ChaosConfig.builder()
.enabled(true)
.latency(LatencyConfig.builder()
.enabled(true)
.minDelayMs(1000)
.maxDelayMs(5000)
.build())
.build();
Inbox inbox = client.createInbox(
CreateInboxOptions.builder()
.chaos(chaos)
.build()
);
ChaosConfig config = inbox.setChaos(ChaosConfig.builder()
.enabled(true)
.latency(LatencyConfig.builder()
.enabled(true)
.minDelayMs(1000)
.maxDelayMs(5000)
.build())
.build());
ChaosConfig config = inbox.getChaos();
System.out.println("Chaos enabled: " + config.isEnabled());
if (config.getLatency() != null) {
System.out.println("Latency enabled: " + config.getLatency().isEnabled());
}
inbox.disableChaos();
import com.vaultsandbox.client.model.ChaosConfig;
import com.vaultsandbox.client.model.LatencyConfig;
import com.vaultsandbox.client.model.ConnectionDropConfig;
import com.vaultsandbox.client.model.RandomErrorConfig;
import com.vaultsandbox.client.model.GreylistConfig;
import com.vaultsandbox.client.model.BlackholeConfig;
PropertyTypeRequiredDescription
enabledBooleanYesMaster switch for all chaos features
expiresAtStringNoAuto-disable chaos after this time
latencyLatencyConfigNoInject artificial delays
connectionDropConnectionDropConfigNoSimulate connection failures
randomErrorRandomErrorConfigNoReturn random SMTP error codes
greylistGreylistConfigNoSimulate greylisting behavior
blackholeBlackholeConfigNoAccept but silently discard emails

Inject artificial delays into email processing to test timeout handling and slow connections.

inbox.setChaos(ChaosConfig.builder()
.enabled(true)
.latency(LatencyConfig.builder()
.enabled(true)
.minDelayMs(500) // Minimum delay (default: 500)
.maxDelayMs(5000) // Maximum delay (default: 10000, max: 60000)
.jitter(true) // Randomize within range (default: true)
.probability(0.5) // 50% of emails affected (default: 1.0)
.build())
.build());
PropertyTypeDefaultDescription
enabledBooleanEnable/disable latency injection
minDelayMsInteger500Minimum delay in milliseconds
maxDelayMsInteger10000Maximum delay in milliseconds (max: 60000)
jitterBooleantrueRandomize delay within range. If false, uses maxDelay
probabilityDouble1.0Probability of applying delay (0.0-1.0)
  • Test application timeout handling
  • Verify UI responsiveness during slow email delivery
  • Test retry logic with variable delays

Simulate connection failures by dropping SMTP connections.

inbox.setChaos(ChaosConfig.builder()
.enabled(true)
.connectionDrop(ConnectionDropConfig.builder()
.enabled(true)
.probability(0.3) // 30% of connections dropped
.graceful(false) // Abrupt RST instead of graceful FIN
.build())
.build());
PropertyTypeDefaultDescription
enabledBooleanEnable/disable connection dropping
probabilityDouble1.0Probability of dropping connection (0.0-1.0)
gracefulBooleantrueUse graceful close (FIN) vs abrupt (RST)
  • Test connection reset handling
  • Verify TCP error recovery
  • Test application behavior when SMTP connections fail mid-delivery

Return random SMTP error codes to test error handling.

inbox.setChaos(ChaosConfig.builder()
.enabled(true)
.randomError(RandomErrorConfig.builder()
.enabled(true)
.errorRate(0.1) // 10% of emails return errors
.errorTypes(ChaosErrorType.TEMPORARY) // Only 4xx errors
.build())
.build());
PropertyTypeDefaultDescription
enabledBooleanEnable/disable random errors
errorRateDouble0.1Probability of returning an error
errorTypesList<ChaosErrorType>TEMPORARYTypes of errors to return
TypeSMTP CodesDescription
TEMPORARY4xxTemporary failures, should retry
PERMANENT5xxPermanent failures, should not retry
  • Test 4xx SMTP error handling and retry logic
  • Test 5xx SMTP error handling and failure notifications
  • Verify application handles both error types correctly

Simulate greylisting behavior where the first delivery attempt is rejected and subsequent retries are accepted.

inbox.setChaos(ChaosConfig.builder()
.enabled(true)
.greylist(GreylistConfig.builder()
.enabled(true)
.retryWindowMs(300000) // 5 minute window
.maxAttempts(2) // Accept on second attempt
.trackBy(GreylistTrackBy.IP_SENDER) // Track by IP and sender combination
.build())
.build());
PropertyTypeDefaultDescription
enabledBooleanEnable/disable greylisting
retryWindowMsInteger300000Window for tracking retries (5 min)
maxAttemptsInteger2Attempts before accepting
trackByGreylistTrackByIP_SENDERHow to identify unique delivery attempts
MethodDescription
IPTrack by sender IP only
SENDERTrack by sender email only
IP_SENDERTrack by combination of IP and sender
  • Test SMTP retry behavior when mail servers use greylisting
  • Verify retry intervals and backoff logic
  • Test handling of temporary 4xx rejections

Accept emails but silently discard them without storing.

inbox.setChaos(ChaosConfig.builder()
.enabled(true)
.blackhole(BlackholeConfig.builder()
.enabled(true)
.triggerWebhooks(false) // Don't trigger webhooks for discarded emails
.build())
.build());
PropertyTypeDefaultDescription
enabledBooleanEnable/disable blackhole mode
triggerWebhooksBooleanfalseTrigger webhooks for discarded emails
  • Test behavior when emails are silently lost
  • Test webhook integration even when emails aren’t stored
  • Simulate email delivery that succeeds at SMTP level but fails at storage

Set chaos to automatically disable after a specific time:

import java.time.Instant;
import java.time.temporal.ChronoUnit;
// Enable chaos for 1 hour
String expiresAt = Instant.now().plus(1, ChronoUnit.HOURS).toString();
inbox.setChaos(ChaosConfig.builder()
.enabled(true)
.expiresAt(expiresAt)
.latency(LatencyConfig.builder()
.enabled(true)
.maxDelayMs(3000)
.build())
.build());

After expiresAt, chaos is automatically disabled and normal email delivery resumes.

Multiple chaos features can be enabled simultaneously:

inbox.setChaos(ChaosConfig.builder()
.enabled(true)
// 30% of emails delayed 1-5 seconds
.latency(LatencyConfig.builder()
.enabled(true)
.minDelayMs(1000)
.maxDelayMs(5000)
.probability(0.3)
.build())
// 10% of emails return temporary errors
.randomError(RandomErrorConfig.builder()
.enabled(true)
.errorRate(0.1)
.errorTypes(ChaosErrorType.TEMPORARY)
.build())
.build());
import com.vaultsandbox.client.VaultSandboxClient;
import com.vaultsandbox.client.Inbox;
import com.vaultsandbox.client.CreateInboxOptions;
import com.vaultsandbox.client.model.*;
public class ChaosExample {
public static void main(String[] args) {
VaultSandboxClient client = VaultSandboxClient.builder()
.url(System.getenv("VAULTSANDBOX_URL"))
.apiKey(System.getenv("VAULTSANDBOX_API_KEY"))
.build();
try {
// Check if chaos is available
ServerInfo serverInfo = client.getServerInfo();
if (!serverInfo.isChaosEnabled()) {
System.out.println("Chaos features not available on this server");
return;
}
// Create inbox with chaos enabled
ChaosConfig chaos = ChaosConfig.builder()
.enabled(true)
.latency(LatencyConfig.builder()
.enabled(true)
.minDelayMs(2000)
.maxDelayMs(5000)
.probability(0.5)
.build())
.randomError(RandomErrorConfig.builder()
.enabled(true)
.errorRate(0.1)
.errorTypes(ChaosErrorType.TEMPORARY)
.build())
.build();
Inbox inbox = client.createInbox(
CreateInboxOptions.builder()
.chaos(chaos)
.build()
);
System.out.println("Testing with chaos: " + inbox.getEmailAddress());
// Get current chaos configuration
ChaosConfig config = inbox.getChaos();
System.out.println("Chaos enabled: " + config.isEnabled());
if (config.getLatency() != null) {
System.out.println("Latency enabled: " + config.getLatency().isEnabled());
}
// Send test emails and verify handling
// Your test logic here...
// Update chaos configuration
inbox.setChaos(ChaosConfig.builder()
.enabled(true)
.greylist(GreylistConfig.builder()
.enabled(true)
.maxAttempts(3)
.build())
.build());
// More testing...
// Disable chaos for normal operation tests
inbox.disableChaos();
// Clean up
inbox.delete();
} finally {
client.close();
}
}
}
import com.vaultsandbox.client.strategy.WaitOptions;
import java.time.Duration;
// Enable greylisting to test retry behavior
inbox.setChaos(ChaosConfig.builder()
.enabled(true)
.greylist(GreylistConfig.builder()
.enabled(true)
.maxAttempts(2)
.build())
.build());
// Send email - first attempt will fail, retry should succeed
sendEmail(inbox.getEmailAddress());
// If your mail sender retries correctly, email should arrive
Email email = inbox.waitForEmail(WaitOptions.builder()
.timeout(Duration.ofMillis(60000))
.build());
import com.vaultsandbox.client.exception.TimeoutException;
import com.vaultsandbox.client.strategy.WaitOptions;
import java.time.Duration;
// Enable high latency
inbox.setChaos(ChaosConfig.builder()
.enabled(true)
.latency(LatencyConfig.builder()
.enabled(true)
.minDelayMs(10000)
.maxDelayMs(15000)
.build())
.build());
// Test that your application handles timeouts correctly
try {
inbox.waitForEmail(WaitOptions.builder()
.timeout(Duration.ofMillis(5000))
.build());
} catch (TimeoutException e) {
// Expected: TimeoutException
System.out.println("Timeout handled correctly");
}
// Enable high error rate
inbox.setChaos(ChaosConfig.builder()
.enabled(true)
.randomError(RandomErrorConfig.builder()
.enabled(true)
.errorRate(0.8)
.errorTypes(ChaosErrorType.TEMPORARY, ChaosErrorType.PERMANENT)
.build())
.build());
// Test that your application handles errors and retries appropriately
import com.vaultsandbox.client.exception.InboxNotFoundException;
import com.vaultsandbox.client.exception.ApiException;
try {
inbox.setChaos(ChaosConfig.builder()
.enabled(true)
.latency(LatencyConfig.builder()
.enabled(true)
.build())
.build());
} catch (InboxNotFoundException e) {
System.err.println("Inbox not found");
} catch (ApiException e) {
if (e.getStatusCode() == 403) {
System.err.println("Chaos features are disabled on this server");
} else {
System.err.println("API error (" + e.getStatusCode() + "): " + e.getMessage());
}
}
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.TestInstance;
import com.vaultsandbox.client.VaultSandboxClient;
import com.vaultsandbox.client.Inbox;
import static org.junit.jupiter.api.Assumptions.assumeTrue;
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
public abstract class ChaosTestBase {
protected VaultSandboxClient client;
protected Inbox inbox;
@BeforeAll
void setUp() {
client = VaultSandboxClient.builder()
.url(System.getenv("VAULTSANDBOX_URL"))
.apiKey(System.getenv("VAULTSANDBOX_API_KEY"))
.build();
// Skip tests if chaos is not available
ServerInfo serverInfo = client.getServerInfo();
assumeTrue(serverInfo.isChaosEnabled(),
"Chaos features not available on this server");
}
@AfterEach
void cleanUp() {
if (inbox != null) {
inbox.delete();
inbox = null;
}
}
}
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
import java.util.stream.Stream;
class ChaosResilienceTest extends ChaosTestBase {
static Stream<ChaosConfig> chaosScenarios() {
return Stream.of(
ChaosConfig.builder()
.enabled(true)
.latency(LatencyConfig.builder()
.enabled(true)
.minDelayMs(1000)
.maxDelayMs(3000)
.build())
.build(),
ChaosConfig.builder()
.enabled(true)
.randomError(RandomErrorConfig.builder()
.enabled(true)
.errorRate(0.5)
.build())
.build(),
ChaosConfig.builder()
.enabled(true)
.greylist(GreylistConfig.builder()
.enabled(true)
.maxAttempts(2)
.build())
.build()
);
}
@ParameterizedTest
@MethodSource("chaosScenarios")
void testApplicationResilience(ChaosConfig chaosConfig) {
inbox = client.createInbox(
CreateInboxOptions.builder()
.chaos(chaosConfig)
.build()
);
// Test your application's resilience
// ...
}
}