desk: pin the account-id decode check on Answer and Forks, and listing re-admission
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
30a319e5ee
commit
77f562a973
1 changed files with 50 additions and 17 deletions
|
|
@ -1071,6 +1071,20 @@ mod tests {
|
|||
assert!(matches!(read_by(&mind, DAN, 1), DeskResponse::NotStored(_)));
|
||||
}
|
||||
|
||||
/// Re-admitting the listing after unlisting is the same document: AlreadyAdmitted, and the fork stays hidden.
|
||||
#[test]
|
||||
fn admitting_the_listing_again_after_unlisting_does_not_relist() {
|
||||
let mut mind = desk(1);
|
||||
list_it(&mut mind, 1);
|
||||
let withdrawal = unlist(&mut mind, 1, 1);
|
||||
let before = head(&mind);
|
||||
let again = admit(&mut mind, vec![listing_doc(1)]);
|
||||
assert!(matches!(again, PipelineAdmissionOutcome::AlreadyAdmitted { .. }), "{again:?}");
|
||||
assert_eq!((head(&mind), seen_by(&mind, DAN)), (before, Vec::<String>::new()));
|
||||
relist(&mut mind, &withdrawal);
|
||||
assert_eq!(seen_by(&mind, DAN), [fork(1)]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn counts_and_cursor_cover_the_visible_set() {
|
||||
let mut mind = desk(5);
|
||||
|
|
@ -1149,32 +1163,51 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn a_decider_or_subject_outside_the_account_grammar_is_refused_at_decode() {
|
||||
let good = encode_desk_request("m", &DeskRequest::Fork { mind: slug(INSTANCE), subject: acct("a"), deciders: vec![acct("a")], fork_id: fork(1) }, None).unwrap();
|
||||
decode_desk_request(&good).unwrap();
|
||||
let bad_ids = ["@x:server", "meta@gamecult.org", "someone", "7", &acct("a").0.to_uppercase(), &format!("{{{}}}", acct("a").0), ""];
|
||||
for bad in bad_ids {
|
||||
for (field, edit) in [("subject", 0), ("deciders", 1)] {
|
||||
let mut value = serde_json::to_value(DeskRequest::Fork { mind: slug(INSTANCE), subject: acct("a"), deciders: vec![acct("a")], fork_id: fork(1) }).unwrap();
|
||||
if edit == 0 {
|
||||
value["Fork"]["subject"] = bad.into();
|
||||
} else {
|
||||
value["Fork"]["deciders"] = json!([bad]);
|
||||
let ask = ForkAnswerRequest {
|
||||
schema_version: RequestSchema::V0,
|
||||
fork_id: fork(1),
|
||||
options_hash: "h".into(),
|
||||
answer: Some(ForkAnswer { choice: Some("A".into()), note: None }),
|
||||
retracted: false,
|
||||
retracts: None,
|
||||
answered_at: "2026-09-16T11:00:00Z".into(),
|
||||
request_id: request_id(),
|
||||
};
|
||||
let (mind_slug, who) = (slug(INSTANCE), acct("a"));
|
||||
let requests = [
|
||||
DeskRequest::Answer { mind: mind_slug.clone(), subject: who.clone(), authority: DeskAuthority::Member, deciders: vec![who.clone()], request: ask },
|
||||
DeskRequest::Forks { mind: mind_slug.clone(), subject: who.clone(), deciders: vec![who.clone()], status: None, cursor: None, limit: ForkLimit::new(10).unwrap() },
|
||||
DeskRequest::Fork { mind: mind_slug, subject: who.clone(), deciders: vec![who.clone()], fork_id: fork(1) },
|
||||
];
|
||||
for request in &requests {
|
||||
let operation = request.operation();
|
||||
let variant = match operation {
|
||||
"answer" => "Answer",
|
||||
"forks" => "Forks",
|
||||
_ => "Fork",
|
||||
};
|
||||
let good = encode_desk_request_value(operation, &serde_json::to_value(request).unwrap());
|
||||
decode_desk_request(&good).unwrap_or_else(|failure| panic!("{operation}: {failure:?}"));
|
||||
let bad_ids = ["@x:server", "meta@gamecult.org", "someone", "7", &who.0.to_uppercase(), &format!("{{{}}}", who.0), ""];
|
||||
for bad in bad_ids {
|
||||
for field in ["subject", "deciders"] {
|
||||
let mut value = serde_json::to_value(request).unwrap();
|
||||
value[variant][field] = if field == "subject" { bad.into() } else { json!([bad]) };
|
||||
let failure = decode_desk_request(&encode_desk_request_value(operation, &value)).unwrap_err();
|
||||
assert_eq!(failure.code, "payload-not-a-request", "{operation} {field}: {bad:?}");
|
||||
assert!(failure.message.contains(field), "{operation}: {}", failure.message);
|
||||
assert!(bad.is_empty() || !failure.message.contains(bad), "the refusal quotes {bad:?}: {}", failure.message);
|
||||
}
|
||||
let sent = encode_desk_request_value(&value);
|
||||
let failure = decode_desk_request(&sent).unwrap_err();
|
||||
assert_eq!(failure.code, "payload-not-a-request", "{field}: {bad:?}");
|
||||
assert!(failure.message.contains(field), "{}", failure.message);
|
||||
assert!(bad.is_empty() || !failure.message.contains(bad), "the refusal quotes {bad:?}: {}", failure.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn encode_desk_request_value(value: &Value) -> cultnet_rs::CultNetMessage {
|
||||
fn encode_desk_request_value(operation: &str, value: &Value) -> cultnet_rs::CultNetMessage {
|
||||
use base64::Engine;
|
||||
cultnet_rs::CultNetMessage::OperationRequest {
|
||||
message_id: "m".into(),
|
||||
service_id: DESK_SERVICE_ID.into(),
|
||||
operation: "fork".into(),
|
||||
operation: operation.into(),
|
||||
payload_schema: DESK_REQUEST_SCHEMA.into(),
|
||||
payload_encoding: "messagepack-base64".into(),
|
||||
payload: base64::engine::general_purpose::STANDARD.encode(rmp_serde::to_vec_named(value).unwrap()),
|
||||
|
|
|
|||
Loading…
Reference in a new issue