AsyncAPI version: This skill targets AsyncAPI 3.x (3.0 and 3.1). AsyncAPI 2.x is NOT supported by Drift — if the spec has
asyncapi: "2.x.x", tell the user and stop.
Reference files
references/asyncapi-schema-patterns.md— how to read and enumerate AsyncAPI 3.x message schemas (oneOf / anyOf / allOf / discriminator / const / correlationId / optional payload fields)references/asyncapi-drift-mapping.md— how to map each execution mode and payload variant to Drift YAML, including trigger/probe patterns,expectedstructure per mode, and naming conventions
Workflow
0. Navigate large specifications without loading them wholesale
For a specification over 1,000 lines or 100 KB, do not read or paste the whole file. Build a line-number index, then inspect only the operation and definitions that are reachable from it:
# Establish the document size and top-level section boundaries.
wc -l -c asyncapi.yaml
grep -nE '^(operations|channels|components):' asyncapi.yaml
# List AsyncAPI operation IDs without reading their bodies.
awk '/^operations:/{in_operations=1; next} in_operations && /^[^ ]/{exit} in_operations && /^ [A-Za-z0-9._-]+:$/ {print NR ":" $0}' asyncapi.yaml
For a requested operation, locate its line number, then use sed -n to read a
bounded range ending at the next operation at the same indentation. Follow the
same pattern for the referenced channel, message, and schema definitions. Never
expand unrelated operations, channels, or every item under components merely
to find a match.
Keep a compact fact ledger while resolving references:
| item | source line | refs still to resolve | resolved facts |
|---|---|---|---|
| operation | action, channel, messages, reply | ||
| channel | address | ||
| message | correlation ID, headers, payload ref | ||
| schema | variants, required fields, discriminator |
Process the refs still to resolve column as a queue, deduplicating repeated
$ref values. Stop when every reachable reference is resolved to primitives or
the schema patterns needed to generate the requested tests. For a request to
cover the entire specification, first index operation IDs, then process one
operation at a time and emit a separate fact ledger entry and test block for
each. This keeps very long documents within context while preserving complete
operation coverage.
1. Locate the operation
Extract only what's needed:
# Find the operation block
grep -n "sendOrderCreated\|receiveInventoryAdjusted" asyncapi.yaml
# Read the operations block and its channel/messages refs
grep -n "^operations:" asyncapi.yaml
From the operation block extract:
action—sendorreceivechannel.$ref— resolves to a channel name inchannels:messages[]— list of$refto message objectsreplyblock — present only for request-reply interactions
From the channel block:
address— the topic/queue name
From the message block (via components.messages):
correlationId.location— where Drift finds the correlation IDheadersschema — header fieldspayloadschema — the message payload (may be complex: oneOf, anyOf, allOf)
2. Resolve $refs recursively
- Grep for
MessageName:incomponents.messagesto find the definition - If the payload itself contains refs, follow those into
components.schemas - Track visited refs. When a schema recursively refers to an active ancestor, preserve the
$refback-edge and stop expanding that branch. - For external refs such as
./messages.yaml#/Order, open the referenced file and continue from its JSON Pointer. Report unresolved files or pointers as gaps; never treat them as empty schemas. - Stop at primitive types (
string,integer,boolean,array,object,const)
allOf: [$ref: Base, properties: {...}] is composition — merge all schemas into one full payload. See references/asyncapi-schema-patterns.md for all patterns.
3. Determine execution mode
Auto-selected by Drift based on action + presence of reply block:
| operation.action | reply block | mode | Drift role |
|---|---|---|---|
send |
absent | async-observe |
Drift subscribes and captures |
receive |
absent | async-inject (+ probe) or async-inject-capture (+ probe-topic) |
Drift publishes |
send |
present | async-request-reply |
Drift publishes request, captures reply |
Critical: action reflects the application's perspective, not Drift's. send means your service publishes — Drift subscribes. receive means your service consumes — Drift publishes.
4. Enumerate payload variants
For each operation that has multiple message variants, enumerate structurally distinct schema variants. Aim for minimum tests that maximise schema coverage.
| Pattern | Tests to generate |
|---|---|
oneOf / anyOf with N branches in payload |
N tests — one per branch |
discriminator with N mapping values |
N tests — one per discriminator value |
allOf (composition) |
1 test — merge all schemas into one payload |
const field |
1 test (const is a fixed value — no variants) |
| Optional field cluster in payload | 2 tests: one with all optional fields, one with required only |
nullable payload field |
1 null variant only if it changes behaviour |
See references/asyncapi-schema-patterns.md for worked examples.
5. Generate Drift test cases
For each variant produce a Drift operation block. See references/asyncapi-drift-mapping.md for full patterns. Key conventions:
- Name single-variant operations as
{operationId}_{mode}— e.g.SendOrderCreated_Observe - Name multi-variant operations as
{operationId}_{variant}— e.g.ReceiveInventoryAdjusted_commandType,ReceiveInventoryAdjusted_minimal - Always include
correlation-idinparameters— it is required for message matching - Always set
timeout-ms(default 5000ms; increase for slow services) - For
async-observe: include atriggerhook — Drift cannot make your service publish - For
async-request-reply: do not include a trigger — Drift publishes the request and captures the reply - For
async-inject: include aprobehook to verify side effects, OR useprobe-topicfor race-condition-free capture expected.headers+expected.payloadfor message capture modes;expectedmatches probe stdout JSON forasync-injectmode- Omitting an
expected.payloadfield lets Drift validate it against the AsyncAPI message schema automatically; add explicit field assertions only when asserting specific variant values (e.g. the discriminator property came back correctly)
Output format
Always produce:
- Analysis — operation name, action, channel address, execution mode, message schema patterns found, how many test variants will be generated and why
- Drift operations YAML — the complete
operations:block, ready to paste into a test file - Trigger/probe hooks — shell scripts or Lua function stubs if needed (label as FILL_IN where service-specific)
- Gaps — payload combinations intentionally excluded, with reason
Example
Given sendOrderCreated (action: send, payload has const: order.created on eventType):
operations:
SendOrderCreated_Observe:
target: async-send:sendOrderCreated:orderCreated
description: "Observe the order.created event after trigger fires"
parameters:
correlation-id: send-order-created-001
timeout-ms: 5000
orderId: ORD-100
customerId: CUST-9
trigger:
executable-type: command
value: python3
parameters:
args:
- ./hooks/trigger-send.py
- --correlation-id
- ${parameters.correlation-id}
- --order-id
- ${parameters.orderId}
- --customer-id
- ${parameters.customerId}
timeout-ms: 2000
expected:
headers:
correlation-id: ${parameters.correlation-id}
payload:
eventType: order.created
orderId: ${parameters.orderId}
customerId: ${parameters.customerId}
status: created