Explain this: the local model reads delivery failures, verdicts, logs and settings #56
@@ -36,7 +36,7 @@ use registry::{
|
|||||||
jmap::IntoValue,
|
jmap::IntoValue,
|
||||||
schema::{
|
schema::{
|
||||||
enums::SpamClassifyResult,
|
enums::SpamClassifyResult,
|
||||||
prelude::ObjectType,
|
prelude::{OBJ_SINGLETON, Object, ObjectType},
|
||||||
structs::{QueuedMessage, QueuedRecipient, RecipientStatus},
|
structs::{QueuedMessage, QueuedRecipient, RecipientStatus},
|
||||||
},
|
},
|
||||||
types::{EnumImpl, id::ObjectId},
|
types::{EnumImpl, id::ObjectId},
|
||||||
@@ -431,8 +431,13 @@ a negative one toward legitimate mail. The result follows the total against the
|
|||||||
let Ok(id) = Id::from_str(id) else {
|
let Ok(id) = Id::from_str(id) else {
|
||||||
return Ok(Err(not_found()));
|
return Ok(Err(not_found()));
|
||||||
};
|
};
|
||||||
let Some(stored) = server.registry().get(ObjectId::new(object_type, id)).await? else {
|
// A singleton never saved holds its defaults, as its /get shows it
|
||||||
return Ok(Err(not_found()));
|
let stored = match server.registry().get(ObjectId::new(object_type, id)).await? {
|
||||||
|
Some(stored) => stored,
|
||||||
|
None if id.is_singleton() && object_type.flags() & OBJ_SINGLETON != 0 => {
|
||||||
|
Object::from(object_type)
|
||||||
|
}
|
||||||
|
None => return Ok(Err(not_found())),
|
||||||
};
|
};
|
||||||
let stored = serde_json::to_value(stored.into_value()).unwrap_or_default();
|
let stored = serde_json::to_value(stored.into_value()).unwrap_or_default();
|
||||||
let current = stored.get(property.as_str()).cloned().unwrap_or(serde_json::Value::Null);
|
let current = stored.get(property.as_str()).cloned().unwrap_or(serde_json::Value::Null);
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ use registry::{
|
|||||||
Role,
|
Role,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
types::EnumImpl,
|
types::{EnumImpl, id::ObjectId},
|
||||||
};
|
};
|
||||||
use serde_json::{Value, json};
|
use serde_json::{Value, json};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
@@ -95,6 +95,29 @@ pub async fn test(test: &mut TestServer) {
|
|||||||
assert!(user.contains("Current value: true"), "{user}");
|
assert!(user.contains("Current value: true"), "{user}");
|
||||||
assert!(user.contains("-----BEGIN DETAILS "), "{user}");
|
assert!(user.contains("-----BEGIN DETAILS "), "{user}");
|
||||||
|
|
||||||
|
// A singleton never saved is explained with its defaults, as /get shows it
|
||||||
|
let spam_settings = ObjectId::new(ObjectType::SpamSettings, Id::singleton());
|
||||||
|
assert!(
|
||||||
|
test.server
|
||||||
|
.registry()
|
||||||
|
.get(spam_settings)
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
.is_none(),
|
||||||
|
"x:SpamSettings is stored; pick a singleton the suite never saves"
|
||||||
|
);
|
||||||
|
stub.set(Mode::Answer("Mail scoring this much is spam.".into()));
|
||||||
|
let (created, failed) = admin
|
||||||
|
.explain(json!({"@type": "Setting", "object": "x:SpamSettings",
|
||||||
|
"id": "singleton", "property": "scoreSpam"}))
|
||||||
|
.await;
|
||||||
|
created.unwrap_or_else(|| panic!("unsaved singleton: {failed}"));
|
||||||
|
let (_, user) = messages(&stub.last().1);
|
||||||
|
assert!(
|
||||||
|
user.contains("Current value: 5"),
|
||||||
|
"unsaved singleton: {user}"
|
||||||
|
);
|
||||||
|
|
||||||
// Acceptance test 7: a secret setting is refused, not masked (EX-9)
|
// Acceptance test 7: a secret setting is refused, not masked (EX-9)
|
||||||
let before = stub.count();
|
let before = stub.count();
|
||||||
for (object, property) in [("x:AiModel", "httpAuth"), ("x:AcmeProvider", "accountKey")] {
|
for (object, property) in [("x:AiModel", "httpAuth"), ("x:AcmeProvider", "accountKey")] {
|
||||||
|
|||||||
Reference in New Issue
Block a user