acyclic-fs 0.1.5

Rust SDK for versioned workspaces and embedded native mounts
Documentation
use super::*;

fn root() -> GenerationRoot {
    GenerationRoot {
        volume_id: VolumeId::from_bytes([1; 16]),
        root_file_id: FileId::from_bytes([2; 16]),
        file_table: ObjectId {
            kind: ObjectKind::FileTablePage,
            digest: Digest::from_bytes([3; 32]),
        },
        parents: vec![GenerationId::new(Digest::from_bytes([4; 32]))],
        required_features: 0,
    }
}

#[test]
fn generation_root_round_trip_and_identity_are_locked() -> Result<(), Box<dyn std::error::Error>> {
    let value = root();
    let encoded = encode_generation_root(&value)?;
    assert_eq!(
        generation_root_parent_count(&encoded, DecodeLimits::default())?,
        1
    );
    assert!(u64::try_from(encoded.len())? <= MAXIMUM_GENERATION_ROOT_BYTES);
    assert_eq!(
        decode_generation_root(&encoded, DecodeLimits::default())?,
        value
    );
    assert_eq!(
        hex::encode(encoded),
        "414359465347454e0100010101010101010101010101010101010202020202020202020202020202020203030303030303030303030303030303030303030303030303030303030303030100000004040404040404040404040404040404040404040404040404040404040404040000000000000000"
    );
    Ok(())
}

#[test]
fn duplicate_merge_parents_and_unknown_features_fail_closed()
-> Result<(), Box<dyn std::error::Error>> {
    let mut value = root();
    value
        .parents
        .push(GenerationId::new(Digest::from_bytes([5; 32])));
    assert_eq!(
        u64::try_from(encode_generation_root(&value)?.len())?,
        MAXIMUM_GENERATION_ROOT_BYTES
    );
    value.parents[1] = value.parents[0];
    assert!(encode_generation_root(&value).is_err());
    value.parents.pop();
    value.required_features = 1;
    assert!(encode_generation_root(&value).is_err());
    Ok(())
}

#[test]
fn every_generation_invariant_fails_closed_on_encode_and_decode()
-> Result<(), Box<dyn std::error::Error>> {
    let mut wrong_kind = root();
    wrong_kind.file_table.kind = ObjectKind::Blob;
    assert!(matches!(
        wrong_kind.validate(),
        Err(CanonicalDecodeError::Invariant(_))
    ));
    assert!(encode_generation_root(&wrong_kind).is_err());

    let mut too_many = root();
    too_many.parents = vec![
        GenerationId::new(Digest::from_bytes([4; 32])),
        GenerationId::new(Digest::from_bytes([5; 32])),
        GenerationId::new(Digest::from_bytes([6; 32])),
    ];
    assert!(matches!(
        too_many.validate(),
        Err(CanonicalDecodeError::Invariant(_))
    ));

    let mut encoded = encode_generation_root(&root())?;
    let parent_count_offset = 8 + 2 + 16 + 16 + 32;
    encoded[parent_count_offset..parent_count_offset + 4].copy_from_slice(&3_u32.to_le_bytes());
    assert!(matches!(
        decode_generation_root(&encoded, DecodeLimits::default()),
        Err(CanonicalDecodeError::FieldTooLarge {
            observed: 3,
            maximum: 2
        })
    ));
    assert!(matches!(
        generation_root_parent_count(&encoded, DecodeLimits::default()),
        Err(CanonicalDecodeError::FieldTooLarge {
            observed: 3,
            maximum: 2
        })
    ));
    Ok(())
}

#[test]
fn generation_decoder_revalidates_duplicate_parents_and_features()
-> Result<(), Box<dyn std::error::Error>> {
    let mut two_parents = root();
    two_parents
        .parents
        .push(GenerationId::new(Digest::from_bytes([5; 32])));
    let mut encoded = encode_generation_root(&two_parents)?;
    let parents_offset = 8 + 2 + 16 + 16 + 32 + 4;
    let first_parent = encoded[parents_offset..parents_offset + 32].to_vec();
    encoded[parents_offset + 32..parents_offset + 64].copy_from_slice(&first_parent);
    assert!(matches!(
        decode_generation_root(&encoded, DecodeLimits::default()),
        Err(CanonicalDecodeError::Invariant(_))
    ));

    let mut unsupported = encode_generation_root(&root())?;
    let features_offset = unsupported.len() - 8;
    unsupported[features_offset..].copy_from_slice(&1_u64.to_le_bytes());
    assert!(matches!(
        decode_generation_root(&unsupported, DecodeLimits::default()),
        Err(CanonicalDecodeError::Invariant(_))
    ));

    let value = root();
    let (object, generation) = generation_root_id(&value)?;
    assert_eq!(object.kind, ObjectKind::GenerationRoot);
    assert_eq!(generation.digest(), object.digest);
    assert_eq!(generation_root_id(&value)?, (object, generation));
    assert!(generation_root_id(&too_many_parent_root()).is_err());

    let mut trailing = encode_generation_root(&value)?;
    trailing.push(0);
    assert!(decode_generation_root(&trailing, DecodeLimits::default()).is_err());
    let mut wrong_domain = encode_generation_root(&value)?;
    wrong_domain[0] ^= 1;
    assert!(decode_generation_root(&wrong_domain, DecodeLimits::default()).is_err());
    Ok(())
}

fn too_many_parent_root() -> GenerationRoot {
    let mut value = root();
    value.parents.extend([
        GenerationId::new(Digest::from_bytes([5; 32])),
        GenerationId::new(Digest::from_bytes([6; 32])),
    ]);
    value
}