Query paging for masked email and archived items #297
No files matched your search
@@ -291,7 +291,7 @@ pub(crate) async fn query(mut req: RegistryQueryResponse<'_>) -> trc::Result<Que
|
||||
_ => false,
|
||||
})?;
|
||||
req.request
|
||||
.extract_parameters(req.server.core.jmap.query_max_results, Some(Property::Id))?;
|
||||
.extract_parameters(req.server.core.jmap.query_max_results, None)?;
|
||||
|
||||
// ME-19: one account's masks, or every mask for a server-level
|
||||
// administrator who asks for no account in particular
|
||||
|
||||
@@ -180,8 +180,10 @@ pub(crate) async fn query(mut req: RegistryQueryResponse<'_>) -> trc::Result<Que
|
||||
(Property::AccountId, _, _) => true,
|
||||
_ => false,
|
||||
})?;
|
||||
// `None`: the items are filtered and sorted here, so position, anchor
|
||||
// and limit stay on the request for the response builder
|
||||
req.request
|
||||
.extract_parameters(req.server.core.jmap.query_max_results, Some(Property::Id))?;
|
||||
.extract_parameters(req.server.core.jmap.query_max_results, None)?;
|
||||
|
||||
let mut items = undelete::records::of_account(
|
||||
&req.server.core.storage.data,
|
||||
|
||||
@@ -492,6 +492,58 @@ pub async fn masked_email_compat() {
|
||||
}
|
||||
}
|
||||
|
||||
/// `cargo test -p tests masked_email_query_pagination -- --ignored`.
|
||||
#[ignore]
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
pub async fn masked_email_query_pagination() {
|
||||
let test = TestServerBuilder::new("masked_email_query_pagination")
|
||||
.await
|
||||
.with_default_listeners()
|
||||
.await
|
||||
.build()
|
||||
.await;
|
||||
let admin = test.create_admin_account("[email protected]").await;
|
||||
admin.find_or_create_domain("example.org").await;
|
||||
let alice = admin
|
||||
.create_user_account("[email protected]", SECRET_ALICE, "Alice", &[], vec![])
|
||||
.await;
|
||||
let mut ids = Vec::new();
|
||||
for prefix in ["first", "second", "third"] {
|
||||
ids.push(alice.x_create(json!({"emailPrefix": prefix})).await);
|
||||
}
|
||||
ids.sort_unstable();
|
||||
|
||||
let mut seen = Vec::new();
|
||||
for (position, id) in ids.iter().enumerate() {
|
||||
// calculateTotal must be false to exercise parameter extraction's
|
||||
// storage-level pagination path.
|
||||
let page = alice
|
||||
.registry_query_paginated(
|
||||
ObjectType::MaskedEmail,
|
||||
"id",
|
||||
true,
|
||||
Some(position as i32),
|
||||
Some(1),
|
||||
None,
|
||||
None,
|
||||
false,
|
||||
)
|
||||
.await;
|
||||
let page_ids = page.object_ids().collect::<Vec<_>>();
|
||||
assert!(
|
||||
page_ids.iter().all(|id| !seen.contains(id)),
|
||||
"a previous page's result was repeated"
|
||||
);
|
||||
assert_eq!(page_ids, vec![*id], "page at position {position}");
|
||||
assert_eq!(page.method_response()["position"], json!(position));
|
||||
seen.extend(page_ids);
|
||||
}
|
||||
|
||||
if test.is_reset() {
|
||||
test.temp_dir.delete();
|
||||
}
|
||||
}
|
||||
|
||||
/// Runs the masked email tests alone:
|
||||
/// `cargo test -p tests masked_email_tests -- --ignored`.
|
||||
#[ignore]
|
||||
|
||||
@@ -273,6 +273,44 @@ pub async fn test(test: &mut TestServer) {
|
||||
.await;
|
||||
assert_eq!(found.len(), 1, "query text");
|
||||
|
||||
// /query pages: each position gives the next item, never the first page
|
||||
// again (calculateTotal off, the path that dropped them)
|
||||
let all = user
|
||||
.registry_query_paginated(
|
||||
ObjectType::ArchivedItem,
|
||||
"id",
|
||||
false,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
false,
|
||||
)
|
||||
.await
|
||||
.object_ids()
|
||||
.collect::<Vec<_>>();
|
||||
assert!(all.len() >= 3, "query pages: {all:?}");
|
||||
for (position, id) in all.iter().enumerate() {
|
||||
let page = user
|
||||
.registry_query_paginated(
|
||||
ObjectType::ArchivedItem,
|
||||
"id",
|
||||
false,
|
||||
Some(position as i32),
|
||||
Some(1),
|
||||
None,
|
||||
None,
|
||||
false,
|
||||
)
|
||||
.await;
|
||||
assert_eq!(
|
||||
page.object_ids().collect::<Vec<_>>(),
|
||||
vec![*id],
|
||||
"query page at position {position}"
|
||||
);
|
||||
assert_eq!(page.method_response()["position"], json!(position));
|
||||
}
|
||||
|
||||
// Acceptance test 15: nobody else sees the archive (UD-7)
|
||||
let other = admin
|
||||
.create_user_account("[email protected]", SECRET, "Other", &[], vec![])
|
||||
@@ -630,6 +668,13 @@ pub async fn undelete_tests() {
|
||||
..Default::default()
|
||||
})
|
||||
.await
|
||||
// test 14 shares with an account it has nothing else to do with, as
|
||||
// system_tests allows
|
||||
.with_object(registry::schema::structs::Sharing {
|
||||
allow_directory_queries: true,
|
||||
..Default::default()
|
||||
})
|
||||
.await
|
||||
.build()
|
||||
.await;
|
||||
let admin = test.create_admin_account("[email protected]").await;
|
||||
|
||||
Reference in new issue
Block a user