def test_suppress_revocation_for_zero_dollar_refund(self): """ Verify that the function does not require use of fulfillment modules to mark lines in a refund corresponding to a total credit of $0 as complete. """ refund = RefundFactory(status=REFUND.PAYMENT_REFUNDED) refund.total_credit_excl_tax = 0 refund.save() self.assertTrue(revoke_fulfillment_for_refund(refund)) self.assertEqual(refund.status, REFUND.PAYMENT_REFUNDED) self.assertEqual(set([line.status for line in refund.lines.all()]), {REFUND_LINE.COMPLETE})
class RefundProcessViewTests(ThrottlingMixin, TestCase): def setUp(self): super(RefundProcessViewTests, self).setUp() self.user = self.create_user(is_staff=True) self.client.login(username=self.user.username, password=self.password) self.refund = RefundFactory(user=self.user) def put(self, action): data = '{{"action": "{}"}}'.format(action) path = reverse('api:v2:refunds:process', kwargs={'pk': self.refund.id}) return self.client.put(path, data, JSON_CONTENT_TYPE) def test_staff_only(self): """ The view should only be accessible to staff users. """ user = self.create_user(is_staff=False) self.client.login(username=user.username, password=self.password) response = self.put('approve') self.assertEqual(response.status_code, 403) def test_invalid_action(self): """ If the action is neither approve nor deny, the view should return HTTP 400. """ response = self.put('reject') self.assertEqual(response.status_code, 400) @ddt.data('approve', 'deny') def test_success(self, action): """ If the action succeeds, the view should return HTTP 200 and the serialized Refund. """ with mock.patch('ecommerce.extensions.refund.models.Refund.{}'.format(action), mock.Mock(return_value=True)): response = self.put(action) self.assertEqual(response.status_code, 200) self.assertEqual(response.data, RefundSerializer(self.refund).data) @mock.patch('ecommerce.extensions.refund.models.Refund._revoke_lines') @mock.patch('ecommerce.extensions.refund.models.Refund._issue_credit') def test_success_approve_payment_only(self, mock_issue_credit, mock_revoke_lines): """ Verify the endpoint supports approving the refund, and issuing credit without revoking fulfillment. """ mock_issue_credit.return_value = None with mock.patch('ecommerce.extensions.refund.models.logger') as patched_log: response = self.put('approve_payment_only') self.assertEqual(response.status_code, 200) self.refund.refresh_from_db() self.assertEqual(response.data['status'], self.refund.status) self.assertEqual(response.data['status'], 'Complete') patched_log.info.assert_called_with('Skipping the revocation step for refund [%d].', self.refund.id) self.assertFalse(mock_revoke_lines.called) @ddt.data( ('approve', 'approve'), ('approve', 'approve_payment_only'), ('deny', 'deny') ) @ddt.unpack def test_failure(self, action, decision): """ If the action fails, the view should return HTTP 500 and the serialized Refund. """ with mock.patch('ecommerce.extensions.refund.models.Refund.{}'.format(action), mock.Mock(return_value=False)): response = self.put(decision) self.assertEqual(response.status_code, 500) self.assertEqual(response.data, RefundSerializer(self.refund).data) @ddt.data( ('approve', REFUND.COMPLETE), ('approve_payment_only', REFUND.COMPLETE), ('deny', REFUND.DENIED) ) @ddt.unpack def test_subsequent_approval(self, action, _status): """ Verify the endpoint supports reprocessing a previously-processed refund. """ self.refund.status = _status self.refund.save() response = self.put(action) self.assertEqual(response.status_code, 200)