use std::time::Duration;
use super::*;
use crate::storage::s3::tests::{
bucket, bucket_ignoring_checksums, bucket_ignoring_conditions, keyspace,
};
#[tokio::test]
async fn a_store_that_refuses_a_body_which_does_not_match_is_trusted() {
crate::tls::install_crypto_provider();
let (endpoint, objects) = bucket().await;
assert_eq!(checksums(&keyspace(&endpoint)).await, Checksums::Enforced);
assert!(
objects.lock().unwrap().is_empty(),
"the probe body does not hash to the digest it was signed for, so a store that checks \
keeps none of it"
);
}
#[tokio::test]
async fn a_store_that_keeps_a_body_which_does_not_match_is_not_trusted() {
crate::tls::install_crypto_provider();
let (endpoint, objects) = bucket_ignoring_checksums().await;
assert_eq!(checksums(&keyspace(&endpoint)).await, Checksums::Ignored);
assert!(
objects.lock().unwrap().is_empty(),
"the probe wrote an object to find that out and has to take it back with it"
);
}
#[tokio::test]
async fn a_store_that_cannot_be_asked_is_not_trusted() {
crate::tls::install_crypto_provider();
let listener = tokio::net::TcpListener::bind("127.0.0.1:0").await.unwrap();
let closed = listener.local_addr().unwrap();
drop(listener);
let keys = keyspace(&format!("http://{closed}"));
assert_eq!(checksums(&keys).await, Checksums::Unknown);
}
fn presigning(endpoint: &str) -> crate::config::Config {
crate::config::Config {
bind: "127.0.0.1:0".parse().unwrap(),
storage_root: std::path::PathBuf::from("."),
public_url: Some("https://lfs.example".into()),
action_lifetime: 1800,
gc_grace: Duration::from_secs(0),
staging_max_age: Duration::from_secs(0),
lock_max_age: None,
max_object_size: None,
repo_quota: None,
compression: None,
encryption_key_file: None,
storage: crate::config::Storage::Bucket {
endpoint: endpoint.to_owned(),
bucket: "assets".into(),
region: "us-east-1".into(),
access_key: "key".into(),
secret_key: "secret".into(),
path_style: true,
presign: true,
locking: true,
},
auth: crate::config::Auth::Disabled,
}
}
fn presigns(config: &crate::config::Config) -> bool {
matches!(
config.storage,
crate::config::Storage::Bucket { presign: true, .. }
)
}
#[tokio::test]
async fn a_store_that_ignores_checksums_loses_pre_signing() {
crate::tls::install_crypto_provider();
let (endpoint, _objects) = bucket_ignoring_checksums().await;
let mut config = presigning(&endpoint);
assert!(presigns(&config));
crate::verify_presign(&mut config).await;
assert!(
!presigns(&config),
"uploads have to fall back to coming through this server, which hashes what it is sent"
);
}
#[tokio::test]
async fn a_store_that_verifies_them_keeps_pre_signing() {
crate::tls::install_crypto_provider();
let (endpoint, _objects) = bucket().await;
let mut config = presigning(&endpoint);
crate::verify_presign(&mut config).await;
assert!(presigns(&config));
}
#[tokio::test]
async fn a_store_that_was_not_going_to_pre_sign_is_never_probed() {
crate::tls::install_crypto_provider();
let (endpoint, objects) = bucket_ignoring_checksums().await;
let mut config = presigning(&endpoint);
if let crate::config::Storage::Bucket { presign, .. } = &mut config.storage {
*presign = false;
}
crate::verify_presign(&mut config).await;
assert!(
objects.lock().unwrap().is_empty(),
"a store that ignores checksums keeps whatever it is sent, so anything written here would \
show up"
);
}
#[tokio::test]
async fn a_store_that_refuses_the_second_write_can_hold_locks() {
crate::tls::install_crypto_provider();
let (endpoint, objects) = bucket().await;
assert_eq!(
conditional_writes(&keyspace(&endpoint)).await,
Conditional::Enforced
);
assert!(
objects.lock().unwrap().is_empty(),
"the probe wrote a key to find that out and has to take it back with it"
);
}
#[tokio::test]
async fn a_store_that_writes_twice_cannot() {
crate::tls::install_crypto_provider();
let (endpoint, objects) = bucket_ignoring_conditions().await;
assert_eq!(
conditional_writes(&keyspace(&endpoint)).await,
Conditional::Ignored
);
assert!(objects.lock().unwrap().is_empty());
}
#[tokio::test]
async fn a_store_that_cannot_be_asked_about_conditions_is_not_trusted_either() {
crate::tls::install_crypto_provider();
let listener = tokio::net::TcpListener::bind("127.0.0.1:0").await.unwrap();
let closed = listener.local_addr().unwrap();
drop(listener);
assert_eq!(
conditional_writes(&keyspace(&format!("http://{closed}"))).await,
Conditional::Unknown
);
}
fn locks(config: &crate::config::Config) -> bool {
matches!(
config.storage,
crate::config::Storage::Bucket { locking: true, .. }
)
}
#[tokio::test]
async fn a_store_that_ignores_conditions_loses_locking() {
crate::tls::install_crypto_provider();
let (endpoint, _objects) = bucket_ignoring_conditions().await;
let mut config = presigning(&endpoint);
assert!(locks(&config));
crate::verify_locking(&mut config).await;
assert!(!locks(&config));
}
#[tokio::test]
async fn a_store_that_honours_them_keeps_locking() {
crate::tls::install_crypto_provider();
let (endpoint, _objects) = bucket().await;
let mut config = presigning(&endpoint);
crate::verify_locking(&mut config).await;
assert!(locks(&config));
}
#[tokio::test]
async fn a_probe_key_left_by_an_earlier_run_does_not_pass_for_enforcement() {
crate::tls::install_crypto_provider();
let (endpoint, objects) = bucket().await;
objects
.lock()
.unwrap()
.insert(".probe/conditional".to_owned(), b"left behind".to_vec());
assert_eq!(
conditional_writes(&keyspace(&endpoint)).await,
Conditional::Enforced,
"the stale key is cleared first, so what is measured is a fresh pair of writes"
);
}