@@ -923,3 +923,177 @@ def test_checkout_intent_different_slug_ignored(self, mock_lms_api_client, mock_
923923 # Verify workflow was still created successfully
924924 workflow = ProvisionNewCustomerWorkflow .objects .first ()
925925 self .assertIsNotNone (workflow )
926+
927+
928+ @ddt .ddt
929+ class TestSubscriptionPlanOLIUpdateView (APITest ):
930+ """
931+ Tests for the SubscriptionPlan OLI update endpoint.
932+ """
933+
934+ def setUp (self ):
935+ super ().setUp ()
936+ self .user = UserFactory ()
937+ self .checkout_intent = CheckoutIntent .objects .create (
938+ user = self .user ,
939+ enterprise_slug = 'test-enterprise' ,
940+ enterprise_name = 'Test Enterprise' ,
941+ quantity = 10 ,
942+ state = CheckoutIntentState .FULFILLED ,
943+ expires_at = timezone .now () + timedelta (hours = 1 ),
944+ enterprise_uuid = TEST_ENTERPRISE_UUID ,
945+ )
946+ self .workflow = ProvisionNewCustomerWorkflow .objects .create (
947+ input_data = {'test' : 'data' }
948+ )
949+ self .checkout_intent .workflow = self .workflow
950+ self .checkout_intent .save ()
951+
952+ self .subscription_plan_uuid = uuid .uuid4 ()
953+ # Create a subscription plan step with complete output data
954+ GetCreateSubscriptionPlanStep .objects .create (
955+ workflow_record_uuid = self .workflow .uuid ,
956+ input_data = {
957+ 'title' : 'Paid Plan' ,
958+ 'is_trial' : False ,
959+ 'salesforce_opportunity_line_item' : 'existing_oli_123' ,
960+ 'start_date' : '2025-01-15T00:00:00Z' ,
961+ 'expiration_date' : '2025-12-31T23:59:59Z' ,
962+ 'desired_num_licenses' : 10 ,
963+ 'product_id' : 1 ,
964+ },
965+ output_data = {
966+ 'uuid' : str (self .subscription_plan_uuid ),
967+ 'title' : 'Paid Plan' ,
968+ 'salesforce_opportunity_line_item' : 'existing_oli_123' ,
969+ 'created' : '2025-01-01T00:00:00Z' ,
970+ 'start_date' : '2025-01-15T00:00:00Z' ,
971+ 'expiration_date' : '2025-12-31T23:59:59Z' ,
972+ 'is_active' : True ,
973+ 'is_current' : True ,
974+ 'plan_type' : 'Standard' ,
975+ 'enterprise_catalog_uuid' : str (TEST_CATALOG_UUID ),
976+ 'product' : 1 ,
977+ }
978+ )
979+
980+ self .endpoint_url = reverse ('api:v1:subscription-plan-oli-update' )
981+ self .set_jwt_cookie ([
982+ {
983+ 'system_wide_role' : SYSTEM_ENTERPRISE_PROVISIONING_ADMIN_ROLE ,
984+ 'context' : ALL_ACCESS_CONTEXT ,
985+ },
986+ ])
987+
988+ def tearDown (self ):
989+ super ().tearDown ()
990+ CheckoutIntent .objects .all ().delete ()
991+ ProvisionNewCustomerWorkflow .objects .all ().delete ()
992+ GetCreateSubscriptionPlanStep .objects .all ().delete ()
993+
994+ @mock .patch ('enterprise_access.apps.api.v1.views.provisioning.LicenseManagerApiClient' )
995+ def test_successful_oli_update (self , mock_license_manager_client ):
996+ """Test successful update of SubscriptionPlan OLI."""
997+ mock_client = mock_license_manager_client .return_value
998+ mock_client .update_subscription_plan .return_value = {
999+ 'uuid' : str (self .subscription_plan_uuid ),
1000+ 'salesforce_opportunity_line_item' : 'new_oli_456' ,
1001+ }
1002+
1003+ request_data = {
1004+ 'checkout_intent_id' : self .checkout_intent .id ,
1005+ 'salesforce_opportunity_line_item' : 'new_oli_456' ,
1006+ 'is_trial' : False ,
1007+ }
1008+
1009+ response = self .client .post (self .endpoint_url , data = request_data )
1010+
1011+ self .assertEqual (response .status_code , status .HTTP_200_OK , response .json ())
1012+ response_data = response .json ()
1013+ self .assertTrue (response_data ['success' ])
1014+ self .assertEqual (str (response_data ['subscription_plan_uuid' ]), str (self .subscription_plan_uuid ))
1015+ self .assertEqual (response_data ['salesforce_opportunity_line_item' ], 'new_oli_456' )
1016+ self .assertEqual (str (response_data ['checkout_intent_uuid' ]), str (self .checkout_intent .uuid ))
1017+ self .assertEqual (str (response_data ['checkout_intent_id' ]), str (self .checkout_intent .id ))
1018+
1019+ mock_client .update_subscription_plan .assert_called_once_with (
1020+ subscription_uuid = str (self .subscription_plan_uuid ),
1021+ salesforce_opportunity_line_item = 'new_oli_456'
1022+ )
1023+
1024+ def test_checkout_intent_not_found (self ):
1025+ """Test error when CheckoutIntent doesn't exist."""
1026+ request_data = {
1027+ 'checkout_intent_uuid' : str (uuid .uuid4 ()), # Non-existent id
1028+ 'salesforce_opportunity_line_item' : 'new_oli_456' ,
1029+ }
1030+
1031+ response = self .client .post (self .endpoint_url , data = request_data )
1032+
1033+ self .assertEqual (response .status_code , status .HTTP_404_NOT_FOUND )
1034+ response_data = response .json ()
1035+ if isinstance (response_data , dict ):
1036+ self .assertIn ('not found' , response_data .get ('detail' , '' ).lower ())
1037+ else :
1038+ self .assertTrue (any ('not found' in str (item ).lower () for item in response_data ))
1039+
1040+ def test_no_workflow_associated (self ):
1041+ """Test error when CheckoutIntent has no workflow."""
1042+ checkout_intent_no_workflow = CheckoutIntent .objects .create (
1043+ user = UserFactory (),
1044+ enterprise_slug = 'test-no-workflow' ,
1045+ enterprise_name = 'Test No Workflow' ,
1046+ quantity = 5 ,
1047+ state = CheckoutIntentState .PAID ,
1048+ expires_at = timezone .now () + timedelta (hours = 1 ),
1049+ )
1050+
1051+ request_data = {
1052+ 'checkout_intent_uuid' : str (checkout_intent_no_workflow .uuid ),
1053+ 'salesforce_opportunity_line_item' : 'new_oli_456' ,
1054+ }
1055+
1056+ response = self .client .post (self .endpoint_url , data = request_data )
1057+
1058+ self .assertEqual (response .status_code , status .HTTP_400_BAD_REQUEST )
1059+ response_data = response .json ()
1060+ # Handle both dict and list response formats
1061+ if isinstance (response_data , dict ):
1062+ self .assertIn ('no associated workflow' , response_data .get ('detail' , '' ).lower ())
1063+ else :
1064+ # If it's a list (field errors), check the first item
1065+ self .assertTrue (any ('no associated workflow' in str (item ).lower () for item in response_data ))
1066+
1067+ @mock .patch ('enterprise_access.apps.api.v1.views.provisioning.LicenseManagerApiClient' )
1068+ def test_license_manager_api_error (self , mock_license_manager_client ):
1069+ """Test error handling when License Manager API fails."""
1070+ mock_client = mock_license_manager_client .return_value
1071+ mock_client .update_subscription_plan .side_effect = Exception ('License Manager API Error' )
1072+
1073+ request_data = {
1074+ 'checkout_intent_uuid' : str (self .checkout_intent .uuid ),
1075+ 'salesforce_opportunity_line_item' : 'new_oli_456' ,
1076+ 'is_trial' : False ,
1077+ }
1078+
1079+ response = self .client .post (self .endpoint_url , data = request_data )
1080+
1081+ self .assertEqual (response .status_code , status .HTTP_500_INTERNAL_SERVER_ERROR )
1082+ response_data = response .json ()
1083+ if isinstance (response_data , dict ):
1084+ self .assertIn ('failed to update subscription plan' , response_data .get ('detail' , '' ).lower ())
1085+ else :
1086+ self .assertTrue (any ('failed to update subscription plan' in str (item ).lower () for item in response_data ))
1087+
1088+ def test_unauthorized_access (self ):
1089+ """Test that unauthorized users cannot access the endpoint."""
1090+ self .set_jwt_cookie ([]) # Remove authorization
1091+
1092+ request_data = {
1093+ 'checkout_intent_id' : str (self .checkout_intent .id ),
1094+ 'salesforce_opportunity_line_item' : 'new_oli_456' ,
1095+ }
1096+
1097+ response = self .client .post (self .endpoint_url , data = request_data )
1098+
1099+ self .assertEqual (response .status_code , status .HTTP_403_FORBIDDEN )
0 commit comments