use super::*;
use crate::prelude::HeadInfo;
use std::fmt::Debug;
#[derive(Debug, Clone)]
pub struct AuthoredChainHeadQuery;
impl AuthoredChainHeadQuery {
pub fn new() -> Self {
Self
}
}
impl Default for AuthoredChainHeadQuery {
fn default() -> Self {
Self::new()
}
}
impl Query for AuthoredChainHeadQuery {
type Item = Judged<SignedActionHashed>;
type State = Option<SignedActionHashed>;
type Output = Option<HeadInfo>;
fn query(&self) -> String {
"
SELECT Action.blob, Action.hash
FROM Action
ORDER BY Action.seq DESC LIMIT 1
"
.into()
}
fn init_fold(&self) -> StateQueryResult<Self::State> {
Ok(None)
}
fn fold(&self, state: Self::State, sh: Self::Item) -> StateQueryResult<Self::State> {
let sh = sh.data;
Ok(Some(match state {
None => sh,
Some(old) => {
if sh.action().action_seq() > old.action().action_seq() {
sh
} else {
old
}
}
}))
}
fn render<S>(&self, state: Self::State, _stores: S) -> StateQueryResult<Self::Output>
where
S: Store,
{
Ok(state.map(|sh| {
let seq = sh.action().action_seq();
let timestamp = sh.action().timestamp();
let action = sh.hashed.hash;
HeadInfo {
action,
seq,
timestamp,
}
}))
}
fn as_map(&self) -> Arc<dyn Fn(&Row) -> StateQueryResult<Self::Item>> {
let f = row_blob_and_hash_to_action("blob", "hash");
Arc::new(move |r| Ok(Judged::valid(f(r)?)))
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::mutations::{insert_action, insert_op_lite};
use ::fixt::prelude::*;
use holo_hash::fixt::AgentPubKeyFixturator;
use holo_hash::fixt::DhtOpHashFixturator;
use holochain_sqlite::rusqlite::Connection;
use holochain_sqlite::rusqlite::TransactionBehavior;
use holochain_sqlite::schema::SCHEMA_CELL;
use holochain_types::dht_op::DhtOpLite;
use holochain_types::dht_op::OpOrder;
#[test]
fn test_chain_head_query() {
holochain_trace::test_run();
let mut conn = Connection::open_in_memory().unwrap();
SCHEMA_CELL.initialize(&mut conn, None).unwrap();
let mut txn = conn
.transaction_with_behavior(TransactionBehavior::Exclusive)
.unwrap();
let author = fixt!(AgentPubKey);
let mut actions: Vec<_> = vec![
fixt!(ActionBuilderCommon),
fixt!(ActionBuilderCommon),
fixt!(ActionBuilderCommon),
fixt!(ActionBuilderCommon),
fixt!(ActionBuilderCommon),
]
.into_iter()
.enumerate()
.map(|(seq, action)| {
let mut chain_action = action.clone();
chain_action.action_seq = seq as u32;
chain_action.author = author.clone();
SignedActionHashed::with_presigned(
ActionHashed::from_content_sync(InitZomesComplete::from_builder(chain_action)),
fixt!(Signature),
)
})
.collect();
let expected_head = actions[4].clone();
actions.shuffle(&mut rand::rng());
for action in &actions[..2] {
let hash = action.action_address();
let op = DhtOpLite::from(ChainOpLite::StoreRecord(
hash.clone(),
None,
hash.clone().into(),
));
let op_order = OpOrder::new(op.get_type(), action.action().timestamp());
insert_action(&mut txn, action).unwrap();
insert_op_lite(
&mut txn,
&op,
&fixt!(DhtOpHash),
&op_order,
&action.action().timestamp(),
0,
None,
)
.unwrap();
}
let mut scratch = Scratch::new();
for action in &actions[2..] {
scratch.add_action(action.clone(), ChainTopOrdering::default());
}
let query = AuthoredChainHeadQuery::new();
let head = query.run(DbScratch::new(&[&mut txn], &scratch)).unwrap();
assert_eq!(
head.unwrap(),
HeadInfo {
action: expected_head.as_hash().clone(),
seq: expected_head.action().action_seq(),
timestamp: expected_head.action().timestamp()
}
);
}
}