Further test improvements (#1166)

This commit is contained in:
Firstyear 2022-11-02 19:46:09 +10:00 committed by GitHub
parent df4043cf10
commit 692c0a3978
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 733 additions and 649 deletions

View file

@ -97,3 +97,84 @@ pub(crate) fn qs_test(_args: &TokenStream, item: TokenStream, with_init: bool) -
result.into() result.into()
} }
pub(crate) fn idm_test(_args: &TokenStream, item: TokenStream) -> TokenStream {
let input: syn::ItemFn = match syn::parse(item.clone()) {
Ok(it) => it,
Err(e) => return token_stream_with_error(item, e),
};
if let Some(attr) = input.attrs.iter().find(|attr| attr.path.is_ident("test")) {
let msg = "second test attribute is supplied";
return token_stream_with_error(item, syn::Error::new_spanned(&attr, msg));
};
if input.sig.asyncness.is_none() {
let msg = "the `async` keyword is missing from the function declaration";
return token_stream_with_error(item, syn::Error::new_spanned(input.sig.fn_token, msg));
}
// If type mismatch occurs, the current rustc points to the last statement.
let (last_stmt_start_span, _last_stmt_end_span) = {
let mut last_stmt = input
.block
.stmts
.last()
.map(ToTokens::into_token_stream)
.unwrap_or_default()
.into_iter();
// `Span` on stable Rust has a limitation that only points to the first
// token, not the whole tokens. We can work around this limitation by
// using the first/last span of the tokens like
// `syn::Error::new_spanned` does.
let start = last_stmt.next().map_or_else(Span::call_site, |t| t.span());
let end = last_stmt.last().map_or(start, |t| t.span());
(start, end)
};
let rt = quote_spanned! {last_stmt_start_span=>
tokio::runtime::Builder::new_current_thread()
};
let header = quote! {
#[::core::prelude::v1::test]
};
let test_fn = &input.sig.ident;
let test_driver = Ident::new(&format!("idm_{}", test_fn), input.sig.span());
// Effectively we are just injecting a real test function around this which we will
// call.
let result = quote! {
#input
#header
fn #test_driver() {
let body = async {
let (test_server, mut idms_delayed) = crate::testkit::setup_idm_test().await;
#test_fn(&test_server, &mut idms_delayed).await;
// Any needed teardown?
// Make sure there are no errors.
let mut idm_read_txn = test_server.proxy_read().await;
let verifications = idm_read_txn.qs_read.verify();
trace!("Verification result: {:?}", verifications);
assert!(verifications.len() == 0);
idms_delayed.check_is_empty_or_panic();
};
#[allow(clippy::expect_used, clippy::diverging_sub_expression)]
{
return #rt
.enable_all()
.build()
.expect("Failed building the Runtime")
.block_on(body);
}
}
};
result.into()
}

View file

@ -26,3 +26,8 @@ pub fn qs_test(args: TokenStream, item: TokenStream) -> TokenStream {
pub fn qs_test_no_init(args: TokenStream, item: TokenStream) -> TokenStream { pub fn qs_test_no_init(args: TokenStream, item: TokenStream) -> TokenStream {
entry::qs_test(&args, item, false) entry::qs_test(&args, item, false)
} }
#[proc_macro_attribute]
pub fn idm_test(args: TokenStream, item: TokenStream) -> TokenStream {
entry::idm_test(&args, item)
}

View file

@ -1,15 +1,11 @@
use std::time::{Duration, Instant}; use std::time::{Duration, Instant};
use async_std::task;
use criterion::{ use criterion::{
criterion_group, criterion_main, BenchmarkId, Criterion, SamplingMode, Throughput, criterion_group, criterion_main, BenchmarkId, Criterion, SamplingMode, Throughput,
}; };
use kanidmd_lib; use kanidmd_lib;
use kanidmd_lib::entry::{Entry, EntryInit, EntryNew}; use kanidmd_lib::entry::{Entry, EntryInit, EntryNew};
use kanidmd_lib::entry_init; use kanidmd_lib::entry_init;
use kanidmd_lib::idm::server::{IdmServer, IdmServerDelayed};
use kanidmd_lib::macros::run_idm_test_no_logging;
use kanidmd_lib::server::QueryServer;
use kanidmd_lib::utils::duration_from_epoch_now; use kanidmd_lib::utils::duration_from_epoch_now;
use kanidmd_lib::value::Value; use kanidmd_lib::value::Value;
@ -28,14 +24,19 @@ pub fn scaling_user_create_single(c: &mut Criterion) {
println!("iters, size -> {:?}, {:?}", iters, size); println!("iters, size -> {:?}, {:?}", iters, size);
for _i in 0..iters { for _i in 0..iters {
run_idm_test_no_logging( let mut rt = tokio::runtime::Builder::new_current_thread();
|_qs: &QueryServer, idms: &IdmServer, _idms_delayed: &IdmServerDelayed| { elapsed = rt
let ct = duration_from_epoch_now(); .enable_all()
.build()
.expect("Failed building the Runtime")
.block_on(async {
let (idms, _idms_delayed) =
kanidmd_lib::testkit::setup_idm_test().await;
let ct = duration_from_epoch_now();
let start = Instant::now(); let start = Instant::now();
for counter in 0..size { for counter in 0..size {
let idms_prox_write = task::block_on(idms.proxy_write_async(ct)); let mut idms_prox_write = idms.proxy_write(ct).await;
let name = format!("testperson_{}", counter); let name = format!("testperson_{}", counter);
let e1 = entry_init!( let e1 = entry_init!(
("class", Value::new_class("object")), ("class", Value::new_class("object")),
@ -51,9 +52,8 @@ pub fn scaling_user_create_single(c: &mut Criterion) {
idms_prox_write.commit().expect("Must not fail"); idms_prox_write.commit().expect("Must not fail");
} }
elapsed = elapsed.checked_add(start.elapsed()).unwrap(); elapsed.checked_add(start.elapsed()).unwrap()
}, });
);
} }
elapsed elapsed
}); });
@ -92,20 +92,25 @@ pub fn scaling_user_create_batched(c: &mut Criterion) {
.collect(); .collect();
for _i in 0..iters { for _i in 0..iters {
kanidmd_lib::macros::run_idm_test_no_logging( let mut rt = tokio::runtime::Builder::new_current_thread();
|_qs: &QueryServer, idms: &IdmServer, _idms_delayed: &IdmServerDelayed| { elapsed = rt
let ct = duration_from_epoch_now(); .enable_all()
.build()
.expect("Failed building the Runtime")
.block_on(async {
let (idms, _idms_delayed) =
kanidmd_lib::testkit::setup_idm_test().await;
let ct = duration_from_epoch_now();
let start = Instant::now(); let start = Instant::now();
let idms_prox_write = task::block_on(idms.proxy_write_async(ct)); let mut idms_prox_write = idms.proxy_write(ct).await;
let cr = idms_prox_write.qs_write.internal_create(data.clone()); let cr = idms_prox_write.qs_write.internal_create(data.clone());
assert!(cr.is_ok()); assert!(cr.is_ok());
idms_prox_write.commit().expect("Must not fail"); idms_prox_write.commit().expect("Must not fail");
elapsed = elapsed.checked_add(start.elapsed()).unwrap(); elapsed.checked_add(start.elapsed()).unwrap()
}, });
);
} }
elapsed elapsed
}); });

View file

@ -1472,7 +1472,6 @@ impl<'a> IdmServerCredUpdateTransaction<'a> {
mod tests { mod tests {
use std::time::Duration; use std::time::Duration;
use async_std::task;
use kanidm_proto::v1::{AuthAllowed, AuthIssueSession, AuthMech, CredentialDetailType}; use kanidm_proto::v1::{AuthAllowed, AuthIssueSession, AuthMech, CredentialDetailType};
use uuid::uuid; use uuid::uuid;
use webauthn_authenticator_rs::softpasskey::SoftPasskey; use webauthn_authenticator_rs::softpasskey::SoftPasskey;
@ -1494,13 +1493,13 @@ mod tests {
const TEST_CURRENT_TIME: u64 = 6000; const TEST_CURRENT_TIME: u64 = 6000;
const TESTPERSON_UUID: Uuid = uuid!("cf231fea-1a8f-4410-a520-fd9b1a379c86"); const TESTPERSON_UUID: Uuid = uuid!("cf231fea-1a8f-4410-a520-fd9b1a379c86");
#[test] #[idm_test]
fn test_idm_credential_update_session_init() { async fn test_idm_credential_update_session_init(
run_idm_test!(|_qs: &QueryServer,
idms: &IdmServer, idms: &IdmServer,
_idms_delayed: &mut IdmServerDelayed| { _idms_delayed: &mut IdmServerDelayed,
) {
let ct = Duration::from_secs(TEST_CURRENT_TIME); let ct = Duration::from_secs(TEST_CURRENT_TIME);
let mut idms_prox_write = task::block_on(idms.proxy_write(ct)); let mut idms_prox_write = idms.proxy_write(ct).await;
let testaccount_uuid = Uuid::new_v4(); let testaccount_uuid = Uuid::new_v4();
@ -1600,14 +1599,13 @@ mod tests {
trace!(?cur); trace!(?cur);
assert!(cur.is_err()); assert!(cur.is_err());
})
} }
fn setup_test_session( async fn setup_test_session(
idms: &IdmServer, idms: &IdmServer,
ct: Duration, ct: Duration,
) -> (CredentialUpdateSessionToken, CredentialUpdateSessionStatus) { ) -> (CredentialUpdateSessionToken, CredentialUpdateSessionStatus) {
let mut idms_prox_write = task::block_on(idms.proxy_write(ct)); let mut idms_prox_write = idms.proxy_write(ct).await;
let e2 = entry_init!( let e2 = entry_init!(
("class", Value::new_class("object")), ("class", Value::new_class("object")),
@ -1638,11 +1636,11 @@ mod tests {
cur.expect("Failed to start update") cur.expect("Failed to start update")
} }
fn renew_test_session( async fn renew_test_session(
idms: &IdmServer, idms: &IdmServer,
ct: Duration, ct: Duration,
) -> (CredentialUpdateSessionToken, CredentialUpdateSessionStatus) { ) -> (CredentialUpdateSessionToken, CredentialUpdateSessionStatus) {
let mut idms_prox_write = task::block_on(idms.proxy_write(ct)); let mut idms_prox_write = idms.proxy_write(ct).await;
let testperson = idms_prox_write let testperson = idms_prox_write
.qs_write .qs_write
@ -1659,8 +1657,8 @@ mod tests {
cur.expect("Failed to start update") cur.expect("Failed to start update")
} }
fn commit_session(idms: &IdmServer, ct: Duration, cust: CredentialUpdateSessionToken) { async fn commit_session(idms: &IdmServer, ct: Duration, cust: CredentialUpdateSessionToken) {
let mut idms_prox_write = task::block_on(idms.proxy_write(ct)); let mut idms_prox_write = idms.proxy_write(ct).await;
idms_prox_write idms_prox_write
.commit_credential_update(&cust, ct) .commit_credential_update(&cust, ct)
@ -1669,7 +1667,7 @@ mod tests {
idms_prox_write.commit().expect("Failed to commit txn"); idms_prox_write.commit().expect("Failed to commit txn");
} }
fn check_testperson_password( async fn check_testperson_password(
idms: &IdmServer, idms: &IdmServer,
idms_delayed: &mut IdmServerDelayed, idms_delayed: &mut IdmServerDelayed,
pw: &str, pw: &str,
@ -1679,7 +1677,7 @@ mod tests {
let auth_init = AuthEvent::named_init("testperson"); let auth_init = AuthEvent::named_init("testperson");
let r1 = task::block_on(idms_auth.auth(&auth_init, ct)); let r1 = idms_auth.auth(&auth_init, ct).await;
let ar = r1.unwrap(); let ar = r1.unwrap();
let AuthResult { let AuthResult {
sessionid, sessionid,
@ -1694,7 +1692,7 @@ mod tests {
let auth_begin = AuthEvent::begin_mech(sessionid, AuthMech::Password); let auth_begin = AuthEvent::begin_mech(sessionid, AuthMech::Password);
let r2 = task::block_on(idms_auth.auth(&auth_begin, ct)); let r2 = idms_auth.auth(&auth_begin, ct).await;
let ar = r2.unwrap(); let ar = r2.unwrap();
let AuthResult { let AuthResult {
sessionid, sessionid,
@ -1707,7 +1705,7 @@ mod tests {
let pw_step = AuthEvent::cred_step_password(sessionid, pw); let pw_step = AuthEvent::cred_step_password(sessionid, pw);
// Expect success // Expect success
let r2 = task::block_on(idms_auth.auth(&pw_step, ct)); let r2 = idms_auth.auth(&pw_step, ct).await;
debug!("r2 ==> {:?}", r2); debug!("r2 ==> {:?}", r2);
idms_auth.commit().expect("Must not fail"); idms_auth.commit().expect("Must not fail");
@ -1727,7 +1725,7 @@ mod tests {
} }
} }
fn check_testperson_password_totp( async fn check_testperson_password_totp(
idms: &IdmServer, idms: &IdmServer,
idms_delayed: &mut IdmServerDelayed, idms_delayed: &mut IdmServerDelayed,
pw: &str, pw: &str,
@ -1738,7 +1736,7 @@ mod tests {
let auth_init = AuthEvent::named_init("testperson"); let auth_init = AuthEvent::named_init("testperson");
let r1 = task::block_on(idms_auth.auth(&auth_init, ct)); let r1 = idms_auth.auth(&auth_init, ct).await;
let ar = r1.unwrap(); let ar = r1.unwrap();
let AuthResult { let AuthResult {
sessionid, sessionid,
@ -1753,7 +1751,7 @@ mod tests {
let auth_begin = AuthEvent::begin_mech(sessionid, AuthMech::PasswordMfa); let auth_begin = AuthEvent::begin_mech(sessionid, AuthMech::PasswordMfa);
let r2 = task::block_on(idms_auth.auth(&auth_begin, ct)); let r2 = idms_auth.auth(&auth_begin, ct).await;
let ar = r2.unwrap(); let ar = r2.unwrap();
let AuthResult { let AuthResult {
sessionid, sessionid,
@ -1768,7 +1766,7 @@ mod tests {
.expect("Failed to perform totp step"); .expect("Failed to perform totp step");
let totp_step = AuthEvent::cred_step_totp(sessionid, totp); let totp_step = AuthEvent::cred_step_totp(sessionid, totp);
let r2 = task::block_on(idms_auth.auth(&totp_step, ct)); let r2 = idms_auth.auth(&totp_step, ct).await;
let ar = r2.unwrap(); let ar = r2.unwrap();
let AuthResult { let AuthResult {
sessionid, sessionid,
@ -1781,7 +1779,7 @@ mod tests {
let pw_step = AuthEvent::cred_step_password(sessionid, pw); let pw_step = AuthEvent::cred_step_password(sessionid, pw);
// Expect success // Expect success
let r3 = task::block_on(idms_auth.auth(&pw_step, ct)); let r3 = idms_auth.auth(&pw_step, ct).await;
debug!("r3 ==> {:?}", r3); debug!("r3 ==> {:?}", r3);
idms_auth.commit().expect("Must not fail"); idms_auth.commit().expect("Must not fail");
@ -1800,7 +1798,7 @@ mod tests {
} }
} }
fn check_testperson_password_backup_code( async fn check_testperson_password_backup_code(
idms: &IdmServer, idms: &IdmServer,
idms_delayed: &mut IdmServerDelayed, idms_delayed: &mut IdmServerDelayed,
pw: &str, pw: &str,
@ -1811,7 +1809,7 @@ mod tests {
let auth_init = AuthEvent::named_init("testperson"); let auth_init = AuthEvent::named_init("testperson");
let r1 = task::block_on(idms_auth.auth(&auth_init, ct)); let r1 = idms_auth.auth(&auth_init, ct).await;
let ar = r1.unwrap(); let ar = r1.unwrap();
let AuthResult { let AuthResult {
sessionid, sessionid,
@ -1826,7 +1824,7 @@ mod tests {
let auth_begin = AuthEvent::begin_mech(sessionid, AuthMech::PasswordMfa); let auth_begin = AuthEvent::begin_mech(sessionid, AuthMech::PasswordMfa);
let r2 = task::block_on(idms_auth.auth(&auth_begin, ct)); let r2 = idms_auth.auth(&auth_begin, ct).await;
let ar = r2.unwrap(); let ar = r2.unwrap();
let AuthResult { let AuthResult {
sessionid, sessionid,
@ -1837,7 +1835,7 @@ mod tests {
assert!(matches!(state, AuthState::Continue(_))); assert!(matches!(state, AuthState::Continue(_)));
let code_step = AuthEvent::cred_step_backup_code(sessionid, code); let code_step = AuthEvent::cred_step_backup_code(sessionid, code);
let r2 = task::block_on(idms_auth.auth(&code_step, ct)); let r2 = idms_auth.auth(&code_step, ct).await;
let ar = r2.unwrap(); let ar = r2.unwrap();
let AuthResult { let AuthResult {
sessionid, sessionid,
@ -1850,7 +1848,7 @@ mod tests {
let pw_step = AuthEvent::cred_step_password(sessionid, pw); let pw_step = AuthEvent::cred_step_password(sessionid, pw);
// Expect success // Expect success
let r3 = task::block_on(idms_auth.auth(&pw_step, ct)); let r3 = idms_auth.auth(&pw_step, ct).await;
debug!("r3 ==> {:?}", r3); debug!("r3 ==> {:?}", r3);
idms_auth.commit().expect("Must not fail"); idms_auth.commit().expect("Must not fail");
@ -1863,7 +1861,7 @@ mod tests {
// There now should be a backup code invalidation present // There now should be a backup code invalidation present
let da = idms_delayed.try_recv().expect("invalid"); let da = idms_delayed.try_recv().expect("invalid");
assert!(matches!(da, DelayedAction::BackupCodeRemoval(_))); assert!(matches!(da, DelayedAction::BackupCodeRemoval(_)));
let r = task::block_on(idms.delayed_action(ct, da)); let r = idms.delayed_action(ct, da).await;
assert!(r.is_ok()); assert!(r.is_ok());
// Process the auth session // Process the auth session
@ -1875,7 +1873,7 @@ mod tests {
} }
} }
fn check_testperson_passkey( async fn check_testperson_passkey(
idms: &IdmServer, idms: &IdmServer,
idms_delayed: &mut IdmServerDelayed, idms_delayed: &mut IdmServerDelayed,
wa: &mut WebauthnAuthenticator<SoftPasskey>, wa: &mut WebauthnAuthenticator<SoftPasskey>,
@ -1886,7 +1884,7 @@ mod tests {
let auth_init = AuthEvent::named_init("testperson"); let auth_init = AuthEvent::named_init("testperson");
let r1 = task::block_on(idms_auth.auth(&auth_init, ct)); let r1 = idms_auth.auth(&auth_init, ct).await;
let ar = r1.unwrap(); let ar = r1.unwrap();
let AuthResult { let AuthResult {
sessionid, sessionid,
@ -1901,7 +1899,7 @@ mod tests {
let auth_begin = AuthEvent::begin_mech(sessionid, AuthMech::Passkey); let auth_begin = AuthEvent::begin_mech(sessionid, AuthMech::Passkey);
let r2 = task::block_on(idms_auth.auth(&auth_begin, ct)); let r2 = idms_auth.auth(&auth_begin, ct).await;
let ar = r2.unwrap(); let ar = r2.unwrap();
let AuthResult { let AuthResult {
sessionid, sessionid,
@ -1927,7 +1925,7 @@ mod tests {
let passkey_step = AuthEvent::cred_step_passkey(sessionid, resp); let passkey_step = AuthEvent::cred_step_passkey(sessionid, resp);
let r3 = task::block_on(idms_auth.auth(&passkey_step, ct)); let r3 = idms_auth.auth(&passkey_step, ct).await;
debug!("r3 ==> {:?}", r3); debug!("r3 ==> {:?}", r3);
idms_auth.commit().expect("Must not fail"); idms_auth.commit().expect("Must not fail");
@ -1940,7 +1938,7 @@ mod tests {
// Process the webauthn update // Process the webauthn update
let da = idms_delayed.try_recv().expect("invalid"); let da = idms_delayed.try_recv().expect("invalid");
assert!(matches!(da, DelayedAction::WebauthnCounterIncrement(_))); assert!(matches!(da, DelayedAction::WebauthnCounterIncrement(_)));
let r = task::block_on(idms.delayed_action(ct, da)); let r = idms.delayed_action(ct, da).await;
assert!(r.is_ok()); assert!(r.is_ok());
// Process the auth session // Process the auth session
@ -1953,13 +1951,13 @@ mod tests {
} }
} }
#[test] #[idm_test]
fn test_idm_credential_update_session_cleanup() { async fn test_idm_credential_update_session_cleanup(
run_idm_test!(|_qs: &QueryServer,
idms: &IdmServer, idms: &IdmServer,
_idms_delayed: &mut IdmServerDelayed| { _idms_delayed: &mut IdmServerDelayed,
) {
let ct = Duration::from_secs(TEST_CURRENT_TIME); let ct = Duration::from_secs(TEST_CURRENT_TIME);
let (cust, _) = setup_test_session(idms, ct); let (cust, _) = setup_test_session(idms, ct).await;
let cutxn = idms.cred_update_transaction(); let cutxn = idms.cred_update_transaction();
// The session exists // The session exists
@ -1968,7 +1966,8 @@ mod tests {
drop(cutxn); drop(cutxn);
// Making a new session is what triggers the clean of old sessions. // Making a new session is what triggers the clean of old sessions.
let _ = renew_test_session(idms, ct + MAXIMUM_CRED_UPDATE_TTL + Duration::from_secs(1)); let (_cust, _) =
renew_test_session(idms, ct + MAXIMUM_CRED_UPDATE_TTL + Duration::from_secs(1)).await;
let cutxn = idms.cred_update_transaction(); let cutxn = idms.cred_update_transaction();
@ -1978,18 +1977,17 @@ mod tests {
.credential_update_status(&cust, ct) .credential_update_status(&cust, ct)
.expect_err("Session is still valid!"); .expect_err("Session is still valid!");
assert!(matches!(c_status, OperationError::InvalidState)); assert!(matches!(c_status, OperationError::InvalidState));
})
} }
#[test] #[idm_test]
fn test_idm_credential_update_onboarding_create_new_pw() { async fn test_idm_credential_update_onboarding_create_new_pw(
run_idm_test!( idms: &IdmServer,
|_qs: &QueryServer, idms: &IdmServer, idms_delayed: &mut IdmServerDelayed| { idms_delayed: &mut IdmServerDelayed,
let test_pw = ) {
"fo3EitierohF9AelaNgiem0Ei6vup4equo1Oogeevaetehah8Tobeengae3Ci0ooh0uki"; let test_pw = "fo3EitierohF9AelaNgiem0Ei6vup4equo1Oogeevaetehah8Tobeengae3Ci0ooh0uki";
let ct = Duration::from_secs(TEST_CURRENT_TIME); let ct = Duration::from_secs(TEST_CURRENT_TIME);
let (cust, _) = setup_test_session(idms, ct); let (cust, _) = setup_test_session(idms, ct).await;
let cutxn = idms.cred_update_transaction(); let cutxn = idms.cred_update_transaction();
@ -2013,13 +2011,15 @@ mod tests {
assert!(c_status.can_commit); assert!(c_status.can_commit);
drop(cutxn); drop(cutxn);
commit_session(idms, ct, cust); commit_session(idms, ct, cust).await;
// Check it works! // Check it works!
assert!(check_testperson_password(idms, idms_delayed, test_pw, ct).is_some()); assert!(check_testperson_password(idms, idms_delayed, test_pw, ct)
.await
.is_some());
// Test deleting the pw // Test deleting the pw
let (cust, _) = renew_test_session(idms, ct); let (cust, _) = renew_test_session(idms, ct).await;
let cutxn = idms.cred_update_transaction(); let cutxn = idms.cred_update_transaction();
let c_status = cutxn let c_status = cutxn
@ -2035,12 +2035,12 @@ mod tests {
assert!(c_status.primary.is_none()); assert!(c_status.primary.is_none());
drop(cutxn); drop(cutxn);
commit_session(idms, ct, cust); commit_session(idms, ct, cust).await;
// Must fail now! // Must fail now!
assert!(check_testperson_password(idms, idms_delayed, test_pw, ct).is_none()); assert!(check_testperson_password(idms, idms_delayed, test_pw, ct)
} .await
) .is_none());
} }
// Test set of primary account password // Test set of primary account password
@ -2048,15 +2048,15 @@ mod tests {
// - set correctly. // - set correctly.
// - setup TOTP // - setup TOTP
#[test] #[idm_test]
fn test_idm_credential_update_onboarding_create_new_mfa_totp_basic() { async fn test_idm_credential_update_onboarding_create_new_mfa_totp_basic(
run_idm_test!( idms: &IdmServer,
|_qs: &QueryServer, idms: &IdmServer, idms_delayed: &mut IdmServerDelayed| { idms_delayed: &mut IdmServerDelayed,
let test_pw = ) {
"fo3EitierohF9AelaNgiem0Ei6vup4equo1Oogeevaetehah8Tobeengae3Ci0ooh0uki"; let test_pw = "fo3EitierohF9AelaNgiem0Ei6vup4equo1Oogeevaetehah8Tobeengae3Ci0ooh0uki";
let ct = Duration::from_secs(TEST_CURRENT_TIME); let ct = Duration::from_secs(TEST_CURRENT_TIME);
let (cust, _) = setup_test_session(idms, ct); let (cust, _) = setup_test_session(idms, ct).await;
let cutxn = idms.cred_update_transaction(); let cutxn = idms.cred_update_transaction();
// Setup the PW // Setup the PW
@ -2108,21 +2108,18 @@ mod tests {
// Should be okay now! // Should be okay now!
drop(cutxn); drop(cutxn);
commit_session(idms, ct, cust); commit_session(idms, ct, cust).await;
// Check it works! // Check it works!
assert!(check_testperson_password_totp( assert!(
idms, check_testperson_password_totp(idms, idms_delayed, test_pw, &totp_token, ct)
idms_delayed, .await
test_pw, .is_some()
&totp_token, );
ct
)
.is_some());
// No need to test delete of the whole cred, we already did with pw above. // No need to test delete of the whole cred, we already did with pw above.
// If we remove TOTP, show it reverts back. // If we remove TOTP, show it reverts back.
let (cust, _) = renew_test_session(idms, ct); let (cust, _) = renew_test_session(idms, ct).await;
let cutxn = idms.cred_update_transaction(); let cutxn = idms.cred_update_transaction();
let c_status = cutxn let c_status = cutxn
@ -2136,24 +2133,24 @@ mod tests {
)); ));
drop(cutxn); drop(cutxn);
commit_session(idms, ct, cust); commit_session(idms, ct, cust).await;
// Check it works with totp removed. // Check it works with totp removed.
assert!(check_testperson_password(idms, idms_delayed, test_pw, ct).is_some()); assert!(check_testperson_password(idms, idms_delayed, test_pw, ct)
} .await
) .is_some());
} }
// Check sha1 totp. // Check sha1 totp.
#[test] #[idm_test]
fn test_idm_credential_update_onboarding_create_new_mfa_totp_sha1() { async fn test_idm_credential_update_onboarding_create_new_mfa_totp_sha1(
run_idm_test!( idms: &IdmServer,
|_qs: &QueryServer, idms: &IdmServer, idms_delayed: &mut IdmServerDelayed| { idms_delayed: &mut IdmServerDelayed,
let test_pw = ) {
"fo3EitierohF9AelaNgiem0Ei6vup4equo1Oogeevaetehah8Tobeengae3Ci0ooh0uki"; let test_pw = "fo3EitierohF9AelaNgiem0Ei6vup4equo1Oogeevaetehah8Tobeengae3Ci0ooh0uki";
let ct = Duration::from_secs(TEST_CURRENT_TIME); let ct = Duration::from_secs(TEST_CURRENT_TIME);
let (cust, _) = setup_test_session(idms, ct); let (cust, _) = setup_test_session(idms, ct).await;
let cutxn = idms.cred_update_transaction(); let cutxn = idms.cred_update_transaction();
// Setup the PW // Setup the PW
@ -2208,31 +2205,26 @@ mod tests {
// Should be okay now! // Should be okay now!
drop(cutxn); drop(cutxn);
commit_session(idms, ct, cust); commit_session(idms, ct, cust).await;
// Check it works! // Check it works!
assert!(check_testperson_password_totp( assert!(
idms, check_testperson_password_totp(idms, idms_delayed, test_pw, &totp_token, ct)
idms_delayed, .await
test_pw, .is_some()
&totp_token, );
ct
)
.is_some());
// No need to test delete, we already did with pw above. // No need to test delete, we already did with pw above.
} }
)
}
#[test] #[idm_test]
fn test_idm_credential_update_onboarding_create_new_mfa_totp_backup_codes() { async fn test_idm_credential_update_onboarding_create_new_mfa_totp_backup_codes(
run_idm_test!( idms: &IdmServer,
|_qs: &QueryServer, idms: &IdmServer, idms_delayed: &mut IdmServerDelayed| { idms_delayed: &mut IdmServerDelayed,
let test_pw = ) {
"fo3EitierohF9AelaNgiem0Ei6vup4equo1Oogeevaetehah8Tobeengae3Ci0ooh0uki"; let test_pw = "fo3EitierohF9AelaNgiem0Ei6vup4equo1Oogeevaetehah8Tobeengae3Ci0ooh0uki";
let ct = Duration::from_secs(TEST_CURRENT_TIME); let ct = Duration::from_secs(TEST_CURRENT_TIME);
let (cust, _) = setup_test_session(idms, ct); let (cust, _) = setup_test_session(idms, ct).await;
let cutxn = idms.cred_update_transaction(); let cutxn = idms.cred_update_transaction();
// Setup the PW // Setup the PW
@ -2293,7 +2285,7 @@ mod tests {
// Should be okay now! // Should be okay now!
drop(cutxn); drop(cutxn);
commit_session(idms, ct, cust); commit_session(idms, ct, cust).await;
let backup_code = codes.iter().next().expect("No codes available"); let backup_code = codes.iter().next().expect("No codes available");
@ -2305,10 +2297,11 @@ mod tests {
backup_code, backup_code,
ct ct
) )
.await
.is_some()); .is_some());
// Renew to start the next steps // Renew to start the next steps
let (cust, _) = renew_test_session(idms, ct); let (cust, _) = renew_test_session(idms, ct).await;
let cutxn = idms.cred_update_transaction(); let cutxn = idms.cred_update_transaction();
// Only 7 codes left. // Only 7 codes left.
@ -2358,20 +2351,18 @@ mod tests {
)); ));
drop(cutxn); drop(cutxn);
commit_session(idms, ct, cust); commit_session(idms, ct, cust).await;
}
)
} }
#[test] #[idm_test]
fn test_idm_credential_update_onboarding_cancel_inprogress_totp() { async fn test_idm_credential_update_onboarding_cancel_inprogress_totp(
run_idm_test!( idms: &IdmServer,
|_qs: &QueryServer, idms: &IdmServer, idms_delayed: &mut IdmServerDelayed| { idms_delayed: &mut IdmServerDelayed,
let test_pw = ) {
"fo3EitierohF9AelaNgiem0Ei6vup4equo1Oogeevaetehah8Tobeengae3Ci0ooh0uki"; let test_pw = "fo3EitierohF9AelaNgiem0Ei6vup4equo1Oogeevaetehah8Tobeengae3Ci0ooh0uki";
let ct = Duration::from_secs(TEST_CURRENT_TIME); let ct = Duration::from_secs(TEST_CURRENT_TIME);
let (cust, _) = setup_test_session(idms, ct); let (cust, _) = setup_test_session(idms, ct).await;
let cutxn = idms.cred_update_transaction(); let cutxn = idms.cred_update_transaction();
// Setup the PW // Setup the PW
@ -2402,12 +2393,12 @@ mod tests {
assert!(c_status.can_commit); assert!(c_status.can_commit);
drop(cutxn); drop(cutxn);
commit_session(idms, ct, cust); commit_session(idms, ct, cust).await;
// It's pw only, since we canceled TOTP // It's pw only, since we canceled TOTP
assert!(check_testperson_password(idms, idms_delayed, test_pw, ct).is_some()); assert!(check_testperson_password(idms, idms_delayed, test_pw, ct)
} .await
) .is_some());
} }
// Primary cred must be pw or pwmfa // Primary cred must be pw or pwmfa
@ -2416,13 +2407,14 @@ mod tests {
// - remove webauthn // - remove webauthn
// - test mulitple webauthn token. // - test mulitple webauthn token.
#[test] #[idm_test]
fn test_idm_credential_update_onboarding_create_new_passkey() { async fn test_idm_credential_update_onboarding_create_new_passkey(
run_idm_test!( idms: &IdmServer,
|_qs: &QueryServer, idms: &IdmServer, idms_delayed: &mut IdmServerDelayed| { idms_delayed: &mut IdmServerDelayed,
) {
let ct = Duration::from_secs(TEST_CURRENT_TIME); let ct = Duration::from_secs(TEST_CURRENT_TIME);
let (cust, _) = setup_test_session(idms, ct); let (cust, _) = setup_test_session(idms, ct).await;
let cutxn = idms.cred_update_transaction(); let cutxn = idms.cred_update_transaction();
let origin = cutxn.get_origin().clone(); let origin = cutxn.get_origin().clone();
@ -2468,16 +2460,17 @@ mod tests {
// Commit // Commit
drop(cutxn); drop(cutxn);
commit_session(idms, ct, cust); commit_session(idms, ct, cust).await;
// Do an auth test // Do an auth test
assert!( assert!(
check_testperson_passkey(idms, idms_delayed, &mut wa, origin.clone(), ct) check_testperson_passkey(idms, idms_delayed, &mut wa, origin.clone(), ct)
.await
.is_some() .is_some()
); );
// Now test removing the token // Now test removing the token
let (cust, _) = renew_test_session(idms, ct); let (cust, _) = renew_test_session(idms, ct).await;
let cutxn = idms.cred_update_transaction(); let cutxn = idms.cred_update_transaction();
trace!(?c_status); trace!(?c_status);
@ -2493,15 +2486,15 @@ mod tests {
assert!(c_status.passkeys.is_empty()); assert!(c_status.passkeys.is_empty());
drop(cutxn); drop(cutxn);
commit_session(idms, ct, cust); commit_session(idms, ct, cust).await;
// Must fail now! // Must fail now!
assert!( assert!(
check_testperson_passkey(idms, idms_delayed, &mut wa, origin, ct).is_none() check_testperson_passkey(idms, idms_delayed, &mut wa, origin, ct)
.await
.is_none()
); );
} }
)
}
// W_ policy, assert can't remove MFA if it's enforced. // W_ policy, assert can't remove MFA if it's enforced.

View file

@ -1,4 +1,3 @@
use core::task::{Context, Poll};
use std::convert::TryFrom; use std::convert::TryFrom;
use std::str::FromStr; use std::str::FromStr;
use std::sync::Arc; use std::sync::Arc;
@ -11,8 +10,6 @@ use concread::cowcell::{CowCellReadTxn, CowCellWriteTxn};
use concread::hashmap::HashMap; use concread::hashmap::HashMap;
use concread::CowCell; use concread::CowCell;
use fernet::Fernet; use fernet::Fernet;
// #[cfg(any(test,bench))]
use futures::task as futures_task;
use hashbrown::HashSet; use hashbrown::HashSet;
use kanidm_proto::v1::{ use kanidm_proto::v1::{
ApiToken, BackupCodesView, CredentialStatus, PasswordFeedback, RadiusAuthToken, UatPurpose, ApiToken, BackupCodesView, CredentialStatus, PasswordFeedback, RadiusAuthToken, UatPurpose,
@ -343,8 +340,12 @@ impl IdmServer {
} }
impl IdmServerDelayed { impl IdmServerDelayed {
// #[cfg(any(test,bench))] // I think we can just make this async in the future?
#[cfg(test)]
pub(crate) fn check_is_empty_or_panic(&mut self) { pub(crate) fn check_is_empty_or_panic(&mut self) {
use core::task::{Context, Poll};
use futures::task as futures_task;
let waker = futures_task::noop_waker(); let waker = futures_task::noop_waker();
let mut cx = Context::from_waker(&waker); let mut cx = Context::from_waker(&waker);
match self.async_rx.poll_recv(&mut cx) { match self.async_rx.poll_recv(&mut cx) {
@ -365,6 +366,9 @@ impl IdmServerDelayed {
#[cfg(test)] #[cfg(test)]
pub(crate) fn try_recv(&mut self) -> Result<DelayedAction, OperationError> { pub(crate) fn try_recv(&mut self) -> Result<DelayedAction, OperationError> {
use core::task::{Context, Poll};
use futures::task as futures_task;
let waker = futures_task::noop_waker(); let waker = futures_task::noop_waker();
let mut cx = Context::from_waker(&waker); let mut cx = Context::from_waker(&waker);
match self.async_rx.poll_recv(&mut cx) { match self.async_rx.poll_recv(&mut cx) {

View file

@ -375,24 +375,22 @@ mod tests {
use kanidm_proto::v1::ApiToken; use kanidm_proto::v1::ApiToken;
use super::{DestroyApiTokenEvent, GenerateApiTokenEvent}; use super::{DestroyApiTokenEvent, GenerateApiTokenEvent};
// use crate::prelude::*;
use crate::event::CreateEvent; use crate::event::CreateEvent;
use crate::idm::server::IdmServerTransaction; use crate::idm::server::IdmServerTransaction;
use crate::prelude::*;
use async_std::task;
const TEST_CURRENT_TIME: u64 = 6000; const TEST_CURRENT_TIME: u64 = 6000;
#[test] #[idm_test]
fn test_idm_service_account_api_token() { async fn test_idm_service_account_api_token(
run_idm_test!(|_qs: &QueryServer,
idms: &IdmServer, idms: &IdmServer,
_idms_delayed: &mut IdmServerDelayed| { _idms_delayed: &mut IdmServerDelayed,
) {
let ct = Duration::from_secs(TEST_CURRENT_TIME); let ct = Duration::from_secs(TEST_CURRENT_TIME);
let past_grc = Duration::from_secs(TEST_CURRENT_TIME + 1) + GRACE_WINDOW; let past_grc = Duration::from_secs(TEST_CURRENT_TIME + 1) + GRACE_WINDOW;
let exp = Duration::from_secs(TEST_CURRENT_TIME + 6000); let exp = Duration::from_secs(TEST_CURRENT_TIME + 6000);
let post_exp = Duration::from_secs(TEST_CURRENT_TIME + 6010); let post_exp = Duration::from_secs(TEST_CURRENT_TIME + 6010);
let mut idms_prox_write = task::block_on(idms.proxy_write(ct)); let mut idms_prox_write = idms.proxy_write(ct).await;
let testaccount_uuid = Uuid::new_v4(); let testaccount_uuid = Uuid::new_v4();
@ -443,10 +441,8 @@ mod tests {
); );
// Delete session // Delete session
let dte = DestroyApiTokenEvent::new_internal( let dte =
apitoken_inner.account_id, DestroyApiTokenEvent::new_internal(apitoken_inner.account_id, apitoken_inner.token_id);
apitoken_inner.token_id,
);
assert!(idms_prox_write assert!(idms_prox_write
.service_account_destroy_api_token(&dte) .service_account_destroy_api_token(&dte)
.is_ok()); .is_ok());
@ -467,6 +463,5 @@ mod tests {
); );
assert!(idms_prox_write.commit().is_ok()); assert!(idms_prox_write.commit().is_ok());
});
} }
} }

View file

@ -52,8 +52,7 @@ mod repl;
pub mod schema; pub mod schema;
pub mod server; pub mod server;
pub mod status; pub mod status;
#[cfg(test)] pub mod testkit;
mod testkit;
/// A prelude of imports that should be imported by all other Kanidm modules to /// A prelude of imports that should be imported by all other Kanidm modules to
/// help make imports cleaner. /// help make imports cleaner.
@ -79,6 +78,7 @@ pub mod prelude {
FilterInvalid, FC, FilterInvalid, FC,
}; };
pub use crate::identity::{AccessScope, IdentType, Identity, IdentityId}; pub use crate::identity::{AccessScope, IdentType, Identity, IdentityId};
pub use crate::idm::server::{IdmServer, IdmServerDelayed};
pub use crate::modify::{m_pres, m_purge, m_remove, Modify, ModifyInvalid, ModifyList}; pub use crate::modify::{m_pres, m_purge, m_remove, Modify, ModifyInvalid, ModifyList};
pub use crate::server::{ pub use crate::server::{
QueryServer, QueryServerReadTransaction, QueryServerTransaction, QueryServer, QueryServerReadTransaction, QueryServerTransaction,

View file

@ -1,3 +1,4 @@
#[cfg(test)]
macro_rules! setup_test { macro_rules! setup_test {
() => {{ () => {{
let _ = sketching::test_init(); let _ = sketching::test_init();
@ -71,6 +72,7 @@ macro_rules! entry_str_to_account {
}}; }};
} }
#[cfg(test)]
macro_rules! run_idm_test_inner { macro_rules! run_idm_test_inner {
($test_fn:expr) => {{ ($test_fn:expr) => {{
#[allow(unused_imports)] #[allow(unused_imports)]
@ -112,18 +114,6 @@ macro_rules! run_idm_test {
}}; }};
} }
pub fn run_idm_test_no_logging<F>(mut test_fn: F)
where
F: FnMut(
&crate::server::QueryServer,
&crate::idm::server::IdmServer,
&crate::idm::server::IdmServerDelayed,
),
{
sketching::test_init();
run_idm_test_inner!(test_fn);
}
// Test helpers for all plugins. // Test helpers for all plugins.
// #[macro_export] // #[macro_export]
#[cfg(test)] #[cfg(test)]

View file

@ -1603,6 +1603,7 @@ impl Schema {
} }
} }
#[cfg(any(test))]
pub(crate) fn write_blocking(&self) -> SchemaWriteTransaction<'_> { pub(crate) fn write_blocking(&self) -> SchemaWriteTransaction<'_> {
self.write() self.write()
} }

View file

@ -820,7 +820,7 @@ impl<'a> QueryServerReadTransaction<'a> {
// Verify the data content of the server is as expected. This will probably // Verify the data content of the server is as expected. This will probably
// call various functions for validation, including possibly plugin // call various functions for validation, including possibly plugin
// verifications. // verifications.
fn verify(&mut self) -> Vec<Result<(), ConsistencyError>> { pub(crate) fn verify(&mut self) -> Vec<Result<(), ConsistencyError>> {
// If we fail after backend, we need to return NOW because we can't // If we fail after backend, we need to return NOW because we can't
// assert any other faith in the DB states. // assert any other faith in the DB states.
// * backend // * backend

View file

@ -19,3 +19,13 @@ pub async fn setup_test() -> QueryServer {
// Init is called via the proc macro // Init is called via the proc macro
qs qs
} }
pub async fn setup_idm_test() -> (IdmServer, IdmServerDelayed) {
let qs = setup_test().await;
qs.initialise_helper(duration_from_epoch_now())
.await
.expect("init failed!");
IdmServer::new(qs, "https://idm.example.com").expect("Failed to setup idms")
}