use super::*;
#[test]
fn context_manager_keyword_roundtrip_and_availability() {
for m in [
ContextManager::Standard,
ContextManager::AppendOnly,
ContextManager::Progressive,
ContextManager::Distributed,
] {
assert_eq!(ContextManager::from_keyword(m.keyword()), Some(m));
}
assert_eq!(
ContextManager::from_keyword(" STANDARD "),
Some(ContextManager::Standard),
"case/space-insensitive"
);
assert_eq!(ContextManager::from_keyword("nope"), None);
assert!(ContextManager::Standard.available());
assert!(ContextManager::AppendOnly.available());
assert!(!ContextManager::Progressive.available());
assert!(!ContextManager::Distributed.available());
assert_eq!(ContextManager::default(), ContextManager::Standard);
assert!(!ContextManager::AppendOnly.rewrites_history());
for m in [
ContextManager::Standard,
ContextManager::Progressive,
ContextManager::Distributed,
] {
assert!(m.rewrites_history(), "{m:?} rewrites history");
}
for alias in [
"append-only",
"append_only",
"appendonly",
"append",
" APPEND-ONLY ",
] {
assert_eq!(
ContextManager::from_keyword(alias),
Some(ContextManager::AppendOnly),
"alias {alias:?}"
);
}
}
#[test]
fn context_manager_serde_name_matches_its_keyword() {
for m in [
ContextManager::Standard,
ContextManager::AppendOnly,
ContextManager::Progressive,
ContextManager::Distributed,
] {
let encoded = serde_json::to_string(&m).expect("serialize");
assert_eq!(
encoded,
format!("\"{}\"", m.keyword()),
"{m:?} serializes as something other than its keyword"
);
assert_eq!(
serde_json::from_str::<ContextManager>(&encoded).expect("round-trip"),
m
);
}
let parsed: ContextConfig =
toml::from_str("manager = \"append-only\"").expect("append-only must load");
assert_eq!(parsed.manager, ContextManager::AppendOnly);
}
#[test]
fn compaction_trigger_policy_keyword_roundtrip_and_default() {
for policy in [
CompactionTriggerPolicy::HeadroomAware,
CompactionTriggerPolicy::MessageCount,
] {
assert_eq!(
CompactionTriggerPolicy::from_keyword(policy.as_str()),
Some(policy)
);
assert_eq!(policy.keyword(), policy.as_str());
}
assert_eq!(
CompactionTriggerPolicy::from_keyword(" MESSAGE_COUNT "),
Some(CompactionTriggerPolicy::MessageCount),
"case/space-insensitive"
);
assert_eq!(CompactionTriggerPolicy::from_keyword("nope"), None);
assert_eq!(
CompactionTriggerPolicy::default(),
CompactionTriggerPolicy::HeadroomAware
);
}
#[test]
fn context_section_defaults_and_parses() {
let cfg: Config = toml::from_str("").unwrap();
assert!(cfg.context.is_none());
let c: ContextConfig =
toml::from_str("manager = \"progressive\"\ncompaction_trigger_policy = \"message_count\"")
.unwrap();
assert_eq!(c.manager, ContextManager::Progressive);
assert_eq!(
c.compaction_trigger_policy,
CompactionTriggerPolicy::MessageCount
);
let defaults = ContextConfig::default();
assert_eq!(defaults.manager, ContextManager::Standard);
assert_eq!(
defaults.compaction_trigger_policy,
CompactionTriggerPolicy::HeadroomAware
);
let parsed_default: ContextConfig = toml::from_str("manager = \"standard\"").unwrap();
assert_eq!(
parsed_default.compaction_trigger_policy,
CompactionTriggerPolicy::HeadroomAware
);
assert!(
toml::from_str::<ContextConfig>("compaction_trigger_policy = \"not_a_policy\"").is_err(),
"an invalid policy must fail config parsing rather than silently changing safety behavior"
);
}
#[test]
fn context_input_ceiling_pct_normalizes_at_deserialization_boundary() {
for value in [1, 80, 99] {
let parsed: ContextConfig =
toml::from_str(&format!("input_ceiling_pct = {value}")).unwrap();
assert_eq!(parsed.input_ceiling_pct, value);
}
for value in [0, 100, 101, u32::MAX] {
let parsed: ContextConfig =
toml::from_str(&format!("input_ceiling_pct = {value}")).unwrap();
assert_eq!(
parsed.input_ceiling_pct,
default_input_ceiling_pct(),
"out-of-range value {value} must fall back to the documented safe default"
);
}
assert_eq!(input_percentage_ceiling(32_768, 90), 29_491);
assert_eq!(
input_percentage_ceiling(32_768, 0),
26_214,
"programmatic callers share the same invalid-value fallback",
);
}
#[test]
fn context_feature_keyword_alias_availability_and_issue() {
for f in ContextFeature::ALL {
assert_eq!(ContextFeature::from_keyword(f.keyword()), Some(f));
}
assert_eq!(
ContextFeature::from_keyword("TOOL-OFFLOAD"),
Some(ContextFeature::ToolOffload)
);
assert_eq!(
ContextFeature::from_keyword("offload"),
Some(ContextFeature::ToolOffload)
);
assert_eq!(
ContextFeature::from_keyword(" state "),
Some(ContextFeature::Scratchpad)
);
assert_eq!(ContextFeature::from_keyword("nope"), None);
assert!(ContextFeature::ToolOffload.available());
assert!(ContextFeature::Scratchpad.available());
assert!(ContextFeature::Semantic.available());
assert!(ContextFeature::Experiential.available());
assert!(ContextFeature::Scheduled.available());
assert!(
!ContextFeature::Provenance.available(),
"provenance still pending"
);
assert!(ContextFeature::ALL
.iter()
.filter(|f| !matches!(f, ContextFeature::Provenance))
.all(|f| f.available()));
assert_eq!(ContextFeature::Semantic.issue(), 582);
assert_eq!(ContextFeature::Scratchpad.issue(), 583);
assert_eq!(ContextFeature::ToolOffload.issue(), 584);
assert_eq!(ContextFeature::Provenance.issue(), 584);
assert_eq!(ContextFeature::Experiential.issue(), 585);
assert_eq!(ContextFeature::Scheduled.issue(), 586);
}
#[test]
fn context_features_override_layering_and_parse() {
use ContextFeature as F;
let base = ContextManager::Standard.base_features();
assert!(base.enabled().is_empty());
let mut ov = ContextFeatures::default();
ov.set(F::Scratchpad, Some(true));
let resolved = ov.apply_to(base);
assert!(resolved.get(F::Scratchpad));
assert!(!resolved.get(F::Semantic));
assert_eq!(resolved.enabled(), vec![F::Scratchpad]);
let mut ov2 = ContextFeatures::default();
ov2.set(F::Scratchpad, Some(false));
assert!(!ov2.apply_to(resolved).get(F::Scratchpad));
let c: ContextConfig =
toml::from_str("manager = \"standard\"\n[features]\nsemantic = true\nscratchpad = false")
.unwrap();
assert_eq!(c.features.get(F::Semantic), Some(true));
assert_eq!(c.features.get(F::Scratchpad), Some(false));
assert_eq!(c.features.get(F::ToolOffload), None);
}
#[test]
fn base_for_defaults_tool_offload_on_and_local_assist_on_for_ollama() {
use ContextFeature as F;
let local = ContextFeatureSet::base_for(ContextManager::Standard, BackendKind::Ollama);
assert!(local.get(F::ToolOffload));
assert!(local.get(F::Scratchpad));
assert!(local.get(F::Semantic));
assert!(local.get(F::Scheduled));
let cloud = ContextFeatureSet::base_for(ContextManager::Standard, BackendKind::Openai);
assert!(cloud.get(F::ToolOffload));
assert!(cloud.get(F::Scratchpad));
assert!(cloud.get(F::Semantic));
assert!(cloud.get(F::Scheduled));
let mut ov = ContextFeatures::default();
ov.set(F::Scheduled, Some(false));
ov.set(F::ToolOffload, Some(false));
assert!(!ov.apply_to(local).get(F::Scheduled));
assert!(!ov.apply_to(local).get(F::ToolOffload));
assert!(ov.apply_to(local).get(F::Scratchpad)); }