use datasynth_core::distributions::behavioral_priors::UserPersonaPrior;
use datasynth_eval::behavioral_fidelity::Record;
pub fn extract_user_personas(records: &[Record], min_user_records: usize) -> UserPersonaPrior {
let _ = (records, min_user_records);
UserPersonaPrior::default()
}
#[cfg(test)]
mod tests {
use super::*;
use chrono::NaiveDate;
use datasynth_eval::behavioral_fidelity::Record;
fn rec(source: &str) -> Record {
let d = NaiveDate::from_ymd_opt(2022, 1, 1).expect("date");
Record {
source: source.to_string(),
gl_account: "1000".to_string(),
cost_center: None,
profit_center: None,
trading_partner: None,
je_number: "J1".to_string(),
je_line_number: "001".to_string(),
effective_date: d,
entry_date: d,
created_at: None,
functional_amount: 100.0,
header_text: String::new(),
line_text: String::new(),
}
}
#[test]
fn stub_returns_empty_prior_on_any_input() {
let recs: Vec<Record> = (0..500).map(|_| rec("KR")).collect();
let prior = extract_user_personas(&recs, 100);
assert!(prior.users.is_empty(), "stub must return empty users map");
assert!(!prior.has_data(), "has_data() must be false on empty prior");
}
#[test]
fn stub_returns_empty_prior_on_empty_input() {
let prior = extract_user_personas(&[], 100);
assert!(prior.users.is_empty());
}
#[test]
fn has_data_false_on_default() {
let prior = UserPersonaPrior::default();
assert!(!prior.has_data());
}
#[test]
fn sample_user_for_source_returns_none_on_empty_prior() {
use rand::SeedableRng;
use rand_chacha::ChaCha8Rng;
let prior = UserPersonaPrior::default();
let mut rng = ChaCha8Rng::seed_from_u64(42);
assert!(prior.sample_user_for_source("KR", &mut rng).is_none());
}
#[test]
fn sample_timestamp_for_user_returns_none_on_unknown_user() {
use rand::SeedableRng;
use rand_chacha::ChaCha8Rng;
let prior = UserPersonaPrior::default();
let mut rng = ChaCha8Rng::seed_from_u64(42);
assert!(prior
.sample_timestamp_for_user("USER0001", &mut rng)
.is_none());
}
#[test]
fn sample_user_for_source_with_real_data() {
use datasynth_core::distributions::behavioral_priors::UserBehavior;
use rand::SeedableRng;
use rand_chacha::ChaCha8Rng;
use std::collections::BTreeMap;
let mut users = BTreeMap::new();
let mut ap_mix = BTreeMap::new();
ap_mix.insert("KR".to_string(), 0.7);
ap_mix.insert("KZ".to_string(), 0.3);
users.insert(
"USER0010".to_string(),
UserBehavior {
source_mix: ap_mix,
hourly_density: {
let mut h = [0.0; 24];
h[9] = 0.5; h[14] = 0.5; h
},
weekday_density: {
let mut w = [0.0; 7];
w[0] = 0.25; w[1] = 0.25; w[2] = 0.25; w[3] = 0.25; w
},
volume_share: 0.6,
},
);
let mut ar_mix = BTreeMap::new();
ar_mix.insert("RV".to_string(), 1.0);
users.insert(
"USER0020".to_string(),
UserBehavior {
source_mix: ar_mix,
hourly_density: {
let mut h = [0.0; 24];
h[10] = 1.0;
h
},
weekday_density: {
let mut w = [0.0; 7];
w[4] = 1.0; w
},
volume_share: 0.4,
},
);
let prior = UserPersonaPrior {
users,
user_count_distribution: Default::default(),
};
assert!(prior.has_data());
let mut rng = ChaCha8Rng::seed_from_u64(99);
for _ in 0..20 {
let uid = prior
.sample_user_for_source("KR", &mut rng)
.expect("must return a user");
assert_eq!(uid, "USER0010", "KR must map to the AP clerk");
}
for _ in 0..20 {
let uid = prior
.sample_user_for_source("RV", &mut rng)
.expect("must return a user");
assert_eq!(uid, "USER0020", "RV must map to the AR clerk");
}
assert!(prior.sample_user_for_source("XX", &mut rng).is_none());
let (hour, weekday) = prior
.sample_timestamp_for_user("USER0010", &mut rng)
.expect("must return timestamp");
assert!(hour == 9 || hour == 14, "expected hour 9 or 14, got {hour}");
assert!(weekday <= 3, "expected weekday 0..=3, got {weekday}");
let (hour, weekday) = prior
.sample_timestamp_for_user("USER0020", &mut rng)
.expect("must return timestamp");
assert_eq!(hour, 10);
assert_eq!(weekday, 4);
}
}