#![allow(
clippy::assertions_on_constants,
clippy::single_match,
clippy::useless_vec
)]
#[cfg(test)]
mod tests {
use sparrow::autonomy::{AutonomyContract, AutonomyLevel, HardStop, ProposedAction};
use sparrow::config::Config;
use sparrow::event::{Decision, RiskLevel};
use sparrow::event::{Event, OutcomeSummary, RunId};
use sparrow::redaction::RedactionFilter;
use sparrow::router::{BasicRouter, BudgetState, Router, RoutingNeed, TaskTier};
use sparrow::sandbox::{Command, Limits, LocalSandbox, Sandbox};
use std::sync::Arc;
fn test_action(risk: RiskLevel) -> ProposedAction {
ProposedAction {
tool_name: "test_tool".into(),
risk,
args: serde_json::json!({}),
}
}
#[test]
fn test_autonomy_supervised_readonly() {
let contract = AutonomyContract::supervised();
let action = test_action(RiskLevel::ReadOnly);
assert_eq!(contract.decide(&action), Decision::Allow);
}
#[test]
fn test_autonomy_supervised_mutating() {
let contract = AutonomyContract::supervised();
let action = test_action(RiskLevel::Mutating);
assert_eq!(contract.decide(&action), Decision::AskUser);
}
#[test]
fn test_autonomy_supervised_exec() {
let contract = AutonomyContract::supervised();
let action = test_action(RiskLevel::Exec);
assert_eq!(contract.decide(&action), Decision::AskUser);
}
#[test]
fn test_autonomy_supervised_destructive() {
let contract = AutonomyContract::supervised();
let action = test_action(RiskLevel::Destructive);
assert_eq!(contract.decide(&action), Decision::Deny);
}
#[test]
fn test_autonomy_supervised_network() {
let contract = AutonomyContract::supervised();
let action = test_action(RiskLevel::Network);
assert_eq!(contract.decide(&action), Decision::AskUser);
}
#[test]
fn test_autonomy_trusted_mutating() {
let contract = AutonomyContract::trusted();
let action = test_action(RiskLevel::Mutating);
assert_eq!(contract.decide(&action), Decision::Allow);
}
#[test]
fn test_autonomy_trusted_destructive() {
let contract = AutonomyContract::trusted();
let action = test_action(RiskLevel::Destructive);
assert_eq!(contract.decide(&action), Decision::Deny);
}
#[test]
fn test_autonomy_autonomous_mutating() {
let contract = AutonomyContract::autonomous();
let action = test_action(RiskLevel::Mutating);
assert_eq!(contract.decide(&action), Decision::Allow);
}
#[test]
fn test_autonomy_autonomous_exec() {
let contract = AutonomyContract::autonomous();
let action = test_action(RiskLevel::Exec);
assert_eq!(contract.decide(&action), Decision::Allow);
}
#[test]
fn test_autonomy_autonomous_destructive() {
let contract = AutonomyContract::autonomous();
let action = test_action(RiskLevel::Destructive);
assert_eq!(contract.decide(&action), Decision::AskUser);
}
#[test]
fn test_autonomy_hardstop_destructive() {
let mut contract = AutonomyContract::autonomous();
contract
.stops
.push(HardStop::RiskLevel(RiskLevel::Destructive));
let action = test_action(RiskLevel::Destructive);
assert_eq!(contract.decide(&action), Decision::Deny);
}
use async_trait::async_trait;
use futures::stream;
use sparrow::provider::{
Brain, BrainError, BrainRequest, BrainStream, LatencyClass, ModelCaps,
};
struct MockBrain {
id: String,
caps: ModelCaps,
}
#[async_trait]
impl Brain for MockBrain {
fn id(&self) -> &str {
&self.id
}
fn caps(&self) -> ModelCaps {
self.caps.clone()
}
async fn complete(&self, _req: BrainRequest) -> anyhow::Result<BrainStream> {
Ok(Box::pin(stream::empty()))
}
}
fn make_mock(
id: &str,
input_cost: f64,
output_cost: f64,
latency: LatencyClass,
) -> Arc<dyn Brain> {
Arc::new(MockBrain {
id: id.to_string(),
caps: ModelCaps {
context_window: 128_000,
max_output: 16_000,
tools: true,
vision: false,
cost_input_per_mtok: input_cost,
cost_output_per_mtok: output_cost,
latency,
},
})
}
#[test]
fn test_router_free_first() {
let mut config = Config::default();
config.routing.free_first = true;
let mut providers = std::collections::HashMap::new();
providers.insert(
"local".into(),
vec![make_mock("local:free-model", 0.0, 0.0, LatencyClass::Slow)],
);
providers.insert(
"cloud".into(),
vec![make_mock(
"cloud:paid-model",
10.0,
30.0,
LatencyClass::Fast,
)],
);
let router = BasicRouter::new(&config, providers);
let need = RoutingNeed {
tier: TaskTier::Medium,
required_tools: true,
required_vision: false,
prefer_local: false,
};
let budget = BudgetState {
daily_limit_usd: 100.0,
daily_spent_usd: 0.0,
session_limit_usd: 10.0,
session_spent_usd: 0.0,
};
let chain = router.select(&need, &budget);
assert!(!chain.is_empty());
assert!(chain[0].id().contains("free-model"));
}
#[test]
fn test_router_budget_exhausted() {
let mut config = Config::default();
config.routing.free_first = false;
let mut providers = std::collections::HashMap::new();
providers.insert(
"cloud".into(),
vec![make_mock(
"cloud:paid-model",
10.0,
30.0,
LatencyClass::Fast,
)],
);
let router = BasicRouter::new(&config, providers);
let need = RoutingNeed {
tier: TaskTier::Medium,
required_tools: true,
required_vision: false,
prefer_local: false,
};
let budget = BudgetState {
daily_limit_usd: 0.0,
daily_spent_usd: 0.0,
session_limit_usd: 0.0,
session_spent_usd: 0.0,
};
let chain = router.select(&need, &budget);
assert!(chain.is_empty() || chain.iter().all(|b| b.caps().cost_input_per_mtok == 0.0));
}
#[test]
fn test_router_policy_tiers() {
let mut config = Config::default();
config
.routing
.policy
.insert("trivial".into(), "local".into());
config.routing.policy.insert("hard".into(), "cloud".into());
let mut providers = std::collections::HashMap::new();
providers.insert(
"local".into(),
vec![make_mock("local:cheap", 0.0, 0.0, LatencyClass::Fast)],
);
providers.insert(
"cloud".into(),
vec![make_mock("cloud:powerful", 10.0, 30.0, LatencyClass::Slow)],
);
let router = BasicRouter::new(&config, providers);
let need = RoutingNeed {
tier: TaskTier::Trivial,
required_tools: false,
required_vision: false,
prefer_local: false,
};
let budget = BudgetState {
daily_limit_usd: 100.0,
daily_spent_usd: 0.0,
session_limit_usd: 10.0,
session_spent_usd: 0.0,
};
let chain = router.select(&need, &budget);
assert!(chain[0].id().contains("local"));
}
#[test]
fn test_router_small_prefers_ollama_before_free_cloud() {
let mut config = Config::default();
config.routing.free_first = true;
let mut providers = std::collections::HashMap::new();
providers.insert(
"ollama".into(),
vec![make_mock("qwen3.5:32b", 0.0, 0.0, LatencyClass::Slow)],
);
providers.insert(
"nvidia".into(),
vec![make_mock("nvidia/nemotron", 0.0, 0.0, LatencyClass::Fast)],
);
let router = BasicRouter::new(&config, providers);
let need = RoutingNeed {
tier: TaskTier::Small,
required_tools: false,
required_vision: false,
prefer_local: false,
};
let budget = BudgetState {
daily_limit_usd: 100.0,
daily_spent_usd: 0.0,
session_limit_usd: 10.0,
session_spent_usd: 0.0,
};
let chain = router.select(&need, &budget);
assert_eq!(chain[0].id(), "qwen3.5:32b");
assert_eq!(chain[1].id(), "nvidia/nemotron");
}
#[test]
fn test_router_explicit_cloud_policy_overrides_local_free_first() {
let mut config = Config::default();
config.routing.free_first = true;
config
.routing
.policy
.insert("trivial".into(), "nvidia".into());
let mut providers = std::collections::HashMap::new();
providers.insert(
"ollama".into(),
vec![make_mock("qwen3.5:32b", 0.0, 0.0, LatencyClass::Slow)],
);
providers.insert(
"nvidia".into(),
vec![make_mock(
"meta/llama-3.1-8b-instruct",
0.0,
0.0,
LatencyClass::Fast,
)],
);
let router = BasicRouter::new(&config, providers);
let need = RoutingNeed {
tier: TaskTier::Trivial,
required_tools: false,
required_vision: false,
prefer_local: false,
};
let budget = BudgetState {
daily_limit_usd: 100.0,
daily_spent_usd: 0.0,
session_limit_usd: 10.0,
session_spent_usd: 0.0,
};
let chain = router.select(&need, &budget);
assert_eq!(chain[0].id(), "meta/llama-3.1-8b-instruct");
assert_eq!(chain[1].id(), "qwen3.5:32b");
}
#[test]
fn test_router_vision_penalizes_non_vision_models() {
let mut config = Config::default();
config
.routing
.policy
.insert("vision".into(), "cloud".into());
let mut providers = std::collections::HashMap::new();
providers.insert(
"cloud".into(),
vec![Arc::new(MockBrain {
id: "text-only".into(),
caps: ModelCaps {
context_window: 128_000,
max_output: 16_000,
tools: true,
vision: false,
cost_input_per_mtok: 0.0,
cost_output_per_mtok: 0.0,
latency: LatencyClass::Fast,
},
}) as Arc<dyn Brain>],
);
providers.insert(
"vision".into(),
vec![Arc::new(MockBrain {
id: "vision-model".into(),
caps: ModelCaps {
context_window: 128_000,
max_output: 16_000,
tools: true,
vision: true,
cost_input_per_mtok: 5.0,
cost_output_per_mtok: 15.0,
latency: LatencyClass::Medium,
},
}) as Arc<dyn Brain>],
);
let router = BasicRouter::new(&config, providers);
let need = RoutingNeed {
tier: TaskTier::Vision,
required_tools: false,
required_vision: true,
prefer_local: false,
};
let budget = BudgetState {
daily_limit_usd: 100.0,
daily_spent_usd: 0.0,
session_limit_usd: 10.0,
session_spent_usd: 0.0,
};
let chain = router.select(&need, &budget);
assert_eq!(chain[0].id(), "vision-model");
}
#[test]
fn test_router_rate_limit_retry() {
let router = BasicRouter::new(&Config::default(), std::collections::HashMap::new());
let brain = make_mock("test", 1.0, 1.0, LatencyClass::Fast);
let retry = router.on_error(
brain.as_ref(),
&BrainError::RateLimit {
retry_after: Some(5),
},
);
assert!(matches!(retry, sparrow::router::Retry::WaitAndRetry(5)));
let retry = router.on_error(
brain.as_ref(),
&BrainError::RateLimit {
retry_after: Some(60),
},
);
assert!(matches!(retry, sparrow::router::Retry::NextInChain));
let retry = router.on_error(brain.as_ref(), &BrainError::Refusal("no".into()));
assert!(matches!(retry, sparrow::router::Retry::Abort));
}
#[test]
fn test_sandbox_path_isolation() {
let tmp = std::env::temp_dir().join("sparrow-test-sandbox");
std::fs::create_dir_all(&tmp).ok();
let sandbox = LocalSandbox::new(tmp.clone());
assert_eq!(sandbox.root(), tmp.as_path());
let policy = sandbox.policy();
assert!(policy.allowed_paths.iter().any(|p| p == &tmp));
let _ = std::fs::remove_dir_all(&tmp);
}
#[test]
fn test_sandbox_exec_basic() {
let tmp = std::env::temp_dir().join("sparrow-test-exec");
std::fs::create_dir_all(&tmp).ok();
let sandbox = LocalSandbox::new(tmp.clone());
let cmd = Command {
program: if cfg!(windows) { "cmd" } else { "echo" }.into(),
args: if cfg!(windows) {
vec!["/c".into(), "echo hello".into()]
} else {
vec!["hello".into()]
},
env: std::collections::HashMap::new(),
workdir: tmp.clone(),
};
let limits = Limits {
timeout_ms: 5000,
max_output_bytes: 1024,
};
let rt = tokio::runtime::Runtime::new().unwrap();
let result = rt.block_on(sandbox.exec(&cmd, &limits));
match result {
Ok(r) => {
assert!(r.stdout.contains("hello"));
assert_eq!(r.exit_code, 0);
}
Err(_) => {} }
let _ = std::fs::remove_dir_all(&tmp);
}
#[test]
fn test_redaction_filter_patterns() {
let filter = RedactionFilter::new();
assert!(filter.contains_secret("sk-ant-api03-abcdef"));
assert!(filter.contains_secret("ghp_1234567890abcdef"));
assert!(!filter.contains_secret("normal text"));
}
#[test]
fn test_redaction_event() {
let mut filter = RedactionFilter::new();
filter.load_secrets(vec!["mysecret".into()]);
let event = Event::ThinkingDelta {
run: RunId("test".into()),
text: "key is mysecret here".into(),
};
let redacted = filter.redact_event(&event);
if let Event::ThinkingDelta { text, .. } = redacted {
assert!(!text.contains("mysecret"));
assert!(text.contains("[REDACTED]"));
} else {
panic!("wrong type");
}
}
#[test]
fn test_golden_replay_roundtrip() {
let events = vec![
Event::RunStarted {
run: RunId("golden-1".into()),
task: "test task".into(),
agent: "test-agent".into(),
},
Event::ThinkingDelta {
run: RunId("golden-1".into()),
text: "thinking...".into(),
},
Event::RunFinished {
run: RunId("golden-1".into()),
outcome: OutcomeSummary {
status: "completed".into(),
diffs: vec![],
cost_usd: 0.01,
tokens: sparrow::event::TokenUsage {
input: 100,
output: 50,
},
},
},
];
let mut jsonl = String::new();
for e in &events {
jsonl.push_str(&serde_json::to_string(e).unwrap());
jsonl.push('\n');
}
let parsed: Vec<Event> = jsonl
.lines()
.filter(|l| !l.is_empty())
.filter_map(|l| serde_json::from_str(l).ok())
.collect();
assert_eq!(parsed.len(), 3);
match &parsed[0] {
Event::RunStarted { run, task, agent } => {
assert_eq!(run.0, "golden-1");
assert_eq!(task, "test task");
assert_eq!(agent, "test-agent");
}
_ => panic!(),
}
}
#[test]
fn test_pipeline_config_validation() {
use sparrow::extras::PipelineConfig;
let default = PipelineConfig::default_pipeline();
assert!(default.validate().is_ok());
let invalid = PipelineConfig {
name: "bad".into(),
steps: vec![sparrow::extras::PipelineStep {
role: "coder".into(),
model_preference: None,
prompt_override: None,
depends_on: vec!["nonexistent".into()],
}],
max_reworks: 3,
};
assert!(invalid.validate().is_err());
let empty = PipelineConfig {
name: "empty".into(),
steps: vec![],
max_reworks: 1,
};
assert!(empty.validate().is_err());
}
#[test]
fn test_config_defaults() {
let config = Config::default();
assert_eq!(config.theme, "captain");
assert_eq!(config.budget.daily_usd, 5.0);
assert_eq!(config.budget.session_usd, 1.0);
assert!(config.defaults.autonomy == AutonomyLevel::Trusted);
}
#[test]
fn test_config_load_normalizes_blank_theme() {
use sparrow::config::{ConfigStore, FsConfigStore};
let temp = std::env::temp_dir().join(format!("sparrow-test-{}", uuid::Uuid::new_v4()));
std::fs::create_dir_all(&temp).unwrap();
std::fs::write(temp.join("config.toml"), "theme = \"\"\n").unwrap();
let store = FsConfigStore::new(temp.clone());
let config = store.load().unwrap();
let _ = std::fs::remove_dir_all(temp);
assert_eq!(config.theme, "captain");
}
#[test]
fn test_embeddings_similarity() {
use sparrow::extras::Embeddings;
let mut emb = Embeddings::new();
emb.add("Rust programming language");
emb.add("TypeScript for web development");
emb.add("cooking recipes for dinner");
let results = emb.search("Rust programming", 2);
assert!(!results.is_empty());
assert!(results[0].contains("Rust"));
}
#[test]
fn test_embeddings_have_stable_dimensions_and_scores() {
use sparrow::extras::Embeddings;
let mut emb = Embeddings::with_dimensions(64);
emb.add("local model routing with provider fallback");
emb.add("visual webview polish and code cards");
emb.add("persistent knowledge graph memory");
assert_eq!(emb.embed("provider routing").len(), 64);
assert!(emb.vectors.iter().all(|(_, vector)| vector.len() == 64));
let results = emb.search_scored("cheap model provider routing", 2);
assert_eq!(results.len(), 1);
assert!(results[0].0 > 0.0);
assert!(results[0].1.contains("model routing"));
}
#[test]
fn test_embeddings_persist_and_reload() {
use sparrow::extras::Embeddings;
let temp =
std::env::temp_dir().join(format!("sparrow-embeddings-{}.json", std::process::id()));
let _ = std::fs::remove_file(&temp);
let mut emb = Embeddings::with_dimensions(96);
emb.add_many([
"knowledge graph stores long term project facts",
"browser automation verifies webview interactions",
]);
emb.save_to_path(&temp).unwrap();
let loaded = Embeddings::load_from_path(&temp).unwrap();
assert_eq!(loaded.embed("graph memory").len(), 96);
assert!(loaded.search("project knowledge graph", 1)[0].contains("knowledge graph"));
let _ = std::fs::remove_file(&temp);
}
#[test]
fn test_task_tier_from_str() {
assert!(matches!(TaskTier::from_str("trivial"), TaskTier::Trivial));
assert!(matches!(TaskTier::from_str("HARD"), TaskTier::Hard));
assert!(matches!(TaskTier::from_str("unknown"), TaskTier::Medium));
}
#[test]
fn test_provider_registry_not_empty() {
let providers = sparrow::config::providers::provider_registry();
assert!(!providers.is_empty());
assert!(providers.iter().any(|p| p.id == "ollama"));
assert!(providers.iter().any(|p| p.id == "anthropic"));
assert!(providers.iter().any(|p| p.id == "nvidia"));
assert!(providers.iter().any(|p| p.id == "openai-codex"));
}
#[test]
fn test_provider_registry_models_have_tags() {
let providers = sparrow::config::providers::provider_registry();
for p in &providers {
for m in &p.models {
assert!(!m.tags.is_empty(), "Model {}/{} has no tags", p.id, m.name);
}
}
}
#[test]
fn test_find_provider() {
let found = sparrow::config::providers::find_provider("nvidia");
assert!(found.is_some());
assert_eq!(found.unwrap().label, "NVIDIA NIM");
}
#[test]
fn test_find_model() {
let found = sparrow::config::providers::find_model("anthropic", "claude-sonnet-4-6");
assert!(found.is_some());
assert!(found.unwrap().tags.contains(&"code".to_string()));
}
#[test]
fn test_provider_registry_caps_feed_router() {
let nvidia =
sparrow::config::providers::model_caps("nvidia", "nvidia/nemotron-3-super-120b-a12b");
assert_eq!(nvidia.cost_input_per_mtok, 0.0);
assert!(nvidia.tools);
assert!(nvidia.context_window >= 100_000);
let anthropic = sparrow::config::providers::model_caps("anthropic", "claude-sonnet-4-6");
assert!(anthropic.vision);
assert!(anthropic.cost_input_per_mtok > 0.0);
}
#[test]
fn test_discovery_filters_non_chat_nvidia_models() {
use sparrow::provider::discovery::is_chat_model_id;
assert!(is_chat_model_id("meta/llama-3.1-8b-instruct"));
assert!(is_chat_model_id("deepseek-ai/deepseek-v4-flash"));
assert!(is_chat_model_id("qwen/qwen3-coder-480b-a35b-instruct"));
assert!(!is_chat_model_id("baai/bge-m3"));
assert!(!is_chat_model_id("nvidia/nemotron-parse"));
assert!(!is_chat_model_id("nvidia/nvclip"));
assert!(!is_chat_model_id("nvidia/ai-synthetic-video-detector"));
assert!(!is_chat_model_id(
"nvidia/llama-3.1-nemoguard-8b-content-safety"
));
}
#[test]
fn test_onboarding_providers() {
let providers = sparrow::config::providers::onboarding_providers();
let ids: Vec<&str> = providers.iter().map(|p| p.id.as_str()).collect();
assert!(ids.contains(&"ollama"));
assert!(ids.contains(&"nvidia"));
assert!(ids.contains(&"anthropic"));
}
#[test]
fn test_api_key_not_in_config_view() {
use sparrow::config::ProviderConfig;
let mut cfg = sparrow::config::Config::default();
cfg.providers.insert(
"test".into(),
ProviderConfig {
adapter: "openai-compatible".into(),
base_url: Some("https://example.com".into()),
models: vec!["gpt-5".into()],
api_key_env: Some("sk-real-key-12345".into()),
},
);
let p = cfg.providers.get("test").unwrap();
assert!(p.api_key_env.as_ref().unwrap().contains("sk-"));
}
#[test]
fn test_config_defaults_have_providers_empty() {
let cfg = sparrow::config::Config::default();
assert!(cfg.providers.is_empty());
assert_eq!(cfg.budget.daily_usd, 5.0);
}
#[test]
fn test_checkpoints_rewind() {
use sparrow::autonomy::{Checkpoints, GitCheckpoints};
let tmp = std::env::temp_dir().join("sparrow-m1-checkpoint-test");
let _ = std::fs::remove_dir_all(&tmp);
std::fs::create_dir_all(&tmp).ok();
std::process::Command::new("git")
.args(["init"])
.current_dir(&tmp)
.output()
.ok();
std::process::Command::new("git")
.args(["config", "user.email", "test@sparrow.dev"])
.current_dir(&tmp)
.output()
.ok();
std::process::Command::new("git")
.args(["config", "user.name", "Test"])
.current_dir(&tmp)
.output()
.ok();
std::fs::write(tmp.join("test.txt"), "original").ok();
std::process::Command::new("git")
.args(["add", "test.txt"])
.current_dir(&tmp)
.output()
.ok();
std::process::Command::new("git")
.args(["commit", "-m", "init"])
.current_dir(&tmp)
.output()
.ok();
let cp = GitCheckpoints::new(tmp.clone());
let id = cp
.snapshot("pre-mutation")
.expect("snapshot should succeed");
assert!(!id.0.is_empty());
std::fs::write(tmp.join("test.txt"), "modified").ok();
cp.rewind(id).expect("rewind should succeed");
let content = std::fs::read_to_string(tmp.join("test.txt")).unwrap_or_default();
assert_eq!(
content.trim(),
"original",
"rewind must restore original content"
);
let _ = std::fs::remove_dir_all(&tmp);
}
#[test]
fn test_memory_persistence() {
use sparrow::memory::{Fact, Memory, SqliteMemory};
let tmp = std::env::temp_dir().join("sparrow-m1-memory.db");
let _ = std::fs::remove_file(&tmp);
let mem = SqliteMemory::open(&tmp).expect("open memory");
let fact = Fact {
id: "test-1".into(),
key: "user:language".into(),
value: "Rust".into(),
created_at: "2026-01-01".into(),
updated_at: "2026-01-01".into(),
};
mem.remember(fact.clone()).expect("remember");
drop(mem);
let mem2 = SqliteMemory::open(&tmp).expect("re-open memory");
let facts = mem2.all_facts();
assert_eq!(facts.len(), 1);
assert_eq!(facts[0].key, "user:language");
assert_eq!(facts[0].value, "Rust");
let recalled = mem2.recall("Rust", 5);
assert!(!recalled.is_empty());
let _ = std::fs::remove_file(&tmp);
}
#[test]
fn test_autonomy_matrix_15_combinations() {
use sparrow::autonomy::{AutonomyContract, ProposedAction};
use sparrow::event::{AutonomyLevel, RiskLevel};
let levels = [
AutonomyLevel::Supervised,
AutonomyLevel::Trusted,
AutonomyLevel::Autonomous,
];
let risks = [
RiskLevel::ReadOnly,
RiskLevel::Mutating,
RiskLevel::Exec,
RiskLevel::Destructive,
RiskLevel::Network,
];
for level in &levels {
for risk in &risks {
let contract = match level {
AutonomyLevel::Supervised => AutonomyContract::supervised(),
AutonomyLevel::Trusted => AutonomyContract::trusted(),
AutonomyLevel::Autonomous => AutonomyContract::autonomous(),
};
let action = ProposedAction {
tool_name: "test".into(),
risk: risk.clone(),
args: serde_json::json!({}),
};
let decision = contract.decide(&action);
assert!(matches!(
decision,
sparrow::event::Decision::Allow
| sparrow::event::Decision::AskUser
| sparrow::event::Decision::Deny
));
}
}
}
#[test]
fn test_budget_hard_stop_in_contract() {
use sparrow::autonomy::AutonomyContract;
let contract = AutonomyContract::supervised();
assert!(
contract
.stops
.iter()
.any(|s| matches!(s, sparrow::autonomy::HardStop::BudgetExceeded))
);
assert!(contract.stops.iter().any(|s| matches!(
s,
sparrow::autonomy::HardStop::RiskLevel(sparrow::event::RiskLevel::Destructive)
)));
}
#[test]
fn test_memory_redaction() {
use sparrow::memory::{Fact, Memory, SqliteMemory};
let tmp = std::env::temp_dir().join("sparrow-m1-redact.db");
let _ = std::fs::remove_file(&tmp);
let mem = SqliteMemory::open(&tmp).expect("open memory");
let fact_with_secret = Fact {
id: "secret-1".into(),
key: "token".into(),
value: "sk-ant-api03-secret-key-here".into(),
created_at: "2026-01-01".into(),
updated_at: "2026-01-01".into(),
};
mem.remember(fact_with_secret).expect("remember");
let facts = mem.all_facts();
assert!(!facts.is_empty());
let val = &facts[0].value;
assert!(
!val.contains("sk-ant-api03"),
"Secret should be redacted: {}",
val
);
let _ = std::fs::remove_file(&tmp);
}
#[test]
fn test_file_locks_prevent_collision() {
use sparrow::orchestrator::FileLocks;
let rt = tokio::runtime::Runtime::new().unwrap();
rt.block_on(async {
let locks = FileLocks::new();
assert!(locks.try_lock(&["auth.rs".into()]).await.is_ok());
assert!(locks.try_lock(&["auth.rs".into()]).await.is_err());
assert!(locks.try_lock(&["token.rs".into()]).await.is_ok());
locks.release(&["auth.rs".into()]).await;
assert!(locks.try_lock(&["auth.rs".into()]).await.is_ok());
});
}
#[test]
fn test_swarm_verdict_pass_rework() {
use sparrow::orchestrator::Verdict;
let pass = Verdict::Pass;
let rework = Verdict::Rework {
findings: vec!["missing edge case".into()],
};
assert!(matches!(pass, Verdict::Pass));
if let Verdict::Rework { findings } = rework {
assert_eq!(findings.len(), 1);
} else {
panic!("expected Rework");
}
}
#[test]
fn test_swarm_plan_defaults() {
use sparrow::orchestrator::SwarmPlan;
let plan = SwarmPlan::default();
assert_eq!(plan.max_reworks, 3);
}
#[test]
fn test_orchestrator_events_emitted() {
let config = sparrow::config::Config::default();
let router = Arc::new(sparrow::router::BasicRouter::new(
&config,
std::collections::HashMap::new(),
));
let memory: Arc<dyn sparrow::memory::Memory> = Arc::new(
sparrow::memory::SqliteMemory::open(
&std::env::temp_dir().join("sparrow-m2-swarm-smoke.db"),
)
.unwrap(),
);
let _orchestrator = sparrow::orchestrator::DefaultOrchestrator::new(router, config, memory);
assert!(true);
}
#[test]
fn test_skill_parse_from_markdown() {
use sparrow::capabilities::Skill;
let md = "# Skill: Rust Error Handling\n\n**Trigger:** error, Result, anyhow\n\n## Body\nUse anyhow for app code.";
let skill = Skill::from_markdown(md, "test.skill.md");
assert!(skill.is_some());
let s = skill.unwrap();
assert_eq!(s.name, "Rust Error Handling");
assert!(s.trigger.contains(&"error".to_string()));
assert!(s.trigger.contains(&"result".to_string()));
assert!(s.body.contains("anyhow"));
}
#[test]
fn test_skill_relevance_scoring() {
use sparrow::capabilities::Skill;
let skill = Skill {
name: "test".into(),
description: "desc".into(),
trigger: vec!["rust".into(), "error".into(), "handling".into()],
body: "test body".into(),
source_file: "".into(),
usage_count: 0,
created_at: "".into(),
score: 0.5,
auto_generated: false,
references: Vec::new(),
templates: Vec::new(),
scripts: Vec::new(),
assets: Vec::new(),
};
let score = skill.relevance("I need help with Rust error handling in my code");
assert!(score > 0.0, "Should be relevant for matching context");
let score2 = skill.relevance("cooking recipes");
assert_eq!(score2, 0.0, "Should not match unrelated context");
}
#[test]
fn test_skill_markdown_roundtrip() {
use sparrow::capabilities::Skill;
let original = Skill {
name: "TestSkill".into(),
description: "A test skill".into(),
trigger: vec!["test".into(), "demo".into()],
body: "This is the body".into(),
source_file: "test.skill.md".into(),
usage_count: 2,
created_at: "2026-01-01".into(),
score: 0.7,
auto_generated: true,
references: Vec::new(),
templates: Vec::new(),
scripts: Vec::new(),
assets: Vec::new(),
};
let md = original.to_markdown();
let parsed = Skill::from_markdown(&md, "test.skill.md");
assert!(parsed.is_some());
assert_eq!(parsed.unwrap().name, "TestSkill");
}
#[test]
fn test_curator_propose_skill() {
use sparrow::capabilities::Curator;
let candidate = Curator::propose_skill(
"write unit tests for the error handling module",
"added tests and fixed the failing assertions",
);
assert!(candidate.is_some());
let skill = candidate.unwrap();
assert!(skill.auto_generated);
assert_eq!(skill.name, "write-and-fix-tests");
assert!(!skill.trigger.is_empty());
}
#[test]
fn test_fs_skill_library_add_and_get() {
use sparrow::capabilities::{FsSkillLibrary, Skill, SkillLibrary};
let tmp = std::env::temp_dir().join("sparrow-m3-skills-test");
let _ = std::fs::remove_dir_all(&tmp);
let lib = FsSkillLibrary::new(tmp.clone());
let skill = Skill {
name: "test-skill".into(),
description: "test".into(),
trigger: vec!["test".into()],
body: "body".into(),
source_file: "test".into(),
usage_count: 0,
created_at: "2026-01-01".into(),
score: 0.5,
auto_generated: true,
references: Vec::new(),
templates: Vec::new(),
scripts: Vec::new(),
assets: Vec::new(),
};
lib.add(skill).expect("add skill");
let found = lib.get("test-skill");
assert!(found.is_some());
assert_eq!(found.unwrap().body, "body");
let _ = std::fs::remove_dir_all(&tmp);
}
#[test]
fn test_mcp_client_server_config() {
use sparrow::capabilities::mcp::{BasicMcpClient, McpClient, McpServer, Transport};
let tmp = std::env::temp_dir().join("sparrow-m3-mcp-test");
let _ = std::fs::remove_dir_all(&tmp);
std::fs::create_dir_all(&tmp).ok();
let client = BasicMcpClient::new(tmp.clone());
let server = McpServer {
name: "test-server".into(),
transport: Transport::Stdio,
command: Some("echo".into()),
args: vec!["hello".into()],
url: None,
env: Default::default(),
allow_tools: vec![],
};
client.add_server(server).expect("add server");
let found = client.get_server("test-server");
assert!(found.is_some());
let rt = tokio::runtime::Runtime::new().unwrap();
let servers = rt.block_on(async { client.list_servers().await });
assert_eq!(servers.len(), 1);
client.remove_server("test-server").expect("remove");
let servers = rt.block_on(async { client.list_servers().await });
assert_eq!(servers.len(), 0);
let _ = std::fs::remove_dir_all(&tmp);
}
#[test]
fn test_scheduler_job_persistence() {
use sparrow::runtime::scheduler::{Job, MemoryScheduler, Scheduler};
let scheduler = MemoryScheduler::new();
let job = Job::new("test task".into(), "0 */6 * * *".into());
let id = scheduler.schedule(job).expect("schedule");
let jobs = scheduler.list();
assert_eq!(jobs.len(), 1);
assert_eq!(jobs[0].task, "test task");
scheduler.cancel(&id).expect("cancel");
assert_eq!(scheduler.list().len(), 0);
}
#[test]
fn test_recorder_transcript_roundtrip() {
use sparrow::event::{Event, OutcomeSummary, RunId, TokenUsage};
use sparrow::runtime::recorder::{FsRecorder, Recorder, Replayer, RunInputs};
let tmp = std::env::temp_dir().join("sparrow-m4-transcript-test");
let _ = std::fs::remove_dir_all(&tmp);
let recorder = FsRecorder::new(tmp.clone());
let run_id = "test-run-1".to_string();
recorder.start_run(
run_id.clone(),
RunInputs {
task: "test".into(),
config_snapshot: serde_json::json!({}),
model_id: "test-model".into(),
repo_head: None,
timestamp: "2026-01-01".into(),
agent: "test-agent".into(),
},
);
recorder.record(&Event::RunStarted {
run: RunId(run_id.clone()),
task: "test".into(),
agent: "test-agent".into(),
});
recorder.record(&Event::ThinkingDelta {
run: RunId(run_id.clone()),
text: "thinking...".into(),
});
recorder.record(&Event::RunFinished {
run: RunId(run_id.clone()),
outcome: OutcomeSummary {
status: "completed".into(),
diffs: vec![],
cost_usd: 0.0,
tokens: TokenUsage {
input: 100,
output: 50,
},
},
});
let transcript = recorder.finalize(&run_id).expect("finalize");
assert_eq!(transcript.events.len(), 3);
let loaded = recorder.load(&run_id);
assert!(loaded.is_some());
assert_eq!(loaded.unwrap().events.len(), 3);
let transcripts = recorder.list_transcripts();
assert!(transcripts.contains(&run_id));
let _ = std::fs::remove_dir_all(&tmp);
}
#[test]
fn test_golden_replay_deterministic() {
use sparrow::event::{Event, OutcomeSummary, RunId, TokenUsage};
let events = vec![
Event::RunStarted {
run: RunId("g1".into()),
task: "t1".into(),
agent: "a1".into(),
},
Event::ThinkingDelta {
run: RunId("g1".into()),
text: "hello".into(),
},
Event::RunFinished {
run: RunId("g1".into()),
outcome: OutcomeSummary {
status: "ok".into(),
diffs: vec![],
cost_usd: 0.0,
tokens: TokenUsage {
input: 10,
output: 5,
},
},
},
];
let jsonl: String = events
.iter()
.map(|e| serde_json::to_string(e).unwrap())
.collect::<Vec<_>>()
.join("\n");
let jsonl = jsonl + "\n";
let parsed: Vec<Event> = jsonl
.lines()
.filter(|l| !l.is_empty())
.filter_map(|l| serde_json::from_str(l).ok())
.collect();
assert_eq!(parsed.len(), 3, "Deterministic: 3 events roundtrip");
if let Event::RunStarted { task, .. } = &parsed[0] {
assert_eq!(task, "t1");
} else {
panic!("Wrong event type");
}
if let Event::ThinkingDelta { text, .. } = &parsed[1] {
assert_eq!(text, "hello");
} else {
panic!("Wrong event type");
}
if let Event::RunFinished { outcome, .. } = &parsed[2] {
assert_eq!(outcome.status, "ok");
} else {
panic!("Wrong event type");
}
}
#[test]
fn test_gateway_message_router_commands() {
use sparrow::gateway::GatewayMessage;
let msg = GatewayMessage {
surface: "telegram".into(),
user_id: "123".into(),
chat_id: "456".into(),
text: "/help".into(),
message_id: None,
};
assert_eq!(msg.surface, "telegram");
assert_eq!(msg.text, "/help");
}
#[test]
fn test_gateway_event_formatting() {
use sparrow::event::{Event, RunId};
use sparrow::gateway::format_event;
let event = Event::RunStarted {
run: RunId("test".into()),
task: "fix auth with key sk-ant-secret".into(),
agent: "sparrow".into(),
};
let formatted = format_event(&event);
assert!(formatted.is_some());
let text = formatted.unwrap();
assert!(text.contains("fix auth"));
}
#[test]
fn test_local_model_404_is_presented_as_fallback_not_raw_error() {
use sparrow::event::{Event, RunId};
use sparrow::gateway::format_event;
let reason = r#"Ollama API error 404: {"error":"model 'qwen3.5:32b' not found"}"#;
assert_eq!(
sparrow::event::friendly_model_switch_reason(reason),
"modèle local indisponible"
);
assert!(sparrow::event::is_local_model_unavailable(reason));
let event = Event::ModelSwitched {
run: RunId("test".into()),
from: "qwen3.5:32b".into(),
to: "nvidia/nemotron-3-super-120b-a12b".into(),
reason: reason.into(),
};
let formatted = format_event(&event).unwrap();
assert!(formatted.contains("modèle local indisponible"));
assert!(!formatted.contains("Ollama API error 404"));
}
#[test]
fn test_gateway_response_buttons() {
use sparrow::gateway::GatewayResponse;
let resp = GatewayResponse {
surface: "telegram".into(),
chat_id: "123".into(),
text: "Approve?".into(),
reply_to: None,
buttons: vec![vec!["/approve".into(), "/deny".into()]],
};
assert_eq!(resp.buttons.len(), 1);
assert_eq!(resp.buttons[0].len(), 2);
assert_eq!(resp.buttons[0][0], "/approve");
}
#[test]
fn test_session_bridge_continuity() {
use sparrow::tools::extras::SessionBridge;
let rt = tokio::runtime::Runtime::new().unwrap();
rt.block_on(async {
let bridge = SessionBridge::new();
assert!(!bridge.session_id.is_empty());
bridge.set_surface("telegram").await;
bridge
.add_approval(sparrow::gateway::GatewayResponse {
surface: "telegram".into(),
chat_id: "123".into(),
text: "Approve?".into(),
reply_to: None,
buttons: vec![],
})
.await;
let approvals = bridge.drain_approvals().await;
assert_eq!(approvals.len(), 1);
});
}
#[test]
fn test_user_mode_default_beginner() {
use sparrow::onboarding::UserMode;
assert_eq!(UserMode::default(), UserMode::Beginner);
}
#[test]
fn test_lessons_count() {
use sparrow::onboarding::Onboarding;
let ob = Onboarding::default();
assert_eq!(ob.lessons.len(), 5);
assert_eq!(ob.lessons[0].id, "run");
assert_eq!(ob.lessons[4].id, "gateway");
}
#[test]
fn test_friendly_error_no_provider() {
let msg = sparrow::onboarding::Onboarding::friendly_error("no_provider", "nvidia");
assert!(msg.contains("sparrow auth add"));
assert!(msg.contains("NVIDIA_API_KEY"));
}
#[test]
fn test_examples_gallery() {
let examples = sparrow::onboarding::Onboarding::examples_gallery();
assert!(!examples.is_empty());
assert!(examples.iter().any(|(cmd, _)| cmd.contains("run")));
}
#[test]
fn test_migration_detect_installed_does_not_panic() {
let found = sparrow::onboarding::migration::Migration::detect_installed();
assert!(found.len() <= 5);
}
#[test]
fn test_migration_openclaw_result_fields() {
let result = sparrow::onboarding::migration::MigrationResult {
tool: "openclaw".into(),
agents: 3,
skills: 5,
cron_jobs: 2,
config_entries: 10,
surfaces: 1,
};
assert_eq!(result.agents, 3);
assert_eq!(result.skills, 5);
}
#[test]
fn test_org_policy_enforces_autonomy_ceiling() {
use sparrow::onboarding::enterprise::OrgPolicy;
let policy = OrgPolicy::default();
let result = policy.enforce(
&sparrow::event::AutonomyLevel::Autonomous,
1.0,
"src/main.rs",
);
assert!(result.is_err());
assert!(result.unwrap_err().contains("limits autonomy"));
}
#[test]
fn test_org_policy_blocks_protected_paths() {
use sparrow::onboarding::enterprise::OrgPolicy;
let policy = OrgPolicy::default();
let result = policy.enforce(&sparrow::event::AutonomyLevel::Supervised, 0.0, ".env");
assert!(result.is_err());
assert!(result.unwrap_err().contains("protected"));
}
#[test]
fn test_org_policy_allows_safe_paths() {
use sparrow::onboarding::enterprise::OrgPolicy;
let policy = OrgPolicy::default();
let result = policy.enforce(
&sparrow::event::AutonomyLevel::Supervised,
0.0,
"src/lib.rs",
);
assert!(result.is_ok());
}
#[test]
fn test_audit_export_json() {
use sparrow::onboarding::enterprise::{AuditEntry, export_audit_log};
let entries = vec![AuditEntry {
timestamp: "2026-01-01".into(),
user: "alice".into(),
action: "run".into(),
run_id: "r1".into(),
cost_usd: 0.01,
tokens: 500,
autonomy: "supervised".into(),
status: "completed".into(),
}];
let json = export_audit_log(&entries, "json");
assert!(json.contains("alice"));
assert!(json.contains("r1"));
}
#[test]
fn test_crash_recovery_transcript_persistence() {
use sparrow::event::{Event, RunId};
use sparrow::runtime::recorder::{FsRecorder, Recorder, Replayer, RunInputs};
let tmp = std::env::temp_dir().join("sparrow-ws16-crash");
let _ = std::fs::remove_dir_all(&tmp);
let recorder = FsRecorder::new(tmp.clone());
let rid = "crash-test-1".to_string();
recorder.start_run(
rid.clone(),
RunInputs {
task: "test".into(),
config_snapshot: serde_json::json!({}),
model_id: "test".into(),
repo_head: None,
timestamp: "2026-01-01".into(),
agent: "test".into(),
},
);
let event = Event::RunStarted {
run: RunId(rid.clone()),
task: "test".into(),
agent: "test".into(),
};
recorder.record(&event);
let transcript = recorder.finalize(&rid).expect("finalize after crash");
assert!(
!transcript.events.is_empty(),
"Partial transcript saved despite crash"
);
let loaded = recorder.load(&rid);
assert!(
loaded.is_some(),
"Transcript recoverable after simulated crash"
);
let _ = std::fs::remove_dir_all(&tmp);
}
#[test]
fn test_anti_simulation_guard_detects_fabrication() {
use sparrow::provider::{ContentBlock, Msg};
use sparrow::reasoning::AntiSimulationGuard;
let messages = vec![Msg {
role: "assistant".into(),
content: vec![ContentBlock::Text {
text: "All tests pass and the build succeeds.".into(),
}],
}];
let violation = AntiSimulationGuard::check(&messages, false);
assert!(
violation.is_some(),
"Guard must detect fabricated test claims"
);
}
#[test]
fn test_hallucination_guard_detects_code_claim() {
use sparrow::reasoning::AntiSimulationGuard;
let violation = AntiSimulationGuard::check_code_claim(
"the validate() function exists in auth.rs and returns a bool",
false,
false,
);
assert!(
violation.is_some(),
"Guard must detect unverified code claims"
);
}
#[test]
fn test_hallucination_guard_allows_verified_claim() {
use sparrow::reasoning::AntiSimulationGuard;
let violation = AntiSimulationGuard::check_code_claim(
"the validate() function exists in auth.rs",
true,
false, );
assert!(
violation.is_none(),
"Guard must allow claims backed by fs_read"
);
}
#[test]
fn test_config_fuzzing_roundtrip() {
let cfg = sparrow::config::Config::default();
let serialized = toml::to_string_pretty(&cfg).expect("serialize");
let deserialized: sparrow::config::Config =
toml::from_str(&serialized).expect("deserialize");
assert_eq!(deserialized.budget.daily_usd, cfg.budget.daily_usd);
}
#[test]
fn test_provider_json_fuzzing() {
let providers = sparrow::config::providers::provider_registry();
assert!(!providers.is_empty());
for p in &providers {
assert!(!p.id.is_empty(), "Provider {} has empty id", p.label);
assert!(!p.adapter.is_empty(), "Provider {} has empty adapter", p.id);
for m in &p.models {
assert!(
!m.name.is_empty(),
"Model {}/{} has empty name",
p.id,
m.label
);
assert!(
m.context_window > 0,
"Model {}/{} has zero context window",
p.id,
m.name
);
}
}
}
#[test]
fn test_no_hardcoded_secrets_in_source() {
let providers = sparrow::config::providers::provider_registry();
for p in &providers {
if let Some(env) = &p.api_key_env {
assert!(
!env.contains("sk-"),
"Hardcoded secret in provider {}",
p.id
);
assert!(
!env.contains("nvapi-"),
"Hardcoded secret in provider {}",
p.id
);
}
}
}
#[test]
fn test_redaction_covers_all_secret_patterns() {
let filter = sparrow::redaction::RedactionFilter::new();
let test_cases = vec![
("sk-ant-api03-abc123def456", true),
("nvapi-xyz789ghi012jkl345", true),
("ghp_1234567890abcdefgh", true),
("hf_abcdefghijklmnopqrst", true),
("normal text without secrets", false),
("cargo build --release", false),
];
for (input, should_contain_secret) in test_cases {
let has = filter.contains_secret(input);
assert_eq!(has, should_contain_secret, "Failed for: {}", input);
}
}
#[test]
fn test_redaction_replaces_secrets_in_text() {
let mut filter = sparrow::redaction::RedactionFilter::new();
filter.load_secrets(vec!["my-api-key-12345".into()]);
let input = "Authorization: Bearer my-api-key-12345";
let output = filter.redact_str(input);
assert!(!output.contains("my-api-key-12345"));
assert!(output.contains("[REDACTED]"));
}
#[test]
fn test_engine_budget_stop() {
let mut config = sparrow::config::Config::default();
config.budget.session_usd = 0.0; let router = Arc::new(sparrow::router::BasicRouter::new(
&config,
std::collections::HashMap::new(),
));
let _engine = sparrow::engine::Engine::new(router, config);
assert!(true);
}
#[test]
fn test_engine_identity_default() {
let identity = sparrow::engine::Identity::default();
assert_eq!(identity.name, "sparrow");
assert!(!identity.personality.is_empty());
}
#[test]
fn test_engine_summarizes_long_model_chain() {
let chain = vec![
"meta/llama-3.1-8b-instruct".to_string(),
"stepfun-ai/step-3.5-flash".to_string(),
"nvidia/nemotron-3-super-120b-a12b".to_string(),
"deepseek-ai/deepseek-v4-pro".to_string(),
];
let summary = sparrow::engine::summarize_model_chain(&chain, 2);
assert_eq!(
summary,
"meta/llama-3.1-8b-instruct -> stepfun-ai/step-3.5-flash -> +2 autres fallbacks"
);
}
#[test]
fn test_repo_map_scans_current_dir() {
let map = sparrow::memory::RepoMap::scan(&std::path::PathBuf::from("src"));
assert!(
!map.files.is_empty(),
"Should find at least some files in src/"
);
}
#[test]
fn test_context_manager_compaction_preserves_last() {
let cm = sparrow::redaction::ContextManager::new(1000);
let messages = (0..10)
.map(|i| sparrow::provider::Msg {
role: if i % 2 == 0 { "user" } else { "assistant" }.into(),
content: vec![sparrow::provider::ContentBlock::Text {
text: format!("msg {}", i),
}],
})
.collect::<Vec<_>>();
let compacted = cm.compact_messages(&messages, 100, 2);
assert!(compacted.len() <= 6); }
#[test]
fn test_token_estimation() {
let cm = sparrow::redaction::ContextManager::new(128_000);
let tokens = cm.estimate_tokens("hello world");
assert!(tokens > 0);
assert!(tokens < 10);
}
#[test]
fn test_pipeline_config_serialization() {
let pipeline = sparrow::extras::PipelineConfig::default_pipeline();
let toml_str = pipeline.to_toml().expect("serialize");
let parsed = sparrow::extras::PipelineConfig::from_toml(&toml_str).expect("deserialize");
assert_eq!(parsed.name, pipeline.name);
}
#[test]
fn test_regression_git_commit_uses_f_flag() {
assert!(true); }
#[test]
fn test_regression_tokio_sleep_in_sandbox() {
use std::fs;
let src = fs::read_to_string("src/sandbox/mod.rs").unwrap();
assert!(
!src.contains("std::thread::sleep"),
"No std::thread::sleep in sandbox"
);
}
#[test]
fn test_regression_todo_persistence() {
use sparrow::tools::{Tool, ToolCtx};
let rt = tokio::runtime::Runtime::new().unwrap();
rt.block_on(async {
let todo = sparrow::tools::todo::Todo::new();
let ctx = ToolCtx {
workspace_root: std::env::temp_dir(),
run_id: sparrow::event::RunId("test-todo".into()),
};
let r1 = todo
.call(
serde_json::json!({"action":"create","content":"task 1"}),
&ctx,
)
.await
.unwrap();
let r2 = todo
.call(
serde_json::json!({"action":"create","content":"task 2"}),
&ctx,
)
.await
.unwrap();
assert!(!r1.is_error);
assert!(!r2.is_error);
let list = todo
.call(serde_json::json!({"action":"list"}), &ctx)
.await
.unwrap();
assert!(!list.is_error);
let _ = todo
.call(serde_json::json!({"action":"clear_completed"}), &ctx)
.await;
});
}
#[test]
fn test_regression_cron_parser_full_expression() {
use sparrow::runtime::scheduler::Job;
let job = Job::new("test".into(), "0 */6 * * *".into());
let _ = job.next_schedule(); }
#[test]
fn test_regression_context_compaction_dynamic() {
let cm = sparrow::redaction::ContextManager::new(5000);
let messages = vec![
sparrow::provider::Msg {
role: "user".into(),
content: vec![sparrow::provider::ContentBlock::Text {
text: "read src/main.rs".into(),
}],
},
sparrow::provider::Msg {
role: "assistant".into(),
content: vec![sparrow::provider::ContentBlock::Text {
text: "I used fs_read to check src/main.rs".into(),
}],
},
sparrow::provider::Msg {
role: "user".into(),
content: vec![sparrow::provider::ContentBlock::Text {
text: "now edit src/lib.rs".into(),
}],
},
];
let compacted = cm.compact_messages(&messages, 50, 1);
assert!(!compacted.is_empty());
let has_summary = compacted.iter().any(|m| {
m.content.iter().any(|b| matches!(b, sparrow::provider::ContentBlock::Text { text } if text.contains("summarized")))
});
assert!(has_summary, "Compaction must produce a summary message");
}
#[test]
fn test_regression_mcp_real_execution() {
assert!(true); }
#[test]
fn prop_cron_expressions_dont_panic() {
let mut rt = proptest::test_runner::TestRunner::new(Default::default());
rt.run(
&(0u32..59, 0u32..23, 1u32..31, 1u32..12, 0u32..7),
|(min, hour, day, month, wday)| {
let expr = format!("{} {} {} {} {}", min, hour, day, month, wday);
let job = sparrow::runtime::scheduler::Job::new("prop-test".into(), expr);
let _ = job.next_schedule(); Ok(())
},
)
.unwrap();
}
#[test]
fn prop_config_roundtrip_doesnt_panic() {
use proptest::prelude::*;
let mut rt = proptest::test_runner::TestRunner::new(Default::default());
rt.run(
&(any::<String>(), any::<f64>(), any::<f64>()),
|(theme, daily, session)| {
let cfg = sparrow::config::Config {
theme,
budget: sparrow::config::Budget {
daily_usd: daily.max(0.0),
session_usd: session.max(0.0),
},
..Default::default()
};
let serialized = toml::to_string_pretty(&cfg).unwrap();
let _: sparrow::config::Config = toml::from_str(&serialized).unwrap();
Ok(())
},
)
.unwrap();
}
#[test]
fn prop_search_query_doesnt_panic() {
use proptest::prelude::*;
let mut rt = proptest::test_runner::TestRunner::new(Default::default());
rt.run(&(any::<String>(), 1usize..100), |(query, k)| {
use sparrow::memory::Memory;
let tmp = std::env::temp_dir().join("sparrow-prop-search.db");
let _ = std::fs::remove_file(&tmp);
let mem = sparrow::memory::SqliteMemory::open(&tmp).unwrap();
let _ = mem.recall(&query, k); let _ = std::fs::remove_file(&tmp);
Ok(())
})
.unwrap();
}
}