Expand description
§alux-http-conformance
alux-http-conformance states one alux-http surface and the scenario
every interpretation of it must satisfy.
Two interpretations can be compared only when they compile the same declaration and answer the same requests. This crate provides that shared evidence: one declaration, compiled by the interpretation under test, and one set of exchanges describing the required methods, paths, media types, and statuses. The exchanges know nothing about the domain or any framework.
use alux_http::HttpProgramExt;
use alux_http_conformance::{Shop, ShopApiExt, expect};
use alux_http_direct::DirectHandlerImpl;
let api = DirectHandlerImpl::new(Shop);
let surface = api.compile_http(api.shop_api::<Shop>());
expect(&surface).await.unwrap();An interpretation that answers with a framework’s own response states an adapter for
AnswerAlg, which is the only place a framework is named.
§Opening and closing
The other half is for interpretations that serve rather than answer: what opening and closing one bound address must do. ExpectLifecycleExt states it against the real thing, over a socket. A server is opened, a client uses it, the server is closed, and another is opened at the same address. Opening the second one is the assertion, because it can only succeed if closing really released the address.
Three scenarios, which differ in what the close finds. expect_reopening closes with nothing in flight. expect_reopening_while_a_connection_is_held closes while a caller is still waiting on GET /slow, which takes longer to answer than any interpretation waits before closing anyway, so the connection outlives the close instead of the close waiting it out. expect_answering_what_is_in_flight closes while a caller is waiting on GET /pause, which takes less than any drain, so the close has time to finish it and the answer must reach that caller. expect_ending_what_outlives_the_drain closes while a caller is waiting on GET /slow, which takes more, so the caller must be left without an answer rather than handed one by a server that no longer exists.
The last two are the same statement from both sides, and each is run with the close and the caller’s read racing rather than sequenced, because sequencing them would state nothing: by the time a close has returned, whatever was going to happen already has.
Each scenario runs on a local task set of its own, because several interpretations serve from tasks that are not Send. That is the interpretation’s business rather than the caller’s, so a test states only the server and the route it serves:
use alux_http::HttpProgramExt;
use alux_http_conformance::{ExpectLifecycleExt, LifecycleApiExt, Shop};
use alux_http_salvo::{SalvoHandlerImpl, SalvoRoute, SalvoServer};
fn route() -> SalvoRoute {
let api = SalvoHandlerImpl::new(Shop);
api.compile_http(api.lifecycle_api::<Shop>())
}
#[tokio::test]
async fn reopens_the_address_it_closed() {
SalvoServer.expect_reopening(route).await.unwrap();
}
#[tokio::test]
async fn reopens_the_address_it_closed_while_a_connection_is_held() {
SalvoServer.expect_reopening_while_a_connection_is_held(route).await.unwrap();
}
#[tokio::test]
async fn answers_a_request_in_flight_when_it_closes() {
SalvoServer.expect_answering_what_is_in_flight(route).await.unwrap();
}
#[tokio::test]
async fn ends_a_request_that_outlives_the_drain_when_it_closes() {
SalvoServer.expect_ending_what_outlives_the_drain(route).await.unwrap();
}expect_closing and expect_ending are the two the scenarios build on, and either can be used alone: each acts on one open server and states that the call returned inside LIMIT. Which one a scenario reaches for is the point of the split, and expect_ending_what_outlives_the_drain needs the second: closing alone releases the address and leaves the drain running, so nothing would end the request.
measure_closing is the same shape as the last two scenarios, reporting what it saw instead of holding the server to it. It answers a Measured, which reads as a row:
alux-http-salvo | /pause | close returned 1.00s | answered 200 at 1.00s | reusable true
alux-http-salvo | /slow | close returned 5.00s | ended unanswered at 5.00s | reusable trueThe http-providers example depends on every interpretation, so it gathers all seven in one place, in benches/closing.rs. It is a benchmark rather than a test because it measures rather than states.
One declared HTTP surface, and the scenario every interpretation of it must satisfy.
An interpretation agreeing with another is only evidence when both were held to the same thing. What lives here is that thing: one declaration, compiled by whoever is under test, and one set of exchanges naming what answering it must produce. The scenario knows methods, paths, media types and statuses, and nothing of the domain or of any framework.
ExpectLifecycleExt states the other half for interpretations that serve rather than answer:
what opening and closing one bound address must do, and MeasureLifecycleExt what it costs.
Structs§
- Agent
- What a caller said about themselves in the headers they sent.
- Amount
- One reading, sent as a form.
- Averaged
- Several measurements of the same thing, taken together.
- Closing
- What is on the connection when a close begins, and how far the round waits for it.
- Exchange
- One exchange: a request, and what answering it must produce.
- Measured
- One measurement of what closing did, which reads as a row of the table it is gathered for.
- Session
- Who a caller says they are, sent as cookies.
- Shop
- The reference domain every interpretation is held to.
- Ticks
- A body the domain produces a piece at a time.
- Upload
- What a caller sent as parts, read the same way by every interpretation.
Enums§
- Handover
- What is on the connection while one address is handed from a server to the next.
- InFlight
- What became of a request that was in flight when its server was closed.
Constants§
- ANSWERS_
LATE - How long after a close begins the slow endpoint would answer.
- ANSWERS_
SOON - How long after a close begins the pausing endpoint answers.
- LABELS
- Every label the declared surface states, in declaration order.
- LIMIT
- Bounds how long closing may take before the scenario calls it a close that never returns.
- PAUSE
- How long the pausing endpoint takes to answer.
- SETTLE
- How long the scenario waits for a server to take a request it has been sent.
- SLOW
- How long the slow endpoint takes to answer.
Traits§
- Answer
Alg - Answers a stated request, however the interpretation under test answers one.
- Expect
Lifecycle Ext - Holds a concrete server to what opening and closing one bound address must do.
- Lifecycle
ApiExt - Declares the surface the lifecycle scenario serves.
- Measure
Lifecycle Ext - Measures the lifecycle of a concrete HTTP server.
- Multipart
ApiExt - Declares the parts surface, which every interpretation reading a body as parts compiles.
- ShopAlg
- Keeps whatever readings the domain has been told about.
- Shop
ApiExt - Declares the shared surface, which every interpretation compiles unchanged.
- Shop
Operation Ext - Derives the operations the shared surface exposes.
- SlowAlg
- Answers after taking some time, which is how a caller holds a request in flight.
- Slow
Operation Ext - Derives the one operation the slow surface exposes.
- Stream
ApiExt - Declares the streamed surface, which every interpretation carrying a produced body compiles.
- Ticks
Alg - Answers with a body it produces a piece at a time.
- Ticks
Operation Ext - Derives the one operation the streamed surface exposes.
- Upload
Alg - Reads a body a caller sent as parts.
- Upload
Operation Ext - Derives the one operation the parts surface exposes.
- WideAlg
- Reads however many arguments a caller states.
- Wide
ApiExt - Declares the widest endpoint the specification states, which every interpretation compiles.
- Wide
Operation Ext - Derives the one operation the widest surface exposes.
Functions§
- exchanges
- Every exchange the declared surface must satisfy.
- expect
- Holds one interpretation to every exchange the surface states.
- expect_
multipart - Holds one interpretation to every exchange the parts surface states.
- expect_
streaming - Holds one interpretation to every exchange the streamed surface states.
- multipart_
exchanges - Every exchange the parts surface must satisfy.
- stream_
exchanges - Every exchange the streamed surface must satisfy.
Type Aliases§
- Lifecycle
Error - Carries why a lifecycle scenario was not satisfied.