deterministic RNGs for tests

This commit is contained in:
Brandon Thomas committed 2023-12-14 03:20:47 -05:00
1 parent f9a913b944
commit faff80fcc6
7 files changed
+107 -2

No files matched your search

Generated
+1
View File
@@ -6442,6 +6442,7 @@ name = "tokens"
version = "0.0.1"
dependencies = [
"my-workspace-hack",
"once_cell",
"rand 0.8.5",
"serde",
"serde_derive",
+3 -1
View File
@@ -25,7 +25,9 @@ serde = "1.0.125"
serde_derive = "1.0.125"
# Random
rand = "0.8.5"
rand = { version = "0.8.5", features = ["std_rng"] }
once_cell = { version = "1.18.0", features = [] }
sqlx = { version = "0.7.2", features = [ "mysql", "runtime-tokio", "tls-rustls", "chrono" ] }
sqlx-core = { version = "0.7.2" }
@@ -0,0 +1,43 @@
use std::sync::{Arc, LockResult, RwLock, RwLockWriteGuard};
use once_cell::sync::Lazy;
use rand::rngs::StdRng;
use rand::SeedableRng;
// FIXME(bt,2023-12-14): Is this threadsafe? Looks like it should be fine.
/// Only use this in tests (and dev seed tools), never in production!
#[derive(Clone)]
pub struct DeterministicRng {
rng : Arc<RwLock<StdRng>>
}
impl DeterministicRng {
pub fn get_instance() -> LockResult<RwLockWriteGuard<'static, DeterministicRng>> {
static DETERMINISTIC_RNG : Lazy<Arc<RwLock<DeterministicRng>>> = Lazy::new(|| {
Arc::new(RwLock::new(DeterministicRng::new()))
});
DETERMINISTIC_RNG.write()
}
fn new() -> Self {
Self {
rng: Arc::new(RwLock::new(StdRng::seed_from_u64(0)))
}
}
/// Reset the deterministic RNG's seed.
pub fn reset_rng(&self) {
match self.rng.write() {
Err(err) => panic!(format!("test panic due to rng failure {err}")),
Ok(mut lock) => {
*lock = StdRng::seed_from_u64(0);
}
}
}
/// Grab the deterministic RNG
pub fn get_rng(&self) -> LockResult<RwLockWriteGuard<StdRng>>{
self.rng.write()
}
}
+1
View File
@@ -49,6 +49,7 @@ pub(crate) static MINIMUM_CHARACTER_ENTROPY : usize = 8;
#[macro_use]
mod macros;
pub (crate) mod deterministic_rng;
pub (crate) mod prefixes;
pub mod tokens;
+35
View File
@@ -60,6 +60,41 @@ macro_rules! impl_crockford_generator {
$t(token)
}
/// Constructor for a new token to be used **DETERMINISTICALLY** in tests and seeding.
/// This will not use a real RNG, so do not use in production code!
/// Once called, it will output the same random numbers in sequence.
pub fn reset_rng_for_testing_and_dev_seeding_never_use_in_production_seriously() {
let deterministic = crate::deterministic_rng::DeterministicRng::get_instance().expect("tests should not fail due to mutex");
deterministic.reset_rng();
}
/// Constructor for a new token to be used **DETERMINISTICALLY** in tests and seeding.
/// This will not use a real RNG, so do not use in production code!
/// Once called, it will output the same random numbers in sequence.
pub fn generate_for_testing_and_dev_seeding_never_use_in_production_seriously() -> Self {
use rand::Rng;
// FIXME(bt,2023-12-14): This is bad error handling. (Maybe panic if lock is poisoned?)
let deterministic = crate::deterministic_rng::DeterministicRng::get_instance().expect("tests should not fail due to mutex");
let mut rng = deterministic.get_rng().expect("tests should not fail due to mutex");
let charset = Self::token_character_set();
let entropy_length = Self::entropic_character_len();
let entropy_part: String = (0..entropy_length)
.map(|_| {
let idx = rng.gen_range(0..charset.len());
charset[idx] as char
})
.collect();
let token_prefix = Self::token_prefix();
let token = format!("{}{}", token_prefix, entropy_part);
$t(token)
}
#[inline]
pub fn entropy_suffix(&self) -> &str {
use crate::prefixes::PrefixGenerator;
@@ -21,6 +21,30 @@ mod interface {
assert!(token.to_string().starts_with("m_"));
}
#[test]
fn generate_for_testing_and_dev_seeding_never_use_in_production_seriously_1() {
MediaFileToken::reset_rng_for_testing_and_dev_seeding_never_use_in_production_seriously();
assert_eq!(MediaFileToken::generate_for_testing_and_dev_seeding_never_use_in_production_seriously().as_str(), "m_q8sz47gmfw2zx02snrbz88ns9m16ab");
assert_eq!(MediaFileToken::generate_for_testing_and_dev_seeding_never_use_in_production_seriously().as_str(), "m_ma1xetxrwbh39vg639a9zrq8b9wk6h");
assert_eq!(MediaFileToken::generate_for_testing_and_dev_seeding_never_use_in_production_seriously().as_str(), "m_4tswec8z27wnm01njypx4vmfhgj41e");
}
#[test]
fn generate_for_testing_and_dev_seeding_never_use_in_production_seriously_2() {
MediaFileToken::reset_rng_for_testing_and_dev_seeding_never_use_in_production_seriously();
assert_eq!(MediaFileToken::generate_for_testing_and_dev_seeding_never_use_in_production_seriously().as_str(), "m_q8sz47gmfw2zx02snrbz88ns9m16ab");
assert_eq!(MediaFileToken::generate_for_testing_and_dev_seeding_never_use_in_production_seriously().as_str(), "m_ma1xetxrwbh39vg639a9zrq8b9wk6h");
assert_eq!(MediaFileToken::generate_for_testing_and_dev_seeding_never_use_in_production_seriously().as_str(), "m_4tswec8z27wnm01njypx4vmfhgj41e");
}
#[test]
fn generate_for_testing_and_dev_seeding_never_use_in_production_seriously_3() {
MediaFileToken::reset_rng_for_testing_and_dev_seeding_never_use_in_production_seriously();
assert_eq!(MediaFileToken::generate_for_testing_and_dev_seeding_never_use_in_production_seriously().as_str(), "m_q8sz47gmfw2zx02snrbz88ns9m16ab");
assert_eq!(MediaFileToken::generate_for_testing_and_dev_seeding_never_use_in_production_seriously().as_str(), "m_ma1xetxrwbh39vg639a9zrq8b9wk6h");
assert_eq!(MediaFileToken::generate_for_testing_and_dev_seeding_never_use_in_production_seriously().as_str(), "m_4tswec8z27wnm01njypx4vmfhgj41e");
}
#[test]
fn new() {
let token = MediaFileToken::new("m_foo".to_string());
@@ -122,7 +122,6 @@ pub async fn list_featured_weights_handler(
};
}
// generate parse a response
let response = ListFeaturedWeightsSuccessResponse {
success: true,
weights: weights.into_iter()