Scheduled reports: a feature byte of their own
ci / fork-checks (pull_request) Skipped
ci / build (pull_request) Skipped
github/ci (branch) GitHub Actions
ci / github (pull_request) Successful in 9m6s

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:
jcoffey-dev committed 2026-10-06 23:05:14 -07:00
1 parent dba15be9f0
commit 3510b28ce1
2 files changed
+150 -3

No files matched your search

+78
View File
@@ -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