fakecloud_sns/
simulation.rs1use crate::state::SharedSnsState;
2
3#[derive(Debug, Clone)]
5pub struct PendingConfirmation {
6 pub subscription_arn: String,
7 pub topic_arn: String,
8 pub protocol: String,
9 pub endpoint: String,
10 pub token: Option<String>,
11}
12
13pub fn list_pending_confirmations(state: &SharedSnsState) -> Vec<PendingConfirmation> {
15 let s = state.read();
16 s.subscriptions
17 .values()
18 .filter(|sub| !sub.confirmed)
19 .map(|sub| PendingConfirmation {
20 subscription_arn: sub.subscription_arn.clone(),
21 topic_arn: sub.topic_arn.clone(),
22 protocol: sub.protocol.clone(),
23 endpoint: sub.endpoint.clone(),
24 token: sub.confirmation_token.clone(),
25 })
26 .collect()
27}
28
29pub fn confirm_subscription(state: &SharedSnsState, subscription_arn: &str) -> bool {
32 let mut s = state.write();
33 match s.subscriptions.get_mut(subscription_arn) {
34 Some(sub) => {
35 sub.confirmed = true;
36 true
37 }
38 None => false,
39 }
40}
41
42#[cfg(test)]
43mod tests {
44 use super::*;
45 use crate::state::SnsState;
46 use parking_lot::RwLock;
47 use std::collections::HashMap;
48 use std::sync::Arc;
49
50 fn make_state() -> SharedSnsState {
51 Arc::new(RwLock::new(SnsState::new(
52 "123456789012",
53 "us-east-1",
54 "http://localhost:4566",
55 )))
56 }
57
58 fn add_subscription(
59 state: &SharedSnsState,
60 topic_arn: &str,
61 protocol: &str,
62 endpoint: &str,
63 confirmed: bool,
64 ) -> String {
65 let sub_arn = format!("{}:{}", topic_arn, uuid::Uuid::new_v4());
66 let mut s = state.write();
67 s.subscriptions.insert(
68 sub_arn.clone(),
69 crate::state::SnsSubscription {
70 subscription_arn: sub_arn.clone(),
71 topic_arn: topic_arn.to_string(),
72 protocol: protocol.to_string(),
73 endpoint: endpoint.to_string(),
74 owner: "123456789012".to_string(),
75 attributes: HashMap::new(),
76 confirmed,
77 confirmation_token: if confirmed {
78 None
79 } else {
80 Some(uuid::Uuid::new_v4().to_string())
81 },
82 },
83 );
84 sub_arn
85 }
86
87 #[test]
88 fn list_pending_finds_unconfirmed() {
89 let state = make_state();
90 let topic_arn = "arn:aws:sns:us-east-1:123456789012:my-topic";
91 add_subscription(&state, topic_arn, "http", "http://example.com/hook", false);
92 add_subscription(
93 &state,
94 topic_arn,
95 "sqs",
96 "arn:aws:sqs:us-east-1:123456789012:q",
97 true,
98 );
99
100 let pending = list_pending_confirmations(&state);
101 assert_eq!(pending.len(), 1);
102 assert_eq!(pending[0].protocol, "http");
103 assert_eq!(pending[0].endpoint, "http://example.com/hook");
104 }
105
106 #[test]
107 fn list_pending_returns_empty_when_all_confirmed() {
108 let state = make_state();
109 let topic_arn = "arn:aws:sns:us-east-1:123456789012:my-topic";
110 add_subscription(
111 &state,
112 topic_arn,
113 "sqs",
114 "arn:aws:sqs:us-east-1:123456789012:q",
115 true,
116 );
117
118 let pending = list_pending_confirmations(&state);
119 assert!(pending.is_empty());
120 }
121
122 #[test]
123 fn confirm_subscription_sets_confirmed() {
124 let state = make_state();
125 let topic_arn = "arn:aws:sns:us-east-1:123456789012:my-topic";
126 let sub_arn = add_subscription(&state, topic_arn, "http", "http://example.com/hook", false);
127
128 assert!(!state.read().subscriptions[&sub_arn].confirmed);
129
130 let result = confirm_subscription(&state, &sub_arn);
131 assert!(result);
132 assert!(state.read().subscriptions[&sub_arn].confirmed);
133 }
134
135 #[test]
136 fn confirm_subscription_returns_false_for_unknown() {
137 let state = make_state();
138 let result = confirm_subscription(&state, "arn:aws:sns:us-east-1:123456789012:nope:xxx");
139 assert!(!result);
140 }
141}