http_gun/testing
Starts offline clients that answer from a script of exchanges, to test
code that takes an http_gun.Client.
let assert Ok(req) = request.to("https://api.example.com/orders/7")
let script =
testing.script([
testing.exchange(
request.set_body(req, <<>>),
testing.Respond(
response.new(200) |> response.set_body([<<"{\"id\":7}">>]),
testing.Finished([]),
),
),
])
let assert Ok(client) = testing.playback(script, config.default())
let assert Ok(buffered) = http_gun.send(client, request.set_body(req, <<>>))
A playback client never opens a connection and ignores the destination
policy. Each request must match the next exchange: a mismatch fails with
PlaybackMismatch(position) and leaves the exchange in place, and a
request after the last fails with PlaybackExhausted. Concurrent
requests are matched in admission order.
Matching is exact by default: method, URL, headers and body bytes, after
the configuration’s redaction (config.with_redaction) is applied to
both sides. ignoring_headers skips headers such as a timestamp or a
signature, and matching replaces the comparison.
exchange drops the credential headers of redaction.default() from
the request it stores, so a script never holds a caller’s credentials.
Types
How a scripted response body ends.
pub type Ending {
Finished(trailers: List(#(String, String)))
Aborted(error.Failure)
Abandoned
}
Constructors
-
Finished(trailers: List(#(String, String)))The body completed, with these trailers.
-
Aborted(error.Failure)The body failed after its chunks.
-
AbandonedThe client closed the body before it completed, as a recorded early stop replays.
How a scripted exchange answers.
pub type Reply {
Respond(
response: response.Response(List(BitArray)),
ending: Ending,
)
Reject(error.Failure)
}
Constructors
-
Respond( response: response.Response(List(BitArray)), ending: Ending, )Respond with a status, headers and body chunks, then end.
-
Reject(error.Failure)Fail before any response, for example with a connection failure.
Values
pub fn exchange(
request: request.Request(BitArray),
reply: Reply,
) -> Exchange
Pair an expected request with its reply.
pub fn ignoring_headers(
script: Script,
names: List(String),
) -> Script
Leave these headers, compared without case, out of matching on both sides, for values that change on every run such as a date or a signature.
pub fn match_key(
redaction: redaction.Redaction,
request: request.Request(BitArray),
) -> request.Request(BitArray)
The form of a request that playback compares: redaction applied, the
host lowercased, an empty path as /, header names lowercased and headers
sorted by name.
pub fn matching(
script: Script,
compare: fn(
request.Request(BitArray),
request.Request(BitArray),
) -> Bool,
) -> Script
Replace the exact comparison. compare receives the expected and the
actual request as match_key forms them: redacted, normalised, without
ignored headers.
pub fn playback(
script: Script,
settings: config.Config,
) -> Result(http_gun.Client, http_gun.StartError)
Start an offline client that answers from script. The script is checked
first: statuses must be 200..599, bodies whole bytes, and the whole
script within 16 MiB.
Each call starts a client with its own cursor at the first exchange.
The immutable Script can be reused to start independent clients. Views
made from one client share its cursor; changing correlation does not create
another replay or affect matching. Stop each started client separately.
pub fn request(exchange: Exchange) -> request.Request(BitArray)
The expected request of an exchange, as stored.