mirror of
https://gitlab.com/veilid/veilid.git
synced 2024-10-01 01:26:08 -04:00
Merge branch 'rust-dht-tests' into 'main'
Rust DHT test suite See merge request veilid/veilid!227
This commit is contained in:
commit
5691e5a6ac
@ -1,3 +1,4 @@
|
||||
pub mod test_dht;
|
||||
pub mod test_protected_store;
|
||||
pub mod test_veilid_config;
|
||||
pub mod test_veilid_core;
|
||||
|
323
veilid-core/src/tests/common/test_dht.rs
Normal file
323
veilid-core/src/tests/common/test_dht.rs
Normal 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;
|
||||
}
|
@ -162,7 +162,7 @@ pub fn setup_veilid_core() -> (UpdateCallback, ConfigCallback) {
|
||||
(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() {
|
||||
"program_name" => Ok(Box::new(String::from("VeilidCoreTests"))),
|
||||
"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.routing_table.node_id" => Ok(Box::new(TypedKeyGroup::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_fully_attached" => Ok(Box::new(32u32)),
|
||||
"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.routing_table.node_id.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_fully_attached, 32u32);
|
||||
assert_eq!(inner.network.routing_table.limit_attached_strong, 16u32);
|
||||
|
@ -17,6 +17,8 @@ pub async fn run_all_tests() {
|
||||
test_types::test_all().await;
|
||||
info!("TEST: test_veilid_core");
|
||||
test_veilid_core::test_all().await;
|
||||
info!("TEST: test_dht");
|
||||
test_dht::test_all().await;
|
||||
info!("TEST: test_veilid_config");
|
||||
test_veilid_config::test_all().await;
|
||||
info!("TEST: test_connection_table");
|
||||
@ -133,5 +135,7 @@ cfg_if! {
|
||||
run_test!(veilid_api, test_serialize_json);
|
||||
|
||||
run_test!(routing_table, test_serialize_routing_table);
|
||||
|
||||
run_test!(test_dht);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user