canic-core 0.99.25

Canic — a canister orchestration and management toolkit for the Internet Computer
Documentation
use super::*;

fn p(id: u8) -> Principal {
    Principal::from_slice(&[id; 29])
}

fn claim_id(id: u64) -> u64 {
    id
}

#[test]
fn claim_pending_returns_bound_when_key_is_already_bound() {
    PlacementBindingRegistryOps::clear_for_test();

    let pid = p(1);
    PlacementBindingRegistryOps::bind("projects", "alpha", pid, 10).expect("initial bind");

    let result =
        PlacementBindingRegistryOps::claim_pending("projects", "alpha", p(9), claim_id(9), 20)
            .expect("claim");

    assert_eq!(
        result,
        PlacementBindingClaimResult::Bound {
            instance_pid: pid,
            bound_at: 10,
        }
    );
}

#[test]
fn bind_promotes_matching_pending_provisional_child() {
    PlacementBindingRegistryOps::clear_for_test();

    let owner_pid = p(1);
    let child_pid = p(2);

    let claim =
        PlacementBindingRegistryOps::claim_pending("projects", "alpha", owner_pid, claim_id(1), 10)
            .expect("initial claim");
    let PlacementBindingClaimResult::Claimed(claim) = claim else {
        panic!("expected new claim");
    };
    PlacementBindingRegistryOps::set_provisional_pid_if_claim_matches(
        "projects",
        "alpha",
        claim.claim_id,
        child_pid,
    )
    .expect("attach provisional child");
    PlacementBindingRegistryOps::bind("projects", "alpha", child_pid, 20)
        .expect("bind should promote matching provisional child");

    assert_eq!(
        PlacementBindingRegistryOps::lookup_key("projects", "alpha"),
        Some(child_pid)
    );
}

#[test]
fn lookup_entry_reports_pending_status() {
    PlacementBindingRegistryOps::clear_for_test();

    let owner_pid = p(1);
    PlacementBindingRegistryOps::claim_pending("projects", "alpha", owner_pid, claim_id(1), 10)
        .expect("initial claim");

    assert_eq!(
        PlacementBindingRegistryOps::lookup_entry("projects", "alpha"),
        Some(PlacementBindingStatusResponse::Pending {
            owner_pid,
            created_at: 10,
            provisional_pid: None,
        })
    );
}

#[test]
fn bind_rejects_conflicting_provisional_child() {
    PlacementBindingRegistryOps::clear_for_test();

    let claim =
        PlacementBindingRegistryOps::claim_pending("projects", "alpha", p(1), claim_id(1), 10)
            .expect("initial claim");
    let PlacementBindingClaimResult::Claimed(claim) = claim else {
        panic!("expected new claim");
    };
    PlacementBindingRegistryOps::set_provisional_pid_if_claim_matches(
        "projects",
        "alpha",
        claim.claim_id,
        p(2),
    )
    .expect("attach provisional child");

    PlacementBindingRegistryOps::bind("projects", "alpha", p(3), 20)
        .expect_err("conflicting provisional child should fail");
}

#[test]
fn release_stale_pending_removes_stale_entry() {
    PlacementBindingRegistryOps::clear_for_test();

    let owner_pid = p(1);
    let provisional_pid = p(2);
    let claim =
        PlacementBindingRegistryOps::claim_pending("projects", "alpha", owner_pid, claim_id(1), 10)
            .expect("initial claim");
    let PlacementBindingClaimResult::Claimed(claim) = claim else {
        panic!("expected new claim");
    };
    PlacementBindingRegistryOps::set_provisional_pid_if_claim_matches(
        "projects",
        "alpha",
        claim.claim_id,
        provisional_pid,
    )
    .expect("attach provisional child");

    let result = PlacementBindingRegistryOps::release_stale_pending_if_claim_matches(
        "projects",
        "alpha",
        claim.claim_id,
        10 + PlacementBindingRegistryOps::PENDING_TTL_SECS + 1,
    )
    .expect("release stale pending");

    assert_eq!(
        result,
        PlacementBindingReleaseResult::ReleasedStalePending {
            owner_pid,
            created_at: 10,
            provisional_pid: Some(provisional_pid),
        }
    );
    assert_eq!(
        PlacementBindingRegistryOps::lookup_entry("projects", "alpha"),
        None
    );
}

#[test]
fn release_stale_pending_keeps_fresh_entry_in_place() {
    PlacementBindingRegistryOps::clear_for_test();

    let owner_pid = p(1);
    let claim =
        PlacementBindingRegistryOps::claim_pending("projects", "alpha", owner_pid, claim_id(1), 10)
            .expect("initial claim");

    let PlacementBindingClaimResult::Claimed(claim) = claim else {
        panic!("expected new claim");
    };
    let result = PlacementBindingRegistryOps::release_stale_pending_if_claim_matches(
        "projects",
        "alpha",
        claim.claim_id,
        11,
    )
    .expect("fresh pending should not be released");

    assert_eq!(
        result,
        PlacementBindingReleaseResult::PendingRetained {
            owner_pid,
            created_at: 10,
            provisional_pid: None,
        }
    );
    std::assert_matches!(
        PlacementBindingRegistryOps::lookup_entry("projects", "alpha"),
        Some(PlacementBindingStatusResponse::Pending { .. })
    );
}

#[test]
fn release_stale_pending_retains_unknown_child_outcome() {
    PlacementBindingRegistryOps::clear_for_test();

    let owner_pid = p(1);
    let claim =
        PlacementBindingRegistryOps::claim_pending("projects", "alpha", owner_pid, claim_id(1), 10)
            .expect("initial claim");
    let PlacementBindingClaimResult::Claimed(claim) = claim else {
        panic!("expected new claim");
    };

    let result = PlacementBindingRegistryOps::release_stale_pending_if_claim_matches(
        "projects",
        "alpha",
        claim.claim_id,
        10 + PlacementBindingRegistryOps::PENDING_TTL_SECS + 1,
    )
    .expect("unknown child outcome must remain retained");

    assert_eq!(
        result,
        PlacementBindingReleaseResult::PendingRetained {
            owner_pid,
            created_at: 10,
            provisional_pid: None,
        }
    );
    assert!(PlacementBindingRegistryOps::lookup_entry("projects", "alpha").is_some());
}

#[test]
fn pending_claim_cannot_be_stolen_after_ttl_without_explicit_recovery() {
    PlacementBindingRegistryOps::clear_for_test();

    let first =
        PlacementBindingRegistryOps::claim_pending("projects", "alpha", p(1), claim_id(1), 10)
            .expect("initial claim");
    let PlacementBindingClaimResult::Claimed(first_claim) = first else {
        panic!("expected first claim");
    };

    let second = PlacementBindingRegistryOps::claim_pending(
        "projects",
        "alpha",
        p(2),
        claim_id(2),
        10 + PlacementBindingRegistryOps::PENDING_TTL_SECS + 1,
    )
    .expect("existing claim should be returned");
    assert_eq!(
        second,
        PlacementBindingClaimResult::PendingExisting {
            claim_id: first_claim.claim_id,
            owner_pid: first_claim.owner_pid,
            created_at: first_claim.created_at,
            provisional_pid: None,
        }
    );

    let attach_ok = PlacementBindingRegistryOps::set_provisional_pid_if_claim_matches(
        "projects",
        "alpha",
        first_claim.claim_id,
        p(9),
    )
    .expect("original claim owner should retain provisional attach authority");
    assert!(attach_ok);

    let bind_ok = PlacementBindingRegistryOps::bind_if_claim_matches(
        "projects",
        "alpha",
        first_claim.claim_id,
        p(9),
        20,
    )
    .expect("original claim owner should retain bind authority");
    assert!(bind_ok);

    std::assert_matches!(
        PlacementBindingRegistryOps::lookup_state("projects", "alpha"),
        Some(PlacementBindingEntryState::Bound { instance_pid, .. }) if instance_pid == p(9)
    );
}