@@ -1903,3 +1903,151 @@ fn update_namespace_permission_smaller_instances() {
19031903 assert_eq ! ( updated. max_instances, 5 ) ;
19041904 } ) ;
19051905}
1906+
1907+ #[ test]
1908+ fn bulk_delegate_namespace_permission_fails_when_redelegation_exceeds_parent_instances ( ) {
1909+ new_test_ext ( ) . execute_with ( || {
1910+ zero_min_burn ( ) ;
1911+ let alice = 0 ;
1912+ let bob = 1 ;
1913+ let charlie = 2 ;
1914+ let dave = 3 ;
1915+
1916+ register_agent ( alice) ;
1917+ register_agent ( bob) ;
1918+ register_agent ( charlie) ;
1919+ register_agent ( dave) ;
1920+
1921+ // Alice creates a namespace and delegates to Bob with 3 instances
1922+ let namespace = register_namespace ( alice, b"agent.alice.compute" ) ;
1923+ let alice_paths = paths_map ! ( None => [ namespace. clone( ) ] ) ;
1924+
1925+ assert_ok ! ( Permission0 :: delegate_namespace_permission(
1926+ get_origin( alice) ,
1927+ bob,
1928+ alice_paths,
1929+ PermissionDuration :: Indefinite ,
1930+ RevocationTerms :: Irrevocable ,
1931+ 3 // Bob gets 3 instances
1932+ ) ) ;
1933+
1934+ let bob_permission_id = get_last_delegated_permission_id ( alice) ;
1935+
1936+ // Bob tries to redelegate to Charlie and Dave with 2 instances each (total 4)
1937+ // This should fail because Bob only has 3 instances available
1938+ let bob_paths = paths_map ! ( Some ( bob_permission_id) => [ namespace] ) ;
1939+
1940+ let mut recipients = BoundedBTreeSet :: new ( ) ;
1941+ recipients. try_insert ( charlie) . unwrap ( ) ;
1942+ recipients. try_insert ( dave) . unwrap ( ) ;
1943+
1944+ // This should fail because 2 recipients × 2 instances = 4 instances needed,
1945+ // but Bob only has 3 instances available
1946+ assert_err ! (
1947+ Permission0 :: bulk_delegate_namespace_permission(
1948+ get_origin( bob) ,
1949+ recipients,
1950+ bob_paths,
1951+ PermissionDuration :: Indefinite ,
1952+ RevocationTerms :: Irrevocable ,
1953+ 2 // 2 instances per recipient
1954+ ) ,
1955+ Error :: <Test >:: NotEnoughInstances
1956+ ) ;
1957+
1958+ // Verify no permissions were created
1959+ assert_eq ! (
1960+ PermissionsByRecipient :: <Test >:: get( charlie) . len( ) ,
1961+ 0 ,
1962+ "Charlie should have no permissions"
1963+ ) ;
1964+ assert_eq ! (
1965+ PermissionsByRecipient :: <Test >:: get( dave) . len( ) ,
1966+ 0 ,
1967+ "Dave should have no permissions"
1968+ ) ;
1969+
1970+ // Verify Bob's permission still has all instances available
1971+ let bob_permission = Permissions :: < Test > :: get ( bob_permission_id) . unwrap ( ) ;
1972+ assert_eq ! ( bob_permission. available_instances( ) , 3 ) ;
1973+ assert_eq ! ( bob_permission. children. len( ) , 0 ) ;
1974+ } ) ;
1975+ }
1976+
1977+ #[ test]
1978+ fn bulk_delegate_namespace_permission_succeeds_within_parent_instance_limit ( ) {
1979+ new_test_ext ( ) . execute_with ( || {
1980+ zero_min_burn ( ) ;
1981+ let alice = 0 ;
1982+ let bob = 1 ;
1983+ let charlie = 2 ;
1984+ let dave = 3 ;
1985+
1986+ register_agent ( alice) ;
1987+ register_agent ( bob) ;
1988+ register_agent ( charlie) ;
1989+ register_agent ( dave) ;
1990+
1991+ // Alice creates a namespace and delegates to Bob with 4 instances
1992+ let namespace = register_namespace ( alice, b"agent.alice.compute" ) ;
1993+ let alice_paths = paths_map ! ( None => [ namespace. clone( ) ] ) ;
1994+
1995+ assert_ok ! ( Permission0 :: delegate_namespace_permission(
1996+ get_origin( alice) ,
1997+ bob,
1998+ alice_paths,
1999+ PermissionDuration :: Indefinite ,
2000+ RevocationTerms :: Irrevocable ,
2001+ 4 // Bob gets 4 instances
2002+ ) ) ;
2003+
2004+ let bob_permission_id = get_last_delegated_permission_id ( alice) ;
2005+
2006+ // Bob redelegates to Charlie and Dave with 2 instances each (total 4)
2007+ // This should succeed because Bob has exactly 4 instances available
2008+ let bob_paths = paths_map ! ( Some ( bob_permission_id) => [ namespace] ) ;
2009+
2010+ let mut recipients = BoundedBTreeSet :: new ( ) ;
2011+ recipients. try_insert ( charlie) . unwrap ( ) ;
2012+ recipients. try_insert ( dave) . unwrap ( ) ;
2013+
2014+ assert_ok ! ( Permission0 :: bulk_delegate_namespace_permission(
2015+ get_origin( bob) ,
2016+ recipients,
2017+ bob_paths,
2018+ PermissionDuration :: Indefinite ,
2019+ RevocationTerms :: Irrevocable ,
2020+ 2 // 2 instances per recipient
2021+ ) ) ;
2022+
2023+ // Verify permissions were created
2024+ assert_eq ! (
2025+ PermissionsByRecipient :: <Test >:: get( charlie) . len( ) ,
2026+ 1 ,
2027+ "Charlie should have 1 permission"
2028+ ) ;
2029+ assert_eq ! (
2030+ PermissionsByRecipient :: <Test >:: get( dave) . len( ) ,
2031+ 1 ,
2032+ "Dave should have 1 permission"
2033+ ) ;
2034+
2035+ // Get the created permission IDs using helper function
2036+ let charlie_permission_id = get_permission_id_for_recipient ( charlie) ;
2037+ let dave_permission_id = get_permission_id_for_recipient ( dave) ;
2038+
2039+ // Verify each permission has 2 instances
2040+ let charlie_permission = Permissions :: < Test > :: get ( charlie_permission_id) . unwrap ( ) ;
2041+ assert_eq ! ( charlie_permission. max_instances, 2 ) ;
2042+
2043+ let dave_permission = Permissions :: < Test > :: get ( dave_permission_id) . unwrap ( ) ;
2044+ assert_eq ! ( dave_permission. max_instances, 2 ) ;
2045+
2046+ // Verify Bob's permission has no instances left available
2047+ let bob_permission = Permissions :: < Test > :: get ( bob_permission_id) . unwrap ( ) ;
2048+ assert_eq ! ( bob_permission. available_instances( ) , 0 ) ; // 4 - (2+2) = 0
2049+ assert_eq ! ( bob_permission. children. len( ) , 2 ) ;
2050+ assert ! ( bob_permission. children. contains( & charlie_permission_id) ) ;
2051+ assert ! ( bob_permission. children. contains( & dave_permission_id) ) ;
2052+ } ) ;
2053+ }
0 commit comments