mirror of
https://github.com/astral-sh/uv.git
synced 2025-11-19 19:44:40 +00:00
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.
280 lines
8.3 KiB
Rust
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");
|
|
}
|
|
}
|