uv/crates/uv-keyring/tests/threading.rs
Zanie Blue 709e0ba238 Remove the native system store from the keyring providers (#15612)
We're not sure what the best way to expose the native store to users is
yet and it's a bit weird that you can use this in the `uv auth` commands
but can't use any of the other keyring provider options. The simplest
path forward is to just not expose it to users as a keyring provider,
and instead frame it as a preview alternative to the plaintext uv
credentials store. We can revisit the best way to expose configuration
before stabilization.

Note this pull request retains the _internal_ keyring provider
implementation — we can refactor it out later but I wanted to avoid a
bunch of churn here.
2025-09-02 13:16:52 -05:00

280 lines
8.3 KiB
Rust

#![cfg(feature = "native-auth")]
use common::{generate_random_string, init_logger};
use uv_keyring::{Entry, Error};
mod common;
#[tokio::test]
async fn test_create_then_move() {
init_logger();
let name = generate_random_string();
let entry = Entry::new(&name, &name).unwrap();
let handle = tokio::spawn(async move {
let password = "test ascii password";
entry
.set_password(password)
.await
.expect("Can't set initial ascii password");
let stored_password = entry
.get_password()
.await
.expect("Can't get ascii password");
assert_eq!(
stored_password, password,
"Retrieved and set initial ascii passwords don't match"
);
let password = "このきれいな花は桜です";
entry
.set_password(password)
.await
.expect("Can't set non-ascii password");
let stored_password = entry
.get_password()
.await
.expect("Can't get non-ascii password");
assert_eq!(
stored_password, password,
"Retrieved and set non-ascii passwords don't match"
);
entry
.delete_credential()
.await
.expect("Can't delete non-ascii password");
assert!(
matches!(entry.get_password().await, Err(Error::NoEntry)),
"Able to read a deleted non-ascii password"
);
});
handle.await.expect("Task failed");
}
#[tokio::test]
async fn test_simultaneous_create_then_move() {
init_logger();
let mut handles = vec![];
for i in 0..10 {
let name = format!("{}-{}", generate_random_string(), i);
let entry = Entry::new(&name, &name).expect("Can't create entry");
let handle = tokio::spawn(async move {
entry
.set_password(&name)
.await
.expect("Can't set ascii password");
let stored_password = entry
.get_password()
.await
.expect("Can't get ascii password");
assert_eq!(
stored_password, name,
"Retrieved and set ascii passwords don't match"
);
entry
.delete_credential()
.await
.expect("Can't delete ascii password");
assert!(
matches!(entry.get_password().await, Err(Error::NoEntry)),
"Able to read a deleted ascii password"
);
});
handles.push(handle);
}
for handle in handles {
handle.await.expect("Task failed");
}
}
#[tokio::test]
#[cfg(not(target_os = "windows"))]
async fn test_create_set_then_move() {
init_logger();
let name = generate_random_string();
let entry = Entry::new(&name, &name).expect("Can't create entry");
let password = "test ascii password";
entry
.set_password(password)
.await
.expect("Can't set ascii password");
let handle = tokio::spawn(async move {
let stored_password = entry
.get_password()
.await
.expect("Can't get ascii password");
assert_eq!(
stored_password, password,
"Retrieved and set ascii passwords don't match"
);
entry
.delete_credential()
.await
.expect("Can't delete ascii password");
assert!(
matches!(entry.get_password().await, Err(Error::NoEntry)),
"Able to read a deleted ascii password"
);
});
handle.await.expect("Task failed");
}
#[tokio::test]
#[cfg(not(target_os = "windows"))]
async fn test_simultaneous_create_set_then_move() {
init_logger();
let mut handles = vec![];
for i in 0..10 {
let name = format!("{}-{}", generate_random_string(), i);
let entry = Entry::new(&name, &name).expect("Can't create entry");
entry
.set_password(&name)
.await
.expect("Can't set ascii password");
let handle = tokio::spawn(async move {
let stored_password = entry
.get_password()
.await
.expect("Can't get ascii password");
assert_eq!(
stored_password, name,
"Retrieved and set ascii passwords don't match"
);
entry
.delete_credential()
.await
.expect("Can't delete ascii password");
assert!(
matches!(entry.get_password().await, Err(Error::NoEntry)),
"Able to read a deleted ascii password"
);
});
handles.push(handle);
}
for handle in handles {
handle.await.expect("Task failed");
}
}
#[tokio::test]
async fn test_simultaneous_independent_create_set() {
init_logger();
let mut handles = vec![];
for i in 0..10 {
let name = format!("thread_entry{i}");
let handle = tokio::spawn(async move {
let entry = Entry::new(&name, &name).expect("Can't create entry");
entry
.set_password(&name)
.await
.expect("Can't set ascii password");
let stored_password = entry
.get_password()
.await
.expect("Can't get ascii password");
assert_eq!(
stored_password, name,
"Retrieved and set ascii passwords don't match"
);
entry
.delete_credential()
.await
.expect("Can't delete ascii password");
assert!(
matches!(entry.get_password().await, Err(Error::NoEntry)),
"Able to read a deleted ascii password"
);
});
handles.push(handle);
}
for handle in handles {
handle.await.expect("Task failed");
}
}
#[tokio::test]
#[cfg(any(target_os = "macos", target_os = "windows"))]
async fn test_multiple_create_delete_single_thread() {
init_logger();
let name = generate_random_string();
let entry = Entry::new(&name, &name).expect("Can't create entry");
let repeats = 10;
for _i in 0..repeats {
entry
.set_password(&name)
.await
.expect("Can't set ascii password");
let stored_password = entry
.get_password()
.await
.expect("Can't get ascii password");
assert_eq!(
stored_password, name,
"Retrieved and set ascii passwords don't match"
);
entry
.delete_credential()
.await
.expect("Can't delete ascii password");
assert!(
matches!(entry.get_password().await, Err(Error::NoEntry)),
"Able to read a deleted ascii password"
);
}
}
#[tokio::test]
#[cfg(any(target_os = "macos", target_os = "windows"))]
async fn test_simultaneous_multiple_create_delete_single_thread() {
init_logger();
let mut handles = vec![];
for t in 0..10 {
let name = generate_random_string();
let handle = tokio::spawn(async move {
let name = format!("{name}-{t}");
let entry = Entry::new(&name, &name).expect("Can't create entry");
let repeats = 10;
for _i in 0..repeats {
entry
.set_password(&name)
.await
.expect("Can't set ascii password");
let stored_password = entry
.get_password()
.await
.expect("Can't get ascii password");
assert_eq!(
stored_password, name,
"Retrieved and set ascii passwords don't match"
);
entry
.delete_credential()
.await
.expect("Can't delete ascii password");
assert!(
matches!(entry.get_password().await, Err(Error::NoEntry)),
"Able to read a deleted ascii password"
);
}
});
handles.push(handle);
}
for handle in handles {
handle.await.expect("Task failed");
}
}