use super::*;
use crate::graph::RestoredAgent;
pub(super) fn memory_profile_snapshot() -> (Store, DaslCid) {
let fixture = &roles()[2];
let blocks = store(fixture);
let (_, command) = command_at(&blocks, root_of(fixture));
let (id, _, _) = Command::descriptor_in_validated(&command);
let state = AgentState::decode(
blocks.get(root_of(fixture)).unwrap(),
&DecodeLimits::default(),
)
.unwrap();
let definition = Definition::decode(
blocks.get(state.definition_link()).unwrap(),
&DecodeLimits::default(),
)
.unwrap();
let authority = definition
.tools()
.find(|tool| tool.id == id)
.unwrap()
.authority_binding
.unwrap();
let (blocks, root, _) = boundary_change(fixture, 0, |r| {
let command = map_mut(map_mut(r).get_mut("exit").unwrap())
.get_mut("command")
.unwrap();
map_mut(command).insert("authority_binding".into(), Cbor::Bytes(authority.to_vec()));
});
(blocks, root)
}
pub(super) fn valid_snapshots() -> Vec<(Store, DaslCid)> {
let base = roles();
let identity = identity_graphs();
let genesis = fixtures();
let mut snapshots = base
.as_array()
.unwrap()
.iter()
.take(1)
.chain(identity.as_array().unwrap())
.chain(genesis.as_array().unwrap())
.map(|fixture| (store(fixture), root_of(fixture)))
.collect::<Vec<_>>();
snapshots.push(memory_profile_snapshot());
snapshots
}
#[test]
fn complete_genesis_and_post_genesis_snapshots_expose_exact_borrowed_domain_views() {
for (blocks, root) in valid_snapshots() {
let snapshot = RestoredAgent::restore(root, &blocks, &Limits::default()).unwrap();
assert_eq!(snapshot.root(), root);
assert_eq!(snapshot.state().cid(), root);
assert_eq!(
snapshot.definition().cid(),
snapshot.state().definition_link()
);
assert_eq!(
snapshot.encoded_bytes(),
snapshot.blocks().map(|(_, b)| b.len() as u64).sum::<u64>()
);
assert_eq!(snapshot.block_count(), snapshot.blocks().count() as u64);
assert_eq!(
snapshot.current_episode().is_some(),
snapshot.state().current_episode_link().is_some()
);
assert_eq!(
snapshot.history().count() as u64,
snapshot.state().history_count()
);
assert_eq!(
snapshot.memory().map(Memory::cid),
snapshot.state().memory_link()
);
let exact = snapshot.blocks().collect::<BTreeMap<_, _>>();
assert_eq!(snapshot.state().as_bytes().as_ptr(), exact[&root].as_ptr());
for episode in snapshot.history().chain(snapshot.current_episode()) {
assert_eq!(episode.as_bytes().as_ptr(), exact[&episode.cid()].as_ptr());
if let Some(cid) = episode.entry_memory_link() {
assert_eq!(
snapshot.memory_block(cid).unwrap().as_bytes().as_ptr(),
exact[&cid].as_ptr()
);
}
for cid in episode.boundaries() {
let boundary = snapshot.boundary(cid).unwrap();
assert_eq!(boundary.as_bytes().as_ptr(), exact[&cid].as_ptr());
let input = snapshot.input(boundary.input_link()).unwrap();
assert_eq!(input.as_bytes().as_ptr(), exact[&input.cid()].as_ptr());
if let Some(cid) = boundary.memory_link() {
assert_eq!(snapshot.memory_block(cid).unwrap().as_bytes(), exact[&cid]);
}
}
}
if snapshot.state().revision() == 0 {
let genesis = Genesis::restore(root, &blocks, &Limits::default()).unwrap();
assert_eq!(snapshot.state().as_bytes(), genesis.state().as_bytes());
assert_eq!(
snapshot
.blocks()
.map(|(cid, b)| (cid, b.to_vec()))
.collect::<BTreeMap<_, _>>(),
genesis.blocks().map(|(cid, b)| (cid, b.to_vec())).collect()
);
} else {
assert_eq!(
Genesis::restore(root, &blocks, &Limits::default()),
Err(GraphError::NotGenesis)
);
}
}
}
#[test]
fn promotion_moves_inspected_buffers_without_new_store_reads_or_payload_allocations() {
struct Counting(Store, Cell<u64>);
impl MaterializedBlocks for Counting {
fn get(&self, cid: DaslCid) -> Option<&[u8]> {
self.1.set(self.1.get() + 1);
self.0.get(cid)
}
}
for (blocks, root) in valid_snapshots() {
let blocks = Counting(blocks, Cell::new(0));
let inspected = InspectedClosure::inspect(root, &blocks, &Limits::default()).unwrap();
let pointers = inspected
.blocks()
.map(|(cid, b)| (cid, (b.as_ptr() as usize, b.len())))
.collect::<BTreeMap<_, _>>();
let calls = blocks.1.get();
let snapshot = RestoredAgent::from_inspected(inspected).unwrap();
assert_eq!(blocks.1.get(), calls);
assert_eq!(
pointers,
snapshot
.blocks()
.map(|(cid, b)| (cid, (b.as_ptr() as usize, b.len())))
.collect()
);
drop(blocks);
assert_eq!(snapshot.root(), root); }
}
#[test]
fn every_required_missing_or_corrupt_block_fails_restoration_without_genesis_or_memory_fallback() {
for (blocks, root) in valid_snapshots() {
let snapshot = RestoredAgent::restore(root, &blocks, &Limits::default()).unwrap();
for (cid, _) in snapshot.blocks() {
let mut missing = Store(blocks.0.clone());
missing.0.remove(&cid);
assert_eq!(
RestoredAgent::restore(root, &missing, &Limits::default()),
Err(GraphError::InputUnavailable(cid))
);
let mut corrupt = Store(blocks.0.clone());
corrupt.0.get_mut(&cid).unwrap()[0] ^= 1;
assert_eq!(
RestoredAgent::restore(root, &corrupt, &Limits::default()),
Err(GraphError::IntegrityFailure(cid))
);
}
}
}
#[test]
fn partial_role_graphs_and_bad_roles_ids_ordinals_or_grants_cannot_promote() {
for (index, fixture) in roles().as_array().unwrap().iter().enumerate().skip(1) {
let blocks = store(fixture);
let (boundary, _) = command_at(&blocks, root_of(fixture));
let error = if index == 1 {
ToolBindingError::UnknownTool(boundary)
} else {
ToolBindingError::AuthorityMismatch(boundary)
};
let inspected =
InspectedClosure::inspect(root_of(fixture), &blocks, &Limits::default()).unwrap();
assert_eq!(RestoredAgent::from_inspected(inspected), Err(error.into()));
}
let fixture = &identity_graphs()[0];
let (blocks, root, cid) = episode_change(fixture, |r| {
map_mut(r).insert("closed".into(), Cbor::Bool(true));
});
assert_eq!(
RestoredAgent::restore(root, &blocks, &Limits::default()),
Err(EpisodeRoleError::CurrentEpisodeClosed(cid).into())
);
let (blocks, root, cid) = episode_change(fixture, |r| {
map_mut(r).insert("id".into(), Cbor::Bytes(vec![99; 32]));
});
assert_eq!(
RestoredAgent::restore(root, &blocks, &Limits::default()),
Err(IdentityError::EpisodeIdMismatch(cid).into())
);
let (blocks, root, cid) = boundary_change(fixture, 1, |r| {
map_mut(r).insert("ordinal".into(), Cbor::Integer(9.into()));
});
assert_eq!(
RestoredAgent::restore(root, &blocks, &Limits::default()),
Err(TransitionError::OrdinalMismatch(cid).into())
);
let (blocks, root, cid) = super::super::transitions::input_change(fixture, 1, |r| {
map_mut(map_mut(r).get_mut("budget_admission").unwrap())
.insert("remaining_actions".into(), Cbor::Integer(0.into()));
});
assert_eq!(
RestoredAgent::restore(root, &blocks, &Limits::default()),
Err(AdmissionGrantError::Action(cid).into())
);
}
#[test]
fn caller_wire_nested_and_aggregate_caps_reject_without_changing_definition_or_history() {
struct NoRead(Cell<u64>);
impl MaterializedBlocks for NoRead {
fn get(&self, _: DaslCid) -> Option<&[u8]> {
self.0.set(self.0.get() + 1);
None
}
}
let (_, root) = memory_profile_snapshot();
let no_read = NoRead(Cell::new(0));
assert_eq!(
RestoredAgent::restore(
root,
&no_read,
&Limits {
max_blocks: 0,
..Limits::default()
}
),
Err(GraphError::BlockCountLimit)
);
assert_eq!(no_read.0.get(), 0);
for (blocks, root) in valid_snapshots() {
let snapshot = RestoredAgent::restore(root, &blocks, &Limits::default()).unwrap();
assert_eq!(
RestoredAgent::restore(
root,
&blocks,
&Limits {
max_materialized_bytes: snapshot.encoded_bytes() - 1,
..Limits::default()
}
),
Err(GraphError::MaterializedByteLimit)
);
assert_eq!(
RestoredAgent::restore(
root,
&blocks,
&Limits {
max_blocks: snapshot.block_count() - 1,
..Limits::default()
}
),
Err(GraphError::BlockCountLimit)
);
assert!(RestoredAgent::restore(
root,
&blocks,
&Limits {
max_depth: 1,
..Limits::default()
}
)
.is_err());
if snapshot.state().history_count() > 0 {
assert_eq!(
RestoredAgent::restore(
root,
&blocks,
&Limits {
history_episodes: 0,
..Limits::default()
}
),
Err(HistoryConsistencyError::CallerWindowLimit(root).into())
);
}
if snapshot.current_episode().is_some() {
assert!(RestoredAgent::restore(
root,
&blocks,
&Limits {
max_command_bytes: 1,
..Limits::default()
}
)
.is_err());
}
}
}
#[test]
fn typed_queries_do_not_hydrate_unrelated_or_wrong_kind_cids_and_restoration_does_not_authorize_head(
) {
let (mut blocks, root) = memory_profile_snapshot();
let foreign = memories::new_memory(&mut blocks, "unrelated");
let snapshot = RestoredAgent::restore(root, &blocks, &Limits::default()).unwrap();
assert!(snapshot.memory_block(foreign).is_none());
assert!(snapshot.boundary(root).is_none());
assert!(snapshot.input(root).is_none());
assert!(snapshot.memory_block(root).is_none());
assert!(!snapshot.blocks().any(|(cid, _)| cid == foreign));
let raw = DaslCid::for_block(DaslCodec::Raw, b"not state");
assert_eq!(
RestoredAgent::restore(raw, &blocks, &Limits::default()),
Err(GraphError::Codec(crate::format::CodecError::InvalidLink))
);
let roots = identity_graphs();
for fixture in roots.as_array().unwrap() {
assert!(
RestoredAgent::restore(root_of(fixture), &store(fixture), &Limits::default()).is_ok()
);
}
}