hashiverse_lib/client/post_bundle/
stub_post_bundle_feedback_manager.rs1use crate::client::post_bundle::post_bundle_feedback_manager::PostBundleFeedbackManager;
10use crate::protocol::posting::encoded_post_bundle_feedback::EncodedPostBundleFeedbackV1;
11use crate::tools::buckets::BucketLocation;
12use crate::tools::time::TimeMillis;
13
14#[derive(Default)]
15pub struct StubPostBundleFeedbackManager {
16}
17
18
19#[cfg_attr(target_arch = "wasm32", async_trait::async_trait(?Send))]
20#[cfg_attr(not(target_arch = "wasm32"), async_trait::async_trait)]
21impl PostBundleFeedbackManager for StubPostBundleFeedbackManager {
22 async fn get_post_bundle_feedback(&self, _bucket_location: BucketLocation, _time_millis: TimeMillis) -> anyhow::Result<EncodedPostBundleFeedbackV1> {
23 anyhow::bail!("Not implemented");
24 }
25}
26
27#[cfg(test)]
28mod tests {
29 use super::*;
30 use bytes::{Bytes, BytesMut};
31 use crate::protocol::posting::encoded_post_feedback::{EncodedPostFeedbackV1, EncodedPostFeedbackViewV1};
32 use crate::tools::buckets::{BucketLocation, BucketType, BUCKET_DURATIONS};
33 use crate::tools::time::TimeMillis;
34 use crate::tools::types::{Id, Pow, Salt};
35
36 fn make_bucket_location() -> BucketLocation {
37 BucketLocation::new(BucketType::User, Id::random(), BUCKET_DURATIONS[0], TimeMillis(1_000_000)).unwrap()
38 }
39
40 #[tokio::test]
41 async fn stub_always_returns_error() {
42 let stub = StubPostBundleFeedbackManager::default();
43 let result = stub.get_post_bundle_feedback(make_bucket_location(), TimeMillis(1_000_000)).await;
44 assert!(result.is_err(), "stub should always return an error");
45 assert!(result.unwrap_err().to_string().contains("Not implemented"));
46 }
47
48 #[test]
49 fn feedback_roundtrip_encoding() {
50 let post_id = Id::random();
51 let feedback = EncodedPostFeedbackV1 {
52 post_id,
53 feedback_type: 1,
54 salt: Salt::random(),
55 pow: Pow(42),
56 };
57
58 let mut buf = BytesMut::new();
59 feedback.append_encode_to_bytes(&mut buf).unwrap();
60 let bytes = buf.freeze();
61
62 let view = EncodedPostFeedbackViewV1::iter(&bytes)
63 .next()
64 .expect("should yield one entry")
65 .expect("entry should decode without error");
66
67 assert_eq!(view.post_id_bytes(), post_id.as_ref());
68 assert_eq!(view.feedback_type(), 1);
69 assert_eq!(view.pow(), Pow(42));
70 }
71
72 #[test]
73 fn feedback_view_iter_multiple_entries() {
74 let post_ids: Vec<Id> = (0..3).map(|_| Id::random()).collect();
75 let mut combined = BytesMut::new();
76 for (i, &post_id) in post_ids.iter().enumerate() {
77 let feedback = EncodedPostFeedbackV1 {
78 post_id,
79 feedback_type: (i as u8) + 1,
80 salt: Salt::random(),
81 pow: Pow((i as u8) * 10),
82 };
83 feedback.append_encode_to_bytes(&mut combined).unwrap();
84 }
85 let bytes: Bytes = combined.freeze();
86
87 let views: Vec<_> = EncodedPostFeedbackViewV1::iter(&bytes)
88 .collect::<Result<Vec<_>, _>>()
89 .expect("all entries should decode");
90
91 assert_eq!(views.len(), 3);
92 for (i, view) in views.iter().enumerate() {
93 assert_eq!(view.post_id_bytes(), post_ids[i].as_ref());
94 assert_eq!(view.feedback_type(), (i as u8) + 1);
95 assert_eq!(view.pow(), Pow((i as u8) * 10));
96 }
97 }
98
99 #[test]
100 fn feedback_pow_comparison() {
101 assert!(Pow(10) > Pow(5));
104 assert!(Pow(0) < Pow(1));
105 assert_eq!(Pow(7), Pow(7));
106
107 let pows = vec![Pow(3), Pow(15), Pow(7)];
109 let strongest = pows.into_iter().max().unwrap();
110 assert_eq!(strongest, Pow(15));
111 }
112}