test: add mikan client login test

This commit is contained in:
master 2025-05-07 02:15:46 +08:00
parent a7f52fe0eb
commit 791b75b3af
5 changed files with 112 additions and 15 deletions

10
Cargo.lock generated
View File

@ -3959,6 +3959,15 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "16cf681a23b4d0a43fc35024c176437f9dcd818db34e0f42ab456a0ee5ad497b"
[[package]]
name = "nanoid"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3ffa00dec017b5b1a8b7cf5e2c008bfda1aa7e0697ac1508b491fdf2622fb4d8"
dependencies = [
"rand 0.8.5",
]
[[package]]
name = "native-tls"
version = "0.2.14"
@ -5152,6 +5161,7 @@ dependencies = [
"maplit",
"mockito",
"moka",
"nanoid",
"once_cell",
"opendal",
"openidconnect",

View File

@ -115,6 +115,7 @@ reqwest_cookie_store = "0.8.0"
downloader = { workspace = true }
util = { workspace = true }
fetch = { workspace = true }
nanoid = "0.4.0"
[dev-dependencies]
serial_test = "3"

View File

@ -16,7 +16,7 @@ impl CryptoService {
Ok(Self { config })
}
pub fn encrypt_data(&self, data: String) -> Result<String, CryptoError> {
pub fn encrypt_string(&self, data: String) -> Result<String, CryptoError> {
let key = rand::rng().random::<[u8; 32]>();
let mut cocoon = Cocoon::new(&key);
@ -32,7 +32,7 @@ impl CryptoService {
Ok(BASE64_URL_SAFE.encode(combined))
}
pub fn decrypt_data(&self, data: &str) -> Result<String, CryptoError> {
pub fn decrypt_string(&self, data: &str) -> Result<String, CryptoError> {
let decoded = BASE64_URL_SAFE.decode(data)?;
let (key, remain) = decoded.split_at(32);
@ -45,20 +45,17 @@ impl CryptoService {
String::from_utf8(data).map_err(CryptoError::from)
}
pub fn encrypt_credentials<T: Serialize>(
&self,
credentials: &T,
) -> Result<String, CryptoError> {
pub fn encrypt_serialize<T: Serialize>(&self, credentials: &T) -> Result<String, CryptoError> {
let json = serde_json::to_string(credentials)?;
self.encrypt_data(json)
self.encrypt_string(json)
}
pub fn decrypt_credentials<T: for<'de> Deserialize<'de>>(
pub fn decrypt_deserialize<T: for<'de> Deserialize<'de>>(
&self,
encrypted: &str,
) -> Result<T, CryptoError> {
let data = self.decrypt_data(encrypted)?;
let data = self.decrypt_string(encrypted)?;
serde_json::from_str(&data).map_err(CryptoError::from)
}

View File

@ -242,3 +242,92 @@ impl Deref for MikanClient {
}
impl HttpClientTrait for MikanClient {}
#[cfg(test)]
mod tests {
#![allow(unused_variables)]
use std::assert_matches::assert_matches;
use rstest::{fixture, rstest};
use tracing::Level;
use super::*;
use crate::test_utils::{
app::UnitTestAppContext,
crypto::build_testing_crypto_service,
database::build_testing_database_service,
mikan::{MikanMockServer, build_testing_mikan_client, build_testing_mikan_credential_form},
tracing::try_init_testing_tracing,
};
async fn create_testing_context(
mikan_base_url: Url,
) -> RecorderResult<Arc<dyn AppContextTrait>> {
let mikan_client = build_testing_mikan_client(mikan_base_url.clone()).await?;
let db_service = build_testing_database_service().await?;
let crypto_service = build_testing_crypto_service().await?;
let ctx = UnitTestAppContext::builder()
.db(db_service)
.crypto(crypto_service)
.mikan(mikan_client)
.build();
Ok(Arc::new(ctx))
}
#[fixture]
fn before_each() {
try_init_testing_tracing(Level::DEBUG);
}
#[rstest]
#[tokio::test]
async fn test_mikan_client_submit_credential_form(before_each: ()) -> RecorderResult<()> {
let mut mikan_server = MikanMockServer::new().await?;
let app_ctx = create_testing_context(mikan_server.base_url().clone()).await?;
let _login_mock = mikan_server.mock_get_login_page();
let mikan_client = app_ctx.mikan();
let crypto_service = app_ctx.crypto();
let credential_form = build_testing_mikan_credential_form();
let credential_model = mikan_client
.submit_credential_form(app_ctx.clone(), 1, credential_form.clone())
.await?;
let expected_username = &credential_form.username;
let expected_password = &credential_form.password;
let found_username = crypto_service
.decrypt_string(credential_model.username.as_deref().unwrap_or_default())?;
let found_password = crypto_service
.decrypt_string(credential_model.password.as_deref().unwrap_or_default())?;
assert_eq!(&found_username, expected_username);
assert_eq!(&found_password, expected_password);
let has_login = mikan_client.has_login().await?;
assert!(!has_login);
assert_matches!(
mikan_client.login().await,
Err(RecorderError::Credential3rdError { .. })
);
let mikan_client = mikan_client
.fork_with_credential(app_ctx.clone(), credential_model.id)
.await?;
mikan_client.login().await?;
let has_login = mikan_client.has_login().await?;
assert!(has_login);
Ok(())
}
}

View File

@ -74,17 +74,17 @@ impl ActiveModel {
let crypto = ctx.crypto();
if let ActiveValue::Set(Some(username)) = self.username {
let username_enc = crypto.encrypt_credentials(&username)?;
let username_enc = crypto.encrypt_string(username)?;
self.username = ActiveValue::Set(Some(username_enc));
}
if let ActiveValue::Set(Some(password)) = self.password {
let password_enc = crypto.encrypt_credentials(&password)?;
let password_enc = crypto.encrypt_string(password)?;
self.password = ActiveValue::Set(Some(password_enc));
}
if let ActiveValue::Set(Some(cookies)) = self.cookies {
let cookies_enc = crypto.encrypt_credentials(&cookies)?;
let cookies_enc = crypto.encrypt_string(cookies)?;
self.cookies = ActiveValue::Set(Some(cookies_enc));
}
@ -115,7 +115,7 @@ impl Model {
source: None.into(),
})?;
let username: String = crypto.decrypt_credentials(&username_enc)?;
let username: String = crypto.decrypt_string(&username_enc)?;
let password_enc = self
.password
@ -124,10 +124,10 @@ impl Model {
source: None.into(),
})?;
let password: String = crypto.decrypt_credentials(&password_enc)?;
let password: String = crypto.decrypt_string(&password_enc)?;
let cookies: Option<String> = if let Some(cookies_enc) = self.cookies {
let cookies = crypto.decrypt_credentials(&cookies_enc)?;
let cookies = crypto.decrypt_string(&cookies_enc)?;
Some(cookies)
} else {
None