Done the way the owner asked and the spike mapped: the crate and the manifests renamed, then what the build reported fixed. Layers: 138 "proto unresolved" errors across 169 files; cargo fmt reordering imports; six fixture paths built as strings in brokerd's and bxctl's tests, which the compiler cannot see, and comments naming crates/proto/; the crate-dependency check and its self-test. One more the spike did not reach: a `\nproto =` key inside a printf format in the self-test, which a word-boundary replacement skips because `n` and `p` are both word characters. make gate: gate: ok, 944 tests; no new entry in /tmp. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
119 lines
3.2 KiB
Rust
119 lines
3.2 KiB
Rust
//! An answer from Mattermost records who answered and in which post, approved or refused; the
|
|
//! table says whether an approval is still pending (M4b spec, section 5). Do not edit.
|
|
|
|
#[path = "support/build.rs"]
|
|
mod build;
|
|
#[path = "support/rig.rs"]
|
|
mod rig;
|
|
#[path = "support/sink.rs"]
|
|
mod sink;
|
|
#[path = "support/tmp.rs"]
|
|
mod tmp;
|
|
|
|
use brokerd::approvals::Table;
|
|
use brokerd::ledger::{Answer, Decided, Grants, Ledger};
|
|
use brokerd::policy::Ask;
|
|
use build::{grant, now, read, set};
|
|
use bxcore::{ApprovalAnswer, AuditEvent, CallId, DataClass, Mode, PendingApproval, SessionId};
|
|
use rig::Rig;
|
|
|
|
const KYLE: &str = "k0000000000000000000000000";
|
|
const POST: &str = "c0000000000000000000000001";
|
|
|
|
fn asking() -> Grants {
|
|
Ok(set(vec![grant("n", "read_file", Mode::Ask).paths(&["/n"])]))
|
|
}
|
|
|
|
fn pending(ledger: &Ledger) -> Ask {
|
|
match ledger.decide(read("/n/deep/a"), &asking(), now()) {
|
|
Decided::Ask { ask, seq: 0, .. } => ask,
|
|
other => panic!("{other:?}"),
|
|
}
|
|
}
|
|
|
|
/// The one `Approval` record's (answer, by, post, reason).
|
|
fn recorded(
|
|
rig: &Rig,
|
|
) -> (
|
|
ApprovalAnswer,
|
|
Option<String>,
|
|
Option<String>,
|
|
Option<String>,
|
|
) {
|
|
let events = rig.events();
|
|
match &events[1] {
|
|
AuditEvent::Approval {
|
|
answer,
|
|
by,
|
|
post,
|
|
reason,
|
|
..
|
|
} => (*answer, by.clone(), post.clone(), reason.clone()),
|
|
other => panic!("{other:?}"),
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn an_approval_from_mattermost_names_the_user_and_the_post() {
|
|
let rig = Rig::new("post-approve");
|
|
let ledger = rig.ledger();
|
|
let answer = Answer::Approved {
|
|
by: Some(KYLE.to_string()),
|
|
post: Some(POST.to_string()),
|
|
};
|
|
ledger.answer(pending(&ledger), 0, answer, &asking(), now());
|
|
assert_eq!(
|
|
recorded(&rig),
|
|
(
|
|
ApprovalAnswer::Approved,
|
|
Some(KYLE.to_string()),
|
|
Some(POST.to_string()),
|
|
None
|
|
)
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn a_refusal_from_mattermost_names_them_and_keeps_the_reason() {
|
|
let rig = Rig::new("post-refuse");
|
|
let ledger = rig.ledger();
|
|
let answer = Answer::Refused {
|
|
by: Some(KYLE.to_string()),
|
|
post: Some(POST.to_string()),
|
|
reason: Some("not on a Friday".to_string()),
|
|
};
|
|
ledger.answer(pending(&ledger), 0, answer, &asking(), now());
|
|
assert_eq!(
|
|
recorded(&rig),
|
|
(
|
|
ApprovalAnswer::Refused,
|
|
Some(KYLE.to_string()),
|
|
Some(POST.to_string()),
|
|
Some("not on a Friday".to_string())
|
|
)
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn the_table_says_what_is_pending() {
|
|
let rig = Rig::new("post-table");
|
|
let ledger = rig.ledger();
|
|
let table = Table::new();
|
|
assert!(!table.contains(0));
|
|
let info = PendingApproval {
|
|
approval: 0,
|
|
session: SessionId::new("s1").unwrap(),
|
|
call: CallId(1),
|
|
tool: "read_file".to_string(),
|
|
arguments: "{}".to_string(),
|
|
grant: "n".to_string(),
|
|
taint: DataClass::Private,
|
|
created: now(),
|
|
expires: now(),
|
|
};
|
|
let _verdict = table.insert(info, pending(&ledger));
|
|
assert!(table.contains(0) && !table.contains(1));
|
|
assert!(table.take(0).is_some());
|
|
assert!(!table.contains(0));
|
|
}
|