diff --git a/Cargo.lock b/Cargo.lock index 5dc2d7b22..0c441af74 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4265,6 +4265,7 @@ dependencies = [ name = "testkit-macros" version = "0.1.0" dependencies = [ + "kanidmd_core", "proc-macro2", "quote", "syn 2.0.28", diff --git a/server/core/src/config.rs b/server/core/src/config.rs index bf49211e5..4a8861a5a 100644 --- a/server/core/src/config.rs +++ b/server/core/src/config.rs @@ -245,6 +245,13 @@ impl Configuration { } } + pub fn new_for_test() -> Self { + Configuration { + threads: 1, + ..Configuration::new() + } + } + pub fn update_online_backup(&mut self, cfg: &Option) { match cfg { None => {} diff --git a/server/testkit-macros/Cargo.toml b/server/testkit-macros/Cargo.toml index 2264ded80..f4ae31db5 100644 --- a/server/testkit-macros/Cargo.toml +++ b/server/testkit-macros/Cargo.toml @@ -7,6 +7,7 @@ edition = "2021" proc-macro = true [dependencies] +kanidmd_core.workspace = true proc-macro2 = { workspace = true } quote = { workspace = true } syn = { workspace = true } diff --git a/server/testkit-macros/src/entry.rs b/server/testkit-macros/src/entry.rs index 9b43595b5..d6a3561da 100644 --- a/server/testkit-macros/src/entry.rs +++ b/server/testkit-macros/src/entry.rs @@ -1,10 +1,25 @@ use proc_macro::TokenStream; use proc_macro2::{Ident, Span}; -use syn::spanned::Spanned; +use syn::{parse::Parser, punctuated::Punctuated, spanned::Spanned, ExprAssign, Token}; use quote::{quote, quote_spanned, ToTokens}; -fn parse_knobs(input: &syn::ItemFn) -> TokenStream { +// for now we only allow a subset of the configuration to be tweaked, but it can be expanded in the future as needed + +const ALLOWED_ATTRIBUTES: &[&str] = &[ + "threads", + "db_path", + "maximum_request", + "trust_x_forward_for", + "role", + "output_mode", + "log_level", +]; + +fn parse_knobs( + input: &syn::ItemFn, + server_config: &Punctuated, +) -> TokenStream { // 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 @@ -23,6 +38,22 @@ fn parse_knobs(input: &syn::ItemFn) -> TokenStream { (start, end) }; + // here we gather all the provided configuration in a struct like declaration + // By now we have already checked that the configurations provided belong to the allowed subset + let mut field_modifications = quote! {}; + server_config.pairs().for_each(|p| { + let field_name = p.value().left.to_token_stream(); // here we can use to_token_stream as we know we're iterating over ExprAssigns + let field_value = p.value().right.to_token_stream(); + field_modifications.extend(quote! { + #field_name: #field_value,}) + }); + + // Setup the config filling the remaining fields with the default values + let default_config_struct = quote!(kanidmd_core::config::Configuration { + #field_modifications + ..kanidmd_core::config::Configuration::new_for_test() + }); + let rt = quote_spanned! {last_stmt_start_span=> tokio::runtime::Builder::new_current_thread() }; @@ -43,7 +74,7 @@ fn parse_knobs(input: &syn::ItemFn) -> TokenStream { #header fn #test_driver() { let body = async { - let (rsclient, mut core_handle) = kanidmd_testkit::setup_async_test().await; + let (rsclient, mut core_handle) = kanidmd_testkit::setup_async_test(#default_config_struct).await; #fn_name(rsclient).await; core_handle.shutdown().await; }; @@ -66,7 +97,7 @@ fn token_stream_with_error(mut tokens: TokenStream, error: syn::Error) -> TokenS tokens } -pub(crate) fn test(_args: &TokenStream, item: TokenStream) -> TokenStream { +pub(crate) fn test(args: TokenStream, item: TokenStream) -> TokenStream { // If any of the steps for this macro fail, we still want to expand to an item that is as close // to the expected output as possible. This helps out IDEs such that completions and other // related features keep working. @@ -84,6 +115,31 @@ pub(crate) fn test(_args: &TokenStream, item: TokenStream) -> TokenStream { 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)); } - - parse_knobs(&input) + let args: Punctuated = + match Punctuated::::parse_terminated.parse(args.clone()) { + Ok(it) => it, + Err(e) => return token_stream_with_error(args, e), + }; + let args_are_allowed = args.pairs().all(|p| { + ALLOWED_ATTRIBUTES.to_vec().contains( + &p.value() + .left + .span() + .source_text() + .unwrap_or_default() + .as_str(), + ) + }); + if !args_are_allowed { + let msg = + "Currently only a subset of all the server configs can be set. Here is the full list"; + return token_stream_with_error( + item, + syn::Error::new_spanned( + input.sig.fn_token, + format!("{}: {}", msg, ALLOWED_ATTRIBUTES.join(", ")), + ), + ); + } + parse_knobs(&input, &args) } diff --git a/server/testkit-macros/src/lib.rs b/server/testkit-macros/src/lib.rs index 463b6c0b0..42af50275 100644 --- a/server/testkit-macros/src/lib.rs +++ b/server/testkit-macros/src/lib.rs @@ -19,5 +19,5 @@ use proc_macro::TokenStream; #[proc_macro_attribute] pub fn test(args: TokenStream, item: TokenStream) -> TokenStream { - entry::test(&args, item) + entry::test(args, item) } diff --git a/server/testkit/src/lib.rs b/server/testkit/src/lib.rs index 5f884b5d6..fc67e8a59 100644 --- a/server/testkit/src/lib.rs +++ b/server/testkit/src/lib.rs @@ -15,7 +15,7 @@ use std::sync::atomic::{AtomicU16, Ordering}; use kanidm_client::{KanidmClient, KanidmClientBuilder}; use kanidm_proto::v1::{Filter, Modify, ModifyList}; -use kanidmd_core::config::{Configuration, IntegrationTestConfig, ServerRole}; +use kanidmd_core::config::{Configuration, IntegrationTestConfig}; use kanidmd_core::{create_server_core, CoreHandle}; use tokio::task; @@ -37,7 +37,7 @@ pub fn is_free_port(port: u16) -> bool { // allowed because the use of this function is behind a test gate #[allow(dead_code)] -pub async fn setup_async_test() -> (KanidmClient, CoreHandle) { +pub async fn setup_async_test(mut config: Configuration) -> (KanidmClient, CoreHandle) { sketching::test_init(); let mut counter = 0; @@ -61,14 +61,11 @@ pub async fn setup_async_test() -> (KanidmClient, CoreHandle) { let addr = format!("http://localhost:{}", port); - // Setup the config ... - let mut config = Configuration::new(); + // Setup the address and origin.. config.address = format!("127.0.0.1:{}", port); config.integration_test_config = Some(int_config); - config.role = ServerRole::WriteReplica; config.domain = "localhost".to_string(); config.origin = addr.clone(); - config.threads = 1; let core_handle = match create_server_core(config, false).await { Ok(val) => val,