Add deterministic agent protocol harness (#133)
This commit is contained in:
@@ -1,16 +1,16 @@
|
||||
use libremetaverse_types::UUID;
|
||||
use libremetaverse_types::compat::CancellationToken;
|
||||
use metacrate_grid_agent::testing::FakeGrid;
|
||||
use metacrate_grid_agent::{
|
||||
ActionOrigin, AllowedOrigins, AuthorizedAction, AuthorizedToolBackend, BackendError,
|
||||
BackendFuture, BoundedText, Capability, FixedCost, Idempotency, MemoryPolicyAudit, OriginClass,
|
||||
PolicyAuditSink, PolicyDisposition, PolicyFinalOutcome, PolicyGateway, PolicyLimits,
|
||||
PolicyRequestContext, PolicyTool, PolicyToolExecutor, ProposedToolCall, ResourceCost, Risk,
|
||||
ToolCallOutcome, ToolDefinition, ToolExecution, ToolExecutor, ToolSchema,
|
||||
ActionOrigin, AllowedOrigins, AuthorizedToolBackend, BoundedText, Capability, FixedCost,
|
||||
Idempotency, MemoryPolicyAudit, OriginClass, PolicyAuditSink, PolicyDisposition,
|
||||
PolicyFinalOutcome, PolicyGateway, PolicyLimits, PolicyRequestContext, PolicyTool,
|
||||
PolicyToolExecutor, ProposedToolCall, ResourceCost, Risk, ToolDefinition, ToolExecution,
|
||||
ToolExecutor, ToolSchema,
|
||||
};
|
||||
use serde_json::{Value, json};
|
||||
use std::collections::{BTreeMap, BTreeSet};
|
||||
use std::sync::Arc;
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
|
||||
fn id(text: &str) -> UUID {
|
||||
UUID::new_with_string(text.to_owned()).expect("fixture UUID")
|
||||
@@ -88,25 +88,6 @@ fn call(name: &str) -> (ProposedToolCall, Value) {
|
||||
)
|
||||
}
|
||||
|
||||
struct RecordingBackend(AtomicUsize);
|
||||
|
||||
impl AuthorizedToolBackend for RecordingBackend {
|
||||
fn apply(
|
||||
&self,
|
||||
action: AuthorizedAction,
|
||||
_cancellation: CancellationToken,
|
||||
) -> BackendFuture<'_, Result<ToolCallOutcome, BackendError>> {
|
||||
self.0.fetch_add(1, Ordering::AcqRel);
|
||||
let call_id = action.call().call_id.clone();
|
||||
Box::pin(async move {
|
||||
Ok(ToolCallOutcome::Completed {
|
||||
call_id,
|
||||
result: BoundedText::new("result", "moved").expect("result"),
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn production_executor_denies_public_mutation_and_executes_authorized_im_once() {
|
||||
let authorized = id("11111111-1111-4111-8111-111111111111");
|
||||
@@ -121,8 +102,8 @@ async fn production_executor_denies_public_mutation_and_executes_authorized_im_o
|
||||
)
|
||||
.expect("gateway"),
|
||||
);
|
||||
let backend = Arc::new(RecordingBackend(AtomicUsize::new(0)));
|
||||
let erased: Arc<dyn AuthorizedToolBackend> = backend.clone();
|
||||
let backend = FakeGrid::scripted([]);
|
||||
let erased: Arc<dyn AuthorizedToolBackend> = Arc::new(backend.clone());
|
||||
let (proposed, arguments) = call("move");
|
||||
let definition = gateway
|
||||
.tools_for(
|
||||
@@ -162,7 +143,7 @@ async fn production_executor_denies_public_mutation_and_executes_authorized_im_o
|
||||
.await,
|
||||
ToolExecution::Rejected(_)
|
||||
));
|
||||
assert_eq!(backend.0.load(Ordering::Acquire), 0);
|
||||
assert!(backend.evidence().is_empty());
|
||||
|
||||
let authorized_executor = PolicyToolExecutor::new(
|
||||
gateway,
|
||||
@@ -188,7 +169,10 @@ async fn production_executor_denies_public_mutation_and_executes_authorized_im_o
|
||||
.await,
|
||||
ToolExecution::Completed(_)
|
||||
));
|
||||
assert_eq!(backend.0.load(Ordering::Acquire), 1);
|
||||
let evidence = backend.evidence();
|
||||
assert_eq!(evidence.len(), 1);
|
||||
assert_eq!(evidence[0].operation, "policy.route.move");
|
||||
assert_eq!(evidence[0].outcome, "authorized");
|
||||
assert_eq!(
|
||||
audit.snapshot().last().expect("outcome").final_outcome,
|
||||
PolicyFinalOutcome::Completed
|
||||
|
||||
Reference in New Issue
Block a user