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