Skip to content

Commit 8a346da

Browse files
committed
[IMP] queue_job: remove DB commits within test of requeue
1 parent 10395fd commit 8a346da

File tree

7 files changed

+167
-5
lines changed

7 files changed

+167
-5
lines changed

test_queue_job/__manifest__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
"data/queue_job_channel_data.xml",
1414
"data/queue_job_function_data.xml",
1515
"security/ir.model.access.csv",
16+
"data/queue_job_test_job.xml",
1617
],
1718
"installable": True,
1819
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<?xml version="1.0" encoding="utf-8" ?>
2+
<odoo noupdate="1">
3+
<!--
4+
To be used in tests cfr. test/test_requeue_dead_job.py
5+
These 'test' data are loaded as 'demo' data to have records within db
6+
and avoid doing commit within tests
7+
-->
8+
<function
9+
name="_create_test_started_job"
10+
model="queue.job"
11+
eval="('test_started_job',)"
12+
/>
13+
<function
14+
name="_create_test_enqueued_job"
15+
model="queue.job"
16+
eval="('test_enqueued_job',)"
17+
/>
18+
</odoo>

test_queue_job/models/test_models.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
# Copyright 2016 Camptocamp SA
22
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html)
33

4+
from datetime import datetime, timedelta
5+
46
from odoo import api, fields, models
57

68
from odoo.addons.queue_job.delay import chain
@@ -28,6 +30,35 @@ def testing_related__url(self, **kwargs):
2830
"url": kwargs["url"].format(subject=subject),
2931
}
3032

33+
@api.model
34+
def _create_test_started_job(self, uuid):
35+
"""Create started jobs to be used within tests"""
36+
self.env["queue.job"].with_context(
37+
_job_edit_sentinel=self.env["queue.job"].EDIT_SENTINEL,
38+
).create(
39+
{
40+
"uuid": uuid,
41+
"state": "started",
42+
"model_name": "queue.job",
43+
"method_name": "write",
44+
}
45+
)
46+
47+
@api.model
48+
def _create_test_enqueued_job(self, uuid):
49+
"""Create enqueued jobs to be used within tests"""
50+
self.env["queue.job"].with_context(
51+
_job_edit_sentinel=self.env["queue.job"].EDIT_SENTINEL,
52+
).create(
53+
{
54+
"uuid": uuid,
55+
"state": "enqueued",
56+
"model_name": "queue.job",
57+
"method_name": "write",
58+
"date_enqueued": datetime.now() - timedelta(minutes=1),
59+
}
60+
)
61+
3162

3263
class ModelTestQueueJob(models.Model):
3364
_name = "test.queue.job"

test_queue_job/tests/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,4 @@
77
from . import test_job_function
88
from . import test_related_actions
99
from . import test_delay_mocks
10+
from . import test_requeue_dead_job

test_queue_job/tests/test_autovacuum.py

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,16 @@ def test_autovacuum(self):
2828
date_done = datetime.now() - timedelta(days=29)
2929
stored.write({"date_done": date_done})
3030
self.env["queue.job"].autovacuum()
31-
self.assertEqual(len(self.env["queue.job"].search([])), 1)
31+
self.assertEqual(
32+
len(self.env["queue.job"].search([("channel", "!=", False)])), 1
33+
)
3234

3335
date_done = datetime.now() - timedelta(days=31)
3436
stored.write({"date_done": date_done})
3537
self.env["queue.job"].autovacuum()
36-
self.assertEqual(len(self.env["queue.job"].search([])), 0)
38+
self.assertEqual(
39+
len(self.env["queue.job"].search([("channel", "!=", False)])), 0
40+
)
3741

3842
def test_autovacuum_multi_channel(self):
3943
root_channel = self.env.ref("queue_job.channel_root")
@@ -48,11 +52,17 @@ def test_autovacuum_multi_channel(self):
4852
{"channel": channel_60days.complete_name, "date_done": date_done}
4953
)
5054

51-
self.assertEqual(len(self.env["queue.job"].search([])), 2)
55+
self.assertEqual(
56+
len(self.env["queue.job"].search([("channel", "!=", False)])), 2
57+
)
5258
self.env["queue.job"].autovacuum()
53-
self.assertEqual(len(self.env["queue.job"].search([])), 1)
59+
self.assertEqual(
60+
len(self.env["queue.job"].search([("channel", "!=", False)])), 1
61+
)
5462

5563
date_done = datetime.now() - timedelta(days=61)
5664
job_60days.write({"date_done": date_done})
5765
self.env["queue.job"].autovacuum()
58-
self.assertEqual(len(self.env["queue.job"].search([])), 0)
66+
self.assertEqual(
67+
len(self.env["queue.job"].search([("channel", "!=", False)])), 0
68+
)
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
# Copyright 2025 ACSONE SA/NV
2+
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
3+
from contextlib import closing
4+
from datetime import datetime, timedelta
5+
6+
from odoo.tests import tagged
7+
8+
from odoo.addons.queue_job.job import Job
9+
from odoo.addons.queue_job.jobrunner.runner import Database
10+
11+
from .common import JobCommonCase
12+
13+
14+
@tagged("post_install", "-at_install")
15+
class TestRequeueDeadJob(JobCommonCase):
16+
def _get_demo_job(self, uuid):
17+
# job created during load of demo data
18+
job = self.env["queue.job"].search(
19+
[
20+
("uuid", "=", uuid),
21+
],
22+
limit=1,
23+
)
24+
25+
self.assertTrue(
26+
job,
27+
f"Demo data queue job {uuid} should be loaded in order"
28+
" to make this tests work",
29+
)
30+
31+
return job
32+
33+
def get_locks(self, uuid, cr=None):
34+
"""
35+
Retrieve lock rows
36+
"""
37+
if cr is None:
38+
cr = self.env.cr
39+
40+
cr.execute(
41+
"""
42+
SELECT
43+
queue_job_id
44+
FROM
45+
queue_job_lock
46+
WHERE
47+
queue_job_id IN (
48+
SELECT
49+
id
50+
FROM
51+
queue_job
52+
WHERE
53+
uuid = %s
54+
)
55+
FOR UPDATE SKIP LOCKED
56+
""",
57+
[uuid],
58+
)
59+
60+
return cr.fetchall()
61+
62+
def test_add_lock_record(self):
63+
queue_job = self._get_demo_job("test_started_job")
64+
self.assertEqual(len(queue_job), 1)
65+
job_obj = Job.load(self.env, queue_job.uuid)
66+
67+
job_obj.set_started()
68+
self.assertEqual(job_obj.state, "started")
69+
70+
locks = self.get_locks(job_obj.uuid)
71+
72+
self.assertEqual(1, len(locks))
73+
74+
def test_lock(self):
75+
queue_job = self._get_demo_job("test_started_job")
76+
job_obj = Job.load(self.env, queue_job.uuid)
77+
78+
job_obj.set_started()
79+
job_obj.lock()
80+
81+
with closing(self.env.registry.cursor()) as new_cr:
82+
locks = self.get_locks(job_obj.uuid, new_cr)
83+
84+
# Row should be locked
85+
self.assertEqual(0, len(locks))
86+
87+
def test_requeue_dead_jobs(self):
88+
queue_job = self._get_demo_job("test_enqueued_job")
89+
job_obj = Job.load(self.env, queue_job.uuid)
90+
91+
job_obj.set_enqueued()
92+
job_obj.set_started()
93+
job_obj.date_enqueued = datetime.now() - timedelta(minutes=1)
94+
job_obj.store()
95+
96+
# requeue dead jobs using current cursor
97+
query = Database(self.env.cr.dbname)._query_requeue_dead_jobs()
98+
self.env.cr.execute(query)
99+
100+
uuids_requeued = self.env.cr.fetchall()
101+
self.assertTrue(queue_job.uuid in j[0] for j in uuids_requeued)

0 commit comments

Comments
 (0)