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"
));
}