introduce missing check_ts everywhere and restructure tests

This commit is contained in:
Lukas Bockstaller
2026-08-19 10:21:31 +02:00
parent 67d1ac7949
commit 74ff52f6a0
3 changed files with 399 additions and 316 deletions
+8 -6
View File
@@ -179,12 +179,13 @@ PositionSet: TypeAlias = Set[OrderPosition]
class PositionCheckFn(Protocol): class PositionCheckFn(Protocol):
def __call__(self, order: Order, keep: PositionSet, position: OrderPosition, /) -> CheckResult: def __call__(self, order: Order, keep: PositionSet, position: OrderPosition, check_ts: datetime.datetime,
/) -> CheckResult:
... ...
class ProcessCheckFn(Protocol): class ProcessCheckFn(Protocol):
def __call__(self, order: Order, keep: PositionSet, /) -> CheckResult: def __call__(self, order: Order, keep: PositionSet, check_ts: datetime.datetime, /) -> CheckResult:
... ...
@@ -197,11 +198,11 @@ class CancellationCheck:
related_selects: List[str] = field(default_factory=list) related_selects: List[str] = field(default_factory=list)
def evaluate(self, order: Order, keep: PositionSet, def evaluate(self, order: Order, keep: PositionSet,
position: OrderPosition | None) -> CheckResult: position: OrderPosition | None, check_ts: datetime.datetime) -> CheckResult:
if position and self.type == CheckTypes.POSITION: if position and self.type == CheckTypes.POSITION:
return self.check_fn(order, keep, position) return self.check_fn(order, keep, position, check_ts)
elif position is None and self.type == CheckTypes.PROCESS: elif position is None and self.type == CheckTypes.PROCESS:
return self.check_fn(order, keep) return self.check_fn(order, keep, check_ts)
else: else:
raise ValidationError("Type of the rule doesn't match the check_fn") raise ValidationError("Type of the rule doesn't match the check_fn")
@@ -256,6 +257,7 @@ class ProcessResult:
class CancellationResult: class CancellationResult:
position_result: PositionResult position_result: PositionResult
process_result: ProcessResult process_result: ProcessResult
check_ts: datetime.datetime
@property @property
def cancellation_possible(self) -> bool: def cancellation_possible(self) -> bool:
@@ -443,7 +445,7 @@ class CancellationRule(models.Model):
process_result = ProcessResult(process_check_results=process_check_results, process_result = ProcessResult(process_check_results=process_check_results,
process_rule_results=process_rule_results) process_rule_results=process_rule_results)
return CancellationResult(position_result=position_results, process_result=process_result) return CancellationResult(position_result=position_results, process_result=process_result, check_ts=check_ts)
@staticmethod @staticmethod
def _prefetch_order(event: Event, order: Order, checks: Checks) -> Order: def _prefetch_order(event: Event, order: Order, checks: Checks) -> Order:
+2 -1
View File
@@ -3563,7 +3563,8 @@ def signal_listener_issue_media(sender: Event, order: Order, **kwargs):
) )
def position_not_used_cancellation_check(order: Order, keep: PositionSet, position: OrderPosition): def position_not_used_cancellation_check(order: Order, keep: PositionSet, position: OrderPosition,
check_ts: datetime):
for pos in order.all_positions.all(): for pos in order.all_positions.all():
if pos == position and position not in keep: if pos == position and position not in keep:
for checkin in pos.all_checkins.all(): for checkin in pos.all_checkins.all():
+389 -309
View File
@@ -13,7 +13,7 @@ from pretix.base.models import (
) )
from pretix.base.models.cancellation import ( from pretix.base.models.cancellation import (
CancellationCheck, CancellationRule, CheckResult, Checks, CheckTypes, CancellationCheck, CancellationRule, CheckResult, Checks, CheckTypes,
FeeType, PositionResult, ProcessCancellationRule, ProcessResult, RuleResult, FeeType, PositionCancellationRule, PositionResult, ProcessCancellationRule, ProcessResult, RuleResult,
) )
from pretix.base.reldate import RelativeDate, RelativeDateWrapper from pretix.base.reldate import RelativeDate, RelativeDateWrapper
from pretix.base.services.orders import signal_listener_position_not_used from pretix.base.services.orders import signal_listener_position_not_used
@@ -82,16 +82,17 @@ def make_rule_result(fee, *, possible: bool = True, fee_type: FeeType = FeeType.
def make_cancellation_check(id: str, type: CheckTypes, result: bool, prefetches=None, def make_cancellation_check(id: str, type: CheckTypes, result: bool, prefetches=None,
related_selects=None) -> CancellationCheck: related_selects=None,
check_ts: datetime = datetime.now(tz=UTC)) -> CancellationCheck:
if related_selects is None: if related_selects is None:
related_selects = [] related_selects = []
if prefetches is None: if prefetches is None:
prefetches = [] prefetches = []
def position_check_fn(_order, _keep, _position): def position_check_fn(_order, _keep, _position, _check_ts=check_ts):
return make_check_result(result, id=id) return make_check_result(result, id=id)
def check_fn(_order, _keep): def check_fn(_order, _keep, _check_ts=check_ts):
return make_check_result(result, id=id) return make_check_result(result, id=id)
if type == CheckTypes.POSITION: if type == CheckTypes.POSITION:
@@ -100,326 +101,405 @@ def make_cancellation_check(id: str, type: CheckTypes, result: bool, prefetches=
return CancellationCheck(id, type, check_fn, prefetches=prefetches, related_selects=related_selects) return CancellationCheck(id, type, check_fn, prefetches=prefetches, related_selects=related_selects)
@pytest.mark.parametrize("partial_results,expected", [ class TestRuleResult:
([True], True), @pytest.mark.parametrize("partial_results,expected", [
([False], False), ([True], True),
([True, True], True), ([False], False),
([False, False], False), ([True, True], True),
([True, False], False), ([False, False], False),
]) ([True, False], False),
def test_rule_result_cancellation_possible(partial_results: List[bool], expected: bool): ])
check_results = [make_check_result(state) for state in partial_results] def test_rule_result_cancellation_possible(self, partial_results: List[bool], expected: bool):
check_results = [make_check_result(state) for state in partial_results]
result = RuleResult(id=1, partial_results=check_results, fee_type=FeeType.POSITION, fee=Decimal(0)) result = RuleResult(id=1, partial_results=check_results, fee_type=FeeType.POSITION, fee=Decimal(0))
assert result.cancellation_possible == expected assert result.cancellation_possible == expected
@pytest.mark.parametrize(
@pytest.mark.parametrize( ("left", "right", "expected"),
("left", "right", "expected"), [
[ (("5", True), ("10", True), True),
(("5", True), ("10", True), True), (("10", True), ("5", True), False),
(("10", True), ("5", True), False), (("5", True), ("5", True), False),
(("5", True), ("5", True), False), (("5", False), ("10", False), True),
(("5", False), ("10", False), True), (("100", True), ("1", False), True),
(("100", True), ("1", False), True), (("1", False), ("100", True), False),
(("1", False), ("100", True), False), ],
], ids=[
ids=[ "cheaper-lt-pricier-both-possible",
"cheaper-lt-pricier-both-possible", "pricier-not-lt-cheaper-both-possible",
"pricier-not-lt-cheaper-both-possible", "equal-fee-not-lt",
"equal-fee-not-lt", "cheaper-lt-pricier-both-impossible",
"cheaper-lt-pricier-both-impossible", "possible-lt-impossible-despite-higher-fee",
"possible-lt-impossible-despite-higher-fee", "impossible-not-lt-possible",
"impossible-not-lt-possible", ],
],
)
def test_lt_returns_expected(left, right, expected):
a = make_rule_result(left[0], possible=left[1])
b = make_rule_result(right[0], possible=right[1])
assert (a < b) is expected
@pytest.mark.parametrize(
("fee_type", "absolute", "reference", "result"),
[
(FeeType.MINIMUM, Decimal(10), Decimal(1), Decimal(9)),
(FeeType.MINIMUM, Decimal(1), Decimal(10), Decimal(0)),
(FeeType.MINIMUM, Decimal(10), Decimal(10), Decimal(0)),
(FeeType.ADDITIONAL, Decimal(10), Decimal(1), Decimal(10)),
(FeeType.ADDITIONAL, Decimal(1), Decimal(10), Decimal(1)),
(FeeType.ADDITIONAL, Decimal(10), Decimal(10), Decimal(10)),
],
ids=[
"minimum-absolute-less-than-reference",
"minimum-absolute-more-than-reference",
"minimum-absolute-equal-reference",
"additional-absolute-less-than-reference",
"additional-absolute-more-than-reference",
"additional-absolute-equal-reference",
],
)
def test_from_process_fee(fee_type: Literal[FeeType.MINIMUM, FeeType.ADDITIONAL], absolute: Decimal, reference: Decimal,
result: Decimal):
res = RuleResult.from_process_fee(id=1, partial_results=[],
fee_type=fee_type, absolute_fee=absolute, reference_price=reference)
assert res.fee == result
@pytest.mark.parametrize(
("check_results", "rule_results", "cancellation_possible", "fee"),
[
({1: [make_check_result(True)]}, {1: [make_rule_result(Decimal(10), possible=True)]}, True, Decimal(10)),
({1: [make_check_result(False)]}, {1: [make_rule_result(Decimal(10), possible=True)]}, False, Decimal(10)),
({1: [make_check_result(False)]}, {1: [make_rule_result(Decimal(10), possible=False)]}, False, Decimal(0)),
({1: [make_check_result(True), make_check_result(False)]}, {1: [make_rule_result(Decimal(10), possible=True)]},
False, Decimal(10)),
({1: [make_check_result(True)]},
{1: [make_rule_result(Decimal(10), possible=False), make_rule_result(Decimal(5), possible=True)]}, True,
Decimal(5)),
({1: [make_check_result(True)]},
{1: [make_rule_result(Decimal(10), possible=False), make_rule_result(Decimal(5), possible=False)]}, False,
Decimal(0)),
],
)
def test_position_results(check_results, rule_results, cancellation_possible, fee):
pos_res = PositionResult(position_check_results=check_results, position_rule_results=rule_results, )
assert pos_res.cancellation_possible == cancellation_possible
assert pos_res.fee_value == fee
@pytest.mark.parametrize(
("check_results", "rule_results", "cancellation_possible", "fee"),
[
([make_check_result(True)], [make_rule_result(Decimal(10), possible=True)], True, Decimal(10)),
([make_check_result(False)], [make_rule_result(Decimal(10), possible=True)], False, Decimal(10)),
([make_check_result(False)], [make_rule_result(Decimal(10), possible=False)], False, Decimal(0)),
([make_check_result(True), make_check_result(False)], [make_rule_result(Decimal(10), possible=True)],
False, Decimal(10)),
([make_check_result(True)],
[make_rule_result(Decimal(10), possible=False), make_rule_result(Decimal(5), possible=True)], True,
Decimal(5)),
([make_check_result(True)],
[make_rule_result(Decimal(10), possible=False), make_rule_result(Decimal(5), possible=False)], False,
Decimal(0)),
],
)
def test_process_results(check_results, rule_results, cancellation_possible, fee):
pos_res = ProcessResult(process_check_results=check_results, process_rule_results=rule_results, )
assert pos_res.cancellation_possible == cancellation_possible
assert pos_res.fee_value == fee
@pytest.mark.parametrize(
("received", "position_checks", "process_checks", "raises"),
[
([('', make_cancellation_check('pos-1', CheckTypes.POSITION, True))],
[make_cancellation_check('pos-1', CheckTypes.POSITION, True)],
[],
contextlib.nullcontext()),
([('', make_cancellation_check('proc-1', CheckTypes.PROCESS, True))],
[],
[make_cancellation_check('proc-1', CheckTypes.PROCESS, True)],
contextlib.nullcontext()),
([('', make_cancellation_check('proc-1', CheckTypes.PROCESS, True)),
('', make_cancellation_check('proc-1', CheckTypes.PROCESS, True))],
[],
[make_cancellation_check('proc-1', CheckTypes.PROCESS, True)],
pytest.raises(ValueError)),
([('', make_cancellation_check('pos-1', CheckTypes.POSITION, True)),
('', make_cancellation_check('pos-1', CheckTypes.POSITION, True))],
[],
[make_cancellation_check('pos-1', CheckTypes.POSITION, True)],
pytest.raises(ValueError)),
([('', 1)],
[],
[make_cancellation_check('pos-1', CheckTypes.POSITION, True)],
pytest.raises(ValueError)),
]
)
def test_cancellation_rule_collect_checks(received, position_checks, process_checks, raises):
event = cast(Event, cast(object, {}))
def send_fn(_event):
return received
with raises:
checks = CancellationRule._collect_checks(event=event, send_fn=send_fn)
assert checks.position == position_checks
assert checks.process == process_checks
@pytest.mark.django_db
def test_prefetch_empty(event, order):
checks = Checks(position=[], process=[])
with scope(organizer=event.organizer):
prefetched_order = CancellationRule._prefetch_order(event, order, checks)
assert prefetched_order.id == order.id
@pytest.mark.django_db
def test_prefetch_incl_values_select_related(event, order):
checks = Checks(
position=[
make_cancellation_check('pos_1', CheckTypes.POSITION, True, prefetches=[lambda: Prefetch('all_positions')],
related_selects=['organizer'])],
process=[
make_cancellation_check('proc_1', CheckTypes.PROCESS, True, prefetches=[lambda: Prefetch('all_positions')],
related_selects=['organizer'])]
) )
def test_lt_returns_expected(self, left, right, expected):
a = make_rule_result(left[0], possible=left[1])
b = make_rule_result(right[0], possible=right[1])
assert (a < b) is expected
with scope(organizer=event.organizer): @pytest.mark.parametrize(
prefetched_order = CancellationRule._prefetch_order(event, order, checks) ("fee_type", "absolute", "reference", "result"),
assert prefetched_order.id == order.id [
(FeeType.MINIMUM, Decimal(10), Decimal(1), Decimal(9)),
(FeeType.MINIMUM, Decimal(1), Decimal(10), Decimal(0)),
@pytest.mark.django_db (FeeType.MINIMUM, Decimal(10), Decimal(10), Decimal(0)),
def test_ticket_not_used(event, order, order_position, checkin_list): (FeeType.ADDITIONAL, Decimal(10), Decimal(1), Decimal(10)),
position_not_used_check = signal_listener_position_not_used(event) (FeeType.ADDITIONAL, Decimal(1), Decimal(10), Decimal(1)),
checks = Checks(position=[position_not_used_check], process=[]) (FeeType.ADDITIONAL, Decimal(10), Decimal(10), Decimal(10)),
keep = set() ],
ids=[
with scope(organizer=event.organizer): "minimum-absolute-less-than-reference",
prefetched_order = CancellationRule._prefetch_order(event, order, checks) "minimum-absolute-more-than-reference",
with ensure_no_queries(): "minimum-absolute-equal-reference",
result = position_not_used_check.evaluate(prefetched_order, keep, order_position) "additional-absolute-less-than-reference",
assert result.cancellation_possible is True "additional-absolute-more-than-reference",
"additional-absolute-equal-reference",
Checkin.objects.create( ],
list=checkin_list,
position=order_position,
successful=True
)
prefetched_order = CancellationRule._prefetch_order(event, order, checks)
with ensure_no_queries():
result = position_not_used_check.evaluate(prefetched_order, keep, order_position)
assert result.cancellation_possible is False
REFERENCE_DT = datetime(2017, 12, 27, 4, 0, 0, tzinfo=UTC)
@pytest.fixture(params=["date", "datetime", "order", "event"])
def rdt_reldate_variants(request):
return request.param
@pytest.fixture
def rdt_reldate(rdt_reldate_variants) -> RelativeDateWrapper:
if rdt_reldate_variants == 'date' or rdt_reldate_variants == 'datetime':
return RelativeDateWrapper.from_string(REFERENCE_DT.isoformat())
elif rdt_reldate_variants == 'order':
return RelativeDateWrapper(
RelativeDate(days=1, time=None, base_date_name='order__datetime', minutes=None, is_after=True))
elif rdt_reldate_variants == 'event':
return RelativeDateWrapper(
RelativeDate(days=1, time=None, base_date_name='event__date_from', minutes=None, is_after=True))
else:
raise ValueError()
@pytest.fixture(params=["single_event", "subevents"])
def rdt_event_variants(request):
return request.param
@pytest.fixture
def rdt_events(rdt_event_variants, event):
if rdt_event_variants == "single_event":
event.date_from = REFERENCE_DT
event.save()
else:
event.has_subevents = True
event.subevents.create(
name='1',
date_from=REFERENCE_DT,
)
event.subevents.create(
name='2',
date_from=REFERENCE_DT + timedelta(days=1),
)
event.subevents.create(
name='3',
date_from=REFERENCE_DT + timedelta(days=2),
)
return event
@pytest.fixture
def rdt_item(rdt_events):
return rdt_events.items.create(
name='Ticket',
category=None, default_price=23,
admission=True
) )
def test_from_process_fee(
self,
fee_type: Literal[FeeType.MINIMUM, FeeType.ADDITIONAL],
absolute: Decimal,
reference: Decimal,
result: Decimal
):
res = RuleResult.from_process_fee(id=1, partial_results=[],
fee_type=fee_type, absolute_fee=absolute, reference_price=reference)
assert res.fee == result
@pytest.mark.parametrize(
@pytest.fixture(params=["EARLIEST", "LATEST"]) ("position_price", "percentage", "result"),
def rdt_mode_variants(request): [
return request.param (Decimal(10), Decimal(10), Decimal(1)),
(Decimal(10), Decimal("9.9"), Decimal("0.99"))
],
@pytest.fixture
def rdt_order(rdt_events):
o = Order.objects.create(
code='123456', event=rdt_events, email='dummy@dummy.test',
status=Order.STATUS_PENDING,
datetime=REFERENCE_DT + timedelta(hours=6), # 6 hours offset mark orders
sales_channel=rdt_events.organizer.sales_channels.get(identifier="web"),
total=14, locale='en'
) )
return o def test_from_relative_fee(self, position_price, percentage, result):
res = RuleResult.from_relative_fee(id=1,
partial_results=[],
fee_type=FeeType.POSITION,
position_price=position_price,
percentage=percentage,
currency="EUR")
assert res.fee == result
@pytest.fixture class TestPositionResult:
def rdt_order_positions(rdt_event_variants, rdt_events, rdt_item, rdt_order): @pytest.mark.parametrize(
if rdt_event_variants == "single_event": ("check_results", "rule_results", "cancellation_possible", "fee"),
op = OrderPosition.objects.create( [
order=rdt_order, ({1: [make_check_result(True)]}, {1: [make_rule_result(Decimal(10), possible=True)]}, True, Decimal(10)),
item=rdt_item, ({1: [make_check_result(False)]}, {1: [make_rule_result(Decimal(10), possible=True)]}, False, Decimal(10)),
variation=None, ({1: [make_check_result(False)]}, {1: [make_rule_result(Decimal(10), possible=False)]}, False, Decimal(0)),
price=Decimal("14"), ({1: [make_check_result(True), make_check_result(False)]},
{1: [make_rule_result(Decimal(10), possible=True)]},
False, Decimal(10)),
({1: [make_check_result(True)]},
{1: [make_rule_result(Decimal(10), possible=False), make_rule_result(Decimal(5), possible=True)]}, True,
Decimal(5)),
({1: [make_check_result(True)]},
{1: [make_rule_result(Decimal(10), possible=False), make_rule_result(Decimal(5), possible=False)]}, False,
Decimal(0)),
],
)
def test_position_results(self, check_results, rule_results, cancellation_possible, fee):
pos_res = PositionResult(position_check_results=check_results, position_rule_results=rule_results, )
assert pos_res.cancellation_possible == cancellation_possible
assert pos_res.fee_value == fee
class TestProcessResults:
@pytest.mark.parametrize(
("check_results", "rule_results", "cancellation_possible", "fee"),
[
([make_check_result(True)], [make_rule_result(Decimal(10), possible=True)], True, Decimal(10)),
([make_check_result(False)], [make_rule_result(Decimal(10), possible=True)], False, Decimal(10)),
([make_check_result(False)], [make_rule_result(Decimal(10), possible=False)], False, Decimal(0)),
([make_check_result(True), make_check_result(False)], [make_rule_result(Decimal(10), possible=True)],
False, Decimal(10)),
([make_check_result(True)],
[make_rule_result(Decimal(10), possible=False), make_rule_result(Decimal(5), possible=True)], True,
Decimal(5)),
([make_check_result(True)],
[make_rule_result(Decimal(10), possible=False), make_rule_result(Decimal(5), possible=False)], False,
Decimal(0)),
],
)
def test_process_results(self, check_results, rule_results, cancellation_possible, fee):
pos_res = ProcessResult(process_check_results=check_results, process_rule_results=rule_results, )
assert pos_res.cancellation_possible == cancellation_possible
assert pos_res.fee_value == fee
class TestCancellationRule:
@pytest.mark.parametrize(
("received", "position_checks", "process_checks", "raises"),
[
(
[make_cancellation_check('pos-1', CheckTypes.POSITION, True)],
[0],
[],
contextlib.nullcontext()
),
(
[make_cancellation_check('proc-1', CheckTypes.PROCESS, True)],
[],
[0],
contextlib.nullcontext()
),
(
[make_cancellation_check('pos-1', CheckTypes.POSITION, True),
make_cancellation_check('proc-1', CheckTypes.PROCESS, True)],
[0],
[1],
contextlib.nullcontext()
),
(
[make_cancellation_check('proc-1', CheckTypes.PROCESS, True),
make_cancellation_check('proc-1', CheckTypes.PROCESS, True)],
[],
[],
pytest.raises(ValueError)
),
(
[make_cancellation_check('pos-1', CheckTypes.POSITION, True),
make_cancellation_check('pos-1', CheckTypes.POSITION, True)],
[],
[],
pytest.raises(ValueError)
),
(
[('', 1)],
[],
[],
pytest.raises(ValueError)
),
]
)
def test_cancellation_rule_collect_checks(
self,
received,
position_checks,
process_checks,
raises
):
event = cast(Event, cast(object, {}))
def send_fn(_event):
return [("", res) for res in received]
with raises:
checks = CancellationRule._collect_checks(event=event, send_fn=send_fn)
for pos in process_checks:
assert received[pos] in checks.process
for pos in position_checks:
assert received[pos] in checks.position
@pytest.mark.django_db
def test_prefetch_no_checks_collected(self, event, order):
checks = Checks(position=[], process=[])
with scope(organizer=event.organizer):
prefetched_order = CancellationRule._prefetch_order(event, order, checks)
assert prefetched_order.id == order.id
@pytest.mark.django_db
def test_prefetch_incl_values_select_related(self, event, order):
checks = Checks(
position=[
make_cancellation_check('pos_1', CheckTypes.POSITION, True,
prefetches=[lambda: Prefetch('all_positions')],
related_selects=['organizer'])],
process=[
make_cancellation_check('proc_1', CheckTypes.PROCESS, True,
prefetches=[lambda: Prefetch('all_positions')],
related_selects=['organizer'])]
) )
else:
op = [] with scope(organizer=event.organizer):
for i in range(0, 3): prefetched_order = CancellationRule._prefetch_order(event, order, checks)
op.append(OrderPosition.objects.create( assert prefetched_order.id == order.id
subevent=rdt_events.subevents.all()[i],
@pytest.mark.django_db
def test_ticket_not_used(self, event, order, order_position, checkin_list):
position_not_used_check = signal_listener_position_not_used(event)
checks = Checks(position=[position_not_used_check], process=[])
keep = set()
with scope(organizer=event.organizer):
prefetched_order = CancellationRule._prefetch_order(event, order, checks)
with ensure_no_queries():
result = position_not_used_check.evaluate(prefetched_order, keep, order_position, datetime.now(tz=UTC))
assert result.cancellation_possible is True
Checkin.objects.create(
list=checkin_list,
position=order_position,
successful=True
)
prefetched_order = CancellationRule._prefetch_order(event, order, checks)
with ensure_no_queries():
result = position_not_used_check.evaluate(prefetched_order, keep, order_position, datetime.now(tz=UTC))
assert result.cancellation_possible is False
class TestResolveDateFields:
REFERENCE_DT = datetime(2017, 12, 27, 4, 0, 0, tzinfo=UTC)
@pytest.fixture(params=["date", "datetime", "order", "event"])
def rdt_reldate_variants(self, request):
return request.param
@pytest.fixture
def rdt_reldate(self, rdt_reldate_variants) -> RelativeDateWrapper:
if rdt_reldate_variants == 'date' or rdt_reldate_variants == 'datetime':
return RelativeDateWrapper.from_string(self.REFERENCE_DT.isoformat())
elif rdt_reldate_variants == 'order':
return RelativeDateWrapper(
RelativeDate(days=1, time=None, base_date_name='order__datetime', minutes=None, is_after=True))
elif rdt_reldate_variants == 'event':
return RelativeDateWrapper(
RelativeDate(days=1, time=None, base_date_name='event__date_from', minutes=None, is_after=True))
else:
raise ValueError()
@pytest.fixture(params=["single_event", "subevents"])
def rdt_event_variants(self, request):
return request.param
@pytest.fixture
def rdt_events(self, rdt_event_variants, event):
if rdt_event_variants == "single_event":
event.date_from = self.REFERENCE_DT
event.save()
else:
event.has_subevents = True
event.subevents.create(
name='1',
date_from=self.REFERENCE_DT,
)
event.subevents.create(
name='2',
date_from=self.REFERENCE_DT + timedelta(days=1),
)
event.subevents.create(
name='3',
date_from=self.REFERENCE_DT + timedelta(days=2),
)
return event
@pytest.fixture
def rdt_item(self, rdt_events):
return rdt_events.items.create(
name='Ticket',
category=None, default_price=23,
admission=True
)
@pytest.fixture(params=["EARLIEST", "LATEST"])
def rdt_mode_variants(self, request):
return request.param
@pytest.fixture
def rdt_order(self, rdt_events):
o = Order.objects.create(
code='123456', event=rdt_events, email='dummy@dummy.test',
status=Order.STATUS_PENDING,
datetime=self.REFERENCE_DT + timedelta(hours=6), # 6 hours offset mark orders
sales_channel=rdt_events.organizer.sales_channels.get(identifier="web"),
total=14, locale='en'
)
return o
@pytest.fixture
def rdt_order_positions(self, rdt_event_variants, rdt_events, rdt_item, rdt_order):
if rdt_event_variants == "single_event":
op = [OrderPosition.objects.create(
order=rdt_order, order=rdt_order,
item=rdt_item, item=rdt_item,
variation=None, variation=None,
price=Decimal("14"), price=Decimal("14"),
)) )]
return op else:
op = []
for i in range(0, 3):
op.append(OrderPosition.objects.create(
subevent=rdt_events.subevents.all()[i],
order=rdt_order,
item=rdt_item,
variation=None,
price=Decimal("14"),
))
return op
@pytest.mark.django_db
@pytest.mark.django_db def test_process_rule_resolve_date_field(
def test_resolve_date_field(rdt_reldate, rdt_reldate_variants, rdt_events, rdt_event_variants, rdt_mode_variants, self,
rdt_order, rdt_reldate,
rdt_order_positions): rdt_reldate_variants,
with scope(organizer=rdt_events.organizer): rdt_events,
date = ProcessCancellationRule._resolve_date_field(rdt_reldate, rdt_order, rdt_mode_variants) rdt_event_variants,
match rdt_reldate_variants: rdt_mode_variants,
case "date": rdt_order,
assert date == REFERENCE_DT rdt_order_positions
case "datetime": ):
assert date == REFERENCE_DT with scope(organizer=rdt_events.organizer):
case "order": date = ProcessCancellationRule._resolve_date_field(rdt_reldate, rdt_order, rdt_mode_variants)
assert date == REFERENCE_DT + timedelta(days=1) + timedelta(hours=6) match rdt_reldate_variants:
case "event": case "date":
if rdt_event_variants == "single_event": assert date == self.REFERENCE_DT
assert date == REFERENCE_DT + timedelta(days=1) case "datetime":
elif rdt_event_variants == "subevents": assert date == self.REFERENCE_DT
if rdt_mode_variants == "EARLIEST": case "order":
assert date == REFERENCE_DT + timedelta(days=1) assert date == self.REFERENCE_DT + timedelta(days=1) + timedelta(hours=6)
elif rdt_mode_variants == "LATEST": case "event":
assert date == REFERENCE_DT + timedelta(days=1) + timedelta(days=2) if rdt_event_variants == "single_event":
assert date == self.REFERENCE_DT + timedelta(days=1)
elif rdt_event_variants == "subevents":
if rdt_mode_variants == "EARLIEST":
assert date == self.REFERENCE_DT + timedelta(days=1)
elif rdt_mode_variants == "LATEST":
assert date == self.REFERENCE_DT + timedelta(days=1) + timedelta(days=2)
else:
raise ValueError("Variant not known")
else: else:
raise ValueError("Variant not known") raise ValueError("Variant not known")
else: case _:
raise ValueError("Variant not known") raise ValueError("Variant not known")
case _:
raise ValueError("Variant not known") @pytest.mark.django_db
def test_position_rule_resolve_date_field(
self,
rdt_reldate,
rdt_reldate_variants,
rdt_events,
rdt_event_variants,
rdt_order,
rdt_order_positions
):
with scope(organizer=rdt_events.organizer):
for pos in rdt_order_positions:
date = PositionCancellationRule._resolve_date_field(rdt_reldate, rdt_order, pos)
match rdt_reldate_variants:
case "date":
assert date == self.REFERENCE_DT
case "datetime":
assert date == self.REFERENCE_DT
case "order":
assert date == self.REFERENCE_DT + timedelta(days=1) + timedelta(hours=6)
case "event":
if rdt_event_variants == "single_event":
assert date == self.REFERENCE_DT + timedelta(days=1)
elif rdt_event_variants == "subevents":
assert date == pos.subevent.date_from + timedelta(days=1)
else:
raise ValueError("Variant not known")
case _:
raise ValueError("Variant not known")