mod common;
use std::sync::Arc;
use common::array_sync::build_schema_snapshot;
use nodedb::bridge::dispatch::Dispatcher;
use nodedb::control::array_sync::{OriginApplyEngine, OriginArrayInbound};
use nodedb::control::security::identity::{AuthMethod, AuthenticatedIdentity, DatabaseSet};
use nodedb::control::server::shared::ddl::neutral::collection::show_collections;
use nodedb::control::server::shared::ddl::result::DdlResult;
use nodedb::control::state::SharedState;
use nodedb::types::TenantId;
use nodedb::wal::WalManager;
use nodedb_types::DatabaseId;
fn build_test_state() -> Arc<SharedState> {
let dir = tempfile::tempdir().expect("tmpdir");
let wal_path = dir.path().join("test.wal");
std::mem::forget(dir);
let wal = Arc::new(WalManager::open_for_testing(&wal_path).expect("wal"));
let (dispatcher, _data_sides) = Dispatcher::new(1, 64);
SharedState::new(dispatcher, wal).unwrap()
}
fn superuser_identity() -> AuthenticatedIdentity {
AuthenticatedIdentity {
user_id: 1,
username: "test".into(),
tenant_id: TenantId::new(0),
auth_method: AuthMethod::Trust,
roles: vec![],
is_superuser: true,
default_database: None,
accessible_databases: DatabaseSet::All,
}
}
fn row_names(results: &[DdlResult]) -> Vec<String> {
results
.iter()
.filter_map(|r| match r {
DdlResult::Rows(shaped) => Some(shaped),
_ => None,
})
.flat_map(|shaped| shaped.rows.iter())
.filter_map(|row| row.get("name").and_then(|v| v.as_str()).map(String::from))
.collect()
}
#[tokio::test]
async fn synced_array_schema_is_visible_in_system_catalog_single_node() {
let shared = build_test_state();
assert!(
shared.raft_proposer.get().is_none(),
"test assumes single-node (no raft_proposer installed)"
);
let engine = Arc::new(OriginApplyEngine::new(
Arc::clone(&shared.array_sync_schemas),
Arc::clone(&shared.array_sync_op_log),
));
let inbound = OriginArrayInbound::new(
engine,
Arc::clone(&shared.array_sync_schemas),
Arc::clone(&shared),
TenantId::new(0),
);
let array_name = "genome_tiles";
let (snapshot_payload, schema_hlc) = build_schema_snapshot(array_name);
let mut schema_hlc_bytes = [0u8; 18];
schema_hlc_bytes.copy_from_slice(&schema_hlc.to_bytes());
let msg = nodedb_types::sync::wire::array::ArraySchemaSyncMsg {
array: array_name.to_string(),
replica_id: 1,
snapshot_payload,
schema_hlc_bytes,
};
inbound
.handle_schema(&msg)
.await
.expect("single-node direct-import schema handling must succeed");
{
let cat = shared.array_catalog.read().expect("array_catalog lock");
assert!(
cat.lookup_by_name(array_name).is_some(),
"array_catalog must be registered by the single-node direct-import path"
);
}
let identity = superuser_identity();
let results =
show_collections(&shared, &identity, DatabaseId::DEFAULT).expect("show_collections");
let names = row_names(&results);
assert!(
names.contains(&array_name.to_string()),
"synced Array collection '{array_name}' must be visible in SHOW COLLECTIONS \
(system catalog introspection); got rows: {names:?}"
);
}