Skip to content

Commit 4549cda

Browse files
authored
Merge pull request #20 from YellowSnnowmann/feat/18-e1-conformance-suite
Add a behavioural conformance suite for MemoryProvider drivers (#18 §E1)
2 parents a39edc5 + 59178d2 commit 4549cda

7 files changed

Lines changed: 1097 additions & 2 deletions

File tree

Cargo.lock

Lines changed: 11 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[workspace]
2-
members = [".", "api", "core", "adapters/tinycortex", "adapters/remote"]
3-
default-members = [".", "api", "core", "adapters/tinycortex", "adapters/remote"]
2+
members = [".", "api", "core", "adapters/tinycortex", "adapters/remote", "conformance"]
3+
default-members = [".", "api", "core", "adapters/tinycortex", "adapters/remote", "conformance"]
44
# `vendor/` holds engine submodules (tinycortex, tinybus, tinyagents), each of
55
# which is its own workspace with its own lockfile. Same exclusion
66
# `vendor/tinycortex` uses for its own nested vendor directory.

conformance/Cargo.toml

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
[package]
2+
name = "tinymemory-conformance"
3+
publish = false
4+
version = "0.1.0"
5+
edition = "2021"
6+
rust-version = "1.96"
7+
license = "MIT"
8+
description = "Behavioural conformance suite every MemoryProvider driver must pass"
9+
repository = "https://github.com/tinyhumansai/tinymemory"
10+
11+
[dependencies]
12+
# The contract under test. This crate deliberately depends on NOTHING else of
13+
# substance: a conformance suite that pulled in an engine would be unable to
14+
# prove that a driver is interchangeable, because it would already have chosen
15+
# one. In particular it must not reach `tinymemory-core`, which links a bundled
16+
# SQLite and the embedded engine unconditionally (issue #18 §D).
17+
tinymemory-api = { path = "../api" }
18+
# `MemoryProvider` and its families are object-safe async traits.
19+
async-trait = "0.1"
20+
# `ExportRecord::payload` is a `serde_json::Value`, so the portability
21+
# assertions have to construct and compare one.
22+
serde_json = "1"
23+
# The reference driver maps a poisoned lock onto `MemoryError::Other`, which is
24+
# `#[from] anyhow::Error`.
25+
anyhow = "1"
26+
27+
[dev-dependencies]
28+
# The suite's own tests drive it against the reference drivers.
29+
tokio = { version = "1", features = ["macros", "rt-multi-thread"] }
30+
31+
[lints.rust]
32+
unsafe_code = "forbid"
33+
missing_docs = "warn"
34+
unreachable_pub = "warn"
35+
36+
[lints.clippy]
37+
all = { level = "warn", priority = -1 }
38+
unwrap_used = "warn"
39+
expect_used = "warn"

conformance/src/lib.rs

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
//! Behavioural conformance for `MemoryProvider` drivers.
2+
//!
3+
//! TinyMemory's premise is that an engine can be swapped without the host
4+
//! learning anything new. [`audit_provider`](tinymemory_api::provider::audit_provider)
5+
//! checks that a driver's advertised capabilities match its reachable
6+
//! accessors, which proves the *shape* is honest. Nothing checked that two
7+
//! drivers answer the same question the same way — and that is the claim the
8+
//! premise actually rests on.
9+
//!
10+
//! This crate is that check. Hand [`assert_provider`] any bound driver and it
11+
//! drives the contract: the mandatory three families, upsert semantics on
12+
//! `(namespace, key)`, namespace isolation, provenance preservation, recall
13+
//! limits, export pagination, and import round-tripping.
14+
//!
15+
//! ```no_run
16+
//! use std::sync::Arc;
17+
//! use tinymemory_conformance::{assert_provider, InMemoryProvider};
18+
//!
19+
//! # async fn run() {
20+
//! assert_provider(Arc::new(InMemoryProvider::new())).await;
21+
//! # }
22+
//! ```
23+
//!
24+
//! # What it deliberately does not depend on
25+
//!
26+
//! Only `tinymemory-api`. A conformance suite that pulled in an engine could
27+
//! not prove interchangeability, because it would already have chosen one — and
28+
//! reaching `tinymemory-core` would drag in a bundled SQLite and the embedded
29+
//! engine besides (issue #18 §D).
30+
//!
31+
//! # Provenance is the sharp one
32+
//!
33+
//! [`assert_taint_is_preserved`] is not a formality. A driver that reads back
34+
//! `Internal` for content stored as `ExternalSync` has laundered external
35+
//! content into internal-trust content, and every policy gate keyed on taint is
36+
//! then silently wrong. That failure is invisible until something acts on it.
37+
38+
#![forbid(unsafe_code)]
39+
#![warn(missing_docs)]
40+
41+
pub mod reference;
42+
pub mod suite;
43+
44+
pub use reference::{InMemoryProvider, REFERENCE_DRIVER_ID};
45+
pub use suite::{
46+
assert_awkward_content_round_trips, assert_capability_audit, assert_export_cursor_terminates,
47+
assert_export_import_round_trip, assert_forget_is_idempotent, assert_list_filters_narrow,
48+
assert_namespaces_are_isolated, assert_provider, assert_recall_respects_limit_and_namespace,
49+
assert_store_get_round_trip, assert_taint_is_preserved,
50+
assert_upsert_replaces_rather_than_duplicates,
51+
};

0 commit comments

Comments
 (0)