Skip to content

Commit 3783e9a

Browse files
committed
[TEST] queue_job_batch: add tests for batch stuck in progress fix
Add tests to verify: - Failed jobs trigger check_state on the batch - Cancelled jobs trigger check_state on the batch - No deduplication occurs when multiple jobs complete (race condition fix)
1 parent 9b17985 commit 3783e9a

2 files changed

Lines changed: 98 additions & 0 deletions

File tree

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
from . import test_queue_job_batch
2+
from . import test_fix_batch_stuck
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
from odoo.tests.common import TransactionCase
2+
3+
4+
class TestQueueJobBatchFix(TransactionCase):
5+
def setUp(self):
6+
super().setUp()
7+
self.QueueJob = self.env["queue.job"]
8+
self.Batch = self.env["queue.job.batch"]
9+
self.TestModel = self.env["test.queue.job"]
10+
11+
def test_batch_failed_job_triggers_check(self):
12+
"""Test that a failed job triggers check_state on the batch."""
13+
self.cr.execute("delete from queue_job")
14+
batch = self.Batch.get_new_batch("TEST_FAIL")
15+
16+
# Create a job in the batch
17+
job = self.TestModel.with_context(job_batch=batch).with_delay().testing_method()
18+
job_record = job.db_record()
19+
20+
# Verify initial state
21+
self.assertEqual(batch.state, "pending")
22+
self.assertEqual(job_record.state, "pending")
23+
24+
# Set job to failed
25+
# Depending on how queue_job works, writing state might trigger the logic
26+
job_record.write({"state": "failed", "exc_info": "Fail"})
27+
28+
# Find jobs for queue.job.batch
29+
check_jobs = self.QueueJob.search(
30+
[
31+
("model_name", "=", "queue.job.batch"),
32+
("method_name", "=", "check_state"),
33+
]
34+
)
35+
36+
# Filter for our batch
37+
check_jobs = check_jobs.filtered(lambda j: batch in j.records)
38+
39+
# WITHOUT FIX: This should be empty because "failed" state doesn't trigger
40+
self.assertTrue(
41+
check_jobs, "check_state job should be created when a job fails"
42+
)
43+
44+
def test_batch_cancelled_job_triggers_check(self):
45+
"""Test that a cancelled job triggers check_state on the batch."""
46+
self.cr.execute("delete from queue_job")
47+
batch = self.Batch.get_new_batch("TEST_CANCEL")
48+
job = self.TestModel.with_context(job_batch=batch).with_delay().testing_method()
49+
job_record = job.db_record()
50+
51+
job_record.write({"state": "cancelled"})
52+
53+
check_jobs = self.QueueJob.search(
54+
[
55+
("model_name", "=", "queue.job.batch"),
56+
("method_name", "=", "check_state"),
57+
]
58+
)
59+
check_jobs = check_jobs.filtered(lambda j: batch in j.records)
60+
61+
self.assertTrue(
62+
check_jobs, "check_state job should be created when a job is cancelled"
63+
)
64+
65+
def test_no_deduplication_race_condition(self):
66+
"""Test that multiple jobs trigger multiple check_state calls."""
67+
self.cr.execute("delete from queue_job")
68+
batch = self.Batch.get_new_batch("TEST_RACE")
69+
70+
# Create 2 jobs
71+
job1 = (
72+
self.TestModel.with_context(job_batch=batch).with_delay().testing_method()
73+
)
74+
job2 = (
75+
self.TestModel.with_context(job_batch=batch).with_delay().testing_method()
76+
)
77+
78+
# Set job1 to done -> creates CheckJob1
79+
job1.db_record().write({"state": "done"})
80+
81+
# Set job2 to done -> creates CheckJob2
82+
# If identity_exact is used, CheckJob2 might be deduplicated
83+
job2.db_record().write({"state": "done"})
84+
85+
check_jobs = self.QueueJob.search(
86+
[
87+
("model_name", "=", "queue.job.batch"),
88+
("method_name", "=", "check_state"),
89+
]
90+
)
91+
check_jobs = check_jobs.filtered(lambda j: batch in j.records)
92+
93+
# WITH FIX: Should have 2 check jobs (no deduplication)
94+
# WITHOUT FIX: Should have 1 check job because of deduplication
95+
self.assertEqual(
96+
len(check_jobs), 2, "Should have 2 check_state jobs (no deduplication)"
97+
)

0 commit comments

Comments
 (0)