#[cfg(test)]
mod tests {
use tempfile::tempdir;
use tracing_subscriber;
use sentinel_wal::StoreWalConfig;
use crate::{events::StoreEvent, SentinelError, Store, StoreMetadata, STORE_METADATA_FILE};
#[tokio::test]
async fn test_store_new_creates_directory() {
let temp_dir = tempdir().unwrap();
let store_path = temp_dir.path().join("store");
let _store = Store::new(&store_path, None).await.unwrap();
assert!(store_path.exists());
assert!(store_path.is_dir());
}
#[tokio::test]
async fn test_store_new_with_existing_directory() {
let temp_dir = tempdir().unwrap();
let store_path = temp_dir.path();
let _store = Store::new(&store_path, None).await.unwrap();
assert!(store_path.exists());
}
#[tokio::test]
async fn test_store_collection_creates_subdirectory() {
let temp_dir = tempdir().unwrap();
let store = Store::new(temp_dir.path(), None).await.unwrap();
let collection = store.collection("users").await.unwrap();
assert!(collection.path.exists());
assert!(collection.path.is_dir());
assert_eq!(collection.name(), "users");
}
#[tokio::test]
async fn test_store_collection_with_valid_special_characters() {
let temp_dir = tempdir().unwrap();
let store = Store::new(temp_dir.path(), None).await.unwrap();
let collection = store.collection("user_data-123").await.unwrap();
assert!(collection.path.exists());
assert_eq!(collection.name(), "user_data-123");
let collection2 = store.collection("test.collection").await.unwrap();
assert!(collection2.path.exists());
assert_eq!(collection2.name(), "test.collection");
let collection3 = store.collection("data_2024-v1.0").await.unwrap();
assert!(collection3.path.exists());
assert_eq!(collection3.name(), "data_2024-v1.0");
}
#[tokio::test]
async fn test_store_collection_multiple_calls() {
let temp_dir = tempdir().unwrap();
let store = Store::new(temp_dir.path(), None).await.unwrap();
let coll1 = store.collection("users").await.unwrap();
let coll2 = store.collection("users").await.unwrap();
assert_eq!(coll1.name(), coll2.name());
assert_eq!(coll1.path, coll2.path);
}
#[tokio::test]
async fn test_store_collection_invalid_empty_name() {
let temp_dir = tempdir().unwrap();
let store = Store::new(temp_dir.path(), None).await.unwrap();
let result = store.collection("").await;
assert!(result.is_err());
assert!(matches!(
result.unwrap_err(),
SentinelError::InvalidCollectionName { .. }
));
}
#[tokio::test]
async fn test_store_collection_invalid_path_separator() {
let temp_dir = tempdir().unwrap();
let store = Store::new(temp_dir.path(), None).await.unwrap();
let result = store.collection("path/traversal").await;
assert!(result.is_err());
assert!(matches!(
result.unwrap_err(),
SentinelError::InvalidCollectionName { .. }
));
let result = store.collection("path\\traversal").await;
assert!(result.is_err());
assert!(matches!(
result.unwrap_err(),
SentinelError::InvalidCollectionName { .. }
));
}
#[tokio::test]
async fn test_store_collection_invalid_hidden_name() {
let temp_dir = tempdir().unwrap();
let store = Store::new(temp_dir.path(), None).await.unwrap();
let result = store.collection(".hidden").await;
assert!(result.is_err());
assert!(matches!(
result.unwrap_err(),
SentinelError::InvalidCollectionName { .. }
));
}
#[tokio::test]
async fn test_store_collection_invalid_windows_reserved_names() {
let temp_dir = tempdir().unwrap();
let store = Store::new(temp_dir.path(), None).await.unwrap();
let reserved_names = vec!["CON", "PRN", "AUX", "NUL", "COM1", "LPT1"];
for name in reserved_names {
let result = store.collection(name).await;
assert!(result.is_err(), "Expected '{}' to be invalid", name);
assert!(matches!(
result.unwrap_err(),
SentinelError::InvalidCollectionName { .. }
));
let result = store.collection(&name.to_lowercase()).await;
assert!(
result.is_err(),
"Expected '{}' to be invalid",
name.to_lowercase()
);
assert!(matches!(
result.unwrap_err(),
SentinelError::InvalidCollectionName { .. }
));
}
}
#[tokio::test]
async fn test_store_collection_invalid_control_characters() {
let temp_dir = tempdir().unwrap();
let store = Store::new(temp_dir.path(), None).await.unwrap();
let result = store.collection("test\0name").await;
assert!(result.is_err());
assert!(matches!(
result.unwrap_err(),
SentinelError::InvalidCollectionName { .. }
));
let result = store.collection("test\x01name").await;
assert!(result.is_err());
assert!(matches!(
result.unwrap_err(),
SentinelError::InvalidCollectionName { .. }
));
}
#[tokio::test]
async fn test_store_collection_invalid_special_characters() {
let temp_dir = tempdir().unwrap();
let store = Store::new(temp_dir.path(), None).await.unwrap();
let invalid_chars = vec!["<", ">", ":", "\"", "|", "?", "*"];
for ch in invalid_chars {
let name = format!("test{}name", ch);
let result = store.collection(&name).await;
assert!(result.is_err(), "Expected name with '{}' to be invalid", ch);
assert!(matches!(
result.unwrap_err(),
SentinelError::InvalidCollectionName { .. }
));
}
}
#[tokio::test]
async fn test_store_collection_invalid_trailing_dot_or_space() {
let temp_dir = tempdir().unwrap();
let store = Store::new(temp_dir.path(), None).await.unwrap();
let result = store.collection("test.").await;
assert!(result.is_err());
assert!(matches!(
result.unwrap_err(),
SentinelError::InvalidCollectionName { .. }
));
let result = store.collection("test ").await;
assert!(result.is_err());
assert!(matches!(
result.unwrap_err(),
SentinelError::InvalidCollectionName { .. }
));
}
#[tokio::test]
async fn test_store_collection_valid_edge_cases() {
let temp_dir = tempdir().unwrap();
let store = Store::new(temp_dir.path(), None).await.unwrap();
let collection = store.collection("a").await.unwrap();
assert_eq!(collection.name(), "a");
let collection = store.collection("123").await.unwrap();
assert_eq!(collection.name(), "123");
let long_name = "a".repeat(255);
let collection = store.collection(&long_name).await.unwrap();
assert_eq!(collection.name(), long_name);
}
#[tokio::test]
async fn test_store_new_with_passphrase() {
let temp_dir = tempdir().unwrap();
let store = Store::new(temp_dir.path(), Some("test_passphrase"))
.await
.unwrap();
assert!(store.signing_key.is_some());
}
#[tokio::test]
async fn test_store_new_with_passphrase_load_existing() {
let temp_dir = tempdir().unwrap();
let store1 = Store::new(temp_dir.path(), Some("test_passphrase"))
.await
.unwrap();
let key1 = store1.signing_key.as_ref().unwrap().clone();
let store2 = Store::new(temp_dir.path(), Some("test_passphrase"))
.await
.unwrap();
let key2 = store2.signing_key.as_ref().unwrap().clone();
assert_eq!(key1.to_bytes(), key2.to_bytes());
}
#[tokio::test]
async fn test_store_new_with_corrupted_keys() {
let temp_dir = tempdir().unwrap();
let _store = Store::new(temp_dir.path(), Some("test_passphrase"))
.await
.unwrap();
let store2 = Store::new(temp_dir.path(), None).await.unwrap();
let keys_coll = store2.collection(".keys").await.unwrap();
let corrupted_data = serde_json::json!({
"salt": "invalid_salt",
});
keys_coll
.insert("signing_key", corrupted_data)
.await
.unwrap();
let result = Store::new(temp_dir.path(), Some("test_passphrase")).await;
assert!(result.is_err());
}
#[tokio::test]
async fn test_store_new_with_invalid_salt_hex() {
let temp_dir = tempdir().unwrap();
let _store = Store::new(temp_dir.path(), Some("test_passphrase"))
.await
.unwrap();
let store2 = Store::new(temp_dir.path(), None).await.unwrap();
let keys_coll = store2.collection(".keys").await.unwrap();
let doc = keys_coll
.get_with_verification("signing_key", &crate::VerificationOptions::disabled())
.await
.unwrap()
.unwrap();
let mut data = doc.data().clone();
data["salt"] = serde_json::Value::String("invalid_hex".to_string());
keys_coll.insert("signing_key", data).await.unwrap();
let result = Store::new(temp_dir.path(), Some("test_passphrase")).await;
assert!(result.is_err());
}
#[tokio::test]
async fn test_store_new_with_invalid_encrypted_length() {
let temp_dir = tempdir().unwrap();
let _store = Store::new(temp_dir.path(), Some("test_passphrase"))
.await
.unwrap();
let store2 = Store::new(temp_dir.path(), None).await.unwrap();
let keys_coll = store2.collection(".keys").await.unwrap();
let doc = keys_coll
.get_with_verification("signing_key", &crate::VerificationOptions::disabled())
.await
.unwrap()
.unwrap();
let mut data = doc.data().clone();
data["encrypted"] = serde_json::Value::String(hex::encode(&[0u8; 10])); keys_coll.insert("signing_key", data).await.unwrap();
let result = Store::new(temp_dir.path(), Some("test_passphrase")).await;
assert!(result.is_err());
}
#[tokio::test]
async fn test_store_new_with_corrupted_keys_missing_salt() {
let temp_dir = tempdir().unwrap();
let _store = Store::new(temp_dir.path(), Some("test_passphrase"))
.await
.unwrap();
let store2 = Store::new(temp_dir.path(), None).await.unwrap();
let keys_coll = store2.collection(".keys").await.unwrap();
let corrupted_data = serde_json::json!({
"encrypted": "some_encrypted_data"
});
keys_coll
.insert("signing_key", corrupted_data)
.await
.unwrap();
let result = Store::new(temp_dir.path(), Some("test_passphrase")).await;
assert!(result.is_err());
}
#[tokio::test]
async fn test_store_new_with_corrupted_keys_invalid_salt_hex() {
let temp_dir = tempdir().unwrap();
let _store = Store::new(temp_dir.path(), Some("test_passphrase"))
.await
.unwrap();
let store2 = Store::new(temp_dir.path(), None).await.unwrap();
let keys_coll = store2.collection(".keys").await.unwrap();
let corrupted_data = serde_json::json!({
"encrypted": "some_encrypted_data",
"salt": "invalid_hex_salt"
});
keys_coll
.insert("signing_key", corrupted_data)
.await
.unwrap();
let result = Store::new(temp_dir.path(), Some("test_passphrase")).await;
assert!(result.is_err());
}
#[tokio::test]
async fn test_store_new_with_invalid_key_length() {
let temp_dir = tempdir().unwrap();
let _store = Store::new(temp_dir.path(), Some("test_passphrase"))
.await
.unwrap();
let store2 = Store::new(temp_dir.path(), None).await.unwrap();
let keys_coll = store2.collection(".keys").await.unwrap();
let existing_doc = keys_coll
.get_with_verification("signing_key", &crate::VerificationOptions::disabled())
.await
.unwrap()
.unwrap();
let salt = existing_doc.data()["salt"].as_str().unwrap();
let encryption_key =
sentinel_crypto::derive_key_from_passphrase_with_salt("test_passphrase", &hex::decode(salt).unwrap())
.await
.unwrap();
let wrong_length_bytes = vec![0u8; 16]; let encrypted = sentinel_crypto::encrypt_data(&wrong_length_bytes, &encryption_key)
.await
.unwrap();
let corrupted_data = serde_json::json!({
"encrypted": encrypted,
"salt": salt
});
keys_coll
.insert("signing_key", corrupted_data)
.await
.unwrap();
let result = Store::new(temp_dir.path(), Some("test_passphrase")).await;
assert!(result.is_err());
}
#[tokio::test]
async fn test_store_new_creates_root_directory() {
let temp_dir = tempdir().unwrap();
let new_path = temp_dir.path().join("new_store");
assert!(!tokio::fs::metadata(&new_path).await.is_ok());
let result = Store::new(&new_path, None).await;
assert!(result.is_ok());
assert!(tokio::fs::metadata(&new_path).await.unwrap().is_dir());
}
#[tokio::test]
async fn test_delete_collection_non_existent() {
let temp_dir = tempdir().unwrap();
let store = Store::new(temp_dir.path(), None).await.unwrap();
let result = store.delete_collection("non_existent").await;
assert!(result.is_ok());
}
#[tokio::test]
async fn test_delete_collection_success() {
let temp_dir = tempdir().unwrap();
let store = Store::new(temp_dir.path(), None).await.unwrap();
let _collection = store.collection("test_delete").await.unwrap();
let collections = store.list_collections().await.unwrap();
assert!(collections.contains(&"test_delete".to_string()));
store.delete_collection("test_delete").await.unwrap();
let collections = store.list_collections().await.unwrap();
assert!(!collections.contains(&"test_delete".to_string()));
}
#[tokio::test]
async fn test_list_collections_creates_data_dir() {
let temp_dir = tempdir().unwrap();
let new_path = temp_dir.path().join("new_store");
let store = Store::new(&new_path, None).await.unwrap();
let collections = store.list_collections().await.unwrap();
assert!(collections.is_empty());
let data_path = new_path.join("data");
assert!(tokio::fs::metadata(&data_path).await.unwrap().is_dir());
}
#[tokio::test]
async fn test_list_collections_with_entries() {
let temp_dir = tempdir().unwrap();
let store = Store::new(temp_dir.path(), None).await.unwrap();
let _c1 = store.collection("collection1").await.unwrap();
let _c2 = store.collection("collection2").await.unwrap();
let _c3 = store.collection("collection3").await.unwrap();
let collections = store.list_collections().await.unwrap();
assert_eq!(collections.len(), 3);
assert!(collections.contains(&"collection1".to_string()));
assert!(collections.contains(&"collection2".to_string()));
assert!(collections.contains(&"collection3".to_string()));
}
#[tokio::test]
async fn test_store_event_sender() {
let temp_dir = tempdir().unwrap();
let store = Store::new_with_config(temp_dir.path(), None, StoreWalConfig::default())
.await
.unwrap();
let sender = store.event_sender();
assert!(sender.is_closed() == false); }
#[tokio::test]
async fn test_store_event_processor_started() {
let temp_dir = tempdir().unwrap();
let mut store = Store::new_with_config(temp_dir.path(), None, StoreWalConfig::default())
.await
.unwrap();
assert!(store.event_task.is_some());
assert!(store.event_receiver.is_none()); }
#[tokio::test]
async fn test_store_event_processor_already_started() {
let temp_dir = tempdir().unwrap();
let mut store = Store::new_with_config(temp_dir.path(), None, StoreWalConfig::default())
.await
.unwrap();
crate::store::events::start_event_processor(&mut store);
assert!(store.event_task.is_some());
}
#[tokio::test]
async fn test_store_event_processor_no_receiver() {
let temp_dir = tempdir().unwrap();
let mut store = Store::new_with_config(temp_dir.path(), None, StoreWalConfig::default())
.await
.unwrap();
assert!(store.event_task.is_some());
let _receiver = store.event_receiver.take();
crate::store::events::start_event_processor(&mut store);
assert!(store.event_task.is_some());
}
#[tokio::test]
async fn test_store_event_processing_collection_created() {
let temp_dir = tempdir().unwrap();
let store = Store::new_with_config(temp_dir.path(), None, StoreWalConfig::default())
.await
.unwrap();
let event = StoreEvent::CollectionCreated {
name: "test_collection".to_string(),
};
let _ = store.event_sender.send(event);
tokio::time::sleep(tokio::time::Duration::from_millis(600)).await;
assert_eq!(store.collection_count(), 1); }
#[tokio::test]
async fn test_store_event_processing_collection_deleted() {
let temp_dir = tempdir().unwrap();
let store = Store::new_with_config(temp_dir.path(), None, StoreWalConfig::default())
.await
.unwrap();
let create_event = StoreEvent::CollectionCreated {
name: "test_collection".to_string(),
};
let _ = store.event_sender.send(create_event);
tokio::time::sleep(tokio::time::Duration::from_millis(200)).await;
let delete_event = StoreEvent::CollectionDeleted {
name: "test_collection".to_string(),
document_count: 0,
total_size_bytes: 0,
};
let result = store.event_sender.send(delete_event);
assert!(result.is_ok(), "Failed to send CollectionDeleted event");
tokio::time::sleep(tokio::time::Duration::from_millis(600)).await;
assert_eq!(store.collection_count(), 0); assert_eq!(store.total_documents(), 0); assert_eq!(store.total_size_bytes(), 0); }
#[tokio::test]
async fn test_store_event_processing_document_inserted() {
let temp_dir = tempdir().unwrap();
let store = Store::new_with_config(temp_dir.path(), None, StoreWalConfig::default())
.await
.unwrap();
let event = StoreEvent::DocumentInserted {
collection: "test_collection".to_string(),
size_bytes: 256,
};
let _ = store.event_sender.send(event);
tokio::time::sleep(tokio::time::Duration::from_millis(600)).await;
assert_eq!(store.total_documents(), 1); assert_eq!(store.total_size_bytes(), 256); }
#[tokio::test]
async fn test_store_event_processing_document_updated() {
let temp_dir = tempdir().unwrap();
let store = Store::new_with_config(temp_dir.path(), None, StoreWalConfig::default())
.await
.unwrap();
let event = StoreEvent::DocumentUpdated {
collection: "test_collection".to_string(),
old_size_bytes: 128,
new_size_bytes: 256,
};
let _ = store.event_sender.send(event);
tokio::time::sleep(tokio::time::Duration::from_millis(600)).await;
assert_eq!(store.total_size_bytes(), 128); }
#[tokio::test]
async fn test_store_event_processing_document_deleted() {
let temp_dir = tempdir().unwrap();
let store = Store::new_with_config(temp_dir.path(), None, StoreWalConfig::default())
.await
.unwrap();
let event_insert = StoreEvent::DocumentInserted {
collection: "test_collection".to_string(),
size_bytes: 256,
};
let _ = store.event_sender.send(event_insert);
tokio::time::sleep(tokio::time::Duration::from_millis(600)).await;
let event_delete = StoreEvent::DocumentDeleted {
collection: "test_collection".to_string(),
size_bytes: 256,
};
let _ = store.event_sender.send(event_delete);
tokio::time::sleep(tokio::time::Duration::from_millis(600)).await;
assert_eq!(store.total_documents(), 0); assert_eq!(store.total_size_bytes(), 0); }
#[tokio::test]
async fn test_store_event_processor_receiver_already_taken() {
let temp_dir = tempdir().unwrap();
let mut store = Store::new_with_config(temp_dir.path(), None, StoreWalConfig::default())
.await
.unwrap();
if let Some(task) = store.event_task.take() {
task.abort();
}
let _receiver = store.event_receiver.take();
crate::store::events::start_event_processor(&mut store);
assert!(store.event_task.is_none());
}
#[tokio::test]
async fn test_store_event_processor_metadata_save_success() {
let _ = tracing_subscriber::fmt()
.with_max_level(tracing::Level::TRACE)
.try_init();
let temp_dir = tempdir().unwrap();
let store = Store::new_with_config(temp_dir.path(), None, StoreWalConfig::default())
.await
.unwrap();
let event = StoreEvent::CollectionCreated {
name: "test_collection".to_string(),
};
let _ = store.event_sender.send(event);
tokio::time::sleep(tokio::time::Duration::from_millis(1200)).await;
let metadata_path = temp_dir.path().join(STORE_METADATA_FILE);
assert!(metadata_path.exists());
let content = tokio::fs::read_to_string(&metadata_path).await.unwrap();
let metadata: StoreMetadata = serde_json::from_str(&content).unwrap();
assert_eq!(metadata.collection_count, 1);
if let Some(ref task) = store.event_task {
task.abort();
}
}
#[tokio::test]
async fn test_store_event_processor_metadata_save_failure() {
let temp_dir = tempdir().unwrap();
let store = Store::new_with_config(temp_dir.path(), None, StoreWalConfig::default())
.await
.unwrap();
let event = StoreEvent::CollectionCreated {
name: "test_collection".to_string(),
};
let _ = store.event_sender.send(event);
tokio::time::sleep(tokio::time::Duration::from_millis(100)).await;
assert!(store.event_task.is_some());
}
#[tokio::test]
async fn test_store_event_processor_metadata_write_failure() {
let _ = tracing_subscriber::fmt()
.with_max_level(tracing::Level::TRACE)
.try_init();
let temp_dir = tempdir().unwrap();
let mut store = Store::new_with_config(temp_dir.path(), None, StoreWalConfig::default())
.await
.unwrap();
let metadata_dir = temp_dir.path().join("data");
tokio::fs::create_dir_all(&metadata_dir).await.unwrap();
let mut perms = tokio::fs::metadata(&metadata_dir)
.await
.unwrap()
.permissions();
#[cfg(unix)]
{
use std::os::unix::fs::PermissionsExt;
perms.set_mode(0o444); tokio::fs::set_permissions(&metadata_dir, perms)
.await
.unwrap();
}
let event = StoreEvent::CollectionCreated {
name: "test_collection".to_string(),
};
let _ = store.event_sender.send(event);
tokio::time::sleep(tokio::time::Duration::from_millis(1200)).await;
assert!(store.event_task.is_some());
}
#[tokio::test]
async fn test_store_new_with_config_passphrase() {
let temp_dir = tempdir().unwrap();
let store = Store::new_with_config(
temp_dir.path(),
Some("test_passphrase"),
StoreWalConfig::default(),
)
.await
.unwrap();
assert!(store.signing_key.is_some());
}
#[tokio::test]
async fn test_store_new_with_config_passphrase_load_existing() {
let temp_dir = tempdir().unwrap();
let store1 = Store::new_with_config(
temp_dir.path(),
Some("test_passphrase"),
StoreWalConfig::default(),
)
.await
.unwrap();
assert!(store1.signing_key.is_some());
drop(store1);
let store2 = Store::new_with_config(
temp_dir.path(),
Some("test_passphrase"),
StoreWalConfig::default(),
)
.await
.unwrap();
assert!(store2.signing_key.is_some());
}
#[tokio::test]
async fn test_store_new_with_config_passphrase_corrupted_salt() {
let temp_dir = tempdir().unwrap();
let store1 = Store::new_with_config(
temp_dir.path(),
Some("test_passphrase"),
StoreWalConfig::default(),
)
.await
.unwrap();
drop(store1);
let keys_path = temp_dir.path().join("data/.keys/signing_key.json");
let content = tokio::fs::read_to_string(&keys_path).await.unwrap();
let mut doc: serde_json::Value = serde_json::from_str(&content).unwrap();
if let Some(obj) = doc.as_object_mut() {
if let Some(data) = obj.get_mut("data").and_then(|d| d.as_object_mut()) {
data.insert(
"encrypted".to_string(),
serde_json::Value::String("invalid".to_string()),
);
}
}
let corrupted_content = serde_json::to_string(&doc).unwrap();
tokio::fs::write(&keys_path, corrupted_content)
.await
.unwrap();
let result = Store::new_with_config(
temp_dir.path(),
Some("test_passphrase"),
StoreWalConfig::default(),
)
.await;
assert!(result.is_err());
}
#[tokio::test]
async fn test_store_new_with_config_passphrase_missing_encrypted_field() {
let temp_dir = tempdir().unwrap();
let store1 = Store::new_with_config(
temp_dir.path(),
Some("test_passphrase"),
StoreWalConfig::default(),
)
.await
.unwrap();
drop(store1);
let keys_path = temp_dir.path().join("data/.keys/signing_key.json");
let content = tokio::fs::read_to_string(&keys_path).await.unwrap();
let mut doc: serde_json::Value = serde_json::from_str(&content).unwrap();
if let Some(obj) = doc.as_object_mut() {
if let Some(data) = obj.get_mut("data").and_then(|d| d.as_object_mut()) {
data.remove("encrypted");
}
}
let corrupted_content = serde_json::to_string(&doc).unwrap();
tokio::fs::write(&keys_path, corrupted_content)
.await
.unwrap();
let result = Store::new_with_config(
temp_dir.path(),
Some("test_passphrase"),
StoreWalConfig::default(),
)
.await;
assert!(result.is_err());
assert!(matches!(
result.unwrap_err(),
SentinelError::StoreCorruption { .. }
));
}
#[tokio::test]
async fn test_store_new_with_config_passphrase_missing_salt_field() {
let temp_dir = tempdir().unwrap();
let store1 = Store::new_with_config(
temp_dir.path(),
Some("test_passphrase"),
StoreWalConfig::default(),
)
.await
.unwrap();
drop(store1);
let keys_path = temp_dir.path().join("data/.keys/signing_key.json");
let content = tokio::fs::read_to_string(&keys_path).await.unwrap();
let mut doc: serde_json::Value = serde_json::from_str(&content).unwrap();
if let Some(obj) = doc.as_object_mut() {
if let Some(data) = obj.get_mut("data").and_then(|d| d.as_object_mut()) {
data.remove("salt");
}
}
let corrupted_content = serde_json::to_string(&doc).unwrap();
tokio::fs::write(&keys_path, corrupted_content)
.await
.unwrap();
let result = Store::new_with_config(
temp_dir.path(),
Some("test_passphrase"),
StoreWalConfig::default(),
)
.await;
assert!(result.is_err());
assert!(matches!(
result.unwrap_err(),
SentinelError::StoreCorruption { .. }
));
}
#[tokio::test]
async fn test_store_new_with_config_passphrase_invalid_salt_hex() {
let temp_dir = tempdir().unwrap();
let store1 = Store::new_with_config(
temp_dir.path(),
Some("test_passphrase"),
StoreWalConfig::default(),
)
.await
.unwrap();
drop(store1);
let keys_path = temp_dir.path().join("data/.keys/signing_key.json");
let content = tokio::fs::read_to_string(&keys_path).await.unwrap();
let mut doc: serde_json::Value = serde_json::from_str(&content).unwrap();
if let Some(obj) = doc.as_object_mut() {
if let Some(data) = obj.get_mut("data").and_then(|d| d.as_object_mut()) {
data.insert(
"salt".to_string(),
serde_json::Value::String("invalid_hex".to_string()),
);
}
}
let corrupted_content = serde_json::to_string(&doc).unwrap();
tokio::fs::write(&keys_path, corrupted_content)
.await
.unwrap();
let result = Store::new_with_config(
temp_dir.path(),
Some("test_passphrase"),
StoreWalConfig::default(),
)
.await;
assert!(result.is_err());
assert!(matches!(
result.unwrap_err(),
SentinelError::StoreCorruption { .. }
));
}
#[tokio::test]
async fn test_store_new_with_config_passphrase_invalid_key_length() {
let temp_dir = tempdir().unwrap();
let store1 = Store::new_with_config(
temp_dir.path(),
Some("test_passphrase"),
StoreWalConfig::default(),
)
.await
.unwrap();
drop(store1);
let keys_path = temp_dir.path().join("data/.keys/signing_key.json");
let content = tokio::fs::read_to_string(&keys_path).await.unwrap();
let mut doc: serde_json::Value = serde_json::from_str(&content).unwrap();
if let Some(obj) = doc.as_object_mut() {
if let Some(data) = obj.get_mut("data").and_then(|d| d.as_object_mut()) {
data.insert(
"encrypted".to_string(),
serde_json::Value::String("short".to_string()),
);
}
}
let corrupted_content = serde_json::to_string(&doc).unwrap();
tokio::fs::write(&keys_path, corrupted_content)
.await
.unwrap();
let result = Store::new_with_config(
temp_dir.path(),
Some("test_passphrase"),
StoreWalConfig::default(),
)
.await;
assert!(result.is_err());
}
}