Mock Server & I/O Determinism
Relevant source files
The following files were used as context for generating this wiki page:
The Mock Server is a core component of the tusk-drift-cli that enables deterministic replay of recorded application behavior. When running tests via tusk run, the CLI starts a local mock server that intercepts outbound I/O calls (like database queries or external API requests) made by the service under test. Instead of making real network calls, the service's Tusk Drift SDK communicates with this mock server over a Unix socket. The server then uses a sophisticated matching algorithm to find the corresponding recorded event (a "span") from a trace file and returns the recorded response.
This process ensures that tests are completely isolated from external dependencies, run quickly, and are perfectly repeatable. The system's primary goal is to guarantee that for any given inbound request, the service produces the exact same sequence of outbound I/O calls, achieving I/O determinism. The logic for this is primarily located in the internal/runner package.
Sources: README.md, internal/runner/mock_matcher.go, internal/runner/server.go
Architecture Overview
The mock server is managed by the Executor and is started when a test run begins. The service under test, instrumented with a Tusk Drift SDK, connects to this server to resolve mock responses for its outbound calls.
The following diagram illustrates the high-level data flow during a test replay.
Mock Server
The mock server is implemented in internal/runner/server.go. It is a critical component that bridges the service under test and the mock matching logic.
Key Responsibilities
- Lifecycle Management: The
Executorstarts the server viaserver.Start()and stops it withserver.Stop(). - Communication: It listens on a Unix socket for incoming
GetMockRequestmessages from the SDK. - Request Handling: For each request, it invokes the
MockMatcherto find the appropriate recorded span. - State Management: It maintains the state of the test run, including which spans have been used and indexing spans for efficient lookups.
Core Data Structures
The Server struct holds all the necessary data for a test run, including indices that map different hash types to recorded spans. This pre-computation allows for fast lookups during matching.
| Field | Type | Description |
|---|---|---|
mu | sync.RWMutex | Protects concurrent access to server state. |
spanUsage | map[string]map[string]bool | Tracks which spans have been used, keyed by traceId and spanId. |
spansByTraceID | map[string][]*core.Span | All spans for a given trace. |
spansByValueHash | map[string][]*core.Span | Spans indexed by their full input value hash. |
spansByReducedValueHash | map[string][]*core.Span | Spans indexed by their reduced-schema input value hash. |
spansBySchemaHash | map[string][]*core.Span | Spans indexed by their full input schema hash. |
spansByReducedSchemaHash | map[string][]*core.Span | Spans indexed by their reduced-schema input schema hash. |
Sources: internal/runner/server.go:37-56
Request Handling Sequence
The following diagram shows the sequence of events when the SDK requests a mock from the server.
Mock Matcher
The MockMatcher contains the logic for finding the best-matching recorded span for an incoming request. This is the most complex part of the system, designed to handle a wide variety of scenarios to ensure determinism.
Sources: internal/runner/mock_matcher.go
Hashing Strategies
To speed up matching, the system relies on several pre-computed hashes of the InputValue and InputSchema of each span.
| Hash Type | Function | Description |
|---|---|---|
| Input Value Hash | span.GetInputValueHash() | A deterministic hash of the full InputValue of the span. Used for exact matches. |
| Reduced Input Value Hash | reducedInputValueHash(span) | A hash of the InputValue after removing fields marked with match_importance: 0 in the schema. This allows matching requests that differ only in unimportant fields (e.g., timestamps, random IDs). |
| Input Schema Hash | span.GetInputSchemaHash() | A hash of the full InputSchema. Used for structural matching. |
| Reduced Input Schema Hash | reducedInputSchemaHash(span) | A hash of the InputSchema after removing fields with match_importance: 0. |
Sources: internal/runner/mock_matcher.go:27-60
The Priority Matching Algorithm
The matcher follows a strict, multi-level priority algorithm to find a match. It prioritizes exact matches within the current trace, then falls back to broader, less-specific matching criteria. This ensures that the most relevant mock is always chosen.
The algorithm is implemented in FindBestMatchWithTracePriority and FindBestMatchAcrossTraces.
Key Principles:
- Trace-Local First: The matcher always attempts to find a span within the current
traceIDbefore looking at spans from other traces. - Unused Before Used: It prefers to match against spans that have not yet been consumed in the current test run.
- Value Before Schema: Exact value matches are always preferred over structural (schema) matches.
- Full Before Reduced: Matches against the full, unaltered data are preferred over matches against data with low-importance fields removed.
Sources: internal/runner/mock_matcher.go:81-314
Similarity Scoring
When a schema-based match (Priorities 7-10 and 14-15) yields multiple candidate spans, the matcher employs a similarity scoring algorithm to find the best fit.
- Function:
calculateSimilarityScorerecursively compares theInputValueof the incoming request with that of each candidate span. - Algorithm: It uses Levenshtein distance for comparing strings and a custom algorithm for comparing maps and slices. For performance, it uses a faster, character-frequency-based comparison for long strings.
- Parallelization: To speed up the process, scores for multiple candidates are calculated in parallel using a worker pool.
- Tie-Breaking: If scores are identical, the span with the older timestamp is chosen.
The best match, its score, and the next-best candidates are recorded in the MatchLevel protobuf message, providing rich debugging information in case of a deviation.
Sources: internal/runner/mock_matcher.go:529-768
HTTP-Specific Matching
For schema-based matches involving http or https packages, the matcher applies additional constraints to avoid ambiguity, as many different HTTP requests can share the same schema. The schemaMatchWithHttpShape function enforces that the following attributes must also match:
- HTTP Method (
GET,POST, etc.) - Hostname
- URL Path (excluding query parameters)
- The set of query parameter keys (values are ignored)
This ensures that a GET /api/users request is not incorrectly matched with a recorded POST /api/orders request, even if their body schemas are identical (e.g., empty).
Sources: internal/runner/mock_matcher.go:900-988
Integration with Test Execution
The tusk-drift-cli provides both an interactive TUI and a headless mode for running tests. Both are orchestrated by the testExecutorModel.
- Initialization: When
RunTestsInteractiveis called, it creates atestExecutorModel, which in turn starts theExecutorand the Mock Server. - Test Execution: The model runs tests (concurrently by default). Each test execution involves the service making calls that are handled by the mock server.
- Result Handling: When a test completes, the
testCompletedMsgis processed. If a deviation occurred, the TUI displays details, including mock-not-found events and diffs for mismatched fields. This information is sourced directly from theExecutorandServercomponents. - AI Agent Verification: The AI agent, part of
tusk exp init, uses theToolTuskRuntool to execute a test run and verify that the automated setup was successful. This provides an end-to-end check that the mock server and SDK instrumentation are working correctly.
Sources: internal/tui/test_executor.go:102-132, 233-333, internal/agent/executor.go:343-363, internal/agent/agent.go:480-550
Summary
The Mock Server and its associated Mock Matcher are the foundation of Tusk Drift's replay functionality. By intercepting I/O calls and deterministically serving recorded responses, they create a hermetic test environment. The sophisticated, multi-layered matching algorithm ensures that the correct mock is served even in the face of minor, non-functional differences between the recorded and replayed requests. This system is what allows Tusk Drift to provide fast, reliable, and realistic API tests based on real-world traffic.