@@ -456,14 +456,95 @@ def test_add_ips_to_group_invalid_ip_id(self, group: Group, mock_web3):
456456 ip_ids = [invalid_ip_id ],
457457 )
458458
459+ def test_add_ips_to_group_max_reward_share_exceeds_100 (
460+ self , group : Group , mock_web3_is_address
461+ ):
462+ """Test add_ips_to_group rejects max_allowed_reward_share_percentage > 100 (via get_revenue_share)."""
463+ with mock_web3_is_address ():
464+ with patch .object (
465+ group .dispute_module_client , "isIpTagged" , return_value = False
466+ ), patch .object (
467+ group .ip_asset_registry_client ,
468+ "isRegisteredGroup" ,
469+ return_value = False ,
470+ ):
471+ with pytest .raises (
472+ ValueError ,
473+ match = "must be between 0 and 100" ,
474+ ):
475+ group .add_ips_to_group (
476+ group_ip_id = IP_ID ,
477+ ip_ids = [IP_ID ],
478+ max_allowed_reward_share_percentage = 101 ,
479+ )
480+
481+ def test_add_ips_to_group_disputed_group (self , group : Group , mock_web3_is_address ):
482+ """Test add_ips_to_group rejects disputed group."""
483+ with mock_web3_is_address ():
484+ with patch .object (
485+ group .dispute_module_client , "isIpTagged" , return_value = True
486+ ):
487+ with pytest .raises (
488+ ValueError ,
489+ match = "Disputed group cannot add IP" ,
490+ ):
491+ group .add_ips_to_group (
492+ group_ip_id = IP_ID ,
493+ ip_ids = [IP_ID ],
494+ )
495+
496+ def test_add_ips_to_group_disputed_ip (self , group : Group , mock_web3_is_address ):
497+ """Test add_ips_to_group rejects disputed IP in ip_ids."""
498+ with mock_web3_is_address ():
499+ with patch .object (
500+ group .dispute_module_client ,
501+ "isIpTagged" ,
502+ side_effect = [False , True ], # group ok, first ip disputed
503+ ):
504+ with pytest .raises (
505+ ValueError ,
506+ match = "Cannot add disputed IP to group" ,
507+ ):
508+ group .add_ips_to_group (
509+ group_ip_id = IP_ID ,
510+ ip_ids = [IP_ID , ADDRESS ],
511+ )
512+
513+ def test_add_ips_to_group_ip_is_registered_group (
514+ self , group : Group , mock_web3_is_address
515+ ):
516+ """Test add_ips_to_group rejects IP that is a registered group."""
517+ with mock_web3_is_address ():
518+ with patch .object (
519+ group .dispute_module_client , "isIpTagged" , return_value = False
520+ ), patch .object (
521+ group .ip_asset_registry_client ,
522+ "isRegisteredGroup" ,
523+ side_effect = [False , True ], # first ip ok, second ip is group
524+ ):
525+ with pytest .raises (
526+ ValueError ,
527+ match = "Cannot add group to group" ,
528+ ):
529+ group .add_ips_to_group (
530+ group_ip_id = IP_ID ,
531+ ip_ids = [IP_ID , ADDRESS ],
532+ )
533+
459534 def test_add_ips_to_group_success (
460535 self ,
461536 group : Group ,
462537 mock_web3_is_address ,
463538 ):
464539 """Test successful add_ips_to_group operation."""
465540 with mock_web3_is_address ():
466- with patch (
541+ with patch .object (
542+ group .dispute_module_client , "isIpTagged" , return_value = False
543+ ), patch .object (
544+ group .ip_asset_registry_client ,
545+ "isRegisteredGroup" ,
546+ return_value = False ,
547+ ), patch (
467548 "story_protocol_python_sdk.resources.Group.build_and_send_transaction" ,
468549 return_value = {"tx_hash" : TX_HASH , "tx_receipt" : {}},
469550 ):
@@ -482,7 +563,13 @@ def test_add_ips_to_group_default_max_allowed_reward_share_percentage(
482563 ):
483564 """Test add_ips_to_group uses default max_allowed_reward_share_percentage 100."""
484565 with mock_web3_is_address ():
485- with patch (
566+ with patch .object (
567+ group .dispute_module_client , "isIpTagged" , return_value = False
568+ ), patch .object (
569+ group .ip_asset_registry_client ,
570+ "isRegisteredGroup" ,
571+ return_value = False ,
572+ ), patch (
486573 "story_protocol_python_sdk.resources.Group.build_and_send_transaction" ,
487574 return_value = {"tx_hash" : TX_HASH , "tx_receipt" : {}},
488575 ) as mock_build :
@@ -501,7 +588,13 @@ def test_add_ips_to_group_max_allowed_reward_share_percentage_zero(
501588 ):
502589 """Test add_ips_to_group with max_allowed_reward_share_percentage 0."""
503590 with mock_web3_is_address ():
504- with patch (
591+ with patch .object (
592+ group .dispute_module_client , "isIpTagged" , return_value = False
593+ ), patch .object (
594+ group .ip_asset_registry_client ,
595+ "isRegisteredGroup" ,
596+ return_value = False ,
597+ ), patch (
505598 "story_protocol_python_sdk.resources.Group.build_and_send_transaction" ,
506599 return_value = {"tx_hash" : TX_HASH , "tx_receipt" : {}},
507600 ) as mock_build :
@@ -518,7 +611,13 @@ def test_add_ips_to_group_transaction_fails(
518611 ):
519612 """Test add_ips_to_group when transaction build/send fails."""
520613 with mock_web3_is_address ():
521- with patch (
614+ with patch .object (
615+ group .dispute_module_client , "isIpTagged" , return_value = False
616+ ), patch .object (
617+ group .ip_asset_registry_client ,
618+ "isRegisteredGroup" ,
619+ return_value = False ,
620+ ), patch (
522621 "story_protocol_python_sdk.resources.Group.build_and_send_transaction" ,
523622 side_effect = Exception ("Transaction build failed" ),
524623 ):
@@ -550,6 +649,48 @@ def test_remove_ips_from_group_invalid_group_ip_id(
550649 ip_ids = [IP_ID ],
551650 )
552651
652+ def test_remove_ips_from_group_group_has_derivative_ips (
653+ self , group : Group , mock_web3_is_address
654+ ):
655+ """Test remove_ips_from_group rejects when group has derivative IPs."""
656+ with mock_web3_is_address ():
657+ with patch .object (
658+ group .license_registry_client ,
659+ "hasDerivativeIps" ,
660+ return_value = True ,
661+ ):
662+ with pytest .raises (
663+ ValueError ,
664+ match = "Group frozen:.*has derivative IPs" ,
665+ ):
666+ group .remove_ips_from_group (
667+ group_ip_id = IP_ID ,
668+ ip_ids = [IP_ID ],
669+ )
670+
671+ def test_remove_ips_from_group_group_has_minted_license_tokens (
672+ self , group : Group , mock_web3_is_address
673+ ):
674+ """Test remove_ips_from_group rejects when group has minted license tokens."""
675+ with mock_web3_is_address ():
676+ with patch .object (
677+ group .license_registry_client ,
678+ "hasDerivativeIps" ,
679+ return_value = False ,
680+ ), patch .object (
681+ group .license_token_client ,
682+ "getTotalTokensByLicensor" ,
683+ return_value = 10 ,
684+ ):
685+ with pytest .raises (
686+ ValueError ,
687+ match = "Group frozen:.*has already minted license tokens" ,
688+ ):
689+ group .remove_ips_from_group (
690+ group_ip_id = IP_ID ,
691+ ip_ids = [IP_ID ],
692+ )
693+
553694 def test_remove_ips_from_group_invalid_ip_id (self , group : Group , mock_web3 ):
554695 """Test remove_ips_from_group with invalid IP ID."""
555696 invalid_ip_id = "invalid_ip_id"
@@ -571,7 +712,15 @@ def test_remove_ips_from_group_success(
571712 ):
572713 """Test successful remove_ips_from_group operation."""
573714 with mock_web3_is_address ():
574- with patch (
715+ with patch .object (
716+ group .license_registry_client ,
717+ "hasDerivativeIps" ,
718+ return_value = False ,
719+ ), patch .object (
720+ group .license_token_client ,
721+ "getTotalTokensByLicensor" ,
722+ return_value = 0 ,
723+ ), patch (
575724 "story_protocol_python_sdk.resources.Group.build_and_send_transaction" ,
576725 return_value = {"tx_hash" : TX_HASH , "tx_receipt" : {}},
577726 ):
@@ -588,7 +737,15 @@ def test_remove_ips_from_group_transaction_fails(
588737 ):
589738 """Test remove_ips_from_group when transaction build/send fails."""
590739 with mock_web3_is_address ():
591- with patch (
740+ with patch .object (
741+ group .license_registry_client ,
742+ "hasDerivativeIps" ,
743+ return_value = False ,
744+ ), patch .object (
745+ group .license_token_client ,
746+ "getTotalTokensByLicensor" ,
747+ return_value = 0 ,
748+ ), patch (
592749 "story_protocol_python_sdk.resources.Group.build_and_send_transaction" ,
593750 side_effect = Exception ("Transaction build failed" ),
594751 ):
0 commit comments