p2panda_core/
test_utils.rs1use std::cell::RefCell;
4use std::rc::Rc;
5
6use crate::timestamp::Timestamp;
7use crate::{Body, Extensions, Hash, Header, Operation, SigningKey, Topic, VerifyingKey};
8
9#[derive(Clone, Default)]
10pub struct TestLog {
11 signing_key: SigningKey,
12 backlink: Rc<RefCell<Option<Hash>>>,
13 seq_num: Rc<RefCell<u64>>,
14 log_id: Topic,
15}
16
17impl TestLog {
18 pub fn new() -> Self {
19 Self {
20 signing_key: SigningKey::generate(),
21 backlink: Rc::default(),
22 seq_num: Rc::default(),
23 log_id: Topic::random(),
24 }
25 }
26
27 pub fn from_signing_key(signing_key: SigningKey) -> Self {
28 let mut log = TestLog::new();
29 log.signing_key = signing_key;
30 log
31 }
32
33 pub fn id(&self) -> Topic {
34 self.log_id
35 }
36
37 pub fn author(&self) -> VerifyingKey {
38 self.signing_key.verifying_key()
39 }
40
41 pub fn operation<E: Extensions>(&self, body: &[u8], extensions: E) -> Operation<E> {
42 let body = Body::from(body);
43
44 let mut seq_num = self.seq_num.borrow_mut();
45 let mut backlink = self.backlink.borrow_mut();
46
47 let mut header = Header::<E> {
48 verifying_key: self.signing_key.verifying_key(),
49 version: 1,
50 signature: None,
51 payload_size: body.size(),
52 payload_hash: if body.size() == 0 {
53 None
54 } else {
55 Some(body.hash())
56 },
57 timestamp: Timestamp::now(),
58 seq_num: *seq_num,
59 backlink: *backlink,
60 extensions,
61 };
62 header.sign(&self.signing_key);
63
64 *backlink = Some(header.hash());
65 *seq_num += 1;
66
67 Operation {
68 hash: header.hash(),
69 header,
70 body: if body.size() == 0 { None } else { Some(body) },
71 }
72 }
73}
74
75#[cfg(test)]
76mod tests {
77 use crate::Header;
78 use crate::cbor::{decode_cbor, encode_cbor};
79
80 use super::TestLog;
81
82 #[test]
83 fn zero_byte_body() {
84 let log = TestLog::new();
85 let operation = log.operation(&[], ());
86 let bytes = encode_cbor(operation.header()).unwrap();
87 assert!(decode_cbor::<Header, _>(&bytes[..]).is_ok());
88 }
89}