use std::error::Error;
use std::sync::Arc;
use haematite::{Database, DatabaseConfig, EventStore};
use liminal::channel::{ChannelMode, InboxInstall};
use liminal::durability::bridge::block_on;
use liminal::durability::{
DurableStore, HaematiteStore, MessageEnvelope as DurableEnvelope, StoredEntry,
};
use liminal::protocol::{CausalContext, MessageEnvelope, SchemaId};
use liminal_protocol::reason_code::{CHANNEL_NOT_REGISTERED_CODE, CHANNEL_QUIESCED_CODE};
use tempfile::TempDir;
use super::super::channel_registry::{
ChannelAccessError, ChannelOrigin, ChannelRegistration, ChannelRegistryError, ChannelState,
ChannelStatus, Registered,
};
use super::{ConnectionServices, LiminalConnectionServices};
use crate::config::types::{ChannelDef, LimitsConfig, ServerConfig};
#[test]
fn runtime_registered_channels_are_absent_after_restart() -> Result<(), Box<dyn Error>> {
let (store, _dir) = disk_store()?;
let config = config_with(vec![boot_channel("boot", false)], Some(4));
let first_payload = br#"{"order":1}"#.to_vec();
{
let services =
LiminalConnectionServices::from_config_with_store(&config, Arc::clone(&store))?;
assert_eq!(
services.register_channel(®istration("runtime", true))?,
Registered::Created
);
services.publish("boot", &envelope(br#"{"boot":1}"#.to_vec()), None)?;
services.publish("runtime", &envelope(first_payload.clone()), None)?;
services.flush_durable_state()?;
}
let restarted = LiminalConnectionServices::from_config_with_store(&config, Arc::clone(&store))?;
let boot_status = restarted.channel_status("boot")?;
assert!(
matches!(
boot_status,
ChannelStatus::Active {
origin: ChannelOrigin::BootConfigured,
mode: ChannelMode::Ephemeral,
..
}
),
"the boot channel is rebuilt from the config file and keeps its origin, got {boot_status:?}"
);
assert_eq!(
restarted.channel_status("runtime")?,
ChannelStatus::NotRegistered,
"a runtime-registered channel is simply absent after a restart"
);
let refusal = restarted
.admit_channel("runtime")
.err()
.ok_or("a publish to an absent channel must be refused")?;
assert!(
matches!(&refusal, ChannelAccessError::NotRegistered { name } if name == "runtime"),
"got {refusal:?}"
);
assert_eq!(refusal.reason_code(), CHANNEL_NOT_REGISTERED_CODE);
assert!(
restarted
.publish("runtime", &envelope(br#"{"order":2}"#.to_vec()), None)
.is_err(),
"the ordinary publish path refuses the absent name too"
);
assert_eq!(
restarted.register_channel(®istration("runtime", true))?,
Registered::Created,
"re-registering after a restart CREATES: the roster really was empty of it"
);
let second_payload = br#"{"order":3}"#.to_vec();
restarted.publish("runtime", &envelope(second_payload.clone()), None)?;
restarted.flush_durable_state()?;
assert_eq!(
read_payloads(store.as_ref(), "runtime:0")?,
vec![first_payload, second_payload],
"a re-registered durable channel resumes its log rather than restarting it"
);
Ok(())
}
#[test]
fn quiesce_admits_a_subscribe_that_read_active() -> Result<(), Box<dyn Error>> {
let (store, _dir) = disk_store()?;
let services = LiminalConnectionServices::from_config_with_store(
&config_with(Vec::new(), Some(4)),
store,
)?;
assert_eq!(
services.register_channel(®istration("orders", false))?,
Registered::Created
);
let admitted = services
.admit_channel("orders")
.map_err(|error| format!("an active channel must admit: {error}"))?;
services.quiesce_channel("orders", "archived")?;
assert_eq!(
services.channel_status("orders")?,
ChannelStatus::Quiesced {
reason: "archived".to_owned(),
origin: ChannelOrigin::RuntimeRegistered,
mode: ChannelMode::Ephemeral,
}
);
let subscription = admitted.handle.subscribe_with_install(InboxInstall {
budget: liminal::channel::ConnectionInboxBudget::new(1024 * 1024),
depth_cap: 64,
notifier: None,
})?;
let payload = br#"{"order":7}"#.to_vec();
admitted.handle.publish_with_delivery(
&payload,
liminal::envelope::PublisherId::default(),
None,
)?;
let delivered = subscription
.try_next()?
.ok_or("an admitted subscriber must still receive deliveries after the quiesce")?;
assert_eq!(delivered.payload, payload);
let refusal = services
.admit_channel("orders")
.err()
.ok_or("a subscribe admitted AFTER the quiesce must be refused")?;
assert!(
matches!(
&refusal,
ChannelAccessError::Quiesced { name, reason } if name == "orders" && reason == "archived"
),
"got {refusal:?}"
);
assert_eq!(refusal.reason_code(), CHANNEL_QUIESCED_CODE);
let publish_error = services
.publish("orders", &envelope(br#"{"order":8}"#.to_vec()), None)
.err()
.ok_or("the ordinary publish path must refuse a quiesced channel")?;
assert_eq!(
publish_error.to_string(),
"listener accept failed: channel 'orders' is quiesced: archived"
);
assert!(
services.subscribe("orders", &[], None).is_err(),
"the ordinary subscribe path must refuse a quiesced channel too"
);
assert!(
admitted.handle.is_actor_spawned(),
"quiesce is a roster-level admission decision, not an actor command"
);
services.quiesce_channel("orders", "archived")?;
let second = services
.quiesce_channel("orders", "decommissioned")
.err()
.ok_or("a second, different quiesce reason must refuse")?;
assert!(
matches!(
&second,
ChannelRegistryError::AlreadyQuiesced { name, reason }
if name == "orders" && reason == "archived"
),
"got {second:?}"
);
Ok(())
}
#[test]
fn registered_idle_channels_spawn_no_actor() -> Result<(), Box<dyn Error>> {
const IDLE_CHANNELS: usize = 16;
const CONTROL_PUBLISHES: u64 = 4;
let (store, _dir) = disk_store()?;
let services = LiminalConnectionServices::from_config_with_store(
&config_with(Vec::new(), Some(IDLE_CHANNELS + 1)),
store,
)?;
let scheduler = services.channel_cluster().supervisor().scheduler();
let baseline_processes = scheduler.process_table().len();
let idle: Vec<String> = (0..IDLE_CHANNELS)
.map(|index| format!("idle-{index}"))
.collect();
for name in &idle {
assert_eq!(
services.register_channel(®istration(name, false))?,
Registered::Created
);
}
assert_eq!(
scheduler.process_table().len(),
baseline_processes,
"{IDLE_CHANNELS} registrations must add no process to the shared scheduler"
);
crate::metrics::init();
let publishes_before = crate::metrics::publishes_total_value()
.ok_or("the publish counter must be readable once metrics are initialized")?;
assert_eq!(
services.register_channel(®istration("control", false))?,
Registered::Created
);
let subscription = services.subscribe_handle_for_test("control")?;
for index in 0..CONTROL_PUBLISHES {
let outcome = services.publish("control", &envelope(control_payload(index)), None)?;
assert!(
outcome.delivered,
"publish {index} to the control channel must reach its subscriber"
);
}
let mut received = Vec::new();
while let Some(delivered) = subscription.try_next()? {
received.push(delivered.payload);
}
let expected: Vec<Vec<u8>> = (0..CONTROL_PUBLISHES).map(control_payload).collect();
assert_eq!(
received, expected,
"the control subscriber must receive every control publish"
);
let publishes_after = crate::metrics::publishes_total_value()
.ok_or("the publish counter must still be readable")?;
assert!(
publishes_after >= publishes_before + CONTROL_PUBLISHES,
"the process-wide publish counter must advance by at least {CONTROL_PUBLISHES}: \
{publishes_before} -> {publishes_after}"
);
assert!(
scheduler.process_table().len() > baseline_processes,
"using a channel must spawn on the same scheduler the flat arm measured"
);
for name in &idle {
let entry = services
.admit_channel(name)
.map_err(|error| format!("idle channel '{name}' must still be admitted: {error}"))?;
assert!(
!entry.handle.is_actor_spawned(),
"registered-but-untouched channel '{name}' must own no actor"
);
}
let descriptors = services.registered_channels()?;
assert_eq!(descriptors.len(), IDLE_CHANNELS + 1);
for descriptor in &descriptors {
assert_eq!(descriptor.origin, ChannelOrigin::RuntimeRegistered);
assert_eq!(descriptor.state, ChannelState::Active);
}
Ok(())
}
#[test]
fn registration_is_idempotent_only_for_an_identical_configuration() -> Result<(), Box<dyn Error>> {
let (store, _dir) = disk_store()?;
let services = LiminalConnectionServices::from_config_with_store(
&config_with(Vec::new(), Some(4)),
store,
)?;
let spec = ChannelRegistration {
name: "orders".to_owned(),
schema_bytes: Some(br#"{"type":"object"}"#.to_vec()),
durable: false,
};
assert_eq!(services.register_channel(&spec)?, Registered::Created);
assert_eq!(
services.register_channel(&spec)?,
Registered::AlreadyIdentical,
"the identical spec must be idempotent, not a refusal"
);
let differing_mode = ChannelRegistration {
durable: true,
..spec.clone()
};
assert_field_conflict(&services, &differing_mode, "mode")?;
let differing_id = ChannelRegistration {
schema_bytes: Some(br#"{"type": "object"}"#.to_vec()),
..spec.clone()
};
assert_field_conflict(&services, &differing_id, "schema id")?;
let differing_document = ChannelRegistration {
schema_bytes: Some(br#"{"type":"array"}"#.to_vec()),
..spec
};
assert_field_conflict(&services, &differing_document, "schema id")?;
Ok(())
}
#[test]
fn registering_over_a_boot_channel_never_flips_its_origin() -> Result<(), Box<dyn Error>> {
let (store, _dir) = disk_store()?;
let services = LiminalConnectionServices::from_config_with_store(
&config_with(vec![boot_channel("boot", false)], Some(1)),
store,
)?;
assert_eq!(
services.register_channel(®istration("boot", false))?,
Registered::AlreadyIdentical
);
assert!(
matches!(
services.channel_status("boot")?,
ChannelStatus::Active {
origin: ChannelOrigin::BootConfigured,
..
}
),
"an identical registration must not re-stamp a boot channel's origin"
);
assert_eq!(
services.register_channel(®istration("runtime", false))?,
Registered::Created
);
Ok(())
}
#[test]
fn the_channel_cap_refuses_undeclared_and_reached() -> Result<(), Box<dyn Error>> {
let (store, _dir) = disk_store()?;
let undeclared = LiminalConnectionServices::from_config_with_store(
&config_with(Vec::new(), None),
Arc::clone(&store),
)?;
let refusal = undeclared
.register_channel(®istration("orders", false))
.err()
.ok_or("registration with no declared cap must refuse")?;
assert!(
matches!(
refusal,
ChannelRegistryError::CapNotConfigured {
cap: "limits.max_channels"
}
),
"got {refusal:?}"
);
assert!(undeclared.registered_channels()?.is_empty());
let bounded = LiminalConnectionServices::from_config_with_store(
&config_with(Vec::new(), Some(1)),
store,
)?;
assert_eq!(
bounded.register_channel(®istration("first", false))?,
Registered::Created
);
let reached = bounded
.register_channel(®istration("second", false))
.err()
.ok_or("registration past the declared cap must refuse")?;
assert!(
matches!(
reached,
ChannelRegistryError::CapReached {
cap: "limits.max_channels",
limit: 1
}
),
"got {reached:?}"
);
assert_eq!(
bounded.register_channel(®istration("first", false))?,
Registered::AlreadyIdentical,
"a reached cap must not turn an identical re-registration into a refusal"
);
Ok(())
}
#[test]
fn quiesce_refuses_an_unregistered_name_and_the_probe_reports_it() -> Result<(), Box<dyn Error>> {
let (store, _dir) = disk_store()?;
let services = LiminalConnectionServices::from_config_with_store(
&config_with(Vec::new(), Some(2)),
store,
)?;
assert_eq!(
services.channel_status("absent")?,
ChannelStatus::NotRegistered
);
let refusal = services
.quiesce_channel("absent", "archived")
.err()
.ok_or("quiescing an unregistered name must refuse")?;
assert!(
matches!(&refusal, ChannelRegistryError::NotRegistered { name } if name == "absent"),
"got {refusal:?}"
);
Ok(())
}
#[test]
fn a_rejected_schema_registers_nothing() -> Result<(), Box<dyn Error>> {
let (store, _dir) = disk_store()?;
let services = LiminalConnectionServices::from_config_with_store(
&config_with(Vec::new(), Some(4)),
store,
)?;
let unparseable = ChannelRegistration {
name: "orders".to_owned(),
schema_bytes: Some(b"not json at all".to_vec()),
durable: false,
};
let refusal = services
.register_channel(&unparseable)
.err()
.ok_or("schema bytes that are not JSON must refuse")?;
assert!(
matches!(&refusal, ChannelRegistryError::SchemaRejected { name, .. } if name == "orders"),
"got {refusal:?}"
);
assert_eq!(
services.channel_status("orders")?,
ChannelStatus::NotRegistered,
"a refused registration must leave the roster untouched"
);
Ok(())
}
#[test]
fn probing_and_enumerating_never_spawn_an_actor() -> Result<(), Box<dyn Error>> {
let (store, _dir) = disk_store()?;
let services = LiminalConnectionServices::from_config_with_store(
&config_with(Vec::new(), Some(2)),
store,
)?;
let scheduler = services.channel_cluster().supervisor().scheduler();
let baseline = scheduler.process_table().len();
services.register_channel(®istration("orders", false))?;
for _probe in 0..3 {
let _status = services.channel_status("orders")?;
let _census = services.registered_channels()?;
}
assert_eq!(
scheduler.process_table().len(),
baseline,
"probing and enumerating must add no process to the scheduler"
);
let entry = services
.admit_channel("orders")
.map_err(|error| format!("the channel must still be admitted: {error}"))?;
assert!(!entry.handle.is_actor_spawned());
Ok(())
}
fn assert_field_conflict(
services: &LiminalConnectionServices,
spec: &ChannelRegistration,
expected_field: &str,
) -> Result<(), Box<dyn Error>> {
let refusal = services
.register_channel(spec)
.err()
.ok_or("a differing configuration must refuse")?;
assert_eq!(
refusal.to_string(),
format!("channel 'orders' is already registered with a different {expected_field}")
);
Ok(())
}
fn registration(name: &str, durable: bool) -> ChannelRegistration {
ChannelRegistration {
name: name.to_owned(),
schema_bytes: None,
durable,
}
}
fn boot_channel(name: &str, durable: bool) -> ChannelDef {
ChannelDef {
name: name.to_owned(),
schema_ref: None,
durable,
loaded_schema: None,
}
}
fn config_with(channels: Vec<ChannelDef>, max_channels: Option<usize>) -> ServerConfig {
ServerConfig {
listen_address: local_address(),
health_listen_address: local_address(),
drain_timeout_ms: 30_000,
channels,
routing_rules: Vec::new(),
persistence_path: None,
cluster: None,
auth: None,
services: crate::config::types::ServicesConfig::default(),
limits: LimitsConfig {
max_channels,
..LimitsConfig::default()
},
participant: None,
websocket: None,
}
}
fn local_address() -> std::net::SocketAddr {
std::net::SocketAddr::from(([127, 0, 0, 1], 0))
}
fn control_payload(index: u64) -> Vec<u8> {
format!(r#"{{"control":{index}}}"#).into_bytes()
}
fn envelope(payload: Vec<u8>) -> MessageEnvelope {
MessageEnvelope::new(
SchemaId::new([0_u8; SchemaId::WIRE_LEN]),
CausalContext::independent(),
payload,
)
}
fn disk_store() -> Result<(Arc<dyn DurableStore>, TempDir), Box<dyn Error>> {
let dir = tempfile::tempdir()?;
let database = Database::create(DatabaseConfig {
data_dir: dir.path().join("db"),
shard_count: 4,
distributed: None,
executor_threads: None,
})?;
let store: Arc<dyn DurableStore> =
Arc::new(HaematiteStore::new(Arc::new(EventStore::new(database))));
Ok((store, dir))
}
fn read_payloads(
store: &dyn DurableStore,
stream_key: &str,
) -> Result<Vec<Vec<u8>>, Box<dyn Error>> {
let entries: Vec<StoredEntry> = block_on(store.read_from(stream_key, 0, 1024))??;
let mut payloads = Vec::with_capacity(entries.len());
for entry in entries {
payloads.push(DurableEnvelope::deserialize(&entry.payload)?.payload);
}
Ok(payloads)
}