initial_commit

This commit is contained in:
MelvynFaith
2026-11-02 14:58:43 +07:00
commit b06be6e0a7
20 changed files with 547 additions and 0 deletions
+123
View File
@@ -0,0 +1,123 @@
"""Pengujian mesin status Order: transisi valid, tidak valid, dan guard pelaku.
Kebutuhan: FR-ORD-04, FR-ORD-06, FR-ORD-09, FR-ORD-10.
"""
from datetime import datetime, timedelta
import pytest
from umkm.order import (
TRANSITIONS,
InvalidTransitionError,
Order,
OrderStatus as S,
PermissionDeniedError,
)
BUYER, SELLER = "buyer-1", "seller-1"
T0 = datetime(2026, 11, 1, 10, 0)
def new_order():
return Order("o-1", BUYER, SELLER)
def order_in(status):
"""Bawa order ke status yang diminta lewat transisi sungguhan."""
order = new_order()
if status in (S.PAID, S.SHIPPED, S.COMPLETED):
order.pay()
if status in (S.SHIPPED, S.COMPLETED):
order.ship(SELLER, T0)
if status is S.COMPLETED:
order.confirm_received(BUYER)
if status is S.CANCELLED:
order.cancel(BUYER)
if status is S.EXPIRED:
order.expire()
assert order.status is status
return order
# Semua event yang dapat dipicu; auto_complete memakai waktu tepat jatuh tempo.
EVENTS = {
"pay": lambda o: o.pay(),
"cancel": lambda o: o.cancel(BUYER),
"expire": lambda o: o.expire(),
"ship": lambda o: o.ship(SELLER, T0),
"complete": lambda o: o.confirm_received(BUYER),
"auto_complete": lambda o: o.auto_complete(T0 + timedelta(days=3)),
}
VALID = dict(TRANSITIONS)
VALID[(S.SHIPPED, "auto_complete")] = S.COMPLETED
INVALID = [(s, e) for s in S for e in EVENTS if (s, e) not in VALID]
def test_order_baru_berstatus_pending_payment():
assert new_order().status is S.PENDING_PAYMENT
@pytest.mark.parametrize(("src", "event"), list(VALID))
def test_transisi_valid(src, event):
order = order_in(src)
EVENTS[event](order)
assert order.status is VALID[(src, event)]
@pytest.mark.parametrize(("src", "event"), INVALID)
def test_transisi_tidak_valid_ditolak_dan_status_tetap(src, event):
order = order_in(src)
with pytest.raises(InvalidTransitionError):
EVENTS[event](order)
assert order.status is src
def test_jumlah_kombinasi_lengkap():
"""6 status x 6 event = 36 kombinasi: 6 valid + 30 tidak valid."""
assert len(VALID) == 6
assert len(INVALID) == 30
@pytest.mark.parametrize("final", [S.COMPLETED, S.CANCELLED, S.EXPIRED])
def test_status_akhir_tidak_punya_transisi_keluar(final):
assert not [key for key in VALID if key[0] is final]
def test_seller_tidak_boleh_menyelesaikan_order():
"""FR-ORD-10: hanya pembeli yang mengonfirmasi."""
order = order_in(S.SHIPPED)
with pytest.raises(PermissionDeniedError):
order.confirm_received(SELLER)
assert order.status is S.SHIPPED
def test_hanya_seller_pemilik_toko_boleh_mengirim():
order = order_in(S.PAID)
with pytest.raises(PermissionDeniedError):
order.ship("seller-lain", T0)
assert order.status is S.PAID
def test_hanya_pembeli_pemilik_order_boleh_membatalkan():
order = new_order()
with pytest.raises(PermissionDeniedError):
order.cancel("buyer-lain")
assert order.status is S.PENDING_PAYMENT
def test_auto_complete_ditolak_sebelum_3_hari():
order = order_in(S.SHIPPED)
with pytest.raises(InvalidTransitionError):
order.auto_complete(T0 + timedelta(days=3) - timedelta(seconds=1))
assert order.status is S.SHIPPED
def test_auto_complete_tepat_3_hari_berhasil():
order = order_in(S.SHIPPED)
order.auto_complete(T0 + timedelta(days=3))
assert order.status is S.COMPLETED
def test_ship_mencatat_waktu_pengiriman():
assert order_in(S.SHIPPED).shipped_at == T0