Scheduled reports: a feature byte of their own
2026.10.6.2 kept scheduled reports under S in the fork's subspace, a byte scale-out storage (Sb, Sm, Sl, replica markers Sr + index) and the spam-rules updater (the Sr marker, Sf, Sn) already use. Nothing was overwritten (the keys differ in length), but a scan of Sr + id could read the others' keys, and one byte per feature is the rule. They move to G, unused anywhere. ensure_digest moves what 2026.10.6.2 wrote, once per process: what reads as a report from the Sr + 8-byte-id range, and the settings at Ss, deleting only those exact keys. The system test writes a legacy digest, legacy settings and the spam marker, and checks the first two moved and the marker didn't.
This commit is contained in:
1 parent
dba15be9f0
commit
3510b28ce1
2 files changed
+150
-3
No files matched your search
@@ -18,6 +18,10 @@ use registry::schema::{
|
||||
};
|
||||
use serde_json::{Value, json};
|
||||
use std::time::{Duration, Instant};
|
||||
use store::{
|
||||
SUBSPACE_INBUXA, ValueKey,
|
||||
write::{AnyClass, BatchBuilder, ValueClass},
|
||||
};
|
||||
|
||||
const USING: &[&str] = &[
|
||||
"urn:ietf:params:jmap:core",
|
||||
@@ -53,6 +57,80 @@ pub async fn test(test: &mut TestServer) {
|
||||
let admin = test.account("[email protected]");
|
||||
let me = "[email protected]";
|
||||
|
||||
// --- Moved from where 2026.10.6.2 kept them (`S`, shared with the spam
|
||||
// rules marker and replica markers), touching nothing else ---------------
|
||||
let any = |key: Vec<u8>| {
|
||||
ValueClass::Any(AnyClass {
|
||||
subspace: SUBSPACE_INBUXA,
|
||||
key,
|
||||
})
|
||||
};
|
||||
let legacy_digest = [b"Sr".as_slice(), &1u64.to_be_bytes()].concat();
|
||||
let mut batch = BatchBuilder::new();
|
||||
batch.set(
|
||||
any(legacy_digest.clone()),
|
||||
serde_json::to_vec(&json!({
|
||||
"id": 1, "name": "Weekly digest (moved)",
|
||||
"enabled": true,
|
||||
"builtIn": true,
|
||||
"sections": ["mailFlow", "queue", "spoofing", "tlsFailures", "deliverability",
|
||||
"security", "storage", "certificates"],
|
||||
"createdAt": 1790000000,
|
||||
"lastDue": 1790000000
|
||||
}))
|
||||
.unwrap(),
|
||||
);
|
||||
batch.set(
|
||||
any(b"Ss".to_vec()),
|
||||
serde_json::to_vec(&json!({"fromName": "Moved sender"})).unwrap(),
|
||||
);
|
||||
batch.set(any(b"Sr".to_vec()), b"3.0.2+2".to_vec());
|
||||
test.server.store().write(batch.build_all()).await.unwrap();
|
||||
let list = reports(admin).await;
|
||||
assert!(
|
||||
list.iter().any(|r| r["name"] == "Weekly digest (moved)"),
|
||||
"the digest wasn't moved: {list:?}"
|
||||
);
|
||||
let (_, response) = call(
|
||||
admin,
|
||||
"inbuxa:ScheduledReportSettings/get",
|
||||
json!({"ids": null}),
|
||||
)
|
||||
.await;
|
||||
assert_eq!(
|
||||
response["list"][0]["fromName"], "Moved sender",
|
||||
"{response}"
|
||||
);
|
||||
let raw = |key: Vec<u8>| {
|
||||
let store = test.server.store().clone();
|
||||
async move {
|
||||
store
|
||||
.get_value::<String>(ValueKey::from(any(key)))
|
||||
.await
|
||||
.unwrap()
|
||||
}
|
||||
};
|
||||
assert!(
|
||||
raw(legacy_digest).await.is_none(),
|
||||
"the old digest key is still there"
|
||||
);
|
||||
assert!(
|
||||
raw(b"Ss".to_vec()).await.is_none(),
|
||||
"the old settings key is still there"
|
||||
);
|
||||
assert_eq!(
|
||||
raw(b"Sr".to_vec()).await.as_deref(),
|
||||
Some("3.0.2+2"),
|
||||
"the spam rules marker was touched"
|
||||
);
|
||||
// Back to the default sender for what follows
|
||||
call(
|
||||
admin,
|
||||
"inbuxa:ScheduledReportSettings/set",
|
||||
json!({"update": {"singleton": {"fromName": ""}}}),
|
||||
)
|
||||
.await;
|
||||
|
||||
// --- The weekly digest every server has (RP-21) ------------------------
|
||||
let list = reports(admin).await;
|
||||
let digest = list
|
||||
|
||||
Reference in new issue
Block a user