Skip to content

Commit c36e62b

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 c36e62b

File tree

2 files changed

+82
-0
lines changed

2 files changed

+82
-0
lines changed
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: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
from odoo.tests.common import TransactionCase
2+
from odoo.addons.queue_job.job import Job
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+
("model_name", "=", "queue.job.batch"),
31+
("method_name", "=", "check_state"),
32+
])
33+
34+
# Filter for our batch
35+
check_jobs = check_jobs.filtered(lambda j: batch in j.records)
36+
37+
# WITHOUT FIX: This should be empty because "failed" state doesn't trigger write logic
38+
self.assertTrue(check_jobs, "check_state job should be created when a job fails")
39+
40+
def test_batch_cancelled_job_triggers_check(self):
41+
"""Test that a cancelled job triggers check_state on the batch."""
42+
self.cr.execute("delete from queue_job")
43+
batch = self.Batch.get_new_batch("TEST_CANCEL")
44+
job = self.TestModel.with_context(job_batch=batch).with_delay().testing_method()
45+
job_record = job.db_record()
46+
47+
job_record.write({"state": "cancelled"})
48+
49+
check_jobs = self.QueueJob.search([
50+
("model_name", "=", "queue.job.batch"),
51+
("method_name", "=", "check_state"),
52+
])
53+
check_jobs = check_jobs.filtered(lambda j: batch in j.records)
54+
55+
self.assertTrue(check_jobs, "check_state job should be created when a job is cancelled")
56+
57+
def test_no_deduplication_race_condition(self):
58+
"""Test that multiple job completions trigger multiple check_state calls (no identity key)."""
59+
self.cr.execute("delete from queue_job")
60+
batch = self.Batch.get_new_batch("TEST_RACE")
61+
62+
# Create 2 jobs
63+
job1 = self.TestModel.with_context(job_batch=batch).with_delay().testing_method()
64+
job2 = self.TestModel.with_context(job_batch=batch).with_delay().testing_method()
65+
66+
# Set job1 to done -> creates CheckJob1
67+
job1.db_record().write({"state": "done"})
68+
69+
# Set job2 to done -> creates CheckJob2
70+
# If identity_exact is used (reverted code), CheckJob2 might be deduplicated if CheckJob1 is pending
71+
job2.db_record().write({"state": "done"})
72+
73+
check_jobs = self.QueueJob.search([
74+
("model_name", "=", "queue.job.batch"),
75+
("method_name", "=", "check_state"),
76+
])
77+
check_jobs = check_jobs.filtered(lambda j: batch in j.records)
78+
79+
# WITH FIX: Should have 2 check jobs (or 1 if one ran, but here they are pending)
80+
# WITHOUT FIX: Should have 1 check job because of deduplication
81+
self.assertEqual(len(check_jobs), 2, "Should have 2 check_state jobs (no deduplication)")

0 commit comments

Comments
 (0)