mcaptcha/
demo.rs

1// Copyright (C) 2022  Aravinth Manivannan <realaravinth@batsense.net>
2// SPDX-FileCopyrightText: 2023 Aravinth Manivannan <realaravinth@batsense.net>
3//
4// SPDX-License-Identifier: AGPL-3.0-or-later
5
6use std::time::Duration;
7//use std::sync::atomicBool
8
9use actix::clock::sleep;
10use actix::spawn;
11use tokio::sync::oneshot::{channel, error::TryRecvError, Receiver, Sender};
12use tokio::task::JoinHandle;
13
14use crate::api::v1::account::delete::runners::delete_user;
15use crate::api::v1::account::{username::runners::username_exists, AccountCheckPayload};
16use crate::api::v1::auth::runners::{register_runner, Register};
17use crate::*;
18
19use errors::*;
20
21/// Demo username
22pub const DEMO_USER: &str = "aaronsw";
23/// Demo password
24pub const DEMO_PASSWORD: &str = "password";
25
26pub struct DemoUser {
27    tx: Sender<()>,
28}
29
30impl DemoUser {
31    pub async fn spawn(
32        data: AppData,
33        duration: u32,
34    ) -> ServiceResult<(Self, JoinHandle<()>)> {
35        let (tx, rx) = channel();
36        let handle = Self::run(data, duration, rx).await?;
37        let d = Self { tx };
38
39        Ok((d, handle))
40    }
41
42    #[allow(dead_code)]
43    pub fn abort(mut self) {
44        self.tx.send(());
45    }
46
47    /// register demo user runner
48    async fn register_demo_user(data: &AppData) -> ServiceResult<()> {
49        let user_exists_payload = AccountCheckPayload {
50            val: DEMO_USER.into(),
51        };
52
53        if !username_exists(&user_exists_payload, data).await?.exists {
54            let register_payload = Register {
55                username: DEMO_USER.into(),
56                password: DEMO_PASSWORD.into(),
57                confirm_password: DEMO_PASSWORD.into(),
58                email: None,
59            };
60
61            log::info!("Registering demo user");
62            match register_runner(&register_payload, data).await {
63                Err(ServiceError::UsernameTaken) | Ok(_) => Ok(()),
64                Err(e) => Err(e),
65            }
66        } else {
67            Ok(())
68        }
69    }
70
71    async fn delete_demo_user(data: &AppData) -> ServiceResult<()> {
72        log::info!("Deleting demo user");
73        delete_user(DEMO_USER, data).await?;
74        Ok(())
75    }
76
77    pub async fn run(
78        data: AppData,
79        duration: u32,
80        mut rx: Receiver<()>,
81    ) -> ServiceResult<JoinHandle<()>> {
82        Self::register_demo_user(&data).await?;
83
84        fn can_run(rx: &mut Receiver<()>) -> bool {
85            match rx.try_recv() {
86                Err(TryRecvError::Empty) => true,
87                _ => false,
88            }
89        }
90
91        let mut exit = false;
92        let fut = async move {
93            loop {
94                if exit {
95                    break;
96                }
97                for _ in 0..duration {
98                    if can_run(&mut rx) {
99                        sleep(Duration::new(1, 0)).await;
100                        continue;
101                    } else {
102                        exit = true;
103                        break;
104                    }
105                }
106
107                if let Err(e) = Self::delete_demo_user(&data).await {
108                    log::error!("Error while deleting demo user: {:?}", e);
109                }
110
111                if let Err(e) = Self::register_demo_user(&data).await {
112                    log::error!("Error while registering demo user: {:?}", e);
113                }
114            }
115        };
116        let handle = spawn(fut);
117        Ok(handle)
118    }
119}
120
121#[cfg(test)]
122mod tests {
123
124    use actix_web::test;
125    use libmcaptcha::defense::Level;
126
127    use super::*;
128    use crate::tests::*;
129
130    const DURATION: u64 = 25;
131
132    #[actix_rt::test]
133    async fn demo_account_works_pg() {
134        let data = crate::tests::pg::get_data().await;
135        demo_account_works(data).await;
136    }
137
138    #[actix_rt::test]
139    async fn demo_account_works_maria() {
140        let data = crate::tests::maria::get_data().await;
141        demo_account_works(data).await;
142    }
143
144    async fn demo_account_works(data_inner: ArcData) {
145        let data_inner = &data_inner;
146        let data = AppData::new(data_inner.clone());
147        crate::tests::delete_user(data_inner, DEMO_USER).await;
148        let duration = Duration::from_secs(DURATION);
149
150        // register works
151        DemoUser::register_demo_user(&data).await.unwrap();
152        let payload = AccountCheckPayload {
153            val: DEMO_USER.into(),
154        };
155        assert!(username_exists(&payload, &data).await.unwrap().exists);
156        signin(data_inner, DEMO_USER, DEMO_PASSWORD).await;
157
158        // deletion works
159        assert!(DemoUser::delete_demo_user(&data).await.is_ok());
160        assert!(!username_exists(&payload, &data).await.unwrap().exists);
161
162        // test the runner
163        let user = DemoUser::spawn(data, DURATION as u32).await.unwrap();
164        let (_, signin_resp, token_key) =
165            add_levels_util(data_inner, DEMO_USER, DEMO_PASSWORD).await;
166        let cookies = get_cookie!(signin_resp);
167        let app = get_app!(data_inner).await;
168
169        let resp = test::call_service(
170            &app,
171            post_request!(&token_key, crate::V1_API_ROUTES.captcha.get)
172                .cookie(cookies.clone())
173                .to_request(),
174        )
175        .await;
176        assert_eq!(resp.status(), StatusCode::OK);
177        let res_levels: Vec<Level> = test::read_body_json(resp).await;
178        assert!(!res_levels.is_empty());
179
180        sleep(Duration::from_secs(DURATION * 2)).await;
181
182        let resp = test::call_service(
183            &app,
184            post_request!(&token_key, crate::V1_API_ROUTES.captcha.get)
185                .cookie(cookies)
186                .to_request(),
187        )
188        .await;
189        assert_eq!(resp.status(), StatusCode::OK);
190        let res_levels: Vec<Level> = test::read_body_json(resp).await;
191        assert!(res_levels.is_empty());
192        user.0.abort();
193        user.1.await.unwrap();
194    }
195}