Lines
100 %
Functions
Branches
use std::sync::Arc;
use oo7::dbus;
use tokio_stream::StreamExt;
use crate::tests::{TestServiceSetup, gnome_prompter_test, plasma_prompter_test};
#[tokio::test]
async fn label_property() -> Result<(), Box<dyn std::error::Error>> {
let setup = TestServiceSetup::plain_session(true).await?;
let secret = oo7::Secret::text("test-secret");
let dbus_secret = dbus::api::DBusSecret::new(setup.session, secret);
let item = setup.collections[0]
.create_item(
"Original Label",
&[("app", "test")],
&dbus_secret,
false,
None,
)
.await?;
// Get label
let label = item.label().await?;
assert_eq!(label, "Original Label");
// Get initial modified timestamp
let initial_modified = item.modified().await?;
// Wait to ensure timestamp will be different
tokio::time::sleep(tokio::time::Duration::from_secs(1)).await;
// Set label
item.set_label("New Label").await?;
// Verify new label
assert_eq!(label, "New Label");
// Verify modified timestamp was updated
let new_modified = item.modified().await?;
println!("New modified: {:?}", new_modified);
assert!(
new_modified > initial_modified,
"Modified timestamp should be updated after label change (initial: {:?}, new: {:?})",
initial_modified,
new_modified
);
Ok(())
}
async fn attributes_property() -> Result<(), Box<dyn std::error::Error>> {
"Test Item",
&[("app", "firefox"), ("username", "user@example.com")],
// Get attributes
let attrs = item.attributes().await?;
assert_eq!(attrs.get("app").unwrap(), "firefox");
assert_eq!(attrs.get("username").unwrap(), "user@example.com");
// Set new attributes
item.set_attributes(&[("app", "chrome"), ("username", "newuser@example.com")])
// Verify new attributes
assert_eq!(attrs.get("app").unwrap(), "chrome");
assert_eq!(attrs.get("username").unwrap(), "newuser@example.com");
"Modified timestamp should be updated after attributes change"
async fn timestamps() -> Result<(), Box<dyn std::error::Error>> {
let collections = setup.service_api.collections().await?;
let item = collections[0]
.create_item("Test Item", &[("app", "test")], &dbus_secret, false, None)
// Get created timestamp
let created = item.created().await?;
assert!(created.as_secs() > 0, "Created timestamp should be set");
// Get modified timestamp
let modified = item.modified().await?;
assert!(modified.as_secs() > 0, "Modified timestamp should be set");
// Created and modified should be close (within a second for new item)
let diff = if created > modified {
created.as_secs() - modified.as_secs()
} else {
modified.as_secs() - created.as_secs()
};
assert!(diff <= 1, "Created and modified should be within 1 second");
async fn secret_retrieval_plain() -> Result<(), Box<dyn std::error::Error>> {
let secret = oo7::Secret::blob(b"my-secret-password");
let dbus_secret = dbus::api::DBusSecret::new(Arc::clone(&setup.session), secret.clone());
// Retrieve secret
let retrieved_secret = item.secret(&setup.session).await?;
assert_eq!(retrieved_secret.value(), secret.as_bytes());
// Verify content-type is preserved
assert_eq!(
retrieved_secret.content_type(),
secret.content_type(),
"Content-type should be preserved"
async fn secret_retrieval_encrypted() -> Result<(), Box<dyn std::error::Error>> {
let setup = TestServiceSetup::encrypted_session(true).await?;
let aes_key = setup.aes_key.as_ref().unwrap();
let secret = oo7::Secret::text("my-encrypted-secret");
let dbus_secret =
dbus::api::DBusSecret::new_encrypted(Arc::clone(&setup.session), secret.clone(), aes_key)?;
retrieved_secret.decrypt(Some(&aes_key.clone()))?.as_bytes(),
secret.as_bytes()
retrieved_secret
.decrypt(Some(&aes_key.clone()))?
.content_type(),
async fn delete_item() -> Result<(), Box<dyn std::error::Error>> {
// Verify item exists
let items = setup.collections[0].items().await?;
assert_eq!(items.len(), 1);
// Delete item
item.delete(None).await?;
// Verify item is deleted
assert_eq!(items.len(), 0, "Item should be deleted from collection");
async fn set_secret_plain() -> Result<(), Box<dyn std::error::Error>> {
let original_secret = oo7::Secret::text("original-password");
dbus::api::DBusSecret::new(Arc::clone(&setup.session), original_secret.clone());
// Verify original secret
let retrieved = item.secret(&setup.session).await?;
assert_eq!(retrieved.value(), original_secret.as_bytes());
retrieved.content_type(),
original_secret.content_type(),
// Update the secret
let new_secret = oo7::Secret::blob(b"new-password");
let new_dbus_secret =
dbus::api::DBusSecret::new(Arc::clone(&setup.session), new_secret.clone());
item.set_secret(&new_dbus_secret).await?;
// Verify updated secret
assert_eq!(retrieved.value(), new_secret.as_bytes());
new_secret.content_type(),
"Modified timestamp should be updated after secret change"
async fn set_secret_encrypted() -> Result<(), Box<dyn std::error::Error>> {
let aes_key = setup.aes_key.unwrap();
let original_secret = oo7::Secret::text("original-encrypted-password");
let dbus_secret = dbus::api::DBusSecret::new_encrypted(
Arc::clone(&setup.session),
original_secret.clone(),
&aes_key,
)?;
retrieved.decrypt(Some(&aes_key.clone()))?.as_bytes(),
original_secret.as_bytes()
let new_secret = oo7::Secret::text("new-encrypted-password");
let new_dbus_secret = dbus::api::DBusSecret::new_encrypted(
new_secret.clone(),
new_secret.as_bytes()
async fn get_secret_invalid_session() -> Result<(), Box<dyn std::error::Error>> {
// Try to get secret with invalid session path
let invalid_session =
oo7::dbus::api::Session::new(&setup.client_conn, "/invalid/session").await?;
let result = item.secret(&invalid_session).await;
matches!(
result,
Err(oo7::dbus::Error::Service(
oo7::dbus::ServiceError::NoSession(_)
))
),
"Should be NoSession error"
async fn set_secret_invalid_session() -> Result<(), Box<dyn std::error::Error>> {
let dbus_secret = dbus::api::DBusSecret::new(Arc::clone(&setup.session), secret);
let new_secret = oo7::Secret::text("new-secret");
let invalid_dbus_secret = dbus::api::DBusSecret::new(
Arc::new(dbus::api::Session::new(&setup.client_conn, "/invalid/session").await?),
new_secret,
let result = item.set_secret(&invalid_dbus_secret).await;
// Should return NoSession error
async fn item_changed_signal() -> Result<(), Box<dyn std::error::Error>> {
// Subscribe to ItemChanged signal
let signal_stream = setup.collections[0].receive_item_changed().await?;
tokio::pin!(signal_stream);
// Change the label
item.set_label("Updated Label").await?;
// Wait for signal
let signal_result =
tokio::time::timeout(tokio::time::Duration::from_secs(1), signal_stream.next()).await;
signal_result.is_ok(),
"Should receive ItemChanged signal after label change"
let signal = signal_result.unwrap();
assert!(signal.is_some(), "Signal should not be None");
let signal_item = signal.unwrap();
signal_item.inner().path().as_str(),
item.inner().path().as_str(),
"Signal should contain the changed item path"
// Change attributes and verify signal again
item.set_attributes(&[("app", "updated-app")]).await?;
"Should receive ItemChanged signal after attributes change"
// Change secret and verify signal again
let new_dbus_secret = dbus::api::DBusSecret::new(Arc::clone(&setup.session), new_secret);
"Should receive ItemChanged signal after secret change"
gnome_prompter_test!(
delete_locked_item_with_prompt_gnome,
delete_locked_item_with_prompt
plasma_prompter_test!(
delete_locked_item_with_prompt_plasma,
async fn delete_locked_item_with_prompt() -> Result<(), Box<dyn std::error::Error>> {
let default_collection = setup.default_collection().await?;
let secret = oo7::Secret::text("test-password");
let item = default_collection
let items = default_collection.items().await?;
assert_eq!(items.len(), 1, "Should have one item");
let collection = setup
.server
.collection_from_path(default_collection.inner().path())
.await
.expect("Collection should exist");
collection
.set_locked(true, setup.keyring_secret.clone())
assert!(item.is_locked().await?, "Item should be locked");
assert_eq!(items.len(), 0, "Item should be deleted after prompt");
async fn locked_item_operations() -> Result<(), Box<dyn std::error::Error>> {
// Create an item
// Verify item is unlocked initially
assert!(!item.is_locked().await?, "Item should start unlocked");
// Lock the collection (which locks the item)
.collection_from_path(setup.collections[0].inner().path())
// Verify item is now locked
item.is_locked().await?,
"Item should be locked after locking collection"
// Test 1: get_secret should fail with IsLocked
let result = item.secret(&setup.session).await;
oo7::dbus::ServiceError::IsLocked(_)
"get_secret should fail with IsLocked error, got: {:?}",
result
// Test 2: set_secret should fail with IsLocked
let new_secret = oo7::Secret::text("new-password");
let result = item.set_secret(&new_dbus_secret).await;
"set_secret should fail with IsLocked error, got: {:?}",
// Test 3: set_attributes should fail with IsLocked
let result = item.set_attributes(&[("app", "new-app")]).await;
matches!(result, Err(oo7::dbus::Error::ZBus(zbus::Error::FDO(_)))),
"set_attributes should fail with IsLocked error, got: {:?}",
// Test 4: set_label should fail with IsLocked
let result = item.set_label("New Label").await;
"set_label should fail with IsLocked error, got: {:?}",
// Test 5: Reading properties should also fail on locked items
let result = item.label().await;
"label should fail on locked item, got: {:?}",
let result = item.attributes().await;
"attributes should fail on locked item, got: {:?}",
let result = item.created().await;
"created should fail on locked item, got: {:?}",
let result = item.modified().await;
"modified should fail on locked item, got: {:?}",