use rasayan::amino_catabolism::{AminoCatabConfig, AminoCatabState};
use rasayan::beta_oxidation::{BetaOxConfig, BetaOxState};
use rasayan::energy::BioenergyState;
use rasayan::enzyme::{self, EnzymeParams};
use rasayan::etc::{EtcConfig, EtcState};
use rasayan::glycolysis::{GlycolysisConfig, GlycolysisState};
use rasayan::membrane::{self, IonicState, MembranePermeability};
use rasayan::metabolism::MetabolicState;
use rasayan::signal::{self, SecondMessenger};
use rasayan::tca::{TcaConfig, TcaState};
#[test]
fn test_metabolic_state_serde_roundtrip() {
let m = MetabolicState::default();
let json = serde_json::to_string(&m).unwrap();
let m2: MetabolicState = serde_json::from_str(&json).unwrap();
assert!((m2.atp - m.atp).abs() < f64::EPSILON);
assert!((m2.adp - m.adp).abs() < f64::EPSILON);
assert!((m2.glucose - m.glucose).abs() < f64::EPSILON);
assert!((m2.oxygen - m.oxygen).abs() < f64::EPSILON);
assert!((m2.lactate - m.lactate).abs() < f64::EPSILON);
assert!((m2.nad_ratio - m.nad_ratio).abs() < f64::EPSILON);
}
#[test]
fn test_enzyme_params_serde_roundtrip() {
let e = EnzymeParams {
vmax: 12.5,
km: 0.003,
hill_n: 2.8,
kcat: 450.0,
};
let json = serde_json::to_string(&e).unwrap();
let e2: EnzymeParams = serde_json::from_str(&json).unwrap();
assert!((e2.vmax - e.vmax).abs() < f64::EPSILON);
assert!((e2.km - e.km).abs() < f64::EPSILON);
assert!((e2.hill_n - e.hill_n).abs() < f64::EPSILON);
assert!((e2.kcat - e.kcat).abs() < f64::EPSILON);
}
#[test]
fn test_second_messenger_serde_roundtrip() {
let mut sm = SecondMessenger::default();
sm.activate_gs(0.7);
let json = serde_json::to_string(&sm).unwrap();
let sm2: SecondMessenger = serde_json::from_str(&json).unwrap();
assert!((sm2.camp - sm.camp).abs() < f64::EPSILON);
assert!((sm2.calcium - sm.calcium).abs() < f64::EPSILON);
assert!((sm2.ip3 - sm.ip3).abs() < f64::EPSILON);
}
#[test]
fn test_ionic_state_serde_roundtrip() {
let ions = IonicState::default();
let json = serde_json::to_string(&ions).unwrap();
let ions2: IonicState = serde_json::from_str(&json).unwrap();
assert!((ions2.na_in - ions.na_in).abs() < f64::EPSILON);
assert!((ions2.na_out - ions.na_out).abs() < f64::EPSILON);
assert!((ions2.k_in - ions.k_in).abs() < f64::EPSILON);
assert!((ions2.k_out - ions.k_out).abs() < f64::EPSILON);
assert!((ions2.cl_in - ions.cl_in).abs() < f64::EPSILON);
assert!((ions2.cl_out - ions.cl_out).abs() < f64::EPSILON);
}
#[test]
fn test_membrane_permeability_serde_roundtrip() {
let perm = MembranePermeability::default();
let json = serde_json::to_string(&perm).unwrap();
let perm2: MembranePermeability = serde_json::from_str(&json).unwrap();
assert!((perm2.p_na - perm.p_na).abs() < f64::EPSILON);
assert!((perm2.p_k - perm.p_k).abs() < f64::EPSILON);
assert!((perm2.p_cl - perm.p_cl).abs() < f64::EPSILON);
}
#[test]
fn test_bioenergy_state_serde_roundtrip() {
let b = BioenergyState::default();
let json = serde_json::to_string(&b).unwrap();
let b2: BioenergyState = serde_json::from_str(&json).unwrap();
assert!((b2.phosphocreatine - b.phosphocreatine).abs() < f64::EPSILON);
assert!((b2.glycogen - b.glycogen).abs() < f64::EPSILON);
assert!((b2.met - b.met).abs() < f64::EPSILON);
assert!((b2.anaerobic_threshold - b.anaerobic_threshold).abs() < f64::EPSILON);
}
fn assert_serde_roundtrip<T>(val: &T)
where
T: serde::Serialize + serde::de::DeserializeOwned + std::fmt::Debug,
{
let v1 = serde_json::to_value(val).unwrap();
let val2: T = serde_json::from_value(v1.clone()).unwrap();
let v2 = serde_json::to_value(&val2).unwrap();
assert_eq!(v1, v2, "serde roundtrip mismatch for {val:?}");
}
#[test]
fn test_signaling_config_serde_roundtrip() {
assert_serde_roundtrip(&rasayan::signaling::SignalingConfig::default());
}
#[test]
fn test_signaling_input_serde_roundtrip() {
assert_serde_roundtrip(&rasayan::signaling::SignalingInput {
growth_factor: 0.5,
cytokine: 0.3,
ip3: 0.2,
});
}
#[test]
fn test_signaling_flux_serde_roundtrip() {
let config = rasayan::signaling::SignalingConfig::default();
let input = rasayan::signaling::SignalingInput {
growth_factor: 0.5,
cytokine: 0.3,
ip3: 0.2,
};
let mut net = rasayan::signaling::SignalingNetwork::default();
let flux = net.tick(&config, &input, 0.01);
assert_serde_roundtrip(&flux);
}
#[test]
fn test_nuclear_receptor_config_serde_roundtrip() {
assert_serde_roundtrip(&rasayan::nuclear_receptor::NuclearReceptorConfig::default());
}
#[test]
fn test_nuclear_receptor_flux_serde_roundtrip() {
let config = rasayan::nuclear_receptor::NuclearReceptorConfig::default();
let mut state = rasayan::nuclear_receptor::NuclearReceptorState::default();
let flux = state.tick(&config, 0.5, 0.01);
assert_serde_roundtrip(&flux);
}
#[test]
fn test_receptor_config_serde_roundtrip() {
assert_serde_roundtrip(&rasayan::receptor::ReceptorConfig::default());
}
#[test]
fn test_receptor_flux_serde_roundtrip() {
let config = rasayan::receptor::ReceptorConfig::default();
let mut state = rasayan::receptor::ReceptorState::default();
let flux = state.tick(&config, 0.5, 0.01);
assert_serde_roundtrip(&flux);
}
#[test]
fn test_calcium_config_serde_roundtrip() {
assert_serde_roundtrip(&rasayan::calcium::CalciumConfig::default());
}
#[test]
fn test_calcium_flux_serde_roundtrip() {
let config = rasayan::calcium::CalciumConfig::default();
let mut state = rasayan::calcium::CalciumState::default();
let flux = state.tick(&config, 0.5, 0.01);
assert_serde_roundtrip(&flux);
}
#[test]
fn test_mapk_config_serde_roundtrip() {
assert_serde_roundtrip(&rasayan::mapk::MapkConfig::default());
}
#[test]
fn test_mapk_flux_serde_roundtrip() {
let config = rasayan::mapk::MapkConfig::default();
let mut state = rasayan::mapk::MapkState::default();
let flux = state.tick(&config, 0.5, 0.01);
assert_serde_roundtrip(&flux);
}
#[test]
fn test_jak_stat_config_serde_roundtrip() {
assert_serde_roundtrip(&rasayan::jak_stat::JakStatConfig::default());
}
#[test]
fn test_jak_stat_flux_serde_roundtrip() {
let config = rasayan::jak_stat::JakStatConfig::default();
let mut state = rasayan::jak_stat::JakStatState::default();
let flux = state.tick(&config, 0.5, 0.01);
assert_serde_roundtrip(&flux);
}
#[test]
fn test_pi3k_config_serde_roundtrip() {
assert_serde_roundtrip(&rasayan::pi3k::Pi3kConfig::default());
}
#[test]
fn test_pi3k_flux_serde_roundtrip() {
let config = rasayan::pi3k::Pi3kConfig::default();
let mut state = rasayan::pi3k::Pi3kState::default();
let flux = state.tick(&config, 0.5, 0.01);
assert_serde_roundtrip(&flux);
}
#[test]
fn test_etc_flux_serde_roundtrip() {
let config = rasayan::etc::EtcConfig::default();
let mut state = rasayan::etc::EtcState::default();
let flux = state.tick(&config, 0.5, 0.1, 1.0, 0.5, 0.01);
assert_serde_roundtrip(&flux);
}
#[test]
fn test_tca_flux_serde_roundtrip() {
let config = rasayan::tca::TcaConfig::default();
let mut state = rasayan::tca::TcaState::default();
let flux = state.tick(&config, 0.1, 6.0, 0.5, 700.0, 0.01);
assert_serde_roundtrip(&flux);
}
#[test]
fn test_beta_ox_flux_serde_roundtrip() {
let config = rasayan::beta_oxidation::BetaOxConfig::default();
let mut state = rasayan::beta_oxidation::BetaOxState::default();
let flux = state.tick(&config, 0.0, 700.0, 0.01);
assert_serde_roundtrip(&flux);
}
#[test]
fn test_amino_catab_flux_serde_roundtrip() {
let config = rasayan::amino_catabolism::AminoCatabConfig::default();
let mut state = rasayan::amino_catabolism::AminoCatabState::default();
let flux = state.tick(&config, 0.3, 700.0, 0.01);
assert_serde_roundtrip(&flux);
}
#[test]
fn test_carbon_skeleton_output_serde_roundtrip() {
assert_serde_roundtrip(&rasayan::amino_catabolism::CarbonSkeletonOutput {
pyruvate: 0.1,
acetyl_coa: 0.2,
alpha_kg: 0.1,
succinyl_coa: 0.05,
fumarate: 0.03,
oxaloacetate: 0.02,
});
}
#[test]
fn test_network_flux_serde_roundtrip() {
let config = rasayan::pathway::NetworkConfig::default();
let mut net = rasayan::pathway::MetabolicNetwork::default();
let flux = net.tick(&config, 0.01);
assert_serde_roundtrip(&flux);
}
#[test]
fn test_hormonal_input_serde_roundtrip() {
assert_serde_roundtrip(&rasayan::hormonal::HormonalInput {
stress: 0.5,
serotonin: 0.4,
light: 0.8,
social_stimulus: 0.3,
neural_activity: 0.6,
});
}
#[test]
fn test_hormonal_flux_serde_roundtrip() {
let config = rasayan::hormonal::HormonalConfig::default();
let mut state = rasayan::hormonal::HormonalState::default();
let input = rasayan::hormonal::HormonalInput {
stress: 0.5,
serotonin: 0.4,
light: 0.8,
social_stimulus: 0.3,
neural_activity: 0.6,
};
let flux = state.tick(&config, &input, 0.01);
assert_serde_roundtrip(&flux);
}
#[test]
fn test_neurotransmitter_flux_serde_roundtrip() {
let config = rasayan::neurotransmitter::NeurotransmitterConfig::default();
let mut state = rasayan::neurotransmitter::NeurotransmitterState::default();
let flux = state.tick(&config, 0.5, 0.5, 0.01);
assert_serde_roundtrip(&flux);
}
#[test]
fn test_alignment_score_serialize() {
let score =
rasayan::alignment::score_alignment("ACDEF", "ACDEF", rasayan::alignment::Matrix::Blosum62)
.unwrap();
let json = serde_json::to_string(&score).unwrap();
assert!(json.contains("identity"));
}
#[test]
fn test_extinction_coefficient_serialize() {
let ec = rasayan::protein::extinction_coefficient("WYCC").unwrap();
let json = serde_json::to_string(&ec).unwrap();
assert!(json.contains("oxidized"));
}
#[test]
fn test_amino_acid_serialize() {
let aa = rasayan::protein::lookup('A').unwrap();
let json = serde_json::to_string(&aa).unwrap();
assert!(json.contains("Alanine"));
}
#[test]
fn test_known_enzyme_serialize() {
let enz = rasayan::enzyme::lookup_enzyme("Catalase").unwrap();
let json = serde_json::to_string(&enz).unwrap();
assert!(json.contains("Catalase"));
}
#[test]
fn test_error_display_negative_concentration() {
let err = rasayan::RasayanError::NegativeConcentration {
name: "ATP".into(),
value: -1.5,
};
let msg = err.to_string();
assert!(msg.contains("ATP"));
assert!(msg.contains("-1.5"));
assert!(msg.contains("concentration"));
}
#[test]
fn test_error_display_invalid_parameter() {
let err = rasayan::RasayanError::InvalidParameter {
name: "Km".into(),
value: -0.01,
reason: "must be positive".into(),
};
let msg = err.to_string();
assert!(msg.contains("Km"));
assert!(msg.contains("-0.01"));
assert!(msg.contains("must be positive"));
}
#[test]
fn test_error_display_unknown_amino_acid() {
let err = rasayan::RasayanError::UnknownAminoAcid('X');
assert!(err.to_string().contains('X'));
}
#[test]
fn test_error_display_unknown_pathway() {
let err = rasayan::RasayanError::UnknownPathway("pentose phosphate".into());
assert!(err.to_string().contains("pentose phosphate"));
}
#[test]
fn test_michaelis_menten_half_vmax_at_km() {
let rate = enzyme::michaelis_menten(1.0, 10.0, 1.0);
assert!((rate - 5.0).abs() < 0.01);
}
#[test]
fn test_michaelis_menten_saturation() {
let rate = enzyme::michaelis_menten(10000.0, 10.0, 1.0);
assert!((rate - 10.0).abs() < 0.01);
}
#[test]
fn test_michaelis_menten_zero_substrate() {
let rate = enzyme::michaelis_menten(0.0, 10.0, 1.0);
assert!((rate - 0.0).abs() < f64::EPSILON);
}
#[test]
fn test_nernst_potassium() {
let e = membrane::nernst(310.0, 1, 4.0, 155.0);
assert!(e < -80.0 && e > -110.0, "Nernst K+ = {e} mV");
}
#[test]
fn test_nernst_sodium() {
let e = membrane::nernst(310.0, 1, 145.0, 12.0);
assert!(e > 50.0 && e < 80.0, "Nernst Na+ = {e} mV");
}
#[test]
fn test_dose_response_at_ec50() {
let r = signal::dose_response(1.0, 1.0, 1.0, 1.0);
assert!((r - 0.5).abs() < 0.01);
}
#[test]
fn test_dose_response_steep_hill() {
let r_low = signal::dose_response(0.5, 1.0, 1.0, 1.0);
let r_high = signal::dose_response(0.5, 1.0, 1.0, 4.0);
assert!(r_high < r_low);
}
#[test]
fn test_metabolic_energy_charge_consistency() {
let m = MetabolicState::default();
let ec = m.energy_charge();
assert!((0.0..=1.0).contains(&ec));
assert!(ec > 0.9);
}
#[test]
fn test_bioenergy_and_metabolism_alignment() {
let met = MetabolicState::default();
let bio = BioenergyState::default();
assert!(!met.is_anaerobic());
assert!(!bio.is_anaerobic());
}
#[test]
fn test_membrane_potential_physiological_range() {
let ions = IonicState::default();
let vm = ions.resting_potential();
assert!(
vm < -50.0 && vm > -100.0,
"Resting potential {vm} mV out of physiological range"
);
}
#[test]
fn test_all_defaults_validate() {
assert!(MetabolicState::default().validate().is_ok());
assert!(BioenergyState::default().validate().is_ok());
assert!(
EnzymeParams {
vmax: 10.0,
km: 1.0,
hill_n: 1.0,
kcat: 100.0,
}
.validate()
.is_ok()
);
}
#[test]
fn test_goldman_with_custom_permeability() {
let ions = IonicState::default();
let k_only = MembranePermeability {
p_na: 0.0,
p_k: 1.0,
p_cl: 0.0,
};
let vm = membrane::goldman(310.0, &ions, &k_only);
let e_k = membrane::nernst(310.0, 1, ions.k_out, ions.k_in);
assert!(
(vm - e_k).abs() < 1.0,
"Goldman K+-only ({vm:.1}) should ~ Nernst K+ ({e_k:.1})"
);
}
#[test]
fn test_nernst_zero_concentrations() {
assert!(membrane::nernst(310.0, 1, 0.0, 155.0).abs() < f64::EPSILON);
assert!(membrane::nernst(310.0, 1, 4.0, 0.0).abs() < f64::EPSILON);
assert!(membrane::nernst(310.0, 0, 4.0, 155.0).abs() < f64::EPSILON);
}
#[test]
fn test_inhibition_hierarchy() {
let base = enzyme::michaelis_menten(1.0, 10.0, 1.0);
let comp = enzyme::competitive_inhibition(1.0, 1.0, 10.0, 1.0, 1.0);
let uncomp = enzyme::uncompetitive_inhibition(1.0, 1.0, 10.0, 1.0, 1.0);
let mixed = enzyme::mixed_inhibition(1.0, 1.0, 10.0, 1.0, 1.0, 1.0);
assert!(comp < base);
assert!(uncomp < base);
assert!(mixed < base);
}
#[test]
fn test_reversible_haldane_consistency() {
let vf = 10.0;
let kmf = 1.0;
let vr = 5.0;
let kmr = 2.0;
let keq = enzyme::haldane_keq(vf, kmf, vr, kmr);
let s = 1.0;
let p = keq * s;
let rate = enzyme::reversible_michaelis_menten(s, p, vf, kmf, vr, kmr);
assert!(
rate.abs() < 0.01,
"Rate at equilibrium should be ~0, got {rate}"
);
}
#[test]
fn test_enzyme_db_params_produce_valid_rates() {
for enz in enzyme::KNOWN_ENZYMES {
let params = enz.params(1e-6);
assert!(params.validate().is_ok(), "Invalid params for {}", enz.name);
let rate = params.rate(enz.km); assert!(rate > 0.0, "Zero rate for {} at Km", enz.name);
assert!(rate.is_finite(), "Non-finite rate for {}", enz.name);
}
}
#[test]
fn test_lineweaver_burk_and_eadie_hofstee_agree() {
let data: Vec<(f64, f64)> = [0.2, 0.5, 1.0, 2.0, 5.0, 10.0]
.iter()
.map(|&s| (s, enzyme::michaelis_menten(s, 8.0, 0.5)))
.collect();
let lb = enzyme::lineweaver_burk_fit(&data).unwrap();
let eh = enzyme::eadie_hofstee_fit(&data).unwrap();
assert!(
(lb.km - eh.km).abs() < 0.05,
"Km mismatch: LB={} EH={}",
lb.km,
eh.km
);
assert!(
(lb.vmax - eh.vmax).abs() < 0.05,
"Vmax mismatch: LB={} EH={}",
lb.vmax,
eh.vmax
);
}
#[test]
fn test_kinetic_fit_serde_roundtrip() {
let fit = enzyme::lineweaver_burk_fit(&[
(0.5, enzyme::michaelis_menten(0.5, 10.0, 1.0)),
(1.0, enzyme::michaelis_menten(1.0, 10.0, 1.0)),
(5.0, enzyme::michaelis_menten(5.0, 10.0, 1.0)),
])
.unwrap();
let json = serde_json::to_string(&fit).unwrap();
let fit2: enzyme::KineticFit = serde_json::from_str(&json).unwrap();
assert!((fit2.km - fit.km).abs() < f64::EPSILON);
assert!((fit2.vmax - fit.vmax).abs() < f64::EPSILON);
}
#[test]
fn test_arrhenius_temperature_sensitivity() {
let k1 = enzyme::arrhenius(1e10, 50_000.0, 300.0);
let k2 = enzyme::arrhenius(1e10, 50_000.0, 310.0);
let ratio = k2 / k1;
assert!(ratio > 1.5 && ratio < 3.0, "Ratio={ratio}");
}
#[test]
fn test_glycolysis_feeds_tca() {
let glyco_config = GlycolysisConfig::default();
let tca_config = TcaConfig::default();
let mut glyco = GlycolysisState::default();
let mut tca = TcaState::default();
let mut total_nadh = 0.0;
let mut total_atp = 0.0;
for _ in 0..500 {
let gflux = glyco.tick(&glyco_config, 6.0, 0.5, 700.0, 0.1);
let tflux = tca.tick(&tca_config, glyco.pyruvate, 6.0, 0.5, 700.0, 0.1);
glyco.pyruvate = (glyco.pyruvate - tflux.pyruvate_consumed).max(0.0);
total_nadh += gflux.nadh_produced + tflux.nadh_produced;
total_atp += gflux.net_atp + tflux.gtp_produced;
}
assert!(total_nadh > 0.0, "Combined NADH should be positive");
assert!(total_atp > 0.0, "Combined ATP+GTP should be positive");
assert!(
glyco.pyruvate < 5.0,
"Pyruvate should not accumulate unbounded when TCA is consuming it"
);
}
#[test]
fn test_glycolysis_tca_pathway_validation() {
assert!(GlycolysisState::default().validate().is_ok());
assert!(GlycolysisConfig::default().validate().is_ok());
assert!(TcaState::default().validate().is_ok());
assert!(TcaConfig::default().validate().is_ok());
assert!(EtcState::default().validate().is_ok());
assert!(EtcConfig::default().validate().is_ok());
}
#[test]
fn test_full_respiration_pipeline() {
let glyco_config = GlycolysisConfig::default();
let tca_config = TcaConfig::default();
let etc_config = EtcConfig::default();
let mut glyco = GlycolysisState::default();
let mut tca = TcaState::default();
let mut etc = EtcState::default();
let mut total_atp = 0.0;
let mut total_o2 = 0.0;
for _ in 0..200 {
let gflux = glyco.tick(&glyco_config, 6.0, 0.5, 700.0, 0.1);
let tflux = tca.tick(&tca_config, glyco.pyruvate, 6.0, 0.5, 700.0, 0.1);
glyco.pyruvate = (glyco.pyruvate - tflux.pyruvate_consumed).max(0.0);
let nadh_pool = gflux.nadh_produced + tflux.nadh_produced;
let eflux = etc.tick(&etc_config, nadh_pool, tflux.fadh2_produced, 1.0, 0.5, 0.1);
total_atp += gflux.net_atp + tflux.gtp_produced + eflux.atp_produced;
total_o2 += eflux.o2_consumed;
}
assert!(total_atp > 0.0, "Full respiration should produce ATP");
assert!(total_o2 > 0.0, "Full respiration should consume O2");
assert!(
total_atp > 0.1,
"Total ATP from full pipeline should be meaningful: {total_atp}"
);
}
#[test]
fn test_beta_ox_feeds_tca() {
let box_config = BetaOxConfig::default();
let tca_config = TcaConfig::default();
let mut beta_ox = BetaOxState::default();
let mut tca = TcaState::default();
let mut total_accoa = 0.0;
let mut total_nadh = 0.0;
for _ in 0..200 {
let bflux = beta_ox.tick(&box_config, 0.0, 700.0, 0.1);
tca.acetyl_coa += bflux.acetyl_coa_produced;
let tflux = tca.tick(&tca_config, 0.0, 6.0, 0.5, 700.0, 0.1);
total_accoa += bflux.acetyl_coa_produced;
total_nadh += bflux.nadh_produced + tflux.nadh_produced;
}
assert!(total_accoa > 0.0, "Beta-ox should produce acetyl-CoA");
assert!(total_nadh > 0.0, "Combined NADH from beta-ox + TCA");
}
#[test]
fn test_beta_ox_validation() {
assert!(BetaOxState::default().validate().is_ok());
assert!(BetaOxConfig::default().validate().is_ok());
}
#[test]
fn test_amino_catab_feeds_tca() {
let aa_config = AminoCatabConfig::default();
let tca_config = TcaConfig::default();
let mut aa = AminoCatabState::default();
let mut tca = TcaState::default();
for _ in 0..200 {
let aflux = aa.tick(&aa_config, tca.alpha_kg, 700.0, 0.1);
tca.acetyl_coa += aflux.to_acetyl_coa;
tca.oxaloacetate += aflux.to_oxaloacetate;
tca.alpha_kg += aflux.to_alpha_kg;
tca.succinyl_coa += aflux.to_succinyl_coa;
tca.fumarate += aflux.to_fumarate;
let _ = tca.tick(&tca_config, aflux.to_pyruvate, 6.0, 0.5, 700.0, 0.1);
}
assert!(
aa.amino_acid_pool < AminoCatabState::default().amino_acid_pool,
"AA pool should deplete as catabolism feeds TCA"
);
}
#[test]
fn test_amino_catab_validation() {
assert!(AminoCatabState::default().validate().is_ok());
assert!(AminoCatabConfig::default().validate().is_ok());
}
#[test]
fn test_network_long_run_stability() {
use rasayan::pathway::{MetabolicNetwork, NetworkConfig};
let mut net = MetabolicNetwork::default();
let config = NetworkConfig::default();
for _ in 0..1000 {
let _ = net.tick(&config, 0.1);
}
assert!(net.atp >= 0.0, "ATP went negative: {}", net.atp);
assert!(net.adp >= 0.0, "ADP went negative: {}", net.adp);
assert!(
net.nad_ratio >= 1.0,
"NAD ratio collapsed: {}",
net.nad_ratio
);
assert!(net.glycolysis.validate().is_ok());
assert!(net.tca.validate().is_ok());
assert!(net.etc.validate().is_ok());
}
#[test]
fn test_neurotransmitter_long_run_stability() {
use rasayan::neurotransmitter::{NeurotransmitterConfig, NeurotransmitterState};
let mut state = NeurotransmitterState::default();
let config = NeurotransmitterConfig::default();
for _ in 0..1000 {
let _ = state.tick(&config, 0.05, 1.0, 0.1);
}
assert!(state.validate().is_ok());
assert!(state.serotonin > 0.0);
assert!(state.dopamine > 0.0);
assert!(state.gaba > 0.0);
assert!(state.glutamate > 0.0);
}
#[test]
fn test_hormonal_long_run_stability() {
use rasayan::hormonal::{HormonalConfig, HormonalInput, HormonalState};
let mut state = HormonalState::default();
let config = HormonalConfig::default();
let input = HormonalInput::default();
for _ in 0..1000 {
let _ = state.tick(&config, &input, 0.1);
}
assert!(state.validate().is_ok());
assert!(state.cortisol > 0.0);
}
#[test]
fn test_network_zero_glucose_graceful() {
use rasayan::pathway::{MetabolicNetwork, NetworkConfig};
let mut net = MetabolicNetwork::default();
net.glycolysis.glucose = 0.0;
let config = NetworkConfig::default();
for _ in 0..500 {
let _ = net.tick(&config, 0.1);
}
assert!(net.atp.is_finite());
assert!(net.adp.is_finite());
}
#[test]
fn test_network_zero_oxygen_graceful() {
use rasayan::pathway::{MetabolicNetwork, NetworkConfig};
let mut net = MetabolicNetwork {
oxygen: 0.0,
..MetabolicNetwork::default()
};
let config = NetworkConfig::default();
for _ in 0..500 {
let _ = net.tick(&config, 0.1);
}
assert!(net.atp.is_finite());
assert!(
net.etc.pmf < 0.5,
"PMF should drop without O2: {}",
net.etc.pmf
);
}
#[test]
fn test_nt_stress_response_chain() {
use rasayan::hormonal::{HormonalConfig, HormonalInput, HormonalState};
use rasayan::neurotransmitter::{NeurotransmitterConfig, NeurotransmitterState};
let mut nt = NeurotransmitterState::default();
let nt_config = NeurotransmitterConfig::default();
let mut horm = HormonalState::default();
let horm_config = HormonalConfig::default();
for _ in 0..100 {
let nt_flux = nt.tick(&nt_config, 0.05, 2.0, 0.1);
let input = HormonalInput {
stress: 2.0,
serotonin: nt.serotonin,
..HormonalInput::default()
};
let _ = horm.tick(&horm_config, &input, 0.1);
let _ = nt_flux; }
assert!(
horm.cortisol > HormonalState::default().cortisol,
"Cortisol should rise under stress"
);
assert!(
nt.norepinephrine > 0.0,
"NE should be present during stress"
);
}