Merge branch 'rust-dht-tests' into 'main'

Rust DHT test suite

See merge request veilid/veilid!227
This commit is contained in:
Christien Rioux 2023-10-21 23:29:09 +00:00
commit 5691e5a6ac
4 changed files with 335 additions and 3 deletions

View File

@ -1,3 +1,4 @@
pub mod test_dht;
pub mod test_protected_store; pub mod test_protected_store;
pub mod test_veilid_config; pub mod test_veilid_config;
pub mod test_veilid_core; pub mod test_veilid_core;

View File

@ -0,0 +1,323 @@
use super::test_veilid_config::*;
use crate::*;
use lazy_static::*;
lazy_static! {
static ref BOGUS_KEY: TypedKey = TypedKey::from(CryptoTyped::new(
CRYPTO_KIND_VLD0,
CryptoKey::new([0u8; 32])
));
}
pub async fn test_get_dht_value_unopened(api: VeilidAPI) {
let rc = api.routing_context();
let result = rc.get_dht_value(*BOGUS_KEY, 0, false).await;
assert_err!(result);
}
pub async fn test_open_dht_record_nonexistent_no_writer(api: VeilidAPI) {
let rc = api.routing_context();
let result = rc.get_dht_value(*BOGUS_KEY, 0, false).await;
assert_err!(result);
}
pub async fn test_close_dht_record_nonexistent(api: VeilidAPI) {
let rc = api.routing_context();
let result = rc.close_dht_record(*BOGUS_KEY).await;
assert_err!(result);
}
pub async fn test_delete_dht_record_nonexistent(api: VeilidAPI) {
let rc = api.routing_context();
let result = rc.delete_dht_record(*BOGUS_KEY).await;
assert_err!(result);
}
pub async fn test_create_delete_dht_record_simple(api: VeilidAPI) {
let rc = api.routing_context();
let rec = rc
.create_dht_record(
DHTSchema::DFLT(DHTSchemaDFLT { o_cnt: 1 }),
Some(CRYPTO_KIND_VLD0),
)
.await
.unwrap();
let dht_key = *rec.key();
rc.close_dht_record(dht_key).await.unwrap();
rc.delete_dht_record(dht_key).await.unwrap();
}
pub async fn test_get_dht_value_nonexistent(api: VeilidAPI) {
let rc = api.routing_context();
let rec = rc
.create_dht_record(
DHTSchema::DFLT(DHTSchemaDFLT { o_cnt: 1 }),
Some(CRYPTO_KIND_VLD0),
)
.await
.unwrap();
let dht_key = *rec.key();
let result = rc.get_dht_value(dht_key, 0, false).await;
assert_eq!(result.expect("should not be error"), None);
rc.close_dht_record(dht_key).await.unwrap();
rc.delete_dht_record(dht_key).await.unwrap();
}
pub async fn test_set_get_dht_value(api: VeilidAPI) {
let rc = api.routing_context();
let rec = rc
.create_dht_record(
DHTSchema::DFLT(DHTSchemaDFLT { o_cnt: 2 }),
Some(CRYPTO_KIND_VLD0),
)
.await
.unwrap();
let dht_key = *rec.key();
let test_value = String::from("BLAH BLAH BLAH").as_bytes().to_vec();
// convert string to byte array
let set_dht_value_result = rc.set_dht_value(dht_key, 0, test_value.clone()).await;
assert_eq!(set_dht_value_result.expect("should be Ok(None)"), None);
let get_dht_value_result_0_non_force = rc.get_dht_value(dht_key, 0, false).await;
assert_eq!(
get_dht_value_result_0_non_force
.expect("should not be error")
.expect("should hold a value")
.data(),
test_value.clone()
);
// works in python, fails in rust
let get_dht_value_result_0_force = rc.get_dht_value(dht_key, 0, true).await;
assert_eq!(
get_dht_value_result_0_force
.expect("should not be error")
.expect("should hold a value")
.data(),
test_value.clone()
);
let get_dht_value_result_1_non_force = rc.get_dht_value(dht_key, 1, false).await;
assert_eq!(
get_dht_value_result_1_non_force.expect("should not be error"),
None
);
// assert_eq!(
// get_dht_value_result_0_non_force.expect("should hold value"),
// get_dht_value_result_1_non_force.expect("should hold value")
// );
rc.close_dht_record(dht_key).await.unwrap();
rc.delete_dht_record(dht_key).await.unwrap();
}
pub async fn test_open_writer_dht_value(api: VeilidAPI) {
let rc = api.routing_context();
let rec = rc
.create_dht_record(
DHTSchema::DFLT(DHTSchemaDFLT { o_cnt: 2 }),
Some(CRYPTO_KIND_VLD0),
)
.await
.unwrap();
let key = *rec.key();
let owner = rec.owner();
let secret = rec.owner_secret().unwrap();
let keypair = KeyPair::new(*owner, *secret);
let test_value_1 = String::from("Qwertyuiop Asdfghjkl Zxcvbnm")
.as_bytes()
.to_vec();
let test_data_2 = String::from("1234567890").as_bytes().to_vec();
let test_data_3 = String::from("!@#$%^&*()").as_bytes().to_vec();
// Scenario 1
// 1. Write test data 1 to subkey 1,
// 2. Read data from subkey 1, without force_refresh, check data, sequence and owner
// 3. Read data from subkey 0, should return an error
// 4. Write test data to subkey 0
// 5. Read data from subkey 0 with force_refresh, check data
// 6. Read data from subkey 1 with force_refresh, check data
// 7. Overwrite value 1 twice, check that there's no errors
let set_dht_test_value_1_result = rc.set_dht_value(key, 1, test_value_1.clone()).await;
assert!(set_dht_test_value_1_result.is_ok());
let get_dht_value_result_1_non_force = rc.get_dht_value(key, 1, false).await;
assert!(get_dht_value_result_1_non_force.is_ok());
let get_dht_value_result_1_non_force = get_dht_value_result_1_non_force
.unwrap()
.expect("should hold value");
assert_eq!(get_dht_value_result_1_non_force.data(), test_value_1);
assert_eq!(get_dht_value_result_1_non_force.seq(), 0);
assert_eq!(get_dht_value_result_1_non_force.writer(), owner);
let get_dht_value_result_0_non_force = rc.get_dht_value(key, 0, false).await;
assert_eq!(
get_dht_value_result_0_non_force.expect("should not be error"),
None
);
let set_dht_test_value_0_result = rc.set_dht_value(key, 0, test_data_2.clone()).await;
assert!(set_dht_test_value_0_result.is_ok());
let get_dht_value_result_0_force = rc.get_dht_value(key, 0, true).await;
assert_eq!(
get_dht_value_result_0_force
.expect("should be OK(result)")
.expect("should hold value")
.data(),
test_data_2
);
let get_dht_value_result_1_force = rc.get_dht_value(key, 1, true).await;
assert_eq!(
get_dht_value_result_1_force
.expect("should be OK(result)")
.expect("should hold value")
.data(),
test_value_1
);
let overwrite_value_1_result_1 = rc.set_dht_value(key, 1, test_value_1.clone()).await;
assert!(overwrite_value_1_result_1.is_ok());
let overwrite_value_1_result_2 = rc.set_dht_value(key, 1, test_data_2.clone()).await;
assert!(overwrite_value_1_result_2.is_ok());
// Now that we initialized some subkeys
// and verified they stored correctly
// Delete things locally and reopen and see if we can write
// with the same writer key
rc.close_dht_record(key).await.unwrap();
rc.delete_dht_record(key).await.unwrap();
// Scenario 2
// 1. Open DHT record with existing keys
// 2. Check record key, owner, record secret and schema against original values
// 3. Write test data 3 to subkey 1, without updating a value check that it still
// holds test data 2, but sequence has incremented, check owner
// 4. Check that subkey 1 can be overwritten
// 5. Read data from subkey 1 with force_refresh, check data
let rec = rc.open_dht_record(key, Some(keypair)).await;
assert!(rec.is_ok());
let rec = rec.unwrap();
assert_eq!(rec.key().value, key.value);
assert_eq!(rec.key().kind, key.kind);
assert_eq!(rec.owner(), owner);
assert_eq!(rec.owner_secret().unwrap(), secret);
assert!(matches!(
rec.schema().clone(),
DHTSchema::DFLT(DHTSchemaDFLT { o_cnt: 2 })
));
//Verify subkey 1 can be set before it is get but newer is available online
let set_dht_test_value_1_result = rc.set_dht_value(key, 1, test_data_3.clone()).await;
assert!(set_dht_test_value_1_result.is_ok());
let vdtemp = set_dht_test_value_1_result.unwrap().unwrap();
assert_eq!(vdtemp.data(), test_data_2);
assert_eq!(vdtemp.seq(), 1);
assert_eq!(vdtemp.writer(), owner);
// Verify subkey 1 can be set a second time and it updates because seq is newer
let set_dht_test_value_1_result = rc.set_dht_value(key, 1, test_data_3.clone()).await;
assert!(set_dht_test_value_1_result.is_ok());
// Verify the network got the subkey update with a refresh check
let get_dht_value_result_1_force = rc.get_dht_value(key, 1, true).await;
assert!(get_dht_value_result_1_force.is_ok());
let get_dht_value_result_1_force = get_dht_value_result_1_force
.expect("should be OK(result)")
.expect("should hold value");
assert_eq!(get_dht_value_result_1_force.data(), test_data_3);
assert_eq!(get_dht_value_result_1_force.seq(), 2);
assert_eq!(get_dht_value_result_1_force.writer(), owner);
// Delete things locally and reopen and see if we can write
// with a different writer key (should fail)
rc.close_dht_record(key).await.unwrap();
rc.delete_dht_record(key).await.unwrap();
// Scenario 3
// 1. Open DHT record with new keypair
// 2. Check record key, owner, record secret and schema against original values
// 3. Try writing to subkey 1, expect error
// 4. Try writing to subkey 0, expect error
let cs = api.crypto().unwrap().get(key.kind).unwrap();
assert!(cs.validate_keypair(owner, secret));
let other_keypair = cs.generate_keypair();
let rec = rc.open_dht_record(key, Some(other_keypair)).await;
assert!(rec.is_ok());
let rec = rec.unwrap();
assert_eq!(rec.key().value, key.value);
assert_eq!(rec.key().kind, key.kind);
assert_eq!(rec.owner(), owner);
assert_eq!(rec.owner_secret(), None);
let schema = rec.schema().clone();
assert!(matches!(
schema,
DHTSchema::DFLT(DHTSchemaDFLT { o_cnt: 2 })
));
// Verify subkey 1 can NOT be set because we have the wrong writer
let set_dht_test_value_0_result = rc.set_dht_value(key, 1, test_value_1.clone()).await;
assert_err!(set_dht_test_value_0_result);
// Verify subkey 0 can NOT be set because we have the wrong writer
let set_dht_test_value_0_result = rc.set_dht_value(key, 0, test_value_1.clone()).await;
assert_err!(set_dht_test_value_0_result);
rc.close_dht_record(key).await.unwrap();
rc.delete_dht_record(key).await.unwrap();
}
// Network-related code to make sure veilid node is connetected to other peers
async fn wait_for_public_internet_ready(api: &VeilidAPI) {
info!("wait_for_public_internet_ready");
loop {
let state = api.get_state().await.unwrap();
if state.attachment.public_internet_ready {
break;
}
sleep(5000).await;
}
info!("wait_for_public_internet_ready, done");
}
pub async fn test_all() {
let (update_callback, config_callback) = setup_veilid_core();
let api = api_startup(update_callback, config_callback)
.await
.expect("startup failed");
let _ = api.attach().await;
wait_for_public_internet_ready(&api).await;
test_get_dht_value_unopened(api.clone()).await;
test_open_dht_record_nonexistent_no_writer(api.clone()).await;
test_close_dht_record_nonexistent(api.clone()).await;
test_delete_dht_record_nonexistent(api.clone()).await;
test_get_dht_value_nonexistent(api.clone()).await;
test_create_delete_dht_record_simple(api.clone()).await;
test_set_get_dht_value(api.clone()).await;
test_open_writer_dht_value(api.clone()).await;
api.shutdown().await;
}

View File

@ -162,7 +162,7 @@ pub fn setup_veilid_core() -> (UpdateCallback, ConfigCallback) {
(Arc::new(update_callback), Arc::new(config_callback)) (Arc::new(update_callback), Arc::new(config_callback))
} }
fn config_callback(key: String) -> ConfigCallbackReturn { pub fn config_callback(key: String) -> ConfigCallbackReturn {
match key.as_str() { match key.as_str() {
"program_name" => Ok(Box::new(String::from("VeilidCoreTests"))), "program_name" => Ok(Box::new(String::from("VeilidCoreTests"))),
"namespace" => Ok(Box::<String>::default()), "namespace" => Ok(Box::<String>::default()),
@ -191,7 +191,8 @@ fn config_callback(key: String) -> ConfigCallbackReturn {
"network.network_key_password" => Ok(Box::new(Option::<String>::None)), "network.network_key_password" => Ok(Box::new(Option::<String>::None)),
"network.routing_table.node_id" => Ok(Box::new(TypedKeyGroup::new())), "network.routing_table.node_id" => Ok(Box::new(TypedKeyGroup::new())),
"network.routing_table.node_id_secret" => Ok(Box::new(TypedSecretGroup::new())), "network.routing_table.node_id_secret" => Ok(Box::new(TypedSecretGroup::new())),
"network.routing_table.bootstrap" => Ok(Box::<Vec<String>>::default()), // "network.routing_table.bootstrap" => Ok(Box::new(Vec::<String>::new())),
"network.routing_table.bootstrap" => Ok(Box::new(vec!["bootstrap.veilid.net".to_string()])),
"network.routing_table.limit_over_attached" => Ok(Box::new(64u32)), "network.routing_table.limit_over_attached" => Ok(Box::new(64u32)),
"network.routing_table.limit_fully_attached" => Ok(Box::new(32u32)), "network.routing_table.limit_fully_attached" => Ok(Box::new(32u32)),
"network.routing_table.limit_attached_strong" => Ok(Box::new(16u32)), "network.routing_table.limit_attached_strong" => Ok(Box::new(16u32)),
@ -325,7 +326,10 @@ pub async fn test_config() {
assert_eq!(inner.network.rpc.default_route_hop_count, 1u8); assert_eq!(inner.network.rpc.default_route_hop_count, 1u8);
assert_eq!(inner.network.routing_table.node_id.len(), 0); assert_eq!(inner.network.routing_table.node_id.len(), 0);
assert_eq!(inner.network.routing_table.node_id_secret.len(), 0); assert_eq!(inner.network.routing_table.node_id_secret.len(), 0);
assert_eq!(inner.network.routing_table.bootstrap, Vec::<String>::new()); assert_eq!(
inner.network.routing_table.bootstrap,
vec!["bootstrap.veilid.net"],
);
assert_eq!(inner.network.routing_table.limit_over_attached, 64u32); assert_eq!(inner.network.routing_table.limit_over_attached, 64u32);
assert_eq!(inner.network.routing_table.limit_fully_attached, 32u32); assert_eq!(inner.network.routing_table.limit_fully_attached, 32u32);
assert_eq!(inner.network.routing_table.limit_attached_strong, 16u32); assert_eq!(inner.network.routing_table.limit_attached_strong, 16u32);

View File

@ -17,6 +17,8 @@ pub async fn run_all_tests() {
test_types::test_all().await; test_types::test_all().await;
info!("TEST: test_veilid_core"); info!("TEST: test_veilid_core");
test_veilid_core::test_all().await; test_veilid_core::test_all().await;
info!("TEST: test_dht");
test_dht::test_all().await;
info!("TEST: test_veilid_config"); info!("TEST: test_veilid_config");
test_veilid_config::test_all().await; test_veilid_config::test_all().await;
info!("TEST: test_connection_table"); info!("TEST: test_connection_table");
@ -133,5 +135,7 @@ cfg_if! {
run_test!(veilid_api, test_serialize_json); run_test!(veilid_api, test_serialize_json);
run_test!(routing_table, test_serialize_routing_table); run_test!(routing_table, test_serialize_routing_table);
run_test!(test_dht);
} }
} }