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 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}