Skip to main content

fakecloud_sns/
simulation.rs

1use crate::state::SharedSnsState;
2
3/// A pending subscription confirmation.
4#[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
13/// List all subscriptions that are pending confirmation.
14pub 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
29/// Force-confirm a subscription by its ARN. Returns true if the
30/// subscription was found and confirmed (or was already confirmed).
31pub 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}