use super::*;
fn served_warm(models: &[&str], warm: &[&str]) -> Served {
Served {
models: models.iter().map(|m| m.to_string()).collect(),
warm: warm.iter().map(|m| m.to_string()).collect(),
}
}
fn managed_mux(model: Option<&str>, mode: ManagedMode) -> BackendConfig {
BackendConfig {
managed: Some(mode),
..openai_backend(model, Some(Serving::Multiplexer))
}
}
#[test]
fn shared_adopts_warm_when_nothing_is_pinned() {
let b = managed_mux(None, ManagedMode::Shared);
let a = adopt(&b, &served_warm(&["cold", "warm-y"], &["warm-y"]), None);
assert_eq!(a.model.as_deref(), Some("warm-y"));
assert!(a.adopted_warm);
assert_eq!(a.pin_conflict, None);
}
#[test]
fn shared_adopts_warm_over_a_conflicting_pin_and_offers_the_force_choice() {
let b = managed_mux(Some("mine"), ManagedMode::Shared);
let a = adopt(&b, &served_warm(&["mine", "warm-y"], &["warm-y"]), None);
assert_eq!(
a.model.as_deref(),
Some("warm-y"),
"cooperative default = warm"
);
assert!(a.adopted_warm);
assert_eq!(
a.pin_conflict.as_deref(),
Some("mine"),
"the pin is surfaced as the force-swap choice"
);
}
#[test]
fn shared_keeps_the_pin_when_the_pin_is_already_warm() {
let b = managed_mux(Some("mine"), ManagedMode::Shared);
let a = adopt(&b, &served_warm(&["mine", "other"], &["mine"]), None);
assert_eq!(a.model.as_deref(), Some("mine"));
assert!(!a.adopted_warm);
assert_eq!(a.pin_conflict, None);
}
#[test]
fn shared_loads_the_pin_when_nothing_is_warm() {
let b = managed_mux(Some("mine"), ManagedMode::Shared);
let a = adopt(&b, &served_warm(&["mine", "other"], &[]), None);
assert_eq!(a.model.as_deref(), Some("mine"));
assert!(!a.adopted_warm);
assert_eq!(a.pin_conflict, None);
}
#[test]
fn shared_surfaces_the_conflict_for_a_session_request_too() {
let b = managed_mux(Some("declared"), ManagedMode::Shared);
let a = adopt(
&b,
&served_warm(&["asked", "warm-y"], &["warm-y"]),
Some("asked"),
);
assert_eq!(a.model.as_deref(), Some("warm-y"));
assert!(a.adopted_warm);
assert_eq!(a.pin_conflict.as_deref(), Some("asked"));
}
#[test]
fn shared_no_pin_two_warm_is_ambiguous_not_list_order() {
let b = managed_mux(None, ManagedMode::Shared);
let a = adopt(&b, &served_warm(&["A", "B"], &["A", "B"]), None);
assert_eq!(
a.model, None,
"Shared mode must route genuine multi-warm ambiguity same as any other mode"
);
assert_eq!(a.ambiguous_warm, vec!["A", "B"]);
}
#[test]
fn shared_no_pin_two_warm_is_ambiguous_regardless_of_order() {
let b = managed_mux(None, ManagedMode::Shared);
let a = adopt(&b, &served_warm(&["B", "A"], &["B", "A"]), None);
assert_eq!(a.model, None);
}
#[test]
fn shared_pin_already_warm_wins_regardless_of_list_order() {
let b = managed_mux(Some("B"), ManagedMode::Shared);
let a = adopt(&b, &served_warm(&["A", "B"], &["A", "B"]), None);
assert_eq!(a.model.as_deref(), Some("B"));
assert!(!a.adopted_warm);
assert_eq!(a.pin_conflict, None);
}
#[test]
fn dedicated_forces_the_pin_and_never_adopts_warm() {
let b = managed_mux(Some("mine"), ManagedMode::Dedicated);
let a = adopt(&b, &served_warm(&["mine", "warm-y"], &["warm-y"]), None);
assert_eq!(a.model.as_deref(), Some("mine"), "dedicated forces its pin");
assert!(!a.adopted_warm);
assert_eq!(a.pin_conflict, None);
}
#[test]
fn unmanaged_keeps_precedence_warm_is_only_a_tiebreaker() {
let b = openai_backend(Some("declared"), Some(Serving::Multiplexer));
let a = adopt(&b, &served_warm(&["declared", "warm-y"], &["warm-y"]), None);
assert_eq!(a.model.as_deref(), Some("declared"));
assert!(!a.adopted_warm);
assert_eq!(a.pin_conflict, None);
}
#[test]
fn multiplexer_prefers_warm_over_first_served() {
let backend = BackendConfig {
name: "b".into(),
endpoint: "http://h:11434".into(),
kind: Some(BackendKind::Ollama),
..Default::default()
};
let adoption = adopt(&backend, &served_warm(&["a", "b", "c"], &["c"]), None);
assert_eq!(adoption.model.as_deref(), Some("c"));
assert!(!adoption.requested_unavailable);
}
#[test]
fn requested_and_declared_still_outrank_warm() {
let declared = BackendConfig {
name: "b".into(),
endpoint: "http://h:11434".into(),
model: Some("b".into()),
kind: Some(BackendKind::Ollama),
..Default::default()
};
let adoption = adopt(&declared, &served_warm(&["a", "b", "c"], &["c"]), Some("a"));
assert_eq!(adoption.model.as_deref(), Some("a"));
let adoption = adopt(&declared, &served_warm(&["a", "b", "c"], &["c"]), None);
assert_eq!(adoption.model.as_deref(), Some("b"));
}
#[test]
fn stale_warm_entry_not_in_served_is_ignored() {
let backend = BackendConfig {
name: "b".into(),
endpoint: "http://h:11434".into(),
kind: Some(BackendKind::Ollama),
..Default::default()
};
let adoption = adopt(&backend, &served_warm(&["a", "b"], &["gone"]), None);
assert_eq!(
adoption.model.as_deref(),
Some("a"),
"falls to first served"
);
}
#[test]
fn two_loaded_models_and_no_pin_does_not_pick_by_list_order() {
let backend = BackendConfig {
name: "b".into(),
endpoint: "http://h:11434".into(),
kind: Some(BackendKind::Ollama),
..Default::default()
};
let adoption = adopt(
&backend,
&served_warm(
&["Qwen2.5-14B-Instruct-1M-Q8_0", "ornith-1.5-35b"],
&["Qwen2.5-14B-Instruct-1M-Q8_0", "ornith-1.5-35b"],
),
None,
);
assert_eq!(
adoption.model, None,
"must not pick either by list order when both are loaded"
);
assert_eq!(
adoption.ambiguous_warm,
vec!["Qwen2.5-14B-Instruct-1M-Q8_0", "ornith-1.5-35b"],
"the candidates are surfaced for the caller to ask/refuse with"
);
}
#[test]
fn one_loaded_model_still_adopts_and_is_named() {
let backend = BackendConfig {
name: "b".into(),
endpoint: "http://h:11434".into(),
kind: Some(BackendKind::Ollama),
..Default::default()
};
let adoption = adopt(
&backend,
&served_warm(
&["Qwen2.5-14B-Instruct-1M-Q8_0", "ornith-1.5-35b"],
&["ornith-1.5-35b"],
),
None,
);
assert_eq!(adoption.model.as_deref(), Some("ornith-1.5-35b"));
assert!(adoption.ambiguous_warm.is_empty());
}
#[test]
fn ambiguous_warm_never_fires_when_a_pin_resolves() {
let declared = BackendConfig {
name: "b".into(),
endpoint: "http://h:11434".into(),
model: Some("Qwen2.5-14B-Instruct-1M-Q8_0".into()),
kind: Some(BackendKind::Ollama),
..Default::default()
};
let adoption = adopt(
&declared,
&served_warm(
&["Qwen2.5-14B-Instruct-1M-Q8_0", "ornith-1.5-35b"],
&["Qwen2.5-14B-Instruct-1M-Q8_0", "ornith-1.5-35b"],
),
None,
);
assert_eq!(
adoption.model.as_deref(),
Some("Qwen2.5-14B-Instruct-1M-Q8_0")
);
assert!(adoption.ambiguous_warm.is_empty());
}
#[test]
fn instance_adoption_unchanged_by_warm() {
let backend = openai_backend(Some("requested"), Some(Serving::Instance));
let adoption = adopt(&backend, &served_warm(&["served"], &["served"]), None);
assert_eq!(adoption.model.as_deref(), Some("served"));
assert!(adoption.requested_ignored);
}