bucketwarden-server 0.1.0

BucketWarden storage server runtime.
Documentation
use super::*;

#[test]
fn runtime_inventory_query_resumes_from_continuation_token() {
    let mut runtime = runtime();
    runtime.set_clock_epoch_seconds(169);
    runtime.create_local_user("root");
    runtime
        .assign_operator_role("root", "root", OperatorRole::ClusterAdmin, "*")
        .expect("cluster admin");
    let alice_tenant = runtime.principal_tenant_id("alice");
    runtime
        .create_bucket("alice", "archive-001")
        .expect("alice bucket");

    for (key, body) in [
        ("records/a.json", br#"{"v":1}"#.as_slice()),
        ("records/b.json", br#"{"v":2}"#.as_slice()),
        ("records/c.json", br#"{"v":3}"#.as_slice()),
    ] {
        runtime
            .put_object(
                "alice",
                PutObjectRequest {
                    bucket: "archive-001".to_string(),
                    key: key.to_string(),
                    body: body.to_vec(),
                    metadata: ObjectMetadata::default(),
                },
                ObjectLock::none(),
            )
            .expect("put runtime object");
    }

    let first_page = runtime
        .runtime_inventory_query(
            "root",
            RuntimeInventoryRequest {
                tenant_prefix: Some(alice_tenant.clone()),
                bucket_prefix: Some("archive-".to_string()),
                key_prefix: Some("records/".to_string()),
                include_delete_markers: false,
                include_noncurrent_versions: true,
                max_entries: Some(2),
                continuation_token: None,
            },
        )
        .expect("first page");
    let continuation_token = first_page
        .next_continuation_token
        .clone()
        .expect("continuation token");
    assert_eq!(first_page.returned_count, 2);
    assert!(first_page.truncated);

    let second_page = runtime
        .runtime_inventory_query(
            "root",
            RuntimeInventoryRequest {
                tenant_prefix: Some(alice_tenant),
                bucket_prefix: Some("archive-".to_string()),
                key_prefix: Some("records/".to_string()),
                include_delete_markers: false,
                include_noncurrent_versions: true,
                max_entries: Some(2),
                continuation_token: Some(continuation_token),
            },
        )
        .expect("second page");

    assert_eq!(second_page.returned_count, 1);
    assert!(!second_page.truncated);
    assert_eq!(second_page.next_continuation_token, None);

    let first_keys = first_page
        .entries
        .iter()
        .map(|entry| entry.key.as_str())
        .collect::<Vec<_>>();
    let second_keys = second_page
        .entries
        .iter()
        .map(|entry| entry.key.as_str())
        .collect::<Vec<_>>();
    assert_eq!(first_keys, vec!["records/a.json", "records/b.json"]);
    assert_eq!(second_keys, vec!["records/c.json"]);
}

#[test]
fn runtime_inventory_query_rejects_unknown_continuation_token() {
    let mut runtime = runtime();
    runtime.create_local_user("root");
    runtime
        .assign_operator_role("root", "root", OperatorRole::ClusterAdmin, "*")
        .expect("cluster admin");
    runtime
        .create_bucket("alice", "archive-001")
        .expect("alice bucket");
    runtime
        .put_object(
            "alice",
            PutObjectRequest {
                bucket: "archive-001".to_string(),
                key: "records/a.json".to_string(),
                body: br#"{"v":1}"#.to_vec(),
                metadata: ObjectMetadata::default(),
            },
            ObjectLock::none(),
        )
        .expect("put runtime object");

    let error = runtime
        .runtime_inventory_query(
            "root",
            RuntimeInventoryRequest {
                tenant_prefix: None,
                bucket_prefix: Some("archive-".to_string()),
                key_prefix: Some("records/".to_string()),
                include_delete_markers: false,
                include_noncurrent_versions: true,
                max_entries: Some(1),
                continuation_token: Some(
                    r#"{"tenant_id":"tenant-missing","bucket":"archive-001","key":"records/a.json","version_id":"v999"}"#
                        .to_string(),
                ),
            },
        )
        .expect_err("invalid token");

    assert!(matches!(
        error,
        RuntimeError::InvalidListParameter { ref name, .. } if name == "continuation-token"
    ));
}