use std::sync::Arc;
use clawft_kernel::boot::{Kernel, KernelState};
use clawft_platform::NativePlatform;
use clawft_types::config::{AgentDefaults, AgentsConfig, Config, KernelConfig, DEFAULT_BRAND};
fn base_config() -> Config {
Config {
agents: AgentsConfig {
defaults: AgentDefaults {
workspace: "~/.clawft/workspace".into(),
model: "test/model".into(),
max_tokens: 1024,
temperature: 0.5,
max_tool_iterations: 5,
memory_window: 10,
},
..AgentsConfig::default()
},
..Config::default()
}
}
fn minimal_kernel_config() -> KernelConfig {
KernelConfig {
governance: Default::default(),
profile: None,
shared_services: Default::default(),
enabled: true,
max_processes: 16,
health_check_interval_secs: 5,
cluster: None,
chain: Some(clawft_types::config::ChainConfig::isolated_in(
&tempfile::tempdir().unwrap().keep(),
)),
resource_tree: None,
vector: None,
spatial: None,
profiles: None,
pairing: None,
mesh: None,
anchor: None,
ipc_tcp: None,
llm: None,
agent: None,
brand: DEFAULT_BRAND.to_string(),
}
}
#[cfg(feature = "exochain")]
fn exochain_kernel_config() -> KernelConfig {
use clawft_types::config::{ChainConfig, ResourceTreeConfig};
KernelConfig {
governance: Default::default(),
profile: None,
shared_services: Default::default(),
enabled: true,
max_processes: 32,
health_check_interval_secs: 5,
cluster: None,
chain: Some(ChainConfig {
enabled: true,
checkpoint_interval: 10_000,
chain_id: 0,
checkpoint_path: Some(
tempfile::tempdir().unwrap().keep().join("chain.json").to_string_lossy().into_owned(),
),
external_anchor: None,
}),
resource_tree: Some(ResourceTreeConfig {
enabled: true,
checkpoint_path: None,
}),
vector: None,
spatial: None,
profiles: None,
pairing: None,
mesh: None,
anchor: None,
ipc_tcp: None,
llm: None,
agent: None,
brand: DEFAULT_BRAND.to_string(),
}
}
#[tokio::test]
async fn feature_comp_native_boots() {
let platform = Arc::new(NativePlatform::new());
let mut kernel = Kernel::boot(base_config(), minimal_kernel_config(), platform)
.await
.unwrap();
assert_eq!(*kernel.state(), KernelState::Running);
assert!(
!kernel.process_table().is_empty(),
"kernel process must exist"
);
assert!(kernel.services().len() >= 2, "cron + containers at minimum");
kernel.shutdown().await.unwrap();
assert_eq!(*kernel.state(), KernelState::Halted);
}
#[cfg(feature = "exochain")]
#[tokio::test]
async fn feature_comp_native_exochain_boots() {
let platform = Arc::new(NativePlatform::new());
let mut kernel = Kernel::boot(base_config(), exochain_kernel_config(), platform)
.await
.unwrap();
assert_eq!(*kernel.state(), KernelState::Running);
assert!(
kernel.chain_manager().is_some(),
"chain manager required with exochain"
);
assert!(
kernel.tree_manager().is_some(),
"tree manager required with exochain"
);
assert!(
kernel.governance_gate().is_some(),
"governance gate required with chain"
);
kernel.shutdown().await.unwrap();
assert_eq!(*kernel.state(), KernelState::Halted);
}
#[cfg(feature = "ecc")]
#[tokio::test]
async fn feature_comp_native_ecc_boots() {
let platform = Arc::new(NativePlatform::new());
let mut kernel = Kernel::boot(base_config(), minimal_kernel_config(), platform)
.await
.unwrap();
assert_eq!(*kernel.state(), KernelState::Running);
assert!(kernel.ecc_hnsw().is_some(), "HNSW required with ecc");
assert!(
kernel.ecc_tick().is_some(),
"cognitive tick required with ecc"
);
assert!(
kernel.ecc_causal().is_some(),
"causal graph required with ecc"
);
assert!(
kernel.ecc_crossrefs().is_some(),
"crossref store required with ecc"
);
assert!(
kernel.ecc_impulses().is_some(),
"impulse queue required with ecc"
);
assert!(
kernel.ecc_calibration().is_some(),
"calibration required with ecc"
);
kernel.shutdown().await.unwrap();
}
#[cfg(all(feature = "exochain", feature = "ecc"))]
#[tokio::test]
async fn feature_comp_exochain_ecc_boots() {
let platform = Arc::new(NativePlatform::new());
let mut kernel = Kernel::boot(base_config(), exochain_kernel_config(), platform)
.await
.unwrap();
assert_eq!(*kernel.state(), KernelState::Running);
assert!(kernel.chain_manager().is_some());
assert!(kernel.tree_manager().is_some());
assert!(kernel.ecc_hnsw().is_some());
assert!(kernel.ecc_tick().is_some());
assert!(kernel.governance_gate().is_some());
let chain = kernel.chain_manager().unwrap();
let events = chain.tail(50);
let ecc_events: Vec<_> = events
.iter()
.filter(|e| e.kind.starts_with("ecc."))
.collect();
assert!(
!ecc_events.is_empty(),
"ECC calibration should be logged to chain"
);
kernel.shutdown().await.unwrap();
}
#[tokio::test]
async fn feature_comp_no_service_name_conflicts() {
let platform = Arc::new(NativePlatform::new());
let kernel = Kernel::boot(base_config(), minimal_kernel_config(), platform)
.await
.unwrap();
let services = kernel.services().list();
let mut names: Vec<&str> = services.iter().map(|(n, _)| n.as_str()).collect();
let total = names.len();
names.sort();
names.dedup();
assert_eq!(
names.len(),
total,
"service names must be unique -- duplicate detected"
);
}
#[tokio::test]
async fn feature_comp_ipc_message_routing() {
use clawft_kernel::ipc::{KernelMessage, MessagePayload, MessageTarget};
let platform = Arc::new(NativePlatform::new());
let kernel = Kernel::boot(base_config(), minimal_kernel_config(), platform)
.await
.unwrap();
let a2a = kernel.a2a_router();
let mut inbox = a2a.create_inbox(0);
use clawft_kernel::capability::AgentCapabilities;
use clawft_kernel::process::{ProcessEntry, ProcessState, ResourceUsage};
let agent_entry = ProcessEntry {
pid: 0, agent_id: "ipc-test-agent".into(),
state: ProcessState::Running,
capabilities: AgentCapabilities::default(),
resource_usage: ResourceUsage::default(),
cancel_token: tokio_util::sync::CancellationToken::new(),
parent_pid: None,
};
let agent_pid = kernel.process_table().insert(agent_entry).unwrap();
let _agent_inbox = a2a.create_inbox(agent_pid);
let msg = KernelMessage::new(
agent_pid,
MessageTarget::Process(0),
MessagePayload::Json(serde_json::json!({"test": "hello"})),
);
a2a.send(msg).await.unwrap();
let received = tokio::time::timeout(std::time::Duration::from_secs(1), inbox.recv())
.await
.unwrap()
.unwrap();
if let MessagePayload::Json(v) = &received.payload {
assert_eq!(v["test"], "hello");
} else {
panic!("expected JSON payload");
}
}
#[tokio::test]
async fn feature_comp_process_table_isolated() {
let platform = Arc::new(NativePlatform::new());
let kernel = Kernel::boot(base_config(), minimal_kernel_config(), platform)
.await
.unwrap();
let processes = kernel.process_table().list();
assert_eq!(processes.len(), 1);
assert_eq!(processes[0].pid, 0);
assert_eq!(processes[0].agent_id, "kernel");
}
#[tokio::test]
async fn feature_comp_cluster_membership_initialized() {
let platform = Arc::new(NativePlatform::new());
let kernel = Kernel::boot(base_config(), minimal_kernel_config(), platform)
.await
.unwrap();
let cm = kernel.cluster_membership();
assert!(!cm.local_node_id().is_empty(), "node ID must be set");
}
#[cfg(all(feature = "exochain", feature = "ecc"))]
#[tokio::test]
async fn feature_comp_ecc_namespaces_in_tree() {
let platform = Arc::new(NativePlatform::new());
let kernel = Kernel::boot(base_config(), exochain_kernel_config(), platform)
.await
.unwrap();
let tree = kernel.tree_manager().unwrap();
let stats = tree.stats();
assert!(
stats.node_count > 10,
"tree should have >10 nodes with ECC namespaces, got {}",
stats.node_count
);
}
#[tokio::test]
async fn feature_comp_shutdown_cleans_spawned_agents() {
let platform = Arc::new(NativePlatform::new());
let mut kernel = Kernel::boot(base_config(), minimal_kernel_config(), platform)
.await
.unwrap();
let spawn_result = kernel
.supervisor()
.spawn(clawft_kernel::supervisor::SpawnRequest {
agent_id: "cleanup-test-agent".into(),
capabilities: None,
parent_pid: None,
env: std::collections::HashMap::new(),
backend: None,
});
assert!(spawn_result.is_ok());
kernel.shutdown().await.unwrap();
assert_eq!(*kernel.state(), KernelState::Halted);
let processes = kernel.process_table().list();
for p in &processes {
if p.pid != 0 {
assert!(
matches!(p.state, clawft_kernel::ProcessState::Exited(_)),
"PID {} should be Exited after shutdown, got {:?}",
p.pid,
p.state
);
}
}
}