use crate::context::mesosystem::{
passes_proximal_process_gate, passes_proximal_process_gate_with_reciprocity, MesosystemState,
INTERACTION_COMPLEXITY_THRESHOLD, INTERACTION_FREQUENCY_THRESHOLD,
INTERACTION_RECIPROCITY_THRESHOLD,
};
use crate::context::EcologicalContext;
use crate::enums::{BirthEra, LifeStage};
use crate::state::IndividualState;
use crate::types::{Duration, Timestamp};
#[must_use]
pub(crate) fn apply_context_effects(
mut state: IndividualState,
context: &EcologicalContext,
relationship_quality: f64,
duration: Duration,
life_stage: LifeStage,
age_years: f64,
current_timestamp: Timestamp,
) -> IndividualState {
let time_scale = duration_scale(duration);
if time_scale <= 0.0 {
return state;
}
let mut shaped_context = context.clone();
shaped_context.invalidate_mesosystem_cache();
apply_person_shaping(&mut shaped_context, &state);
apply_demand_shaping(&mut shaped_context, &state);
apply_withdrawal_feedback(&mut shaped_context, &state);
let has_microsystems = shaped_context.microsystems_iter().next().is_some();
let (avg_frequency, avg_complexity, avg_reciprocity) =
compute_aggregate_interaction_metrics(&shaped_context);
apply_engagement_isolation_effects(&mut state, avg_frequency, time_scale, has_microsystems);
let gate_multiplier = shaped_context
.mesosystem_state()
.microsystem_influence_weight;
let reciprocity_multiplier = match passes_proximal_process_gate_with_reciprocity(
avg_frequency,
avg_complexity,
avg_reciprocity,
INTERACTION_FREQUENCY_THRESHOLD,
INTERACTION_COMPLEXITY_THRESHOLD,
INTERACTION_RECIPROCITY_THRESHOLD,
) {
Ok(()) => 1.0,
Err(_) => 0.0,
};
let microsystem_multiplier = gate_multiplier * reciprocity_multiplier;
apply_microsystem_immediate_effects(&mut state, &shaped_context, time_scale);
let (stress_adj, loneliness_adj) =
shaped_context.compute_context_to_person_effects(relationship_quality);
state.needs_mut().add_stress_delta(stress_adj * time_scale);
state
.social_cognition_mut()
.add_loneliness_delta(loneliness_adj * time_scale);
if microsystem_multiplier > 0.0 {
apply_microsystem_developmental_effects(
&mut state,
&shaped_context,
time_scale,
microsystem_multiplier,
);
apply_mesosystem_spillover(&mut state, &shaped_context, time_scale, microsystem_multiplier);
}
apply_exosystem_effects(&mut state, &shaped_context, time_scale, life_stage);
let macrosystem_snapshot = shaped_context
.macrosystem()
.interpolate_for_timestamp(shaped_context.chronosystem(), current_timestamp);
apply_macrosystem_effects(
&mut state,
&shaped_context,
¯osystem_snapshot,
time_scale,
relationship_quality,
life_stage,
);
apply_chronosystem_effects(
&mut state,
&shaped_context,
time_scale,
age_years,
current_timestamp,
);
state
}
fn duration_scale(duration: Duration) -> f32 {
let days = duration.as_days_f64();
if days <= 0.0 {
return 0.0;
}
((days / 30.0) as f32).min(12.0)
}
const WITHDRAWAL_THRESHOLD: f32 = 0.4;
const WITHDRAWAL_FREQUENCY_REDUCTION: f64 = 0.3;
const LOW_ENGAGEMENT_THRESHOLD: f64 = 0.35;
fn compute_aggregate_interaction_metrics(context: &EcologicalContext) -> (f64, f64, f64) {
let mut total_frequency = 0.0;
let mut total_complexity = 0.0;
let mut total_reciprocity = 0.0;
let mut count = 0.0;
for (_, microsystem) in context.microsystems_iter() {
total_frequency += microsystem.interaction_frequency();
total_complexity += microsystem.interaction_complexity();
total_reciprocity += microsystem.interaction_reciprocity();
count += 1.0;
}
if count == 0.0 {
return (0.0, 0.0, 0.0);
}
(
total_frequency / count,
total_complexity / count,
total_reciprocity / count,
)
}
fn apply_withdrawal_feedback(context: &mut EcologicalContext, state: &IndividualState) {
let social = state.social_cognition();
let mental_health = state.mental_health();
let tb = mental_health.compute_thwarted_belongingness(social);
let pb = mental_health.compute_perceived_burdensomeness(social);
let withdrawal_signal = ((tb.max(pb) - WITHDRAWAL_THRESHOLD) / (1.0 - WITHDRAWAL_THRESHOLD))
.clamp(0.0, 1.0);
if withdrawal_signal <= 0.0 {
return;
}
let reduction = WITHDRAWAL_FREQUENCY_REDUCTION * f64::from(withdrawal_signal);
for microsystem in context.microsystems.values_mut() {
if let Some(family) = microsystem.family_mut() {
family.interaction_profile.interaction_frequency =
(family.interaction_profile.interaction_frequency - reduction).max(0.0);
family.interaction_profile.reciprocity_balance =
(family.interaction_profile.reciprocity_balance - reduction * 0.4).max(0.0);
}
if let Some(social) = microsystem.social_mut() {
social.interaction_profile.interaction_frequency =
(social.interaction_profile.interaction_frequency - reduction).max(0.0);
social.interaction_profile.reciprocity_balance =
(social.interaction_profile.reciprocity_balance - reduction * 0.4).max(0.0);
}
}
}
fn apply_engagement_isolation_effects(
state: &mut IndividualState,
avg_frequency: f64,
time_scale: f32,
has_microsystems: bool,
) {
if !has_microsystems {
return;
}
if avg_frequency >= LOW_ENGAGEMENT_THRESHOLD {
return;
}
let deficit = (LOW_ENGAGEMENT_THRESHOLD - avg_frequency).clamp(0.0, 1.0);
let loneliness_delta = (deficit * 0.08) as f32;
let caring_delta = (deficit * 0.05) as f32;
state
.social_cognition_mut()
.add_loneliness_delta(loneliness_delta * time_scale);
state
.social_cognition_mut()
.add_perceived_reciprocal_caring_delta(-caring_delta * time_scale);
}
fn apply_microsystem_immediate_effects(
state: &mut IndividualState,
context: &EcologicalContext,
time_scale: f32,
) {
for (_, microsystem) in context.microsystems_iter() {
if let Some(work) = microsystem.work() {
let workload_excess = (work.workload_stress - 0.7).max(0.0);
let stress_delta = (workload_excess * 0.15) as f32;
let fatigue_delta = (workload_excess * 0.1) as f32;
state
.needs_mut()
.add_stress_delta(stress_delta * time_scale);
state
.needs_mut()
.add_fatigue_delta(fatigue_delta * time_scale);
}
if let Some(family) = microsystem.family() {
let role_multiplier = family.family_role.effect_multiplier();
let support_level = (family.family_satisfaction + family.warmth) / 2.0;
let support_buffer = (support_level - 0.6).max(0.0) * 0.1 * role_multiplier;
let buffer_delta = support_buffer as f32;
state
.needs_mut()
.add_stress_delta(-buffer_delta * time_scale);
state
.social_cognition_mut()
.add_loneliness_delta(-buffer_delta * time_scale);
let caregiving_load = (family.caregiving_burden - 0.5).max(0.0) * role_multiplier;
let burden_stress = (caregiving_load * 0.1) as f32;
let burden_fatigue = (caregiving_load * 0.08) as f32;
state
.needs_mut()
.add_stress_delta(burden_stress * time_scale);
state
.needs_mut()
.add_fatigue_delta(burden_fatigue * time_scale);
let hostility_load = (family.hostility - 0.5).max(0.0) * role_multiplier;
let hostility_stress = (hostility_load * 0.05) as f32;
state
.needs_mut()
.add_stress_delta(hostility_stress * time_scale);
let warmth_boost = (family.warmth - 0.5).max(0.0) * 0.05 * role_multiplier;
let valence_delta = warmth_boost as f32;
state
.mood_mut()
.add_valence_delta(valence_delta * time_scale);
}
if let Some(social) = microsystem.social() {
let support = (social.warmth - social.hostility).max(0.0);
let support_delta = (support * 0.08) as f32;
state
.social_cognition_mut()
.add_loneliness_delta(-support_delta * time_scale);
let hostility_excess = (social.hostility - 0.5).max(0.0);
let hostility_stress = (hostility_excess * 0.06) as f32;
state
.needs_mut()
.add_stress_delta(hostility_stress * time_scale);
let standing_boost = (social.group_standing - 0.5).max(0.0);
let competence_delta = (standing_boost * 0.05) as f32;
state
.social_cognition_mut()
.add_perceived_competence_delta(competence_delta * time_scale);
let unpredictability = (0.5 - social.predictability).max(0.0);
let unpredictability_stress = (unpredictability * 0.05) as f32;
state
.needs_mut()
.add_stress_delta(unpredictability_stress * time_scale);
}
if let Some(education) = microsystem.education() {
let demand = (education.cognitive_demand - 0.6).max(0.0);
let demand_stress = (demand * 0.08) as f32;
let demand_fatigue = (demand * 0.06) as f32;
state
.needs_mut()
.add_stress_delta(demand_stress * time_scale);
state
.needs_mut()
.add_fatigue_delta(demand_fatigue * time_scale);
let support = (education.competence_support - 0.5).max(0.0);
let competence_delta = (support * 0.1) as f32;
state
.social_cognition_mut()
.add_perceived_competence_delta(competence_delta * time_scale);
let warmth_boost = (education.warmth - 0.5).max(0.0);
let warmth_delta = (warmth_boost * 0.04) as f32;
state
.mood_mut()
.add_valence_delta(warmth_delta * time_scale);
let hostility_excess = (education.hostility - 0.5).max(0.0);
let hostility_stress = (hostility_excess * 0.07) as f32;
state
.needs_mut()
.add_stress_delta(hostility_stress * time_scale);
let self_hate_delta = (hostility_excess * 0.05) as f32;
state
.social_cognition_mut()
.add_self_hate_delta(self_hate_delta * time_scale);
}
if let Some(healthcare) = microsystem.healthcare() {
let access_deficit = (0.5 - healthcare.access_frequency).max(0.0);
let access_stress = (access_deficit * 0.08) as f32;
state
.needs_mut()
.add_stress_delta(access_stress * time_scale);
if access_deficit > 0.0 {
let hopelessness_delta = (access_deficit * 0.04) as f32;
state
.mental_health_mut()
.add_hopelessness_delta(hopelessness_delta * time_scale);
}
let responsiveness = (healthcare.responsiveness - 0.5).max(0.0);
let responsiveness_delta = (responsiveness * 0.05) as f32;
state
.needs_mut()
.add_stress_delta(-responsiveness_delta * time_scale);
let warmth_boost = (healthcare.warmth - 0.5).max(0.0);
let valence_delta = (warmth_boost * 0.03) as f32;
state
.mood_mut()
.add_valence_delta(valence_delta * time_scale);
let hostility_excess = (healthcare.hostility - 0.5).max(0.0);
let hostility_stress = (hostility_excess * 0.06) as f32;
state
.needs_mut()
.add_stress_delta(hostility_stress * time_scale);
}
if let Some(religious) = microsystem.religious() {
let ritual = religious.ritual_frequency;
let purpose_delta = (ritual * 0.04) as f32;
state
.needs_mut()
.add_purpose_delta(purpose_delta * time_scale);
let warmth_boost = (religious.warmth - 0.5).max(0.0);
let warmth_delta = (warmth_boost * 0.05) as f32;
state
.social_cognition_mut()
.add_loneliness_delta(-warmth_delta * time_scale);
let hostility_excess = (religious.hostility - 0.5).max(0.0);
let hostility_delta = (hostility_excess * 0.05) as f32;
state
.social_cognition_mut()
.add_self_hate_delta(hostility_delta * time_scale);
}
if let Some(neighborhood) = microsystem.neighborhood() {
let safety_deficit = (0.6 - neighborhood.safety).max(0.0);
let safety_stress = (safety_deficit * 0.08) as f32;
state
.needs_mut()
.add_stress_delta(safety_stress * time_scale);
let cohesion_boost = (neighborhood.cohesion - 0.5).max(0.0);
let cohesion_delta = (cohesion_boost * 0.05) as f32;
state
.social_cognition_mut()
.add_loneliness_delta(-cohesion_delta * time_scale);
let warmth_boost = (neighborhood.warmth - 0.5).max(0.0);
let warmth_delta = (warmth_boost * 0.04) as f32;
state
.mood_mut()
.add_valence_delta(warmth_delta * time_scale);
let hostility_excess = (neighborhood.hostility - 0.5).max(0.0);
let hostility_stress = (hostility_excess * 0.05) as f32;
state
.needs_mut()
.add_stress_delta(hostility_stress * time_scale);
}
}
}
fn apply_microsystem_developmental_effects(
state: &mut IndividualState,
context: &EcologicalContext,
time_scale: f32,
microsystem_multiplier: f64,
) {
if microsystem_multiplier <= 0.0 {
return;
}
for (_, microsystem) in context.microsystems_iter() {
let local_multiplier = match passes_proximal_process_gate(
microsystem.interaction_frequency(),
microsystem.interaction_complexity(),
INTERACTION_FREQUENCY_THRESHOLD,
INTERACTION_COMPLEXITY_THRESHOLD,
) {
Ok(()) => microsystem_multiplier,
Err(_) => 0.0,
};
if local_multiplier <= 0.0 {
continue;
}
if let Some(work) = microsystem.work() {
let workload_excess = (work.workload_stress - 0.7).max(0.0);
if workload_excess > 0.0 {
let chronic_stress = (workload_excess * 0.04 * local_multiplier) as f32;
state
.needs_mut()
.add_stress_delta(chronic_stress * time_scale);
}
}
if let Some(family) = microsystem.family() {
let warmth_boost = (family.warmth - 0.6).max(0.0);
if warmth_boost > 0.0 {
let caring_delta = (warmth_boost * 0.04 * local_multiplier) as f32;
state
.social_cognition_mut()
.add_perceived_reciprocal_caring_delta(caring_delta * time_scale);
}
let cohesion_deficit = (0.5 - family.cohesion).max(0.0);
if cohesion_deficit > 0.0 {
let loneliness_delta = (cohesion_deficit * 0.06 * local_multiplier) as f32;
let caring_delta = (cohesion_deficit * 0.04 * local_multiplier) as f32;
state
.social_cognition_mut()
.add_loneliness_delta(loneliness_delta * time_scale);
state
.social_cognition_mut()
.add_perceived_reciprocal_caring_delta(-caring_delta * time_scale);
}
}
if let Some(social) = microsystem.social() {
let warmth_boost = (social.warmth - 0.6).max(0.0);
if warmth_boost > 0.0 {
let belonging_delta = (warmth_boost * 0.03 * local_multiplier) as f32;
state
.social_cognition_mut()
.add_perceived_reciprocal_caring_delta(belonging_delta * time_scale);
}
}
if let Some(education) = microsystem.education() {
let demand = (education.cognitive_demand - 0.6).max(0.0);
let demand_stress = (demand * 0.02 * local_multiplier) as f32;
state
.needs_mut()
.add_stress_delta(demand_stress * time_scale);
}
}
}
fn apply_mesosystem_spillover(
state: &mut IndividualState,
context: &EcologicalContext,
time_scale: f32,
microsystem_multiplier: f64,
) {
let mesosystem_state = MesosystemState::compute(&context.microsystems);
let shared_membership_benefit =
(mesosystem_state.shared_membership_strength * 0.5).clamp(0.0, 1.0);
let shared_membership_multiplier = 1.0 - shared_membership_benefit;
for (from_id, to_id) in context.list_linkages() {
let spillover_ab = context.get_spillover(&from_id, &to_id);
let spillover_ba = context.get_spillover(&to_id, &from_id);
let total_spillover = spillover_ab + spillover_ba;
if total_spillover <= 0.0 {
continue;
}
let stress_delta =
(total_spillover * 0.1 * shared_membership_multiplier * microsystem_multiplier) as f32;
state
.needs_mut()
.add_stress_delta(stress_delta * time_scale);
}
let role_conflict = compute_role_conflict(context);
if role_conflict > 0.0 {
let conflict_delta = (role_conflict * 0.1 * microsystem_multiplier) as f32;
state
.needs_mut()
.add_stress_delta(conflict_delta * time_scale);
}
let family_support = mesosystem_state.family_social_support.max(0.0);
if family_support > 0.0 {
let support_delta = (family_support * 0.06 * microsystem_multiplier) as f32;
state
.needs_mut()
.add_stress_delta(-support_delta * time_scale);
state
.social_cognition_mut()
.add_loneliness_delta(-support_delta * time_scale);
}
let work_social_conflict = mesosystem_state.work_social_conflict.max(0.0);
if work_social_conflict > 0.0 {
let conflict_delta = (work_social_conflict * 0.06 * microsystem_multiplier) as f32;
state
.needs_mut()
.add_stress_delta(conflict_delta * time_scale);
}
let inconsistency = (1.0 - mesosystem_state.mesosystem_consistency).max(0.0);
if inconsistency > 0.0 {
let consistency_delta = (inconsistency * 0.05 * microsystem_multiplier) as f32;
state
.needs_mut()
.add_stress_delta(consistency_delta * time_scale);
}
}
fn compute_role_conflict(context: &EcologicalContext) -> f64 {
let work_contexts: Vec<_> = context
.microsystems_iter()
.filter_map(|(_, microsystem)| microsystem.work())
.collect();
let family_contexts: Vec<_> = context
.microsystems_iter()
.filter_map(|(_, microsystem)| microsystem.family())
.collect();
let mut conflict: f64 = 0.0;
for work in &work_contexts {
for family in &family_contexts {
if work.workload_stress > 0.6 && family.caregiving_burden > 0.4 {
conflict += 0.3;
}
}
}
conflict.clamp(0.0, 1.0)
}
fn apply_exosystem_effects(
state: &mut IndividualState,
context: &EcologicalContext,
time_scale: f32,
life_stage: LifeStage,
) {
let exosystem = context.exosystem();
let rule_of_law = context.macrosystem().institutional_structure.rule_of_law;
let reliability_multiplier = 1.0 + (1.0 - rule_of_law);
let health_access = exosystem.health_system_access;
let health_deficit = (0.5 - health_access).max(0.0) * reliability_multiplier;
if health_deficit > 0.0 {
let stress_delta = (health_deficit * 0.08) as f32;
let hopelessness_delta = (health_deficit * 0.04) as f32;
state
.needs_mut()
.add_stress_delta(stress_delta * time_scale);
state
.mental_health_mut()
.add_hopelessness_delta(hopelessness_delta * time_scale);
} else if health_access > 0.7 {
let stress_delta = ((health_access - 0.7) * 0.05) as f32;
state
.needs_mut()
.add_stress_delta(-stress_delta * time_scale);
}
let education_quality = exosystem.educational_system_quality;
if education_quality < 0.4 {
let deficit = (0.4 - education_quality) * 0.08 * reliability_multiplier;
state
.social_cognition_mut()
.add_perceived_competence_delta(-(deficit as f32) * time_scale);
} else if education_quality > 0.7 {
let boost = (education_quality - 0.7) * 0.06;
state
.social_cognition_mut()
.add_perceived_competence_delta((boost as f32) * time_scale);
}
let services = exosystem.community_services_availability;
if services < 0.4 {
let deficit = (0.4 - services) * 0.08 * reliability_multiplier;
state
.social_cognition_mut()
.add_loneliness_delta((deficit as f32) * time_scale);
state
.needs_mut()
.add_stress_delta((deficit as f32) * time_scale);
} else if services > 0.7 {
let boost = (services - 0.7) * 0.05;
state
.social_cognition_mut()
.add_loneliness_delta(-(boost as f32) * time_scale);
}
let resource_availability = exosystem.resource_availability;
let deficit = (0.5 - resource_availability).max(0.0) * reliability_multiplier;
if deficit > 0.0 {
let coping_delta = (deficit * 0.1) as f32;
state
.person_characteristics_mut()
.emotional_regulation_assets_mut()
.add_delta(-coping_delta * time_scale);
let stability_stress = (deficit * 0.1) as f32;
let cohesion_loneliness = (deficit * 0.05) as f32;
state
.needs_mut()
.add_stress_delta(stability_stress * time_scale);
state
.social_cognition_mut()
.add_loneliness_delta(cohesion_loneliness * time_scale);
}
let institutional_support = exosystem.institutional_support;
if institutional_support < 0.4 {
let pressure = (0.4 - institutional_support) * 0.5 * reliability_multiplier;
let reactance_delta = (pressure * 0.05) as f32;
state
.disposition_mut()
.add_reactance_delta(reactance_delta * time_scale);
}
if matches!(life_stage, LifeStage::Child | LifeStage::Adolescent) {
if let Some(capacity) = exosystem.parent_capacity() {
if capacity < 0.5 {
let deficit = 0.5 - capacity;
let anxiety_delta = (deficit * 0.02) as f32;
let loneliness_delta = (deficit * 0.03) as f32;
state
.social_cognition_mut()
.add_perceived_reciprocal_caring_delta(-anxiety_delta * time_scale);
state
.social_cognition_mut()
.add_loneliness_delta(loneliness_delta * time_scale);
}
}
}
}
fn apply_macrosystem_effects(
state: &mut IndividualState,
context: &EcologicalContext,
macrosystem: &crate::context::MacrosystemContext,
time_scale: f32,
relationship_quality: f64,
life_stage: LifeStage,
) {
let stage_multiplier = life_stage_macrosystem_multiplier(life_stage);
let belonging_weight = macrosystem.belonging_need_weight();
let loneliness_adjustment = ((1.0 - belonging_weight) * 0.05) as f32 * stage_multiplier;
state
.social_cognition_mut()
.add_loneliness_delta(loneliness_adjustment * time_scale);
let autonomy_weight = macrosystem.autonomy_need_weight();
let autonomy_adjustment = ((autonomy_weight - 1.0) * 0.05) as f32 * stage_multiplier;
state
.needs_mut()
.add_purpose_delta(autonomy_adjustment * time_scale);
let cultural_stress = macrosystem.cultural_stress;
let stress_adjustment = (cultural_stress * 0.05) as f32 * stage_multiplier;
state
.needs_mut()
.add_stress_delta(stress_adjustment * time_scale);
let trauma = macrosystem.collective_trauma;
if trauma > 0.3 {
let severity = trauma - 0.3;
let trauma_stress = (severity * 0.08) as f32 * stage_multiplier;
let trauma_hopelessness = (severity * 0.05) as f32 * stage_multiplier;
state
.needs_mut()
.add_stress_delta(trauma_stress * time_scale);
state
.mental_health_mut()
.add_hopelessness_delta(trauma_hopelessness * time_scale);
}
let inequality = macrosystem.economic_inequality;
if inequality > 0.6 {
let severity = inequality - 0.6;
let grievance_delta = (severity * 0.08) as f32 * stage_multiplier;
let stress_delta = (severity * 0.05) as f32 * stage_multiplier;
state
.disposition_mut()
.add_grievance_delta(grievance_delta * time_scale);
state
.needs_mut()
.add_stress_delta(stress_delta * time_scale);
}
let mobility = macrosystem.institutional_structure.social_mobility;
if mobility < 0.4 {
let deficit = (0.4 - mobility) * 0.08;
state
.mental_health_mut()
.add_hopelessness_delta((deficit as f32) * time_scale * stage_multiplier);
state
.social_cognition_mut()
.add_perceived_competence_delta(-(deficit as f32) * time_scale * stage_multiplier);
}
let corruption = macrosystem.institutional_structure.corruption_level;
if corruption > 0.5 {
let severity = corruption - 0.5;
let trust_delta = (severity * 0.08) as f32 * stage_multiplier;
let stress_delta = (severity * 0.04) as f32 * stage_multiplier;
state
.disposition_mut()
.add_trustor_propensity_delta(-trust_delta * time_scale);
state
.needs_mut()
.add_stress_delta(stress_delta * time_scale);
}
let hierarchy_penalty = macrosystem.constraint_set().hierarchy_violation_penalty;
if hierarchy_penalty > 0.0 {
let violation_factor = (1.0 - relationship_quality).max(0.0);
let enforcement_delta =
(hierarchy_penalty * violation_factor * 0.05) as f32 * stage_multiplier;
state
.needs_mut()
.add_stress_delta(enforcement_delta * time_scale);
}
let uncertainty_avoidance = macrosystem.cultural_orientation.uncertainty_avoidance;
let mut total_predictability = 0.0;
let mut count = 0.0;
for (_, microsystem) in context.microsystems_iter() {
if let Some(work) = microsystem.work() {
total_predictability += work.predictability;
count += 1.0;
}
}
if count > 0.0 {
let avg_predictability = total_predictability / count;
if avg_predictability < uncertainty_avoidance {
let stress_delta =
((uncertainty_avoidance - avg_predictability) * 0.05) as f32 * stage_multiplier;
state
.needs_mut()
.add_stress_delta(stress_delta * time_scale);
}
}
}
fn life_stage_macrosystem_multiplier(stage: LifeStage) -> f32 {
match stage {
LifeStage::Child => 0.7,
LifeStage::Adolescent => 0.9,
LifeStage::YoungAdult => 0.6,
LifeStage::Adult => 0.5,
LifeStage::MatureAdult => 0.5,
LifeStage::Elder => 0.5,
}
}
fn apply_person_shaping(context: &mut EcologicalContext, state: &IndividualState) {
let hexaco = state.hexaco();
let grievance = state.disposition().grievance_effective();
context.apply_person_to_context_shaping(
hexaco.extraversion(),
hexaco.conscientiousness(),
hexaco.agreeableness(),
hexaco.neuroticism(),
grievance,
);
let person = state.person_characteristics();
let boldness = (hexaco.extraversion() + person.curiosity_tendency_effective()) / 2.0;
let sensitivity = (hexaco.neuroticism() + (1.0 - person.emotional_regulation_assets_effective()))
/ 2.0;
let sociability = (hexaco.extraversion() + person.social_capital_effective()) / 2.0;
context.apply_person_to_context_modulation(boldness, sensitivity, sociability);
}
fn apply_demand_shaping(context: &mut EcologicalContext, state: &IndividualState) {
let demand = state.person_characteristics().demand_characteristics();
context.apply_demand_characteristics_shaping(demand);
}
fn apply_chronosystem_effects(
state: &mut IndividualState,
context: &EcologicalContext,
time_scale: f32,
age_years: f64,
current_timestamp: Timestamp,
) {
let chronosystem = context.chronosystem();
let sensitivity_multiplier = chronosystem
.critical_periods()
.iter()
.filter(|period| period.is_active(age_years))
.map(|period| period.amplification)
.fold(1.0_f64, f64::max);
let sensitivity = sensitivity_multiplier as f32;
let off_time_stress = chronosystem.total_off_time_stress();
let off_time_delta = (off_time_stress * 0.1) as f32 * sensitivity;
state
.needs_mut()
.add_stress_delta(off_time_delta * time_scale);
let historical = chronosystem.historical_period();
let instability = (0.5 - historical.stability_level).max(0.0);
let instability_delta = (instability * 0.03) as f32 * sensitivity;
state
.needs_mut()
.add_stress_delta(instability_delta * time_scale);
let scarcity = (historical.resource_scarcity - 0.5).max(0.0);
let scarcity_delta = (scarcity * 0.02) as f32 * sensitivity;
state
.disposition_mut()
.add_grievance_delta(scarcity_delta * time_scale);
let institutional_trust = (0.5 - historical.institutional_trust).max(0.0);
if institutional_trust > 0.0 {
let trust_delta = (institutional_trust * 0.05) as f32 * sensitivity;
state
.disposition_mut()
.add_trustor_propensity_delta(-trust_delta * time_scale);
}
let current_year = current_timestamp.year();
for event in chronosystem.non_normative_events() {
let end_year = event.end_year.unwrap_or(current_year);
if current_year >= event.start_year && current_year <= end_year {
let stress_delta = (event.severity * 0.05) as f32 * sensitivity;
let hopelessness_delta = (event.severity * 0.03) as f32 * sensitivity;
state
.needs_mut()
.add_stress_delta(stress_delta * time_scale);
state
.mental_health_mut()
.add_hopelessness_delta(hopelessness_delta * time_scale);
}
}
let cohort_effects = chronosystem.cohort_effects();
let birth_era = cohort_effects.birth_era;
let current_era = BirthEra::from_label(&historical.era_name).unwrap_or(BirthEra::Unknown);
if birth_era != BirthEra::Unknown {
if birth_era == BirthEra::Crisis && historical.stability_level < 0.7 {
state
.needs_mut()
.add_stress_delta(0.1 * time_scale * sensitivity);
}
let cohort_affinity: f64 = if current_era == birth_era { 1.0 } else { 0.8 };
if (cohort_affinity - 1.0).abs() > f64::EPSILON {
let cultural_stress = context.macrosystem().cultural_stress;
let stress_delta = (cultural_stress * cohort_affinity - cultural_stress) * 0.05;
state
.needs_mut()
.add_stress_delta((stress_delta as f32) * time_scale * sensitivity);
}
}
let plasticity_boost = chronosystem.turning_point_plasticity_boost(current_timestamp);
if plasticity_boost > 0.0 {
let boost_delta = (plasticity_boost * 0.05) as f32 * sensitivity;
state
.person_characteristics_mut()
.curiosity_tendency_mut()
.add_delta(boost_delta * time_scale);
state
.person_characteristics_mut()
.experience_diversity_mut()
.add_delta(boost_delta * time_scale);
}
let cohort_weight = chronosystem.cohort_effect_weight(current_era);
if cohort_weight > 0.0 {
let weight = cohort_weight as f32;
let hexaco = state.hexaco_mut();
if birth_era == BirthEra::Crisis {
let value = hexaco.neuroticism();
hexaco.set_neuroticism((value + 0.1 * weight).clamp(-1.0, 1.0));
} else if birth_era == BirthEra::Stability {
let value = hexaco.openness();
hexaco.set_openness((value + 0.05 * weight).clamp(-1.0, 1.0));
} else if birth_era == BirthEra::Scarcity {
let value = hexaco.conscientiousness();
hexaco.set_conscientiousness((value + 0.1 * weight).clamp(-1.0, 1.0));
} else {
let openness = hexaco.openness();
let extraversion = hexaco.extraversion();
hexaco.set_openness((openness + 0.1 * weight).clamp(-1.0, 1.0));
hexaco.set_extraversion((extraversion + 0.05 * weight).clamp(-1.0, 1.0));
}
let attachment = state.attachment_mut();
if birth_era == BirthEra::Crisis {
attachment.add_anxiety_delta(0.1 * weight);
} else if birth_era == BirthEra::Stability {
attachment.add_avoidance_delta(-0.1 * weight);
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::context::{
CriticalPeriod, EcologicalContext, EducationContext, FamilyContext, FamilyRole,
HealthcareContext, Microsystem, NeighborhoodContext, NonNormativeEvent, ParentWorkQuality,
ReligiousContext, SocialContext, TurningPoint, TurningPointDomain, WorkContext,
};
use crate::enums::{BirthEra, LifeStage};
use crate::types::{EventId, MicrosystemId, Timestamp};
const TEST_AGE_DAYS: u64 = 365 * 30;
fn test_timestamp() -> Timestamp {
Timestamp::from_ymd_hms(2024, 1, 1, 0, 0, 0) + Duration::days(TEST_AGE_DAYS)
}
fn age_years_for_stage(stage: LifeStage) -> f64 {
match stage {
LifeStage::Child => 8.0,
LifeStage::Adolescent => 15.0,
LifeStage::YoungAdult => 24.0,
LifeStage::Adult => 40.0,
LifeStage::MatureAdult => 62.0,
LifeStage::Elder => 70.0,
}
}
fn apply_context_effects_test(
state: IndividualState,
context: &EcologicalContext,
relationship_quality: f64,
duration: Duration,
life_stage: LifeStage,
current_timestamp: Timestamp,
) -> IndividualState {
let age_years = age_years_for_stage(life_stage);
apply_context_effects(
state,
context,
relationship_quality,
duration,
life_stage,
age_years,
current_timestamp,
)
}
fn gated_family_context(frequency: f64, complexity: f64) -> EcologicalContext {
let mut context = EcologicalContext::default();
context.macrosystem_mut().cultural_stress = 0.0;
let family_id = MicrosystemId::new("family").unwrap();
let mut family = FamilyContext::default();
family.warmth = 0.85;
family.cohesion = 0.7;
family.interaction_profile.interaction_frequency = frequency;
family.interaction_profile.interaction_complexity = complexity;
context.add_microsystem(family_id, Microsystem::new_family(family));
let social_id = MicrosystemId::new("social").unwrap();
let mut social = SocialContext::default();
social.warmth = 0.5;
social.hostility = 0.5;
social.interaction_profile.interaction_frequency = 0.8;
social.interaction_profile.interaction_complexity = 0.8;
context.add_microsystem(social_id, Microsystem::new_social(social));
context
}
#[test]
fn low_family_cohesion_increases_loneliness() {
let family_id = MicrosystemId::new("family").unwrap();
let mut low_context = EcologicalContext::default();
let mut low_family = FamilyContext::default();
low_family.cohesion = 0.2;
low_family.interaction_profile.interaction_frequency = 0.8;
low_family.interaction_profile.interaction_complexity = 0.8;
low_context.add_microsystem(family_id.clone(), Microsystem::new_family(low_family));
let mut high_context = EcologicalContext::default();
let mut high_family = FamilyContext::default();
high_family.cohesion = 0.8;
high_family.interaction_profile.interaction_frequency = 0.8;
high_family.interaction_profile.interaction_complexity = 0.8;
high_context.add_microsystem(family_id, Microsystem::new_family(high_family));
let state = IndividualState::new();
let low_result = apply_context_effects_test(
state.clone(),
&low_context,
1.0,
Duration::days(30),
LifeStage::Adult,
test_timestamp(),
);
let high_result = apply_context_effects_test(
state,
&high_context,
1.0,
Duration::days(30),
LifeStage::Adult,
test_timestamp(),
);
assert!(
low_result.social_cognition().loneliness().delta()
> high_result.social_cognition().loneliness().delta() + 0.01
);
}
#[test]
fn withdrawal_loop_amplifies_loneliness() {
let social_id = MicrosystemId::new("social").unwrap();
let mut context = EcologicalContext::default();
let mut social = SocialContext::default();
social.warmth = 0.5;
social.hostility = 0.5;
social.predictability = 0.5;
social.interaction_profile.interaction_frequency = 0.4;
social.interaction_profile.interaction_complexity = 0.4;
social.interaction_profile.reciprocity_balance = 0.4;
context.add_microsystem(social_id, Microsystem::new_social(social));
let mut high_state = IndividualState::new();
high_state
.social_cognition_mut()
.loneliness_mut()
.set_base(0.9);
high_state
.social_cognition_mut()
.perceived_reciprocal_caring_mut()
.set_base(0.1);
high_state
.social_cognition_mut()
.perceived_liability_mut()
.set_base(0.9);
high_state.social_cognition_mut().self_hate_mut().set_base(0.9);
let mut low_state = IndividualState::new();
low_state
.social_cognition_mut()
.loneliness_mut()
.set_base(0.1);
low_state
.social_cognition_mut()
.perceived_reciprocal_caring_mut()
.set_base(0.9);
low_state
.social_cognition_mut()
.perceived_liability_mut()
.set_base(0.1);
low_state.social_cognition_mut().self_hate_mut().set_base(0.1);
let high_result = apply_context_effects_test(
high_state,
&context,
1.0,
Duration::days(30),
LifeStage::Adult,
test_timestamp(),
);
let low_result = apply_context_effects_test(
low_state,
&context,
1.0,
Duration::days(30),
LifeStage::Adult,
test_timestamp(),
);
assert!(
high_result.social_cognition().loneliness().delta()
> low_result.social_cognition().loneliness().delta() + 0.01
);
}
#[test]
fn high_family_support_buffers_stress() {
let mut context = EcologicalContext::default();
context.macrosystem_mut().cultural_stress = 0.0;
let family_id = MicrosystemId::new("family").unwrap();
let mut family = FamilyContext::default();
family.family_satisfaction = 0.9;
family.warmth = 0.9;
family.interaction_profile.interaction_frequency = 0.8;
family.interaction_profile.interaction_complexity = 0.8;
context.add_microsystem(family_id, Microsystem::new_family(family));
let state = IndividualState::new();
let result = apply_context_effects_test(
state,
&context,
1.0,
Duration::days(30),
LifeStage::Adult,
test_timestamp(),
);
assert!(result.needs().stress().delta() < 0.0);
assert!(result.social_cognition().loneliness().delta() < 0.0);
}
#[test]
fn zero_duration_returns_unchanged_state() {
let context = EcologicalContext::default();
let state = IndividualState::new();
let result = apply_context_effects_test(
state.clone(),
&context,
0.5,
Duration::zero(),
LifeStage::Adult,
test_timestamp(),
);
assert_eq!(result, state);
}
#[test]
fn aggregate_metrics_empty_context() {
let context = EcologicalContext::default();
let (frequency, complexity, reciprocity) = compute_aggregate_interaction_metrics(&context);
assert!((frequency - 0.0).abs() < f64::EPSILON);
assert!((complexity - 0.0).abs() < f64::EPSILON);
assert!((reciprocity - 0.0).abs() < f64::EPSILON);
}
#[test]
fn work_stress_spills_to_home() {
let mut context = EcologicalContext::default();
context.macrosystem_mut().cultural_stress = 0.0;
let work_id = MicrosystemId::new("work").unwrap();
let family_id = MicrosystemId::new("family").unwrap();
let mut work = WorkContext::default();
work.workload_stress = 0.9;
work.interaction_profile.interaction_frequency = 0.8;
work.interaction_profile.interaction_complexity = 0.8;
context.add_microsystem(work_id.clone(), Microsystem::new_work(work));
let mut family = FamilyContext::default();
family.predictability = 0.3;
family.stability = 0.3;
family.interaction_profile.interaction_frequency = 0.8;
family.interaction_profile.interaction_complexity = 0.8;
context.add_microsystem(family_id.clone(), Microsystem::new_family(family));
let state = IndividualState::new();
let result = apply_context_effects_test(
state,
&context,
0.6,
Duration::days(30),
LifeStage::Adult,
test_timestamp(),
);
assert!(result.needs().stress().delta() > 0.0);
}
#[test]
fn mesosystem_spillover_applies_between_work_and_family() {
let mut context = EcologicalContext::default();
context.macrosystem_mut().cultural_stress = 0.0;
let work_id = MicrosystemId::new("work").unwrap();
let family_id = MicrosystemId::new("family").unwrap();
let mut work = WorkContext::default();
work.workload_stress = 0.9;
work.interaction_profile.interaction_frequency = 0.8;
work.interaction_profile.interaction_complexity = 0.8;
context.add_microsystem(work_id, Microsystem::new_work(work));
let mut family = FamilyContext::default();
family.predictability = 0.2;
family.stability = 0.2;
family.interaction_profile.interaction_frequency = 0.8;
family.interaction_profile.interaction_complexity = 0.8;
context.add_microsystem(family_id, Microsystem::new_family(family));
let result = apply_context_effects_test(
IndividualState::new(),
&context,
0.6,
Duration::days(30),
LifeStage::Adult,
test_timestamp(),
);
assert!(result.needs().stress().delta() > 0.0);
}
#[test]
fn mesosystem_spillover_applies_between_family_and_work() {
let mut context = EcologicalContext::default();
context.macrosystem_mut().cultural_stress = 0.0;
let alpha_id = MicrosystemId::new("alpha").unwrap();
let beta_id = MicrosystemId::new("beta").unwrap();
let mut placeholder_a = WorkContext::default();
placeholder_a.interaction_profile.interaction_frequency = 0.8;
placeholder_a.interaction_profile.interaction_complexity = 0.8;
context.add_microsystem(alpha_id.clone(), Microsystem::new_work(placeholder_a));
let mut placeholder_b = WorkContext::default();
placeholder_b.interaction_profile.interaction_frequency = 0.8;
placeholder_b.interaction_profile.interaction_complexity = 0.8;
context.add_microsystem(beta_id.clone(), Microsystem::new_work(placeholder_b));
let linkages = context.list_linkages();
assert_eq!(linkages.len(), 1);
let (from_id, to_id) = (linkages[0].0.clone(), linkages[0].1.clone());
let mut family = FamilyContext::default();
family.caregiving_burden = 0.9;
family.hostility = 0.6;
family.interaction_profile.interaction_frequency = 0.8;
family.interaction_profile.interaction_complexity = 0.8;
let mut work = WorkContext::default();
work.predictability = 0.2;
work.stability = 0.2;
work.interaction_profile.interaction_frequency = 0.8;
work.interaction_profile.interaction_complexity = 0.8;
context.add_microsystem(from_id.clone(), Microsystem::new_family(family));
context.add_microsystem(to_id.clone(), Microsystem::new_work(work));
let updated_linkages = context.list_linkages();
assert_eq!(updated_linkages.len(), 1);
assert_eq!(updated_linkages[0].0, from_id);
assert_eq!(updated_linkages[0].1, to_id);
let mut state = IndividualState::new();
apply_mesosystem_spillover(&mut state, &context, 1.0, 1.0);
assert!(state.needs().stress().delta() > 0.0);
}
#[test]
fn mesosystem_spillover_applies_to_non_work_family_linkages() {
let mut context = EcologicalContext::default();
let social_id = MicrosystemId::new("social").unwrap();
let education_id = MicrosystemId::new("education").unwrap();
let mut social = SocialContext::default();
social.hostility = 0.8;
social.group_standing = 0.2;
social.interaction_profile.interaction_frequency = 0.8;
social.interaction_profile.interaction_complexity = 0.8;
context.add_microsystem(social_id.clone(), Microsystem::new_social(social));
let mut education = EducationContext::default();
education.cognitive_demand = 0.7;
education.hostility = 0.7;
education.interaction_profile.interaction_frequency = 0.8;
education.interaction_profile.interaction_complexity = 0.8;
context.add_microsystem(education_id.clone(), Microsystem::new_education(education));
assert_eq!(context.list_linkages().len(), 1);
assert!(context.get_spillover(&social_id, &education_id) > 0.0);
let mut state = IndividualState::new();
apply_mesosystem_spillover(&mut state, &context, 1.0, 1.0);
assert!(state.needs().stress().delta() > 0.0);
}
#[test]
fn mesosystem_role_conflict_adds_stress() {
let mut baseline = EcologicalContext::default();
let mut work = WorkContext::default();
work.workload_stress = 0.7;
work.role_clarity = 0.5;
work.predictability = 0.5;
work.warmth = 0.5;
work.hostility = 0.2;
work.interaction_profile.interaction_frequency = 0.0;
let mut family = FamilyContext::default();
family.caregiving_burden = 0.3;
family.role_clarity = 0.5;
family.predictability = 0.5;
family.warmth = 0.5;
family.hostility = 0.2;
family.interaction_profile.interaction_frequency = 0.0;
baseline.add_microsystem(
MicrosystemId::new("work").unwrap(),
Microsystem::new_work(work),
);
baseline.add_microsystem(
MicrosystemId::new("family").unwrap(),
Microsystem::new_family(family),
);
let mut baseline_state = IndividualState::new();
apply_mesosystem_spillover(&mut baseline_state, &baseline, 1.0, 1.0);
let mut conflict = baseline.clone();
let family = conflict
.get_microsystem_mut(&MicrosystemId::new("family").unwrap())
.and_then(Microsystem::family_mut)
.expect("family microsystem missing");
family.caregiving_burden = 0.6;
let mut conflict_state = IndividualState::new();
apply_mesosystem_spillover(&mut conflict_state, &conflict, 1.0, 1.0);
assert!(conflict_state.needs().stress().delta() > baseline_state.needs().stress().delta());
}
#[test]
fn compute_role_conflict_increases_when_work_and_family_demands_overlap() {
let mut context = EcologicalContext::default();
let mut work = WorkContext::default();
work.workload_stress = 0.8;
context.add_microsystem(
MicrosystemId::new("work").unwrap(),
Microsystem::new_work(work),
);
let mut family = FamilyContext::default();
family.caregiving_burden = 0.6;
context.add_microsystem(
MicrosystemId::new("family").unwrap(),
Microsystem::new_family(family),
);
assert!(compute_role_conflict(&context) > 0.0);
}
#[test]
fn mesosystem_consistency_buffers_stress() {
let mut consistent = EcologicalContext::default();
let mut work = WorkContext::default();
work.workload_stress = 0.4;
work.role_clarity = 0.7;
work.predictability = 0.7;
work.warmth = 0.6;
work.hostility = 0.1;
work.interaction_profile.interaction_frequency = 0.0;
let mut family = FamilyContext::default();
family.caregiving_burden = 0.3;
family.role_clarity = 0.7;
family.predictability = 0.7;
family.warmth = 0.6;
family.hostility = 0.1;
family.interaction_profile.interaction_frequency = 0.0;
let mut social = SocialContext::default();
social.predictability = 0.7;
social.warmth = 0.6;
social.hostility = 0.1;
social.interaction_profile.interaction_frequency = 0.0;
consistent.add_microsystem(
MicrosystemId::new("work").unwrap(),
Microsystem::new_work(work),
);
consistent.add_microsystem(
MicrosystemId::new("family").unwrap(),
Microsystem::new_family(family),
);
consistent.add_microsystem(
MicrosystemId::new("social").unwrap(),
Microsystem::new_social(social),
);
let mut consistent_state = IndividualState::new();
apply_mesosystem_spillover(&mut consistent_state, &consistent, 1.0, 1.0);
let mut inconsistent = EcologicalContext::default();
let mut work = WorkContext::default();
work.workload_stress = 0.4;
work.role_clarity = 0.2;
work.predictability = 0.2;
work.warmth = 0.2;
work.hostility = 0.7;
work.interaction_profile.interaction_frequency = 0.0;
let mut family = FamilyContext::default();
family.caregiving_burden = 0.3;
family.role_clarity = 0.9;
family.predictability = 0.9;
family.warmth = 0.9;
family.hostility = 0.1;
family.interaction_profile.interaction_frequency = 0.0;
let mut social = SocialContext::default();
social.predictability = 0.1;
social.warmth = 0.9;
social.hostility = 0.1;
social.interaction_profile.interaction_frequency = 0.0;
inconsistent.add_microsystem(
MicrosystemId::new("work").unwrap(),
Microsystem::new_work(work),
);
inconsistent.add_microsystem(
MicrosystemId::new("family").unwrap(),
Microsystem::new_family(family),
);
inconsistent.add_microsystem(
MicrosystemId::new("social").unwrap(),
Microsystem::new_social(social),
);
let mut inconsistent_state = IndividualState::new();
apply_mesosystem_spillover(&mut inconsistent_state, &inconsistent, 1.0, 1.0);
assert!(
inconsistent_state.needs().stress().delta() > consistent_state.needs().stress().delta()
);
}
#[test]
fn shared_membership_buffers_spillover() {
use crate::types::EntityId;
let mut no_overlap = EcologicalContext::default();
let mut overlap = EcologicalContext::default();
let mut work = WorkContext::default();
work.workload_stress = 0.8;
work.role_clarity = 0.6;
work.predictability = 0.6;
work.warmth = 0.6;
work.hostility = 0.1;
work.peer_ids = vec![EntityId::new("coworker").unwrap()];
work.interaction_profile.interaction_frequency = 0.8;
let mut family = FamilyContext::default();
family.caregiving_burden = 0.3;
family.role_clarity = 0.6;
family.predictability = 0.6;
family.warmth = 0.6;
family.hostility = 0.1;
family.family_unit = vec![EntityId::new("relative").unwrap()];
family.interaction_profile.interaction_frequency = 0.8;
no_overlap.add_microsystem(
MicrosystemId::new("work").unwrap(),
Microsystem::new_work(work.clone()),
);
no_overlap.add_microsystem(
MicrosystemId::new("family").unwrap(),
Microsystem::new_family(family.clone()),
);
let shared_id = EntityId::new("shared_member").unwrap();
let mut overlap_work = work;
overlap_work.peer_ids = vec![shared_id.clone()];
let mut overlap_family = family;
overlap_family.family_unit = vec![shared_id];
overlap.add_microsystem(
MicrosystemId::new("work").unwrap(),
Microsystem::new_work(overlap_work),
);
overlap.add_microsystem(
MicrosystemId::new("family").unwrap(),
Microsystem::new_family(overlap_family),
);
let mut no_overlap_state = IndividualState::new();
apply_mesosystem_spillover(&mut no_overlap_state, &no_overlap, 1.0, 1.0);
let mut overlap_state = IndividualState::new();
apply_mesosystem_spillover(&mut overlap_state, &overlap, 1.0, 1.0);
assert!(overlap_state.needs().stress().delta() < no_overlap_state.needs().stress().delta());
}
#[test]
fn low_resources_reduce_coping() {
let mut context = EcologicalContext::default();
context.exosystem_mut().resource_availability = 0.2;
let state = IndividualState::new();
let result = apply_context_effects_test(
state,
&context,
0.6,
Duration::days(30),
LifeStage::Adult,
test_timestamp(),
);
assert!(
result
.person_characteristics()
.emotional_regulation_assets()
.delta()
< 0.0
);
}
#[test]
fn low_institutional_support_increases_reactance() {
let mut context = EcologicalContext::default();
context.exosystem_mut().institutional_support = 0.2;
context.exosystem_mut().resource_availability = 0.6;
let state = IndividualState::new();
let result = apply_context_effects_test(
state,
&context,
0.6,
Duration::days(30),
LifeStage::Adult,
test_timestamp(),
);
assert!(result.disposition().reactance().delta() > 0.0);
}
#[test]
fn parent_capacity_affects_child_attachment() {
let mut context = EcologicalContext::default();
context.exosystem_mut().resource_availability = 0.6;
context.exosystem_mut().institutional_support = 0.6;
context.exosystem_mut().parent_work_environment = Some(ParentWorkQuality {
stress_level: 1.0,
schedule_flexibility: 0.0,
income_stability: 0.5,
});
let child_state = apply_context_effects_test(
IndividualState::new(),
&context,
0.6,
Duration::days(30),
LifeStage::Child,
test_timestamp(),
);
assert!(child_state.social_cognition().loneliness().delta() > 0.0);
assert!(
child_state
.social_cognition()
.perceived_reciprocal_caring()
.delta()
< 0.0
);
let adult_state = apply_context_effects_test(
IndividualState::new(),
&context,
1.0,
Duration::days(30),
LifeStage::Adult,
test_timestamp(),
);
assert!(adult_state.social_cognition().loneliness().delta().abs() < f32::EPSILON);
assert!(
adult_state
.social_cognition()
.perceived_reciprocal_caring()
.delta()
.abs()
< f32::EPSILON
);
}
#[test]
fn parent_capacity_none_skips_child_attachment_effects() {
let mut state = IndividualState::new();
let mut context = EcologicalContext::default();
context.exosystem_mut().health_system_access = 0.5;
context.exosystem_mut().educational_system_quality = 0.5;
context.exosystem_mut().community_services_availability = 0.5;
context.exosystem_mut().resource_availability = 0.5;
context.exosystem_mut().institutional_support = 0.5;
context.exosystem_mut().parent_work_environment = None;
let before_loneliness = state.social_cognition().loneliness().delta();
let before_prc = state
.social_cognition()
.perceived_reciprocal_caring()
.delta();
apply_exosystem_effects(&mut state, &context, 1.0, LifeStage::Child);
assert!(
(state.social_cognition().loneliness().delta() - before_loneliness).abs()
< f32::EPSILON
);
assert!(
(state
.social_cognition()
.perceived_reciprocal_caring()
.delta()
- before_prc)
.abs()
< f32::EPSILON
);
}
#[test]
fn collectivist_culture_increases_belonging() {
let mut collectivist = EcologicalContext::default();
collectivist
.macrosystem_mut()
.cultural_orientation
.individualism_collectivism = -0.6;
let neutral = EcologicalContext::default();
let collectivist_state = apply_context_effects_test(
IndividualState::new(),
&collectivist,
1.0,
Duration::days(30),
LifeStage::Adult,
test_timestamp(),
);
let neutral_state = apply_context_effects_test(
IndividualState::new(),
&neutral,
1.0,
Duration::days(30),
LifeStage::Adult,
test_timestamp(),
);
assert!(
collectivist_state.social_cognition().loneliness().delta()
< neutral_state.social_cognition().loneliness().delta()
);
}
#[test]
fn high_power_distance_penalizes_low_quality_relationships() {
let mut high_pd = EcologicalContext::default();
high_pd.macrosystem_mut().cultural_stress = 0.0;
high_pd
.macrosystem_mut()
.cultural_orientation
.power_distance = 0.9;
let mut low_pd = high_pd.clone();
low_pd.macrosystem_mut().cultural_orientation.power_distance = 0.2;
let high_state = apply_context_effects_test(
IndividualState::new(),
&high_pd,
0.3,
Duration::days(30),
LifeStage::Adult,
test_timestamp(),
);
let low_state = apply_context_effects_test(
IndividualState::new(),
&low_pd,
0.3,
Duration::days(30),
LifeStage::Adult,
test_timestamp(),
);
assert!(high_state.needs().stress().delta() > low_state.needs().stress().delta());
}
#[test]
fn rule_of_law_scales_exosystem_deficit() {
let mut high_rule = EcologicalContext::default();
high_rule.exosystem_mut().resource_availability = 0.2;
high_rule.macrosystem_mut().cultural_stress = 0.0;
high_rule
.macrosystem_mut()
.institutional_structure
.rule_of_law = 1.0;
let mut low_rule = high_rule.clone();
low_rule
.macrosystem_mut()
.institutional_structure
.rule_of_law = 0.2;
let high_state = apply_context_effects_test(
IndividualState::new(),
&high_rule,
0.6,
Duration::days(30),
LifeStage::Adult,
test_timestamp(),
);
let low_state = apply_context_effects_test(
IndividualState::new(),
&low_rule,
0.6,
Duration::days(30),
LifeStage::Adult,
test_timestamp(),
);
assert!(low_state.needs().stress().delta() > high_state.needs().stress().delta());
}
#[test]
fn uncertainty_avoidance_increases_stress_with_low_predictability() {
let mut high_ua = EcologicalContext::default();
high_ua.macrosystem_mut().cultural_stress = 0.0;
high_ua
.macrosystem_mut()
.cultural_orientation
.uncertainty_avoidance = 0.8;
let work_id = MicrosystemId::new("work").unwrap();
let mut work = WorkContext::default();
work.predictability = 0.2;
work.interaction_profile.interaction_frequency = 0.0;
work.interaction_profile.interaction_complexity = 0.0;
high_ua.add_microsystem(work_id, Microsystem::new_work(work));
let mut low_ua = high_ua.clone();
low_ua
.macrosystem_mut()
.cultural_orientation
.uncertainty_avoidance = 0.1;
let high_state = apply_context_effects_test(
IndividualState::new(),
&high_ua,
0.6,
Duration::days(30),
LifeStage::Adult,
test_timestamp(),
);
let low_state = apply_context_effects_test(
IndividualState::new(),
&low_ua,
0.6,
Duration::days(30),
LifeStage::Adult,
test_timestamp(),
);
assert!(high_state.needs().stress().delta() > low_state.needs().stress().delta());
}
#[test]
fn economic_crisis_period_raises_baseline_stress() {
let mut context = EcologicalContext::default();
context
.chronosystem_mut()
.historical_period_mut()
.resource_scarcity = 0.9;
context
.chronosystem_mut()
.historical_period_mut()
.stability_level = 0.4;
context.macrosystem_mut().cultural_stress = 0.0;
let state = IndividualState::new();
let result = apply_context_effects_test(
state,
&context,
0.6,
Duration::days(30),
LifeStage::Adult,
test_timestamp(),
);
assert!(result.needs().stress().delta() > 0.0);
}
#[test]
fn historical_resource_scarcity_increases_grievance() {
let mut context = EcologicalContext::default();
context
.chronosystem_mut()
.historical_period_mut()
.resource_scarcity = 0.8;
context.macrosystem_mut().cultural_stress = 0.0;
let result = apply_context_effects_test(
IndividualState::new(),
&context,
0.6,
Duration::days(30),
LifeStage::Adult,
test_timestamp(),
);
assert!(result.disposition().grievance().delta() > 0.0);
}
#[test]
fn turning_point_boost_increases_curiosity() {
let mut context = EcologicalContext::default();
context.macrosystem_mut().cultural_stress = 0.0;
let tp = TurningPoint {
event_id: EventId::new("tp_001").unwrap(),
timestamp: test_timestamp() - Duration::days(200),
domain: TurningPointDomain::Identity,
magnitude: 1.0,
};
context.chronosystem_mut().add_turning_point(tp);
let result = apply_context_effects_test(
IndividualState::new(),
&context,
0.6,
Duration::days(30),
LifeStage::Adult,
test_timestamp(),
);
assert!(result.person_characteristics().curiosity_tendency().delta() > 0.0);
}
#[test]
fn crisis_cohort_increases_neuroticism() {
let mut context = EcologicalContext::default();
context.macrosystem_mut().cultural_stress = 0.0;
context.chronosystem_mut().historical_period_mut().era_name = "Crisis".to_string();
context
.chronosystem_mut()
.historical_period_mut()
.stability_level = 0.6;
context.chronosystem_mut().cohort_effects_mut().birth_era = BirthEra::Crisis;
let result = apply_context_effects_test(
IndividualState::new(),
&context,
0.6,
Duration::days(30),
LifeStage::Adult,
test_timestamp(),
);
assert!(result.hexaco().neuroticism() > 0.0);
assert!(result.needs().stress().delta() > 0.0);
assert!(result.attachment().anxiety().delta() > 0.0);
}
#[test]
fn cohort_affinity_scales_cultural_stress() {
let mut matching = EcologicalContext::default();
matching.macrosystem_mut().cultural_stress = 0.6;
matching.chronosystem_mut().historical_period_mut().era_name = "Stability".to_string();
matching
.chronosystem_mut()
.historical_period_mut()
.stability_level = 0.7;
matching.chronosystem_mut().cohort_effects_mut().birth_era = BirthEra::Stability;
let mut mismatch = matching.clone();
mismatch.chronosystem_mut().cohort_effects_mut().birth_era = BirthEra::Crisis;
let aligned = apply_context_effects_test(
IndividualState::new(),
&matching,
0.6,
Duration::days(30),
LifeStage::Adult,
test_timestamp(),
);
let mismatched = apply_context_effects_test(
IndividualState::new(),
&mismatch,
0.6,
Duration::days(30),
LifeStage::Adult,
test_timestamp(),
);
assert!(mismatched.needs().stress().delta() < aligned.needs().stress().delta());
}
#[test]
fn duration_scales_cumulative_effects() {
let mut context = EcologicalContext::default();
context.macrosystem_mut().cultural_stress = 0.8;
let state = IndividualState::new();
let short = apply_context_effects_test(
state.clone(),
&context,
0.6,
Duration::days(30),
LifeStage::Adult,
test_timestamp(),
);
let long = apply_context_effects_test(
state,
&context,
0.6,
Duration::days(60),
LifeStage::Adult,
test_timestamp(),
);
assert!(long.needs().stress().delta() > short.needs().stress().delta());
}
#[test]
fn proximal_process_gate_blocks_low_frequency_development() {
let context = gated_family_context(0.2, 0.8);
let gated = apply_context_effects_test(
IndividualState::new(),
&context,
0.6,
Duration::days(30),
LifeStage::Adult,
test_timestamp(),
);
let open_context = gated_family_context(0.8, 0.8);
let open = apply_context_effects_test(
IndividualState::new(),
&open_context,
0.6,
Duration::days(30),
LifeStage::Adult,
test_timestamp(),
);
let gated_caring = gated
.social_cognition()
.perceived_reciprocal_caring()
.delta();
let open_caring = open
.social_cognition()
.perceived_reciprocal_caring()
.delta();
assert!(gated_caring.abs() < 0.001);
assert!(open_caring > gated_caring + 0.0001);
}
#[test]
fn proximal_process_gate_blocks_low_complexity_development() {
let context = gated_family_context(0.8, 0.1);
let gated = apply_context_effects_test(
IndividualState::new(),
&context,
0.6,
Duration::days(30),
LifeStage::Adult,
test_timestamp(),
);
let open_context = gated_family_context(0.8, 0.8);
let open = apply_context_effects_test(
IndividualState::new(),
&open_context,
0.6,
Duration::days(30),
LifeStage::Adult,
test_timestamp(),
);
let gated_caring = gated
.social_cognition()
.perceived_reciprocal_caring()
.delta();
let open_caring = open
.social_cognition()
.perceived_reciprocal_caring()
.delta();
assert!(gated_caring.abs() < 0.001);
assert!(open_caring > gated_caring + 0.0001);
}
#[test]
fn proximal_process_gate_requires_both_thresholds() {
let low_frequency = gated_family_context(0.2, 0.8);
let low_complexity = gated_family_context(0.8, 0.1);
let open_context = gated_family_context(0.8, 0.8);
let low_frequency_state = apply_context_effects_test(
IndividualState::new(),
&low_frequency,
0.6,
Duration::days(30),
LifeStage::Adult,
test_timestamp(),
);
let low_complexity_state = apply_context_effects_test(
IndividualState::new(),
&low_complexity,
0.6,
Duration::days(30),
LifeStage::Adult,
test_timestamp(),
);
let open_state = apply_context_effects_test(
IndividualState::new(),
&open_context,
0.6,
Duration::days(30),
LifeStage::Adult,
test_timestamp(),
);
let low_frequency_delta = low_frequency_state
.social_cognition()
.perceived_reciprocal_caring()
.delta();
let low_complexity_delta = low_complexity_state
.social_cognition()
.perceived_reciprocal_caring()
.delta();
let open_delta = open_state
.social_cognition()
.perceived_reciprocal_caring()
.delta();
assert!(low_frequency_delta.abs() < 0.001);
assert!(low_complexity_delta.abs() < 0.001);
assert!(open_delta > 0.0001);
}
#[test]
fn parent_experiences_higher_family_stress_impact() {
let mut context = EcologicalContext::default();
let family_id = MicrosystemId::new("family").unwrap();
let mut family = FamilyContext::default();
family.caregiving_burden = 0.8;
family.interaction_profile.interaction_frequency = 0.8;
family.interaction_profile.interaction_complexity = 0.8;
family.family_role = FamilyRole::Parent;
context.add_microsystem(family_id, Microsystem::new_family(family));
let parent_state = apply_context_effects_test(
IndividualState::new(),
&context,
0.6,
Duration::days(30),
LifeStage::Adult,
test_timestamp(),
);
let mut context = EcologicalContext::default();
let family_id = MicrosystemId::new("family_extended").unwrap();
let mut family = FamilyContext::default();
family.caregiving_burden = 0.8;
family.interaction_profile.interaction_frequency = 0.8;
family.interaction_profile.interaction_complexity = 0.8;
family.family_role = FamilyRole::Extended;
context.add_microsystem(family_id, Microsystem::new_family(family));
let extended_state = apply_context_effects_test(
IndividualState::new(),
&context,
0.6,
Duration::days(30),
LifeStage::Adult,
test_timestamp(),
);
assert!(parent_state.needs().stress().delta() > extended_state.needs().stress().delta());
}
#[test]
fn child_absorbs_parental_stress_at_higher_rate() {
let mut context = EcologicalContext::default();
let family_id = MicrosystemId::new("family").unwrap();
let mut family = FamilyContext::default();
family.caregiving_burden = 0.8;
family.interaction_profile.interaction_frequency = 0.8;
family.interaction_profile.interaction_complexity = 0.8;
family.family_role = FamilyRole::Child;
context.add_microsystem(family_id, Microsystem::new_family(family));
let child_state = apply_context_effects_test(
IndividualState::new(),
&context,
0.6,
Duration::days(30),
LifeStage::Adult,
test_timestamp(),
);
let mut context = EcologicalContext::default();
let family_id = MicrosystemId::new("family_extended").unwrap();
let mut family = FamilyContext::default();
family.caregiving_burden = 0.8;
family.interaction_profile.interaction_frequency = 0.8;
family.interaction_profile.interaction_complexity = 0.8;
family.family_role = FamilyRole::Extended;
context.add_microsystem(family_id, Microsystem::new_family(family));
let extended_state = apply_context_effects_test(
IndividualState::new(),
&context,
0.6,
Duration::days(30),
LifeStage::Adult,
test_timestamp(),
);
assert!(child_state.needs().stress().delta() > extended_state.needs().stress().delta());
}
#[test]
fn role_multipliers_affect_all_family_dimensions() {
let mut context = EcologicalContext::default();
let family_id = MicrosystemId::new("family").unwrap();
let mut family = FamilyContext::default();
family.family_satisfaction = 0.9;
family.warmth = 0.9;
family.caregiving_burden = 0.8;
family.hostility = 0.6;
family.interaction_profile.interaction_frequency = 0.8;
family.interaction_profile.interaction_complexity = 0.8;
family.family_role = FamilyRole::Parent;
context.add_microsystem(family_id, Microsystem::new_family(family));
let parent_state = apply_context_effects_test(
IndividualState::new(),
&context,
0.6,
Duration::days(30),
LifeStage::Adult,
test_timestamp(),
);
let mut context = EcologicalContext::default();
let family_id = MicrosystemId::new("family_extended").unwrap();
let mut family = FamilyContext::default();
family.family_satisfaction = 0.9;
family.warmth = 0.9;
family.caregiving_burden = 0.8;
family.hostility = 0.6;
family.interaction_profile.interaction_frequency = 0.8;
family.interaction_profile.interaction_complexity = 0.8;
family.family_role = FamilyRole::Extended;
context.add_microsystem(family_id, Microsystem::new_family(family));
let extended_state = apply_context_effects_test(
IndividualState::new(),
&context,
0.6,
Duration::days(30),
LifeStage::Adult,
test_timestamp(),
);
assert!(
parent_state.needs().stress().delta().abs()
> extended_state.needs().stress().delta().abs()
);
assert!(
parent_state.social_cognition().loneliness().delta().abs()
> extended_state.social_cognition().loneliness().delta().abs()
);
assert!(
parent_state.mood().valence_delta().abs() > extended_state.mood().valence_delta().abs()
);
}
#[test]
fn negative_duration_returns_unchanged_state() {
let context = EcologicalContext::default();
let state = IndividualState::new();
let result = apply_context_effects_test(
state.clone(),
&context,
0.5,
Duration::days(0) - Duration::days(1),
LifeStage::Adult,
test_timestamp(),
);
assert_eq!(result, state);
}
#[test]
fn microsystem_multiplier_zero_skips_microsystem_effects() {
let mut context = EcologicalContext::default();
context.macrosystem_mut().cultural_stress = 0.0;
let work_id = MicrosystemId::new("work").unwrap();
let mut work = WorkContext::default();
work.workload_stress = 0.9;
work.interaction_profile.interaction_frequency = 0.0;
work.interaction_profile.interaction_complexity = 0.0;
context.add_microsystem(work_id, Microsystem::new_work(work));
let state = IndividualState::new();
let result = apply_context_effects_test(
state.clone(),
&context,
0.6,
Duration::days(30),
LifeStage::Adult,
test_timestamp(),
);
let mut open_context = EcologicalContext::default();
open_context.macrosystem_mut().cultural_stress = 0.0;
let work_id = MicrosystemId::new("work_open").unwrap();
let mut work = WorkContext::default();
work.workload_stress = 0.9;
work.interaction_profile.interaction_frequency = 0.8;
work.interaction_profile.interaction_complexity = 0.8;
open_context.add_microsystem(work_id, Microsystem::new_work(work));
let open = apply_context_effects_test(
state,
&open_context,
0.6,
Duration::days(30),
LifeStage::Adult,
test_timestamp(),
);
assert!(result.needs().stress().delta() > 0.0);
assert!(result.needs().stress().delta() < open.needs().stress().delta());
}
#[test]
fn microsystem_with_no_work_context_no_additional_work_stress() {
let mut context = EcologicalContext::default();
context.macrosystem_mut().cultural_stress = 0.0;
let social_id = MicrosystemId::new("social").unwrap();
let mut social = SocialContext::default();
social.hostility = 0.6;
social.interaction_profile.interaction_frequency = 0.8;
social.interaction_profile.interaction_complexity = 0.8;
context.add_microsystem(social_id, Microsystem::new_social(social));
let state = IndividualState::new();
let result = apply_context_effects_test(
state.clone(),
&context,
0.6,
Duration::days(30),
LifeStage::Adult,
test_timestamp(),
);
let no_work_context = result.clone();
let mut context_with_work = EcologicalContext::default();
context_with_work.macrosystem_mut().cultural_stress = 0.0;
let social_id = MicrosystemId::new("social").unwrap();
let mut social = SocialContext::default();
social.hostility = 0.6;
social.interaction_profile.interaction_frequency = 0.8;
social.interaction_profile.interaction_complexity = 0.8;
context_with_work.add_microsystem(social_id, Microsystem::new_social(social));
let work_id = MicrosystemId::new("work").unwrap();
let mut work = WorkContext::default();
work.workload_stress = 0.8;
work.interaction_profile.interaction_frequency = 0.8;
work.interaction_profile.interaction_complexity = 0.8;
context_with_work.add_microsystem(work_id, Microsystem::new_work(work));
let with_work = apply_context_effects_test(
IndividualState::new(),
&context_with_work,
0.6,
Duration::days(30),
LifeStage::Adult,
test_timestamp(),
);
assert!(with_work.needs().stress().delta() > no_work_context.needs().stress().delta());
}
#[test]
fn microsystem_with_no_family_context() {
let mut context = EcologicalContext::default();
context.macrosystem_mut().cultural_stress = 0.0;
let work_id = MicrosystemId::new("work").unwrap();
let mut work = WorkContext::default();
work.workload_stress = 0.5;
work.interaction_profile.interaction_frequency = 0.8;
work.interaction_profile.interaction_complexity = 0.8;
context.add_microsystem(work_id, Microsystem::new_work(work));
let state = IndividualState::new();
let result = apply_context_effects_test(
state,
&context,
0.6,
Duration::days(30),
LifeStage::Adult,
test_timestamp(),
);
assert!(result.needs().stress().delta() >= 0.0);
}
#[test]
fn mesosystem_spillover_zero_skips_direct_spillover() {
let mut context = EcologicalContext::default();
let work_id = MicrosystemId::new("work").unwrap();
let family_id = MicrosystemId::new("family").unwrap();
let mut work = WorkContext::default();
work.workload_stress = 0.0;
work.interaction_profile.interaction_frequency = 0.8;
work.interaction_profile.interaction_complexity = 0.8;
context.add_microsystem(work_id, Microsystem::new_work(work));
let mut family = FamilyContext::default();
family.caregiving_burden = 0.0;
family.interaction_profile.interaction_frequency = 0.8;
family.interaction_profile.interaction_complexity = 0.8;
context.add_microsystem(family_id, Microsystem::new_family(family));
let mut state = IndividualState::new();
apply_mesosystem_spillover(&mut state, &context, 1.0, 1.0);
assert!(state.needs().stress().delta() <= 0.01);
}
#[test]
fn role_conflict_zero_skips_effect() {
let mut context = EcologicalContext::default();
let work_id = MicrosystemId::new("work").unwrap();
let family_id = MicrosystemId::new("family").unwrap();
let mut work = WorkContext::default();
work.workload_stress = 0.5;
work.interaction_profile.interaction_frequency = 0.0;
context.add_microsystem(work_id, Microsystem::new_work(work));
let mut family = FamilyContext::default();
family.caregiving_burden = 0.3;
family.interaction_profile.interaction_frequency = 0.0;
context.add_microsystem(family_id, Microsystem::new_family(family));
let mut state = IndividualState::new();
apply_mesosystem_spillover(&mut state, &context, 1.0, 1.0);
let initial_stress = state.needs().stress().delta();
assert!(initial_stress <= 0.01);
}
#[test]
fn mesosystem_consistency_perfect_skips_effect() {
let mut consistent = EcologicalContext::default();
let mut work = WorkContext::default();
work.role_clarity = 1.0;
work.predictability = 1.0;
work.warmth = 1.0;
work.hostility = 0.0;
work.interaction_profile.interaction_frequency = 0.0;
consistent.add_microsystem(
MicrosystemId::new("work").unwrap(),
Microsystem::new_work(work),
);
let mut family = FamilyContext::default();
family.role_clarity = 1.0;
family.predictability = 1.0;
family.warmth = 1.0;
family.hostility = 0.0;
family.interaction_profile.interaction_frequency = 0.0;
consistent.add_microsystem(
MicrosystemId::new("family").unwrap(),
Microsystem::new_family(family),
);
let mut state = IndividualState::new();
apply_mesosystem_spillover(&mut state, &consistent, 1.0, 1.0);
assert!(state.needs().stress().delta() <= 0.0);
}
#[test]
fn exosystem_deficit_zero_skips_coping_reduction() {
let mut context = EcologicalContext::default();
context.exosystem_mut().resource_availability = 0.5;
context.macrosystem_mut().cultural_stress = 0.0;
let state = IndividualState::new();
let result = apply_context_effects_test(
state.clone(),
&context,
0.6,
Duration::days(30),
LifeStage::Adult,
test_timestamp(),
);
assert_eq!(
result
.person_characteristics()
.emotional_regulation_assets()
.delta(),
0.0
);
}
#[test]
fn institutional_support_at_threshold() {
let mut context = EcologicalContext::default();
context.exosystem_mut().institutional_support = 0.4;
context.exosystem_mut().resource_availability = 0.6;
context.macrosystem_mut().cultural_stress = 0.0;
let state = IndividualState::new();
let result = apply_context_effects_test(
state,
&context,
0.6,
Duration::days(30),
LifeStage::Adult,
test_timestamp(),
);
assert_eq!(result.disposition().reactance().delta(), 0.0);
}
#[test]
fn adolescent_with_parent_work_stress() {
let mut context = EcologicalContext::default();
context.exosystem_mut().resource_availability = 0.6;
context.exosystem_mut().institutional_support = 0.6;
context.exosystem_mut().parent_work_environment = Some(ParentWorkQuality {
stress_level: 1.0,
schedule_flexibility: 0.0,
income_stability: 0.5,
});
let result = apply_context_effects_test(
IndividualState::new(),
&context,
0.6,
Duration::days(30),
LifeStage::Adolescent,
test_timestamp(),
);
assert!(result.social_cognition().loneliness().delta() > 0.0);
}
#[test]
fn child_with_high_parent_capacity() {
let mut context = EcologicalContext::default();
context.exosystem_mut().resource_availability = 0.6;
context.exosystem_mut().institutional_support = 0.6;
context.exosystem_mut().parent_work_environment = Some(ParentWorkQuality {
stress_level: 0.1,
schedule_flexibility: 0.8,
income_stability: 0.9,
});
let result = apply_context_effects_test(
IndividualState::new(),
&context,
1.0,
Duration::days(30),
LifeStage::Child,
test_timestamp(),
);
assert!(result.social_cognition().loneliness().delta().abs() < f32::EPSILON);
}
#[test]
fn hierarchy_penalty_zero_skips_effect() {
let mut context = EcologicalContext::default();
context.macrosystem_mut().cultural_stress = 0.0;
context
.macrosystem_mut()
.cultural_orientation
.power_distance = 0.0;
let state = IndividualState::new();
let result = apply_context_effects_test(
state,
&context,
0.6,
Duration::days(30),
LifeStage::Adult,
test_timestamp(),
);
assert_eq!(result.needs().stress().delta(), 0.0);
}
#[test]
fn uncertainty_avoidance_no_work_contexts() {
let mut context = EcologicalContext::default();
context.macrosystem_mut().cultural_stress = 0.0;
context
.macrosystem_mut()
.cultural_orientation
.uncertainty_avoidance = 0.8;
let state = IndividualState::new();
let result = apply_context_effects_test(
state.clone(),
&context,
0.6,
Duration::days(30),
LifeStage::Adult,
test_timestamp(),
);
assert_eq!(
result.needs().stress().delta(),
state.needs().stress().delta()
);
}
#[test]
fn plasticity_boost_zero_skips_effect() {
let mut context = EcologicalContext::default();
context.macrosystem_mut().cultural_stress = 0.0;
let state = IndividualState::new();
let result = apply_context_effects_test(
state.clone(),
&context,
0.6,
Duration::days(30),
LifeStage::Adult,
test_timestamp(),
);
assert_eq!(
result.person_characteristics().curiosity_tendency().delta(),
state.person_characteristics().curiosity_tendency().delta()
);
}
#[test]
fn cohort_weight_zero_skips_hexaco_modification() {
let mut context = EcologicalContext::default();
context.macrosystem_mut().cultural_stress = 0.0;
context.chronosystem_mut().historical_period_mut().era_name = "Unknown".to_string();
let state = IndividualState::new();
let result = apply_context_effects_test(
state.clone(),
&context,
0.6,
Duration::days(30),
LifeStage::Adult,
test_timestamp(),
);
assert_eq!(result.hexaco().neuroticism(), state.hexaco().neuroticism());
assert_eq!(result.hexaco().openness(), state.hexaco().openness());
}
#[test]
fn birth_era_unknown_skips_cohort_effects() {
let mut context = EcologicalContext::default();
context.macrosystem_mut().cultural_stress = 0.0;
context.chronosystem_mut().cohort_effects_mut().birth_era = BirthEra::Unknown;
context.chronosystem_mut().historical_period_mut().era_name = "Stability".to_string();
let state = IndividualState::new();
let result = apply_context_effects_test(
state.clone(),
&context,
0.6,
Duration::days(30),
LifeStage::Adult,
test_timestamp(),
);
assert_eq!(result.hexaco().neuroticism(), state.hexaco().neuroticism());
assert_eq!(result.hexaco().openness(), state.hexaco().openness());
assert!(
(result.attachment().anxiety().delta() - state.attachment().anxiety().delta()).abs()
< f32::EPSILON
);
}
#[test]
fn stability_cohort_increases_openness() {
let mut context = EcologicalContext::default();
context.macrosystem_mut().cultural_stress = 0.0;
context.chronosystem_mut().historical_period_mut().era_name = "Stability".to_string();
context.chronosystem_mut().cohort_effects_mut().birth_era = BirthEra::Stability;
let state = IndividualState::new();
let initial_openness = state.hexaco().openness();
let result = apply_context_effects_test(
state,
&context,
0.6,
Duration::days(30),
LifeStage::Adult,
test_timestamp(),
);
assert!(result.hexaco().openness() > initial_openness);
assert!(result.attachment().avoidance().delta() < 0.0);
}
#[test]
fn scarcity_cohort_increases_conscientiousness() {
let mut context = EcologicalContext::default();
context.macrosystem_mut().cultural_stress = 0.0;
context.chronosystem_mut().historical_period_mut().era_name = "Scarcity".to_string();
context.chronosystem_mut().cohort_effects_mut().birth_era = BirthEra::Scarcity;
let state = IndividualState::new();
let initial_cons = state.hexaco().conscientiousness();
let result = apply_context_effects_test(
state,
&context,
0.6,
Duration::days(30),
LifeStage::Adult,
test_timestamp(),
);
assert!(result.hexaco().conscientiousness() > initial_cons);
}
#[test]
fn expansion_cohort_increases_openness_and_extraversion() {
let mut context = EcologicalContext::default();
context.macrosystem_mut().cultural_stress = 0.0;
context.chronosystem_mut().historical_period_mut().era_name = "Expansion".to_string();
context.chronosystem_mut().cohort_effects_mut().birth_era = BirthEra::Expansion;
let state = IndividualState::new();
let initial_openness = state.hexaco().openness();
let initial_extraversion = state.hexaco().extraversion();
let result = apply_context_effects_test(
state,
&context,
0.6,
Duration::days(30),
LifeStage::Adult,
test_timestamp(),
);
assert!(result.hexaco().openness() > initial_openness);
assert!(result.hexaco().extraversion() > initial_extraversion);
}
#[test]
fn cohort_affinity_matching_era() {
let mut context = EcologicalContext::default();
context.macrosystem_mut().cultural_stress = 0.6;
context.chronosystem_mut().historical_period_mut().era_name = "Crisis".to_string();
context.chronosystem_mut().cohort_effects_mut().birth_era = BirthEra::Crisis;
let result = apply_context_effects_test(
IndividualState::new(),
&context,
0.6,
Duration::days(30),
LifeStage::Adult,
test_timestamp(),
);
assert!(result.needs().stress().delta() > 0.0);
}
#[test]
fn birth_era_crisis_during_unstable_period_increases_stress() {
let mut context = EcologicalContext::default();
context.macrosystem_mut().cultural_stress = 0.0;
context.chronosystem_mut().historical_period_mut().era_name = "Crisis".to_string();
context
.chronosystem_mut()
.historical_period_mut()
.stability_level = 0.6;
context.chronosystem_mut().cohort_effects_mut().birth_era = BirthEra::Crisis;
let state = IndividualState::new();
let result = apply_context_effects_test(
state,
&context,
0.6,
Duration::days(30),
LifeStage::Adult,
test_timestamp(),
);
assert!(result.needs().stress().delta() > 0.0);
}
#[test]
fn birth_era_crisis_during_stable_period_skips_stress_bump() {
let mut context = EcologicalContext::default();
context.macrosystem_mut().cultural_stress = 0.0;
context.chronosystem_mut().historical_period_mut().era_name = "Crisis".to_string();
context
.chronosystem_mut()
.historical_period_mut()
.stability_level = 0.9;
context.chronosystem_mut().cohort_effects_mut().birth_era = BirthEra::Crisis;
let mut baseline_context = context.clone();
baseline_context
.chronosystem_mut()
.cohort_effects_mut()
.birth_era = BirthEra::Unknown;
let baseline = apply_context_effects_test(
IndividualState::new(),
&baseline_context,
0.6,
Duration::days(30),
LifeStage::Adult,
test_timestamp(),
);
let result = apply_context_effects_test(
IndividualState::new(),
&context,
0.6,
Duration::days(30),
LifeStage::Adult,
test_timestamp(),
);
assert!(
(result.needs().stress().delta() - baseline.needs().stress().delta()).abs()
< f32::EPSILON
);
}
#[test]
fn family_support_below_threshold() {
let mut context = EcologicalContext::default();
context.macrosystem_mut().cultural_stress = 0.0;
let family_id = MicrosystemId::new("family").unwrap();
let mut family = FamilyContext::default();
family.family_satisfaction = 0.5;
family.warmth = 0.5;
family.interaction_profile.interaction_frequency = 0.8;
family.interaction_profile.interaction_complexity = 0.8;
context.add_microsystem(family_id, Microsystem::new_family(family));
let state = IndividualState::new();
let result = apply_context_effects_test(
state.clone(),
&context,
0.6,
Duration::days(30),
LifeStage::Adult,
test_timestamp(),
);
assert!(result.needs().stress().delta() <= state.needs().stress().delta() + 0.05);
}
#[test]
fn low_workload_vs_high_workload() {
let mut low_context = EcologicalContext::default();
low_context.macrosystem_mut().cultural_stress = 0.0;
let work_id = MicrosystemId::new("work").unwrap();
let mut work = WorkContext::default();
work.workload_stress = 0.5;
work.interaction_profile.interaction_frequency = 0.8;
work.interaction_profile.interaction_complexity = 0.8;
low_context.add_microsystem(work_id, Microsystem::new_work(work));
let low_result = apply_context_effects_test(
IndividualState::new(),
&low_context,
0.6,
Duration::days(30),
LifeStage::Adult,
test_timestamp(),
);
let mut high_context = EcologicalContext::default();
high_context.macrosystem_mut().cultural_stress = 0.0;
let work_id = MicrosystemId::new("work").unwrap();
let mut work = WorkContext::default();
work.workload_stress = 0.9;
work.interaction_profile.interaction_frequency = 0.8;
work.interaction_profile.interaction_complexity = 0.8;
high_context.add_microsystem(work_id, Microsystem::new_work(work));
let high_result = apply_context_effects_test(
IndividualState::new(),
&high_context,
0.6,
Duration::days(30),
LifeStage::Adult,
test_timestamp(),
);
assert!(high_result.needs().stress().delta() > low_result.needs().stress().delta());
}
#[test]
fn caregiving_below_threshold() {
let mut context = EcologicalContext::default();
context.macrosystem_mut().cultural_stress = 0.0;
let family_id = MicrosystemId::new("family").unwrap();
let mut family = FamilyContext::default();
family.caregiving_burden = 0.3;
family.interaction_profile.interaction_frequency = 0.8;
family.interaction_profile.interaction_complexity = 0.8;
context.add_microsystem(family_id, Microsystem::new_family(family));
let state = IndividualState::new();
let result = apply_context_effects_test(
state.clone(),
&context,
0.6,
Duration::days(30),
LifeStage::Adult,
test_timestamp(),
);
assert!(result.needs().stress().delta() <= state.needs().stress().delta() + 0.05);
}
#[test]
fn role_conflict_requires_both_workload_and_caregiving_thresholds() {
let mut context = EcologicalContext::default();
context.macrosystem_mut().cultural_stress = 0.0;
let work_id = MicrosystemId::new("work").unwrap();
let mut work = WorkContext::default();
work.workload_stress = 0.9;
context.add_microsystem(work_id, Microsystem::new_work(work));
let family_id = MicrosystemId::new("family").unwrap();
let mut family = FamilyContext::default();
family.caregiving_burden = 0.3;
context.add_microsystem(family_id, Microsystem::new_family(family));
assert!((compute_role_conflict(&context) - 0.0).abs() < f64::EPSILON);
}
#[test]
fn hostility_below_threshold() {
let mut context = EcologicalContext::default();
context.macrosystem_mut().cultural_stress = 0.0;
let family_id = MicrosystemId::new("family").unwrap();
let mut family = FamilyContext::default();
family.hostility = 0.4;
family.interaction_profile.interaction_frequency = 0.8;
family.interaction_profile.interaction_complexity = 0.8;
context.add_microsystem(family_id, Microsystem::new_family(family));
let state = IndividualState::new();
let result = apply_context_effects_test(
state.clone(),
&context,
0.6,
Duration::days(30),
LifeStage::Adult,
test_timestamp(),
);
assert!(result.needs().stress().delta() <= state.needs().stress().delta() + 0.05);
}
#[test]
fn warmth_below_threshold() {
let mut context = EcologicalContext::default();
context.macrosystem_mut().cultural_stress = 0.0;
let family_id = MicrosystemId::new("family").unwrap();
let mut family = FamilyContext::default();
family.warmth = 0.4;
family.interaction_profile.interaction_frequency = 0.8;
family.interaction_profile.interaction_complexity = 0.8;
context.add_microsystem(family_id, Microsystem::new_family(family));
let state = IndividualState::new();
let result = apply_context_effects_test(
state.clone(),
&context,
0.6,
Duration::days(30),
LifeStage::Adult,
test_timestamp(),
);
assert!(result.mood().valence_delta() <= state.mood().valence_delta() + 0.01);
}
#[test]
fn high_hierarchy_penalty_with_high_relationship_quality() {
let mut context = EcologicalContext::default();
context.macrosystem_mut().cultural_stress = 0.0;
context
.macrosystem_mut()
.cultural_orientation
.power_distance = 0.7;
let state = IndividualState::new();
let result = apply_context_effects_test(
state,
&context,
0.9,
Duration::days(30),
LifeStage::Adult,
test_timestamp(),
);
assert!(result.needs().stress().delta() < 0.01);
}
#[test]
fn avg_predictability_above_uncertainty_avoidance() {
let mut context = EcologicalContext::default();
context.macrosystem_mut().cultural_stress = 0.0;
context
.macrosystem_mut()
.cultural_orientation
.uncertainty_avoidance = 0.3;
let work_id = MicrosystemId::new("work").unwrap();
let mut work = WorkContext::default();
work.workload_stress = 0.0;
work.predictability = 0.8;
work.interaction_profile.interaction_frequency = 0.0;
work.interaction_profile.interaction_complexity = 0.0;
context.add_microsystem(work_id, Microsystem::new_work(work));
let state = IndividualState::new();
let result = apply_context_effects_test(
state.clone(),
&context,
1.0,
Duration::days(30),
LifeStage::Adult,
test_timestamp(),
);
assert_eq!(
result.needs().stress().delta(),
state.needs().stress().delta()
);
}
#[test]
fn off_time_stress_effects_applied() {
let context = EcologicalContext::default();
let state = IndividualState::new();
let result = apply_context_effects_test(
state.clone(),
&context,
0.6,
Duration::days(30),
LifeStage::Adult,
test_timestamp(),
);
assert_ne!(
result.needs().stress().delta(),
state.needs().stress().delta()
);
}
#[test]
fn stability_at_threshold() {
let mut context = EcologicalContext::default();
context.macrosystem_mut().cultural_stress = 0.0;
context
.chronosystem_mut()
.historical_period_mut()
.stability_level = 0.5;
let state = IndividualState::new();
let result = apply_context_effects_test(
state,
&context,
0.6,
Duration::days(30),
LifeStage::Adult,
test_timestamp(),
);
assert_eq!(result.needs().stress().delta(), 0.0);
}
#[test]
fn resource_scarcity_below_threshold() {
let mut context = EcologicalContext::default();
context.macrosystem_mut().cultural_stress = 0.0;
context
.chronosystem_mut()
.historical_period_mut()
.resource_scarcity = 0.4;
let state = IndividualState::new();
let result = apply_context_effects_test(
state,
&context,
0.6,
Duration::days(30),
LifeStage::Adult,
test_timestamp(),
);
assert_eq!(result.disposition().grievance().delta(), 0.0);
}
#[test]
fn birth_era_matches_current_era_epsilon_check_skips() {
let mut context = EcologicalContext::default();
context.macrosystem_mut().cultural_stress = 0.0;
context.chronosystem_mut().historical_period_mut().era_name = "Stability".to_string();
context.chronosystem_mut().cohort_effects_mut().birth_era = BirthEra::Stability;
let state = IndividualState::new();
let result = apply_context_effects_test(
state.clone(),
&context,
0.6,
Duration::days(30),
LifeStage::Adult,
test_timestamp(),
);
assert_eq!(
result.needs().stress().delta(),
state.needs().stress().delta()
);
}
#[test]
fn multiple_work_family_conflicts_clamped_at_one() {
let mut context = EcologicalContext::default();
context.macrosystem_mut().cultural_stress = 0.0;
let work1_id = MicrosystemId::new("work1").unwrap();
let mut work1 = WorkContext::default();
work1.workload_stress = 0.8;
work1.interaction_profile.interaction_frequency = 0.8;
work1.interaction_profile.interaction_complexity = 0.8;
context.add_microsystem(work1_id, Microsystem::new_work(work1));
let family1_id = MicrosystemId::new("family1").unwrap();
let mut family1 = FamilyContext::default();
family1.caregiving_burden = 0.8;
family1.interaction_profile.interaction_frequency = 0.8;
family1.interaction_profile.interaction_complexity = 0.8;
context.add_microsystem(family1_id, Microsystem::new_family(family1));
let work2_id = MicrosystemId::new("work2").unwrap();
let mut work2 = WorkContext::default();
work2.workload_stress = 0.9;
work2.interaction_profile.interaction_frequency = 0.8;
work2.interaction_profile.interaction_complexity = 0.8;
context.add_microsystem(work2_id, Microsystem::new_work(work2));
let family2_id = MicrosystemId::new("family2").unwrap();
let mut family2 = FamilyContext::default();
family2.caregiving_burden = 0.9;
family2.interaction_profile.interaction_frequency = 0.8;
family2.interaction_profile.interaction_complexity = 0.8;
context.add_microsystem(family2_id, Microsystem::new_family(family2));
let result = apply_context_effects_test(
IndividualState::new(),
&context,
0.6,
Duration::days(30),
LifeStage::Adult,
test_timestamp(),
);
assert!(result.needs().stress().delta() > 0.0);
}
#[test]
fn cohort_affinity_epsilon_check_when_eras_mismatch() {
let mut context = EcologicalContext::default();
context.macrosystem_mut().cultural_stress = 0.6;
context.chronosystem_mut().historical_period_mut().era_name = "Crisis".to_string();
context.chronosystem_mut().cohort_effects_mut().birth_era = BirthEra::Stability;
let state = IndividualState::new();
let result = apply_context_effects_test(
state.clone(),
&context,
0.6,
Duration::days(30),
LifeStage::Adult,
test_timestamp(),
);
assert!(result.needs().stress().delta() != state.needs().stress().delta());
}
#[test]
fn zero_duration_returns_unmodified_state() {
let context = EcologicalContext::default();
let state = IndividualState::new();
let state_clone = state.clone();
let result = apply_context_effects_test(
state,
&context,
0.6,
Duration::days(0),
LifeStage::Adult,
test_timestamp(),
);
assert_eq!(
result.mood().valence().delta(),
state_clone.mood().valence().delta()
);
}
#[test]
fn context_with_no_work_or_family_microsystems() {
let mut context = EcologicalContext::default();
let social_id = MicrosystemId::new("friends").unwrap();
let social = SocialContext::default();
context.add_microsystem(social_id, Microsystem::new_social(social));
let result = apply_context_effects_test(
IndividualState::new(),
&context,
0.6,
Duration::days(30),
LifeStage::Adult,
test_timestamp(),
);
assert!(result.mood().valence().delta() >= -1.0 && result.mood().valence().delta() <= 1.0);
}
#[test]
fn education_support_improves_perceived_competence() {
let mut context = EcologicalContext::default();
let edu_id = MicrosystemId::new("edu").unwrap();
let mut edu = EducationContext::default();
edu.competence_support = 0.9;
edu.warmth = 0.7;
edu.interaction_profile.interaction_frequency = 0.8;
edu.interaction_profile.interaction_complexity = 0.8;
context.add_microsystem(edu_id, Microsystem::new_education(edu));
let result = apply_context_effects_test(
IndividualState::new(),
&context,
1.0,
Duration::days(30),
LifeStage::Adult,
test_timestamp(),
);
assert!(result.social_cognition().perceived_competence().delta() > 0.0);
}
#[test]
fn exosystem_health_access_deficit_increases_stress() {
let mut context = EcologicalContext::default();
context.exosystem_mut().health_system_access = 0.2;
context.macrosystem_mut().institutional_structure.rule_of_law = 1.0;
let result = apply_context_effects_test(
IndividualState::new(),
&context,
0.5,
Duration::days(30),
LifeStage::Adult,
test_timestamp(),
);
assert!(result.needs().stress().delta() > 0.0);
assert!(result.mental_health().hopelessness().delta() > 0.0);
}
#[test]
fn exosystem_low_education_quality_reduces_competence() {
let mut context = EcologicalContext::default();
context.exosystem_mut().educational_system_quality = 0.2;
context.exosystem_mut().health_system_access = 0.5;
context.exosystem_mut().community_services_availability = 0.5;
context.macrosystem_mut().institutional_structure.rule_of_law = 1.0;
let result = apply_context_effects_test(
IndividualState::new(),
&context,
0.5,
Duration::days(30),
LifeStage::Adult,
test_timestamp(),
);
assert!(result.social_cognition().perceived_competence().delta() < 0.0);
}
#[test]
fn exosystem_low_services_increase_loneliness_and_stress() {
let mut context = EcologicalContext::default();
context.exosystem_mut().community_services_availability = 0.2;
context.exosystem_mut().health_system_access = 0.5;
context.exosystem_mut().educational_system_quality = 0.5;
context.macrosystem_mut().institutional_structure.rule_of_law = 1.0;
let result = apply_context_effects_test(
IndividualState::new(),
&context,
0.5,
Duration::days(30),
LifeStage::Adult,
test_timestamp(),
);
assert!(result.social_cognition().loneliness().delta() > 0.0);
assert!(result.needs().stress().delta() > 0.0);
}
#[test]
fn non_normative_event_adds_stress() {
let mut context = EcologicalContext::default();
let current_year = test_timestamp().year();
context
.chronosystem_mut()
.add_non_normative_event(NonNormativeEvent::new("crisis", current_year, 0.9));
let result = apply_context_effects_test(
IndividualState::new(),
&context,
0.5,
Duration::days(30),
LifeStage::Adult,
test_timestamp(),
);
assert!(result.needs().stress().delta() > 0.0);
assert!(result.mental_health().hopelessness().delta() > 0.0);
}
#[test]
fn non_normative_event_outside_window_has_no_extra_effect() {
let mut context = EcologicalContext::default();
let current_year = test_timestamp().year();
context.chronosystem_mut().add_non_normative_event(
NonNormativeEvent::completed("past_crisis", current_year - 10, current_year - 5, 0.9),
);
let baseline = apply_context_effects_test(
IndividualState::new(),
&EcologicalContext::default(),
0.5,
Duration::days(30),
LifeStage::Adult,
test_timestamp(),
);
let result = apply_context_effects_test(
IndividualState::new(),
&context,
0.5,
Duration::days(30),
LifeStage::Adult,
test_timestamp(),
);
assert!(
(result.needs().stress().delta() - baseline.needs().stress().delta()).abs()
< f32::EPSILON
);
assert!(
(result.mental_health().hopelessness().delta()
- baseline.mental_health().hopelessness().delta())
.abs()
< f32::EPSILON
);
}
#[test]
fn non_normative_event_before_start_has_no_extra_effect() {
let mut context = EcologicalContext::default();
let current_year = test_timestamp().year();
context
.chronosystem_mut()
.add_non_normative_event(NonNormativeEvent::new("future_crisis", current_year + 5, 0.9));
let baseline = apply_context_effects_test(
IndividualState::new(),
&EcologicalContext::default(),
0.5,
Duration::days(30),
LifeStage::Adult,
test_timestamp(),
);
let result = apply_context_effects_test(
IndividualState::new(),
&context,
0.5,
Duration::days(30),
LifeStage::Adult,
test_timestamp(),
);
assert!(
(result.needs().stress().delta() - baseline.needs().stress().delta()).abs()
< f32::EPSILON
);
}
#[test]
fn critical_period_amplifies_chronosystem_stress() {
let mut base_context = EcologicalContext::default();
base_context
.chronosystem_mut()
.historical_period_mut()
.stability_level = 0.2;
let mut amplified_context = base_context.clone();
amplified_context
.chronosystem_mut()
.add_critical_period(CriticalPeriod::new("identity", 35.0, 45.0, 2.0));
let baseline = apply_context_effects_test(
IndividualState::new(),
&base_context,
0.5,
Duration::days(30),
LifeStage::Adult,
test_timestamp(),
);
let amplified = apply_context_effects_test(
IndividualState::new(),
&lified_context,
0.5,
Duration::days(30),
LifeStage::Adult,
test_timestamp(),
);
assert!(amplified.needs().stress().delta() > baseline.needs().stress().delta());
}
#[test]
fn healthcare_context_affects_stress_and_valence() {
let mut context = EcologicalContext::default();
let hc_id = MicrosystemId::new("healthcare").unwrap();
let mut healthcare = HealthcareContext::default();
healthcare.access_frequency = 0.2;
healthcare.responsiveness = 0.8;
healthcare.warmth = 0.7;
healthcare.hostility = 0.8;
healthcare.interaction_profile.interaction_frequency = 0.8;
healthcare.interaction_profile.interaction_complexity = 0.8;
context.add_microsystem(hc_id, Microsystem::new_healthcare(healthcare));
let result = apply_context_effects_test(
IndividualState::new(),
&context,
0.5,
Duration::days(30),
LifeStage::Adult,
test_timestamp(),
);
assert!(result.needs().stress().delta() > 0.0);
assert!(result.mental_health().hopelessness().delta() > 0.0);
assert!(result.mood().valence().delta() > 0.0);
}
#[test]
fn healthcare_access_above_threshold_skips_hopelessness() {
let mut context = EcologicalContext::default();
let hc_id = MicrosystemId::new("healthcare").unwrap();
let mut healthcare = HealthcareContext::default();
healthcare.access_frequency = 0.7;
healthcare.responsiveness = 0.8;
context.add_microsystem(hc_id, Microsystem::new_healthcare(healthcare));
let result = apply_context_effects_test(
IndividualState::new(),
&context,
0.5,
Duration::days(30),
LifeStage::Adult,
test_timestamp(),
);
assert!(
result.mental_health().hopelessness().delta().abs() < f32::EPSILON
);
}
#[test]
fn religious_context_affects_purpose_and_self_hate() {
let mut context = EcologicalContext::default();
let rel_id = MicrosystemId::new("religious").unwrap();
let mut religious = ReligiousContext::default();
religious.ritual_frequency = 0.9;
religious.warmth = 0.8;
religious.hostility = 0.8;
religious.interaction_profile.interaction_frequency = 0.8;
religious.interaction_profile.interaction_complexity = 0.8;
context.add_microsystem(rel_id, Microsystem::new_religious(religious));
let result = apply_context_effects_test(
IndividualState::new(),
&context,
0.5,
Duration::days(30),
LifeStage::Adult,
test_timestamp(),
);
assert!(result.needs().purpose().delta() > 0.0);
assert!(result.social_cognition().self_hate().delta() > 0.0);
}
#[test]
fn neighborhood_context_affects_safety_and_warmth() {
let mut context = EcologicalContext::default();
let nb_id = MicrosystemId::new("neighborhood").unwrap();
let mut neighborhood = NeighborhoodContext::default();
neighborhood.safety = 0.2;
neighborhood.cohesion = 0.8;
neighborhood.warmth = 0.8;
neighborhood.hostility = 0.8;
neighborhood.interaction_profile.interaction_frequency = 0.8;
neighborhood.interaction_profile.interaction_complexity = 0.8;
context.add_microsystem(nb_id, Microsystem::new_neighborhood(neighborhood));
let result = apply_context_effects_test(
IndividualState::new(),
&context,
0.5,
Duration::days(30),
LifeStage::Adult,
test_timestamp(),
);
assert!(result.needs().stress().delta() > 0.0);
assert!(result.mood().valence().delta() > 0.0);
}
#[test]
fn exosystem_high_quality_supports_competence_and_reduces_loneliness() {
let mut context = EcologicalContext::default();
context.exosystem_mut().health_system_access = 0.9;
context.exosystem_mut().educational_system_quality = 0.9;
context.exosystem_mut().community_services_availability = 0.9;
context.exosystem_mut().resource_availability = 0.5;
context.exosystem_mut().institutional_support = 0.5;
context.macrosystem_mut().cultural_stress = 0.0;
context
.macrosystem_mut()
.institutional_structure
.rule_of_law = 1.0;
let high_quality = apply_context_effects_test(
IndividualState::new(),
&context,
1.0,
Duration::days(30),
LifeStage::Adult,
test_timestamp(),
);
let mut low_context = context.clone();
low_context.exosystem_mut().community_services_availability = 0.3;
let low_quality = apply_context_effects_test(
IndividualState::new(),
&low_context,
1.0,
Duration::days(30),
LifeStage::Adult,
test_timestamp(),
);
assert!(high_quality.needs().stress().delta() < 0.0);
assert!(high_quality.social_cognition().perceived_competence().delta() > 0.0);
assert!(
high_quality.social_cognition().loneliness().delta()
< low_quality.social_cognition().loneliness().delta()
);
}
#[test]
fn macrosystem_structural_factors_shift_state() {
let mut context = EcologicalContext::default();
context.macrosystem_mut().cultural_stress = 0.0;
context.macrosystem_mut().collective_trauma = 0.7;
context.macrosystem_mut().economic_inequality = 0.9;
context
.macrosystem_mut()
.institutional_structure
.social_mobility = 0.2;
context
.macrosystem_mut()
.institutional_structure
.corruption_level = 0.9;
let result = apply_context_effects_test(
IndividualState::new(),
&context,
0.5,
Duration::days(30),
LifeStage::Adult,
test_timestamp(),
);
assert!(result.mental_health().hopelessness().delta() > 0.0);
assert!(result.disposition().grievance().delta() > 0.0);
assert!(result.disposition().trustor_propensity().delta() < 0.0);
assert!(result.social_cognition().perceived_competence().delta() < 0.0);
}
#[test]
fn chronosystem_low_institutional_trust_reduces_trustor_propensity() {
let mut context = EcologicalContext::default();
context
.chronosystem_mut()
.historical_period_mut()
.institutional_trust = 0.2;
let result = apply_context_effects_test(
IndividualState::new(),
&context,
0.5,
Duration::days(30),
LifeStage::Adult,
test_timestamp(),
);
assert!(result.disposition().trustor_propensity().delta() < 0.0);
}
#[test]
fn apply_context_effects_handles_all_life_stages() {
let context = EcologicalContext::default();
let base = IndividualState::new();
for stage in [LifeStage::YoungAdult, LifeStage::MatureAdult, LifeStage::Elder] {
let result = apply_context_effects_test(
base.clone(),
&context,
0.5,
Duration::days(0),
stage,
test_timestamp(),
);
assert_eq!(result, base);
}
}
#[test]
fn withdrawal_feedback_reduces_family_and_social_frequency() {
let mut context = EcologicalContext::default();
let family_id = MicrosystemId::new("family").unwrap();
let social_id = MicrosystemId::new("social").unwrap();
let mut family = FamilyContext::default();
family.interaction_profile.interaction_frequency = 0.8;
family.interaction_profile.reciprocity_balance = 0.8;
context.add_microsystem(family_id.clone(), Microsystem::new_family(family));
let mut social_context = SocialContext::default();
social_context.interaction_profile.interaction_frequency = 0.7;
social_context.interaction_profile.reciprocity_balance = 0.7;
context.add_microsystem(social_id.clone(), Microsystem::new_social(social_context));
let mut state = IndividualState::new();
let social = state.social_cognition_mut();
social.loneliness_mut().set_base(0.9);
social.perceived_reciprocal_caring_mut().set_base(0.1);
social.perceived_liability_mut().set_base(0.9);
social.self_hate_mut().set_base(0.9);
apply_withdrawal_feedback(&mut context, &state);
let updated_family = context
.get_microsystem(&family_id)
.unwrap()
.family()
.unwrap();
assert!(updated_family.interaction_profile.interaction_frequency < 0.8);
assert!(updated_family.interaction_profile.reciprocity_balance < 0.8);
let updated_social = context
.get_microsystem(&social_id)
.unwrap()
.social()
.unwrap();
assert!(updated_social.interaction_profile.interaction_frequency < 0.7);
assert!(updated_social.interaction_profile.reciprocity_balance < 0.7);
}
#[test]
fn microsystem_developmental_effects_skip_when_multiplier_zero() {
let mut context = EcologicalContext::default();
let family_id = MicrosystemId::new("family").unwrap();
let mut family = FamilyContext::default();
family.interaction_profile.interaction_frequency = 0.9;
family.interaction_profile.interaction_complexity = 0.9;
context.add_microsystem(family_id, Microsystem::new_family(family));
let mut state = IndividualState::new();
let stress_before = state.needs().stress_effective();
apply_microsystem_developmental_effects(&mut state, &context, 1.0, 0.0);
assert!((state.needs().stress_effective() - stress_before).abs() < f32::EPSILON);
}
#[test]
fn microsystem_social_warmth_increases_caring() {
let mut context = EcologicalContext::default();
let social_id = MicrosystemId::new("social").unwrap();
let mut social = SocialContext::default();
social.warmth = 0.9;
social.interaction_profile.interaction_frequency = 0.9;
social.interaction_profile.interaction_complexity = 0.9;
context.add_microsystem(social_id, Microsystem::new_social(social));
let mut state = IndividualState::new();
let caring_before = state
.social_cognition()
.perceived_reciprocal_caring()
.delta();
apply_microsystem_developmental_effects(&mut state, &context, 1.0, 1.0);
let caring_after = state
.social_cognition()
.perceived_reciprocal_caring()
.delta();
assert!(caring_after > caring_before);
}
#[test]
fn life_stage_macrosystem_multiplier_elder_is_half() {
assert!((life_stage_macrosystem_multiplier(LifeStage::Elder) - 0.5).abs() < f32::EPSILON);
}
#[test]
fn life_stage_macrosystem_multiplier_mature_adult_is_half() {
assert!(
(life_stage_macrosystem_multiplier(LifeStage::MatureAdult) - 0.5).abs()
< f32::EPSILON
);
}
}