Skip to main content

p2panda_core/
test_utils.rs

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