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.
Slices (0.3.4, spec-0.4.0). Fixtures::slices holds receipts whose lineage cites
slices: their descriptors, the citing receipts and every member, the manifests by entry,
and the key documents of their signers. A resolver that supplies manifests
(Resolver::manifest) loads them too and is held to
check_slices, which Kit::check and Kit::run include once
Kit::slices says so:
- each citing receipt passes step 7.5 through the resolver: it recomputes, or ends at
E_EVAL_UNAVAILABLE; never another code; verifyasks, per slice entry, for the manifest once and then for each member in the manifest’s order; a probe whose manifest is not loaded stops at it, and one whose manifest names a member that is not loaded stops at that member, bothE_INPUT_UNAVAILABLE;manifestreturns exactly the bytes loaded, andNonefor entries it was not given (unknown, malformed, a receipt hash);- a lineage the resolver reports with an evaluation (
Resolver::evaluation) is made ofinputsentries, and those the receipt does not list are ones the resolver can back with a receipt or a manifest.
The descriptors of the slice fixtures are formula descriptors with signature.order:
read them with crate::FunctionDescriptor (a Descriptor refuses order). A resolver
loaded as for 0.3.3, without the slice fixtures, passes the kit as before as long as
slices is not set.
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.4.0; the files of
the 0.3.3 fixtures are unchanged since spec-0.2.1, the slice fixtures are the
slice-lineage receipt vectors of spec-0.4.0), 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, a convert receipt whose first input is not loaded, and two
receipts citing a slice whose manifest, or one of whose members, 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,slices(orsetup, if the embedded fixtures could not be read, which the crate’s tests rule out). - Slice
Fixtures - The slice fixtures (0.3.4,
spec-0.4.0): receipts whose lineage cites slices, from the specification’s slice-lineage receipt vectors. All bytes are canonical JSON (JCS). A resolver that supplies manifests loads the descriptors under theirfn_hash, the receipts (citing receipts and members) under their hashes and the manifests under their entries. - 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.