jmap: sort Email/query with Comparator objects
Fastmail rejects the string form — IMAP SORT syntax, not JMAP —
with a bare invalidArguments.

Assisted-by: GLM-5.3 via pi
change nsmtozrutlqknutokmxmqurttkrmlsll
commit 3568760ed2b2b59cd7b7542f52c4ce91d39801cf
author Alpha Chen <alpha@kejadlen.dev>
date
parent qwvnouxv
diff --git a/src/jmap.rs b/src/jmap.rs
index f388f86..64c3cb3 100644
--- a/src/jmap.rs
+++ b/src/jmap.rs
@@ -757,7 +757,7 @@ impl Client {
                     "Email/query",
                     json!({"accountId": account_id,
                         "filter": {"inMailbox": inbox},
-                        "sort": ["receivedAt desc"],
+                        "sort": [{"property": "receivedAt", "isAscending": false}],
                         "position": position, "limit": PAGE}),
                 )
                 .await?;
diff --git a/tests/jmap.rs b/tests/jmap.rs
index 0710003..721b6d4 100644
--- a/tests/jmap.rs
+++ b/tests/jmap.rs
@@ -460,6 +460,12 @@ async fn api(world: axum::extract::State<World>, headers: HeaderMap, body: Strin
             "notFound": [], "list": mail.mailboxes.clone(),
         }),
         "Email/query" => {
+            // A sort entry is a Comparator object (RFC 8620 §5.5);
+            // servers answer a string one with invalidArguments.
+            if let Some(sort) = args["sort"].as_array() {
+                assert!(sort.iter().all(Value::is_object),
+                    "sort entries must be Comparator objects");
+            }
             let inbox = args["filter"]["inMailbox"].as_str().expect("an inMailbox");
             let position = args["position"].as_u64().unwrap_or_default() as usize;
             let limit = args["limit"].as_u64().unwrap_or_default() as usize;