• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

polyphony-chat / sonata / 16250718729

13 Jul 2025 03:34PM UTC coverage: 63.362% (-16.7%) from 80.109%
16250718729

push

github

bitfl0wer
feat(squashme): Further work on register endpoint with TODOs

9 of 20 branches covered (45.0%)

Branch coverage included in aggregate %.

0 of 12 new or added lines in 1 file covered. (0.0%)

28 existing lines in 7 files now uncovered.

285 of 444 relevant lines covered (64.19%)

418.69 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

77.78
/src/database/api_keys.rs
1
// This Source Code Form is subject to the terms of the Mozilla Public
2
// License, v. 2.0. If a copy of the MPL was not distributed with this
3
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
4

5
use std::ops::Deref;
6

7
use rand::{
8
        distr::{Alphanumeric, SampleString},
9
        prelude::ThreadRng,
10
};
11
use sqlx::query;
12

13
use crate::{StdError, database::Database, errors::SonataDbError};
14

15
/// Constant used to determine how long auto-generated tokens are supposed to
16
/// be.
17
pub const STANDARD_TOKEN_LENGTH: usize = 128;
18

19
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone)]
20
pub struct ApiKey {
21
        token: String,
22
}
23

24
#[cfg_attr(coverage_nightly, coverage(off))]
25
impl Deref for ApiKey {
26
        type Target = str;
27

28
        fn deref(&self) -> &Self::Target {
29
                &self.token
30
        }
31
}
32

33
impl ApiKey {
34
        /// Create a new `ApiKey`. API Keys must be >= 32 and <= 255 characters in
35
        /// length. If your input string meets these two conditions, you will
36
        /// receive an `Ok(ApiKey)`.
37
        pub fn new(token: &str) -> Result<Self, StdError> {
5✔
38
                if token.is_empty() {
5!
39
                        return Err(String::from("Token must not be empty").into());
1✔
40
                }
4✔
41
                if token.len() < 32 {
4!
42
                        return Err(String::from("Token must be at least 32 characters in length").into());
1✔
43
                }
3✔
44
                if token.len() > 255 {
3!
45
                        return Err(String::from("Token must not be longer than 255 characters").into());
1✔
46
                }
2✔
47
                Ok(Self { token: token.to_owned() })
2✔
48
        }
5✔
49

50
        #[cfg_attr(coverage_nightly, coverage(off))]
51
        /// Getter for the internal token.
52
        pub fn token(&self) -> &str {
53
                &self.token
54
        }
55

56
        /// Generates a new, random [ApiKey] which is [STANDARD_TOKEN_LENGTH]
57
        /// characters in length.
58
        pub fn new_random(rng: &mut ThreadRng) -> Self {
2✔
59
                Self { token: Alphanumeric.sample_string(rng, STANDARD_TOKEN_LENGTH) }
2✔
60
        }
2✔
61
}
62

63
impl std::fmt::Display for ApiKey {
UNCOV
64
        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
×
UNCOV
65
                f.write_str(self)
×
UNCOV
66
        }
×
67
}
68

69
/// Create an [ApiKey] from the given `token`, then insert it into the database.
70
pub(crate) async fn add_api_key_to_database(
1✔
71
        token: &str,
1✔
72
        database: &Database,
1✔
73
) -> Result<ApiKey, crate::errors::SonataDbError> {
1✔
74
        let key = ApiKey::new(token).map_err(SonataDbError::StdError)?;
1✔
75
        query!("INSERT INTO api_keys (token) VALUES ($1)", key.token())
1!
76
                .execute(&database.pool)
1✔
77
                .await
1✔
78
                .map_err(SonataDbError::Sqlx)?;
1✔
79
        Ok(key)
1✔
80
}
1✔
81

82
#[cfg(test)]
83
mod test {
84
        use rand::rng;
85
        use sqlx::{Pool, Postgres};
86

87
        use super::*;
88

89
        #[test]
90
        fn token_length() {
1✔
91
                assert!(ApiKey::new("").is_err());
1✔
92
                assert!(ApiKey::new("token").is_err());
1✔
93
                assert!(ApiKey::new(['c'; 256].iter().collect::<String>().as_str()).is_err());
1✔
94
                assert!(ApiKey::new("transrightsarehumanrights_thefirstpridewasariot").is_ok());
1✔
95
        }
1✔
96

97
        #[test]
98
        fn auto_gen_token() {
1✔
99
                assert_eq!(ApiKey::new_random(&mut rng()).len(), STANDARD_TOKEN_LENGTH);
1✔
100
        }
1✔
101

102
        #[sqlx::test]
103
        async fn insert_key_into_db(db: Pool<Postgres>) {
104
                let key = ApiKey::new_random(&mut rng());
105
                assert!(add_api_key_to_database(key.token(), &Database { pool: db }).await.is_ok());
106
        }
107
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc