armdb 0.11.2

sharded bitcask key-value storage optimized for NVMe
Documentation
//! The encrypted-participant rejection, which needs no fault injection.
//!
//! Kept apart from `tx_encrypted.rs` so it runs under plain `--features
//! encryption`: that file also needs `test-faults`, and a test only reachable
//! with two features at once is a test nobody runs.
#![cfg(feature = "encryption")]

use std::sync::Arc;

use armdb::armour::Db;
use armdb::{CollectionMeta, Config, NoHook, RapiraCodec, TtlHook, TxConfig, TypedMap};
use armour_core::GetType;
use rapira::Rapira;

type Id = [u8; 8];

fn id(n: u64) -> Id {
    n.to_be_bytes()
}

#[derive(Clone, Debug, PartialEq, Eq, GetType, Rapira)]
struct Secret {
    text: String,
}
impl CollectionMeta for Secret {
    type SelfId = Id;
    const NAME: &'static str = "secrets";
}

const DATA_KEY: [u8; 32] = [0x11; 32];

fn open_secrets(db: &Arc<Db>) -> Arc<TypedMap<Id, Secret, RapiraCodec, TtlHook<Secret, NoHook>>> {
    let mut cfg = Config::test();
    cfg.encryption_key = Some(DATA_KEY);
    db.open_typed_map::<Secret, RapiraCodec, _>(cfg, NoHook, &[])
        .unwrap()
}

#[test]
fn tx_encrypted_collection_requires_an_encrypted_log() {
    let dir = tempfile::tempdir().unwrap();
    let db = Arc::new(Db::open_with_tx_config(dir.path(), TxConfig::default()).unwrap());
    let secrets = open_secrets(&db);

    let err = db
        .tx()
        .put(
            &secrets,
            &id(1),
            &Secret {
                text: "hello".into(),
            },
        )
        .unwrap()
        .commit()
        .unwrap_err();
    assert!(matches!(err, armdb::DbError::Config(_)), "{err}");
    assert!(secrets.get(&id(1)).is_none(), "nothing was written");
}

const LOG_KEY: [u8; 32] = [0x22; 32];

/// The mirror of the test above: with a log key the same transaction commits.
/// Opened through the builder, because that is the only way to combine a
/// `TxConfig` with anything else — and the path a consumer's benchmark and test
/// harness now take.
#[test]
fn the_builder_carries_the_log_key_into_the_transaction_log() {
    let dir = tempfile::tempdir().unwrap();
    let db = Arc::new(
        Db::builder(dir.path())
            .for_tests()
            .tx_config(TxConfig {
                log_encryption_key: Some(LOG_KEY),
                ..TxConfig::default()
            })
            .open()
            .unwrap(),
    );
    let secrets = open_secrets(&db);

    db.tx()
        .put(
            &secrets,
            &id(2),
            &Secret {
                text: "world".into(),
            },
        )
        .unwrap()
        .commit()
        .expect("an encrypted participant commits once the log has a key");

    assert_eq!(
        secrets.get(&id(2)).map(|s| s.text.clone()),
        Some("world".to_string())
    );
}