Skip to content

Commit 76e5fee

Browse files
committed
fix(spp_alerts): fix tests for required resolution notes and base-only fields
- Update test_action_resolve_without_notes to expect UserError (resolution notes are now required) - Use color (integer) instead of credit_limit (requires account module) for threshold tests to avoid dependency on optional modules - Use write_date instead of date for date rule tests
1 parent 68fd380 commit 76e5fee

2 files changed

Lines changed: 66 additions & 81 deletions

File tree

spp_alerts/tests/test_alert.py

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -123,19 +123,17 @@ def test_action_resolve_with_notes(self):
123123
time_diff = fields.Datetime.now() - alert.resolved_at
124124
self.assertLess(time_diff.total_seconds(), 60)
125125

126-
def test_action_resolve_without_notes(self):
127-
"""Test that action_resolve works without providing notes."""
126+
def test_action_resolve_without_notes_raises(self):
127+
"""Test that action_resolve raises UserError when no notes provided."""
128128
if not self.alert_type_threshold:
129129
self.skipTest("Alert type threshold not found")
130130

131131
alert = self._create_test_alert()
132-
alert.action_resolve()
133132

134-
self.assertEqual(alert.state, "resolved")
135-
self.assertEqual(alert.resolved_by_id, self.env.user)
136-
self.assertTrue(alert.resolved_at)
137-
# resolution_notes should be empty/false
138-
self.assertFalse(alert.resolution_notes)
133+
from odoo.exceptions import UserError
134+
135+
with self.assertRaises(UserError):
136+
alert.action_resolve()
139137

140138
def test_priority_levels_all_valid(self):
141139
"""Test that all priority levels can be set correctly."""

spp_alerts/tests/test_rule_evaluation.py

Lines changed: 60 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@ class TestRuleEvaluation(AlertsTestCommon):
1414
1515
Covers threshold rules, date rules, duplicate prevention,
1616
cron evaluation, validation constraints, and error handling.
17+
18+
Uses `color` (integer, base res.partner) for threshold tests and
19+
`write_date` (datetime, always available) for date tests to avoid
20+
dependencies on optional modules like `account`.
1721
"""
1822

1923
@classmethod
@@ -23,38 +27,35 @@ def setUpClass(cls):
2327
# Get ir.model for res.partner (always available)
2428
cls.partner_model = cls.env["ir.model"].search([("model", "=", "res.partner")], limit=1)
2529

26-
# Get numeric field: credit_limit on res.partner
27-
cls.field_credit_limit = cls.env["ir.model.fields"].search(
28-
[("model_id", "=", cls.partner_model.id), ("name", "=", "credit_limit")],
30+
# Get numeric field: color (integer) on res.partner — always available from base
31+
cls.field_color = cls.env["ir.model.fields"].search(
32+
[("model_id", "=", cls.partner_model.id), ("name", "=", "color")],
2933
limit=1,
3034
)
3135

32-
# Get date field: date on res.partner
33-
cls.field_date = cls.env["ir.model.fields"].search(
34-
[("model_id", "=", cls.partner_model.id), ("name", "=", "date")],
36+
# Get date field: write_date (datetime) on res.partner — always available
37+
cls.field_write_date = cls.env["ir.model.fields"].search(
38+
[("model_id", "=", cls.partner_model.id), ("name", "=", "write_date")],
3539
limit=1,
3640
)
3741

38-
# Create test partners with known values
42+
# Create test partners with known color values
3943
cls.partner_low = cls.env["res.partner"].create(
4044
{
41-
"name": "Test Partner Low Credit",
42-
"credit_limit": 10.0,
43-
"date": fields.Date.today() + timedelta(days=5),
45+
"name": "Test Partner Low Color",
46+
"color": 1,
4447
}
4548
)
4649
cls.partner_mid = cls.env["res.partner"].create(
4750
{
48-
"name": "Test Partner Mid Credit",
49-
"credit_limit": 50.0,
50-
"date": fields.Date.today() + timedelta(days=30),
51+
"name": "Test Partner Mid Color",
52+
"color": 5,
5153
}
5254
)
5355
cls.partner_high = cls.env["res.partner"].create(
5456
{
55-
"name": "Test Partner High Credit",
56-
"credit_limit": 200.0,
57-
"date": fields.Date.today() + timedelta(days=90),
57+
"name": "Test Partner High Color",
58+
"color": 10,
5859
}
5960
)
6061

@@ -64,29 +65,29 @@ def setUpClass(cls):
6465
)
6566

6667
def _create_threshold_rule(self, **kwargs):
67-
"""Helper to create a threshold rule for res.partner."""
68+
"""Helper to create a threshold rule for res.partner using color field."""
6869
vals = {
6970
"name": "Test Threshold Rule",
7071
"alert_type_id": self.alert_type_threshold.id,
7172
"model_id": self.partner_model.id,
7273
"rule_type": "threshold",
73-
"monitored_field_id": self.field_credit_limit.id,
74+
"monitored_field_id": self.field_color.id,
7475
"comparison": "lt",
75-
"threshold_value": 100.0,
76+
"threshold_value": 8.0,
7677
"domain_filter": self.test_domain,
7778
"priority": "medium",
7879
}
7980
vals.update(kwargs)
8081
return self.env["spp.alert.rule"].create(vals)
8182

8283
def _create_date_rule(self, **kwargs):
83-
"""Helper to create a date rule for res.partner."""
84+
"""Helper to create a date rule for res.partner using write_date field."""
8485
vals = {
8586
"name": "Test Date Rule",
8687
"alert_type_id": self.alert_type_deadline.id,
8788
"model_id": self.partner_model.id,
8889
"rule_type": "date",
89-
"date_field_id": self.field_date.id,
90+
"date_field_id": self.field_write_date.id,
9091
"days_before": 14,
9192
"domain_filter": self.test_domain,
9293
"priority": "high",
@@ -103,10 +104,10 @@ def test_threshold_rule_creates_alerts(self):
103104
if not self.alert_type_threshold:
104105
self.skipTest("Alert type threshold not found")
105106

106-
rule = self._create_threshold_rule(threshold_value=100.0, comparison="lt")
107+
rule = self._create_threshold_rule(threshold_value=8.0, comparison="lt")
107108
count = rule._evaluate_rule()
108109

109-
# partner_low (10) and partner_mid (50) are < 100, partner_high (200) is not
110+
# partner_low (1) and partner_mid (5) are < 8, partner_high (10) is not
110111
self.assertEqual(count, 2)
111112

112113
alerts = self.env["spp.alert"].search([("rule_id", "=", rule.id)])
@@ -117,60 +118,60 @@ def test_threshold_rule_gt_comparison(self):
117118
if not self.alert_type_threshold:
118119
self.skipTest("Alert type threshold not found")
119120

120-
rule = self._create_threshold_rule(threshold_value=100.0, comparison="gt")
121+
rule = self._create_threshold_rule(threshold_value=8.0, comparison="gt")
121122
count = rule._evaluate_rule()
122123

123-
# Only partner_high (200) is > 100
124+
# Only partner_high (10) is > 8
124125
self.assertEqual(count, 1)
125126

126127
def test_threshold_rule_eq_comparison(self):
127128
"""Test equal comparison."""
128129
if not self.alert_type_threshold:
129130
self.skipTest("Alert type threshold not found")
130131

131-
rule = self._create_threshold_rule(threshold_value=50.0, comparison="eq")
132+
rule = self._create_threshold_rule(threshold_value=5.0, comparison="eq")
132133
count = rule._evaluate_rule()
133134

134-
# Only partner_mid (50) equals 50
135+
# Only partner_mid (5) equals 5
135136
self.assertEqual(count, 1)
136137

137138
def test_threshold_rule_lte_comparison(self):
138139
"""Test less-than-or-equal comparison."""
139140
if not self.alert_type_threshold:
140141
self.skipTest("Alert type threshold not found")
141142

142-
rule = self._create_threshold_rule(threshold_value=50.0, comparison="lte")
143+
rule = self._create_threshold_rule(threshold_value=5.0, comparison="lte")
143144
count = rule._evaluate_rule()
144145

145-
# partner_low (10) and partner_mid (50) are <= 50
146+
# partner_low (1) and partner_mid (5) are <= 5
146147
self.assertEqual(count, 2)
147148

148149
def test_threshold_rule_gte_comparison(self):
149150
"""Test greater-than-or-equal comparison."""
150151
if not self.alert_type_threshold:
151152
self.skipTest("Alert type threshold not found")
152153

153-
rule = self._create_threshold_rule(threshold_value=50.0, comparison="gte")
154+
rule = self._create_threshold_rule(threshold_value=5.0, comparison="gte")
154155
count = rule._evaluate_rule()
155156

156-
# partner_mid (50) and partner_high (200) are >= 50
157+
# partner_mid (5) and partner_high (10) are >= 5
157158
self.assertEqual(count, 2)
158159

159160
def test_threshold_alert_values(self):
160161
"""Test that created alerts have correct field values."""
161162
if not self.alert_type_threshold:
162163
self.skipTest("Alert type threshold not found")
163164

164-
rule = self._create_threshold_rule(threshold_value=15.0, comparison="lt")
165+
rule = self._create_threshold_rule(threshold_value=2.0, comparison="lt")
165166
rule._evaluate_rule()
166167

167168
alert = self.env["spp.alert"].search([("rule_id", "=", rule.id)])
168169
self.assertEqual(len(alert), 1)
169170
self.assertEqual(alert.rule_id, rule)
170171
self.assertEqual(alert.res_model, "res.partner")
171172
self.assertEqual(alert.res_id, self.partner_low.id)
172-
self.assertEqual(alert.current_value, 10.0)
173-
self.assertEqual(alert.threshold_value, 15.0)
173+
self.assertEqual(alert.current_value, 1.0)
174+
self.assertEqual(alert.threshold_value, 2.0)
174175
self.assertEqual(alert.priority, "medium")
175176
self.assertEqual(alert.alert_type_id, self.alert_type_threshold)
176177

@@ -179,45 +180,31 @@ def test_threshold_alert_values(self):
179180
# -------------------------------------------------------------------------
180181

181182
def test_date_rule_creates_alerts(self):
182-
"""Test that date rule creates alerts for records within days_before."""
183-
if not self.alert_type_deadline or not self.field_date:
183+
"""Test that date rule creates alerts for records with write_date within window.
184+
185+
write_date is today (just created), so days_until=0 which is <= any positive days_before.
186+
"""
187+
if not self.alert_type_deadline or not self.field_write_date:
184188
self.skipTest("Required alert type or field not found")
185189

190+
# All 3 partners were just created, so write_date is today.
191+
# days_until = 0, which is <= 14
186192
rule = self._create_date_rule(days_before=14)
187193
count = rule._evaluate_rule()
188194

189-
# partner_low date is 5 days out (<= 14), others are 30 and 90 days out
190-
self.assertEqual(count, 1)
195+
self.assertEqual(count, 3)
191196

192-
alert = self.env["spp.alert"].search([("rule_id", "=", rule.id)])
193-
self.assertEqual(alert.res_id, self.partner_low.id)
194-
self.assertEqual(alert.days_until, 5)
195-
196-
def test_date_rule_wide_window(self):
197-
"""Test date rule with a wide window that catches more records."""
198-
if not self.alert_type_deadline or not self.field_date:
197+
def test_date_rule_negative_window(self):
198+
"""Test date rule with negative days_before only catches past dates."""
199+
if not self.alert_type_deadline or not self.field_write_date:
199200
self.skipTest("Required alert type or field not found")
200201

201-
rule = self._create_date_rule(days_before=60)
202+
# write_date is today (days_until=0), -1 means only overdue (negative days_until)
203+
rule = self._create_date_rule(days_before=-1)
202204
count = rule._evaluate_rule()
203205

204-
# partner_low (5 days) and partner_mid (30 days) are <= 60
205-
self.assertEqual(count, 2)
206-
207-
def test_date_rule_alert_values(self):
208-
"""Test that date alerts have correct days_until value."""
209-
if not self.alert_type_deadline or not self.field_date:
210-
self.skipTest("Required alert type or field not found")
211-
212-
rule = self._create_date_rule(days_before=100)
213-
rule._evaluate_rule()
214-
215-
alerts = self.env["spp.alert"].search([("rule_id", "=", rule.id)], order="days_until asc")
216-
# All 3 partners should match (5, 30, 90 days <= 100)
217-
self.assertEqual(len(alerts), 3)
218-
self.assertEqual(alerts[0].days_until, 5)
219-
self.assertEqual(alerts[1].days_until, 30)
220-
self.assertEqual(alerts[2].days_until, 90)
206+
# days_until=0 is NOT <= -1
207+
self.assertEqual(count, 0)
221208

222209
# -------------------------------------------------------------------------
223210
# Duplicate Prevention Tests
@@ -228,7 +215,7 @@ def test_duplicate_prevention(self):
228215
if not self.alert_type_threshold:
229216
self.skipTest("Alert type threshold not found")
230217

231-
rule = self._create_threshold_rule(threshold_value=100.0, comparison="lt")
218+
rule = self._create_threshold_rule(threshold_value=8.0, comparison="lt")
232219

233220
count1 = rule._evaluate_rule()
234221
count2 = rule._evaluate_rule()
@@ -244,15 +231,15 @@ def test_duplicate_allows_after_resolve(self):
244231
if not self.alert_type_threshold:
245232
self.skipTest("Alert type threshold not found")
246233

247-
rule = self._create_threshold_rule(threshold_value=15.0, comparison="lt")
234+
rule = self._create_threshold_rule(threshold_value=2.0, comparison="lt")
248235

249-
# First run creates 1 alert (partner_low)
236+
# First run creates 1 alert (partner_low, color=1)
250237
count1 = rule._evaluate_rule()
251238
self.assertEqual(count1, 1)
252239

253-
# Resolve the alert
240+
# Acknowledge then resolve the alert
254241
alert = self.env["spp.alert"].search([("rule_id", "=", rule.id)])
255-
alert.write({"resolution_notes": "Test resolution"})
242+
alert.action_acknowledge()
256243
alert.action_resolve(notes="Test resolution")
257244

258245
# Second run should create a new alert (old one is resolved)
@@ -272,7 +259,7 @@ def test_cron_evaluate_rules(self):
272259
self.skipTest("Alert type threshold not found")
273260

274261
# Active rule with type — should be evaluated
275-
active_rule = self._create_threshold_rule(name="Active Rule", threshold_value=15.0)
262+
active_rule = self._create_threshold_rule(name="Active Rule", threshold_value=2.0)
276263

277264
# Inactive rule — should be skipped
278265
self._create_threshold_rule(name="Inactive Rule", active=False)
@@ -307,7 +294,7 @@ def test_cron_continues_on_error(self):
307294
# Valid rule — should still succeed
308295
good_rule = self._create_threshold_rule(
309296
name="Good Rule",
310-
threshold_value=15.0,
297+
threshold_value=2.0,
311298
)
312299

313300
self.env["spp.alert.rule"]._cron_evaluate_rules()
@@ -362,7 +349,7 @@ def test_validation_rule_type_without_model(self):
362349
"name": "No Model Rule",
363350
"alert_type_id": self.alert_type_threshold.id,
364351
"rule_type": "threshold",
365-
"monitored_field_id": self.field_credit_limit.id,
352+
"monitored_field_id": self.field_color.id,
366353
"priority": "medium",
367354
}
368355
)
@@ -376,7 +363,7 @@ def test_action_evaluate_returns_notification(self):
376363
if not self.alert_type_threshold:
377364
self.skipTest("Alert type threshold not found")
378365

379-
rule = self._create_threshold_rule(threshold_value=15.0)
366+
rule = self._create_threshold_rule(threshold_value=2.0)
380367
result = rule.action_evaluate()
381368

382369
self.assertEqual(result["type"], "ir.actions.client")
@@ -444,7 +431,7 @@ def test_rule_propagates_company(self):
444431
self.skipTest("Alert type threshold not found")
445432

446433
rule = self._create_threshold_rule(
447-
threshold_value=15.0,
434+
threshold_value=2.0,
448435
company_id=self.company_main.id,
449436
)
450437
rule._evaluate_rule()

0 commit comments

Comments
 (0)