Expand description
A conformance kit for Resolver implementations (feature testkit).
A resolver is read-only, so the kit never writes through it. Instead it hands out
Fixtures (key documents, descriptors, receipts, and the versions of stored cells), the
caller loads them into the resolver under test however that resolver is filled (maps, a
database, files), and the kit then verifies receipts through it and looks things up in
it. Each check returns a Report of what failed; Kit::check runs all of them.
use datumesh_core::testkit::{Fixtures, Kit};
use datumesh_core::MapResolver;
fn load(fx: &Fixtures) -> MapResolver {
let mut m = MapResolver::new();
for (fn_hash, bytes) in &fx.descriptors {
m.insert_descriptor(fn_hash.as_str(), bytes.as_slice());
}
for (hash, bytes) in &fx.receipts {
m.insert_receipt(hash.as_str(), bytes.as_slice());
}
// MapResolver keeps one value per cell: the versions come in tx order, so the
// latest one stays. A time-aware resolver loads them all.
for v in &fx.versions {
m.insert_value(v.cell_id.as_str(), v.value.clone());
}
m
}
if let Err(report) = Kit::new().run(load) {
panic!("{report}");
}The checks:
check_lazy_calls: through aRecordingwrapper,verifyasks the resolver only for what step 7 reaches: the descriptor once; the inputs in order up to the first that fails;evaluateonce, after 7.1-7.5, with the receipt’s cell andasof; and nothing at all when the receipt fails before step 7.check_unavailable: an unknownfn_hashisE_FN_UNAVAILABLE, an input receipt it does not have isE_INPUT_UNAVAILABLE(first or second input), a function it cannot evaluate isE_EVAL_UNAVAILABLE(ok, levelsigned), and looking up unknown or malformed hashes (empty, uppercase, too long, non-hex, NUL, SQL or path syntax, non-ASCII) givesNonewithout panicking.check_bytes:descriptorandreceiptreturn exactly the bytes loaded (all fixture bytes are canonical JSON, so a resolver that canonicalises what it is given still passes).check_asof:evaluatereceives the receipt’sasof, and stored cells recompute. WithKit::time_aware, a cell with versions at several times gives the value of its latest version that answers as of the requested time under the kit’sAnswerRule(Nonebefore the first), both throughverifyand through directevaluatecalls.
The answer rule (Kit::answer_rule) says when a version of a stored cell answers as of
a time A:
AnswerRule::TxAndAsof, the default: itstx <= Aand its receipt’sasof <= A. This is what SPEC 5.5 step 5 (D31 revised) requires of anokresponse to a request withasofA: the receipt’sasof, and so the version answering, at or beforeA. A version is not an answer between itstxand the time it became known.AnswerRule::TxOnly: itstx <= A, whenever its receipt was made (the kit’s only rule before 0.3.3). A time-aware resolver with this rule must say so.
In the fixtures, the EUR/CHF cell’s first version has tx 2024-03-15T16:00 and receipt
asof 2024-03-16T09:00: under TxAndAsof it answers from 2024-03-16T09:00 on (and nothing
does before); under TxOnly, from 2024-03-15T16:00 on. The rule matters only to a resolver
declared time-aware. Receipts recompute through verify the same under both, since each
receipt is verified as of its own asof.
The kit expects a resolver to evaluate only what the fixtures give it: the versions of
stored cells. Computed cells (convert, language expr) are not evaluable, so receipts
of them end at E_EVAL_UNAVAILABLE. A panic in the resolver propagates to the caller.
The fixtures come from the specification’s vectors (revision spec-0.3.0; the files
used are unchanged since spec-0.2.1), in canonical form, plus receipts signed for the
kit with the vectors’ seeds (feature sign): a second
fx_rate cell (EUR/CHF) with versions at two times, and a convert receipt whose first
input is not loaded. They are embedded in the crate (src/testkit/data.rs), generated and
checked against spec/ by the crate’s test tests/testkit_data.rs.
Structs§
- Fixtures
- What the resolver under test is loaded with. All bytes are canonical JSON (JCS).
- Kit
- The resolver test kit: the fixtures and the checks. See the module documentation.
- Recording
- A
Resolverthat forwards to another and records each call, in order. - Report
- The failures of a check, one line each:
<check>: <case>: <what>, where<check>islazy_calls,unavailable,bytes,asof(orsetup, if the embedded fixtures could not be read, which the crate’s tests rule out). - Version
- A version of a stored cell: from
txon, the cell’s value is that of the receipt. When it answers a request as of some time is the kit’sAnswerRule; the receipt’sasofis that of receiptreceipt_hash.
Enums§
- Answer
Rule - When a version of a stored cell answers as of a time
A, for a resolver declared time-aware (Kit::time_aware). Set withKit::answer_rule; the default isTxAndAsof. Among the versions that answer, the one with the latesttxgives the value; when none does,evaluategivesNone. - Call
- A call made to a
Resolver, asRecordingrecords it.