Skip to content

Commit 78d469e

Browse files
committed
fix: throw value error when rule weight exceeds maximum possible weight
1 parent 8baff80 commit 78d469e

File tree

4 files changed

+93
-0
lines changed

4 files changed

+93
-0
lines changed

src/postpwn/cli.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,19 @@ def postpwn(
148148
max_weight = 10
149149
rules = None
150150

151+
if rules:
152+
weight_limit = (
153+
max_weight
154+
if isinstance(max_weight, int)
155+
else max(max_weight.model_dump().values())
156+
)
157+
158+
for rule in rules:
159+
if rule.get("weight", 0) > weight_limit:
160+
raise ValueError(
161+
f"Invalid rule config: {rule['filter']} exceeds max weight {weight_limit}"
162+
)
163+
151164
logger.info(f"Rules: {rules}")
152165

153166
if kwargs["schedule"]:
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
"max_weight": {
3+
"sunday": 0,
4+
"monday": 1,
5+
"tuesday": 2,
6+
"wednesday": 3,
7+
"thursday": 4,
8+
"friday": 5,
9+
"saturday": 6
10+
},
11+
"rules": [
12+
{ "filter": "@weight_one", "weight": 1 },
13+
{ "filter": "@weight_two", "weight": 7 }
14+
]
15+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"max_weight": 2,
3+
"rules": [
4+
{ "filter": "@weight_one", "weight": 1 },
5+
{ "filter": "@weight_two", "weight": 3 }
6+
]
7+
}

tests/rescheduler_test.py

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,64 @@ def test_datetime_is_preserved(event_loop: AbstractEventLoop) -> None:
147147
assert fake_api.update_task.call_args.kwargs["due_datetime"] == curr_datetime
148148

149149

150+
def test_weight_exceeds_single_max_weight(event_loop: AbstractEventLoop) -> None:
151+
"""when a rule weight exceeds the singular max weight, it raises an error"""
152+
153+
kwargs: RescheduleParams = {
154+
"token": "VALID_TOKEN",
155+
"filter": "label:test",
156+
"rules": "tests/fixtures/excessive_single_max_weight_rules.json",
157+
"dry_run": False,
158+
"time_zone": "UTC",
159+
"schedule": None,
160+
}
161+
162+
fake_api = FakeTodoistAPI("VALID_TOKEN")
163+
164+
unlabeled_task = build_task()
165+
labeled_task = build_task({"labels": ["weight_one"]})
166+
167+
fake_api.setup_tasks([unlabeled_task, labeled_task])
168+
169+
curr_datetime = datetime(2025, 1, 5, 12, 0, 0)
170+
171+
with set_env({"RETRY_ATTEMPTS": "1"}):
172+
with pytest.raises(
173+
ValueError,
174+
match="Invalid rule config: @weight_two exceeds max weight 2",
175+
):
176+
postpwn(fake_api, event_loop, curr_datetime, **kwargs)
177+
178+
179+
def test_weight_exceeds_daily_max_weight(event_loop: AbstractEventLoop) -> None:
180+
"""when a rule exceeds one of the daily max weights, it raises an error"""
181+
182+
kwargs: RescheduleParams = {
183+
"token": "VALID_TOKEN",
184+
"filter": "label:test",
185+
"rules": "tests/fixtures/excessive_daily_max_weight_rules.json",
186+
"dry_run": False,
187+
"time_zone": "UTC",
188+
"schedule": None,
189+
}
190+
191+
fake_api = FakeTodoistAPI("VALID_TOKEN")
192+
193+
unlabeled_task = build_task()
194+
labeled_task = build_task({"labels": ["weight_one"]})
195+
196+
fake_api.setup_tasks([unlabeled_task, labeled_task])
197+
198+
curr_datetime = datetime(2025, 1, 5, 12, 0, 0)
199+
200+
with set_env({"RETRY_ATTEMPTS": "1"}):
201+
with pytest.raises(
202+
ValueError,
203+
match="Invalid rule config: @weight_two exceeds max weight 6",
204+
):
205+
postpwn(fake_api, event_loop, curr_datetime, **kwargs)
206+
207+
150208
def test_no_matching_label(event_loop: asyncio.AbstractEventLoop) -> None:
151209
"""when tasks have no matching labels, they are not rescheduled"""
152210

0 commit comments

Comments
 (0)