canic-core 0.64.3

Canic — a canister orchestration and management toolkit for the Internet Computer
Documentation
// Category A - Internal runtime-configured tests (ConfigTestBuilder when needed).

use crate::{
    config::schema::CanisterKind,
    dto::topology::{AppIndexArgs, IndexEntryInput, SubnetIndexArgs},
    ids::CanisterRole,
    ops::storage::{
        index::{app::AppIndexOps, subnet::SubnetIndexOps},
        registry::subnet::SubnetRegistryOps,
    },
    storage::stable::index::{app::AppIndexRecord, subnet::SubnetIndexRecord},
    test::{
        config::ConfigTestBuilder,
        seams::{lock, p},
        support::import_test_env,
    },
    workflow::topology::index::query::SubnetIndexQuery,
};

#[test]
fn index_addressing_prefers_index_over_registry_duplicates() {
    let _guard = lock();

    for (pid, _) in SubnetRegistryOps::data().entries {
        let _ = SubnetRegistryOps::remove(&pid);
    }
    SubnetIndexOps::import_trusted_partial(SubnetIndexRecord {
        entries: Vec::new(),
    })
    .expect("clear subnet index");

    let role = CanisterRole::new("seam_directory_role");
    let root_pid = p(10);
    let pid_a = p(11);
    let pid_b = p(12);

    let created_at = 1;
    SubnetRegistryOps::register_root(root_pid, created_at);
    SubnetRegistryOps::register_unchecked(pid_a, &role, root_pid, vec![], created_at)
        .expect("register first canister");
    SubnetRegistryOps::register_unchecked(pid_b, &role, root_pid, vec![], created_at)
        .expect("register second canister with same role");

    SubnetIndexOps::import_trusted_partial(SubnetIndexRecord {
        entries: vec![(role.clone(), pid_b)],
    })
    .expect("import subnet index");

    let resolved = SubnetIndexQuery::get(role.clone()).expect("index role missing");
    assert_eq!(resolved, pid_b);

    let duplicates = SubnetRegistryOps::data()
        .entries
        .into_iter()
        .filter(|(_, entry)| entry.role == role)
        .count();

    assert_eq!(duplicates, 2);
}

#[test]
fn index_addressing_does_not_fallback_to_registry() {
    let _guard = lock();

    for (pid, _) in SubnetRegistryOps::data().entries {
        let _ = SubnetRegistryOps::remove(&pid);
    }
    SubnetIndexOps::import_trusted_partial(SubnetIndexRecord {
        entries: Vec::new(),
    })
    .expect("clear subnet index");

    let role = CanisterRole::new("seam_directory_no_fallback");
    let root_pid = p(13);
    let pid = p(14);
    let created_at = 1;

    SubnetRegistryOps::register_root(root_pid, created_at);
    SubnetRegistryOps::register_unchecked(pid, &role, root_pid, vec![], created_at)
        .expect("register canister");

    let resolved = SubnetIndexQuery::get(role.clone());
    assert!(resolved.is_none());

    let registry_count = SubnetRegistryOps::data()
        .entries
        .into_iter()
        .filter(|(_, entry)| entry.role == role)
        .count();
    assert_eq!(registry_count, 1);
}

#[test]
fn incomplete_index_imports_reject_roles_outside_configured_service_sets() {
    let _guard = lock();

    let service_role = CanisterRole::new("project_hub");
    let singleton_role = CanisterRole::new("project_ledger");
    let service_pid = p(21);
    let singleton_pid = p(22);

    let _config = ConfigTestBuilder::new()
        .with_prime_canister_kind(service_role.clone(), CanisterKind::Service)
        .with_prime_canister_kind(singleton_role.clone(), CanisterKind::Singleton)
        .with_app_index(service_role.clone())
        .install();
    import_test_env(service_role.clone(), crate::ids::SubnetRole::PRIME, p(20));

    AppIndexOps::import_trusted_partial(AppIndexRecord {
        entries: Vec::new(),
    })
    .expect("clear app index");
    SubnetIndexOps::import_trusted_partial(SubnetIndexRecord {
        entries: Vec::new(),
    })
    .expect("clear subnet index");

    AppIndexOps::import_args_allow_incomplete(AppIndexArgs(vec![IndexEntryInput {
        role: service_role.clone(),
        pid: service_pid,
    }]))
    .expect("configured app service role should import");
    SubnetIndexOps::import_args_allow_incomplete(SubnetIndexArgs(vec![IndexEntryInput {
        role: service_role.clone(),
        pid: service_pid,
    }]))
    .expect("configured subnet service role should import");

    let app_err = AppIndexOps::import_args_allow_incomplete(AppIndexArgs(vec![IndexEntryInput {
        role: singleton_role.clone(),
        pid: singleton_pid,
    }]))
    .expect_err("app index should reject roles outside explicit app_index");
    assert!(
        app_err.to_string().contains("unexpected roles"),
        "expected unexpected app role error, got: {app_err}"
    );

    let subnet_err =
        SubnetIndexOps::import_args_allow_incomplete(SubnetIndexArgs(vec![IndexEntryInput {
            role: singleton_role.clone(),
            pid: singleton_pid,
        }]))
        .expect_err("subnet index should reject non-service roles");
    assert!(
        subnet_err.to_string().contains("unexpected roles"),
        "expected unexpected subnet role error, got: {subnet_err}"
    );

    AppIndexOps::import(AppIndexRecord {
        entries: vec![(service_role.clone(), service_pid)],
    })
    .expect("full app index import should accept exact configured role set");
    SubnetIndexOps::import(SubnetIndexRecord {
        entries: vec![(service_role.clone(), service_pid)],
    })
    .expect("full subnet index import should accept exact configured role set");

    let app_full_err = AppIndexOps::import(AppIndexRecord {
        entries: vec![
            (service_role.clone(), service_pid),
            (singleton_role.clone(), singleton_pid),
        ],
    })
    .expect_err("full app index import should reject roles outside explicit app_index");
    assert!(
        app_full_err.to_string().contains("unexpected roles"),
        "expected unexpected full app role error, got: {app_full_err}"
    );

    let subnet_full_err = SubnetIndexOps::import(SubnetIndexRecord {
        entries: vec![
            (service_role.clone(), service_pid),
            (singleton_role, singleton_pid),
        ],
    })
    .expect_err("full subnet index import should reject non-service roles");
    assert!(
        subnet_full_err.to_string().contains("unexpected roles"),
        "expected unexpected full subnet role error, got: {subnet_full_err}"
    );

    assert_eq!(AppIndexOps::get(&service_role), Some(service_pid));
    assert_eq!(SubnetIndexOps::get(&service_role), Some(service_pid));
}