33
44import pandas
55import pytest
6+ import yaml
67from admin_api .serializers .shop .orders import OrderAdminSerializer
78from admin_api .test .helpers import OrdersAdminApi
89from admin_api .views .shop .orders import OrderAdminViewSet
10+ from core .const .shop_error_messages import NotRefundableErrorMessages
911from freezegun import freeze_time
1012from model_bakery import baker
1113from rest_framework .fields import DateTimeField
1719 HTTP_403_FORBIDDEN ,
1820 HTTP_404_NOT_FOUND ,
1921)
22+ from rest_framework .test import APIClient
2023from shop .order .models import CustomerInfo , Order , OrderProductRelation
2124from shop .payment_history .models import PaymentHistory , PaymentHistoryStatus
2225
@@ -42,6 +45,22 @@ def test_admin_list_returns_only_orders_with_payment_history_and_products(api_cl
4245 }
4346
4447
48+ @pytest .mark .django_db
49+ def test_admin_list_includes_free_completed_order (api_client , order_factory ):
50+ order = order_factory (status = "completed" , product_price = 0 , imp_id = None )
51+
52+ response = OrdersAdminApi (http_client = api_client ).list ()
53+
54+ assert response .status_code == HTTP_200_OK
55+ row = response .json ()["results" ][0 ]
56+ assert row ["id" ] == str (order .id )
57+ assert row ["current_status" ] == PaymentHistoryStatus .completed
58+ assert row ["current_paid_price" ] == 0
59+ assert row ["latest_imp_id" ] is None
60+ assert row ["payment_histories" ][0 ]["price" ] == 0
61+ assert row ["payment_histories" ][0 ]["imp_id" ] is None
62+
63+
4564@pytest .mark .django_db
4665def test_admin_list_orders_by_first_paid_at_desc (api_client , order_factory ):
4766 # 먼저 생성된 주문(= created_at 이 더 과거)이 더 최근에 결제되도록 구성.
@@ -136,6 +155,19 @@ def test_admin_refund_action_refunds_order(api_client, mock_portone_req_cancel_p
136155 assert completed_order .payment_histories .filter (status = PaymentHistoryStatus .refunded ).exists ()
137156
138157
158+ @pytest .mark .django_db
159+ def test_admin_refund_action_rejects_free_completed_order_without_portone_cancel (
160+ api_client , mock_portone_req_cancel_payment , order_factory
161+ ):
162+ order = order_factory (status = "completed" , product_price = 0 , imp_id = None )
163+
164+ response = OrdersAdminApi (http_client = api_client ).refund (order .id )
165+
166+ assert response .status_code == HTTP_400_BAD_REQUEST
167+ assert NotRefundableErrorMessages .ORDER_IMP_ID_NOT_EXIST in str (response .json ())
168+ mock_portone_req_cancel_payment .assert_not_called ()
169+
170+
139171@pytest .mark .django_db
140172def test_admin_refund_product_action_does_partial_refund (
141173 api_client , ticket_product , mock_portone_req_cancel_payment , order_factory
@@ -165,6 +197,39 @@ def test_admin_refund_product_action_returns_404_for_unknown_rel(api_client, ord
165197 assert response .status_code == HTTP_404_NOT_FOUND
166198
167199
200+ @pytest .mark .django_db
201+ def test_admin_refund_product_action_rejects_free_completed_opr_without_portone_cancel (
202+ api_client , mock_portone_req_cancel_payment , order_factory
203+ ):
204+ order = order_factory (status = "completed" , product_price = 0 , imp_id = None )
205+ opr = order .products .get ()
206+
207+ response = OrdersAdminApi (http_client = api_client ).refund_product (order .id , opr .id )
208+
209+ assert response .status_code == HTTP_400_BAD_REQUEST
210+ assert NotRefundableErrorMessages .ORDER_NOT_REFUNDABLE in str (response .json ())
211+ mock_portone_req_cancel_payment .assert_not_called ()
212+
213+
214+ @pytest .mark .django_db
215+ def test_admin_refund_actions_document_validation_error_responses ():
216+ response = APIClient ().get ("/api/schema/v1/" )
217+ assert response .status_code == HTTP_200_OK
218+
219+ schema = yaml .safe_load (response .content )
220+ total_refund_path = next (path for path in schema ["paths" ] if path .endswith ("/admin-api/shop/order/{id}/refund/" ))
221+ product_refund_path = next (
222+ path for path in schema ["paths" ] if path .endswith ("/admin-api/shop/order/{id}/products/{rel_id}/refund/" )
223+ )
224+
225+ for path in (total_refund_path , product_refund_path ):
226+ responses = schema ["paths" ][path ]["post" ]["responses" ]
227+ assert "204" in responses
228+ assert responses ["400" ]["content" ]["application/json" ]["schema" ] == {
229+ "$ref" : "#/components/schemas/ValidationErrorResponse" ,
230+ }
231+
232+
168233@pytest .mark .django_db
169234def test_admin_refund_allows_expired_window (api_client , mock_portone_req_cancel_payment , order_factory ):
170235 completed_order = order_factory (status = "completed" )
0 commit comments