# Copyright 2016 Camptocamp SA # License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html) import hashlib from datetime import datetime, timedelta from unittest import mock import odoo.tests.common as common from odoo.addons.queue_job import identity_exact from odoo.addons.queue_job.delay import DelayableGraph from odoo.addons.queue_job.exception import ( FailedJobError, NoSuchJobError, RetryableJobError, ) from odoo.addons.queue_job.job import ( CANCELLED, DONE, ENQUEUED, FAILED, PENDING, RETRY_INTERVAL, STARTED, WAIT_DEPENDENCIES, Job, ) from .common import JobCommonCase class TestJobsOnTestingMethod(JobCommonCase): """Test Job""" def test_new_job(self): """ Create a job """ test_job = Job(self.method) self.assertEqual(test_job.func.__func__, self.method.__func__) def test_eta(self): """When an `eta` is datetime, it uses it""" now = datetime.now() method = self.env["res.users"].mapped job_a = Job(method, eta=now) self.assertEqual(job_a.eta, now) def test_eta_integer(self): """When an `eta` is an integer, it adds n seconds up to now""" datetime_path = "odoo.addons.queue_job.job.datetime" with mock.patch(datetime_path, autospec=True) as mock_datetime: mock_datetime.now.return_value = datetime(2015, 3, 15, 16, 41, 0) job_a = Job(self.method, eta=60) self.assertEqual(job_a.eta, datetime(2015, 3, 15, 16, 42, 0)) def test_eta_timedelta(self): """When an `eta` is a timedelta, it adds it up to now""" datetime_path = "odoo.addons.queue_job.job.datetime" with mock.patch(datetime_path, autospec=True) as mock_datetime: mock_datetime.now.return_value = datetime(2015, 3, 15, 16, 41, 0) delta = timedelta(hours=3) job_a = Job(self.method, eta=delta) self.assertEqual(job_a.eta, datetime(2015, 3, 15, 19, 41, 0)) def test_perform_args(self): test_job = Job(self.method, args=("o", "k"), kwargs={"c": "!"}) result = test_job.perform() self.assertEqual(result, (("o", "k"), {"c": "!"})) def test_retryable_error(self): test_job = Job(self.method, kwargs={"raise_retry": True}, max_retries=3) self.assertEqual(test_job.retry, 0) with self.assertRaises(RetryableJobError): test_job.perform() self.assertEqual(test_job.retry, 1) with self.assertRaises(RetryableJobError): test_job.perform() self.assertEqual(test_job.retry, 2) with self.assertRaises(FailedJobError): test_job.perform() self.assertEqual(test_job.retry, 3) def test_infinite_retryable_error(self): test_job = Job(self.method, kwargs={"raise_retry": True}, max_retries=0) self.assertEqual(test_job.retry, 0) with self.assertRaises(RetryableJobError): test_job.perform() self.assertEqual(test_job.retry, 1) def test_on_instance_method(self): class A: def method(self): pass with self.assertRaises(TypeError): Job(A.method) def test_on_model_method(self): job_ = Job(self.env["test.queue.job"].testing_method) self.assertEqual(job_.model_name, "test.queue.job") self.assertEqual(job_.method_name, "testing_method") def test_invalid_function(self): with self.assertRaises(TypeError): Job(1) def test_set_pending(self): job_a = Job(self.method) job_a.set_pending(result="test") self.assertEqual(job_a.state, PENDING) self.assertFalse(job_a.date_enqueued) self.assertFalse(job_a.date_started) self.assertEqual(job_a.retry, 0) self.assertEqual(job_a.result, "test") def test_set_enqueued(self): job_a = Job(self.method) datetime_path = "odoo.addons.queue_job.job.datetime" with mock.patch(datetime_path, autospec=True) as mock_datetime: mock_datetime.now.return_value = datetime(2015, 3, 15, 16, 41, 0) job_a.set_enqueued() self.assertEqual(job_a.state, ENQUEUED) self.assertEqual(job_a.date_enqueued, datetime(2015, 3, 15, 16, 41, 0)) self.assertFalse(job_a.date_started) def test_set_started(self): job_a = Job(self.method) datetime_path = "odoo.addons.queue_job.job.datetime" with mock.patch(datetime_path, autospec=True) as mock_datetime: mock_datetime.now.return_value = datetime(2015, 3, 15, 16, 41, 0) job_a.set_started() self.assertEqual(job_a.state, STARTED) self.assertEqual(job_a.date_started, datetime(2015, 3, 15, 16, 41, 0)) def test_worker_pid(self): """When a job is started, it gets the PID of the worker that starts it""" method = self.env["res.users"].mapped job_a = Job(method) self.assertFalse(job_a.worker_pid) with mock.patch("os.getpid", autospec=True) as mock_getpid: mock_getpid.return_value = 99999 job_a.set_started() self.assertEqual(job_a.worker_pid, 99999) # reset the pid job_a.set_pending() self.assertFalse(job_a.worker_pid) def test_set_done(self): job_a = Job(self.method) job_a.date_started = datetime(2015, 3, 15, 16, 40, 0) datetime_path = "odoo.addons.queue_job.job.datetime" with mock.patch(datetime_path, autospec=True) as mock_datetime: mock_datetime.now.return_value = datetime(2015, 3, 15, 16, 41, 0) job_a.set_done(result="test") self.assertEqual(job_a.state, DONE) self.assertEqual(job_a.result, "test") self.assertEqual(job_a.date_done, datetime(2015, 3, 15, 16, 41, 0)) self.assertEqual(job_a.exec_time, 60.0) self.assertFalse(job_a.exc_info) def test_set_failed(self): job_a = Job(self.method) job_a.set_failed( exc_info="failed test", exc_name="FailedTest", exc_message="Sadly this job failed", ) self.assertEqual(job_a.state, FAILED) self.assertEqual(job_a.exc_info, "failed test") self.assertEqual(job_a.exc_name, "FailedTest") self.assertEqual(job_a.exc_message, "Sadly this job failed") def test_postpone(self): job_a = Job(self.method) datetime_path = "odoo.addons.queue_job.job.datetime" with mock.patch(datetime_path, autospec=True) as mock_datetime: mock_datetime.now.return_value = datetime(2015, 3, 15, 16, 41, 0) job_a.postpone(result="test", seconds=60) self.assertEqual(job_a.eta, datetime(2015, 3, 15, 16, 42, 0)) self.assertEqual(job_a.result, "test") self.assertFalse(job_a.exc_info) def test_company_simple(self): company = self.env.ref("base.main_company") eta = datetime.now() + timedelta(hours=5) test_job = Job( self.env["test.queue.job"].with_company(company).testing_method, args=("o", "k"), kwargs={"return_context": 1}, priority=15, eta=eta, description="My description", ) test_job.worker_pid = 99999 # normally set on "set_start" test_job.store() job_read = Job.load(self.env, test_job.uuid) self.assertEqual(test_job.func.__func__, job_read.func.__func__) result_ctx = job_read.func(*tuple(test_job.args), **test_job.kwargs) self.assertEqual(result_ctx.get("allowed_company_ids"), company.ids) def test_company_complex(self): company1 = self.env.ref("base.main_company") company2 = company1.create({"name": "Queue job company"}) companies = company1 | company2 self.env.user.write({"company_ids": [(6, False, companies.ids)]}) # Ensure the main company still the first self.assertEqual(self.env.user.company_id, company1) eta = datetime.now() + timedelta(hours=5) test_job = Job( self.env["test.queue.job"].with_company(company2).testing_method, args=("o", "k"), kwargs={"return_context": 1}, priority=15, eta=eta, description="My description", ) test_job.worker_pid = 99999 # normally set on "set_start" test_job.store() job_read = Job.load(self.env, test_job.uuid) self.assertEqual(test_job.func.__func__, job_read.func.__func__) result_ctx = job_read.func(*tuple(test_job.args), **test_job.kwargs) self.assertEqual(result_ctx.get("allowed_company_ids"), company2.ids) def test_store(self): test_job = Job(self.method) test_job.store() stored = self.queue_job.search([("uuid", "=", test_job.uuid)]) self.assertEqual(len(stored), 1) def test_store_extra_data(self): test_job = Job(self.method) test_job.store() stored = self.queue_job.search([("uuid", "=", test_job.uuid)]) self.assertEqual(stored.additional_info, "JUST_TESTING") test_job.set_failed(exc_info="failed test", exc_name="FailedTest") test_job.store() stored.invalidate_recordset() self.assertEqual(stored.additional_info, "JUST_TESTING_BUT_FAILED") def test_read(self): eta = datetime.now() + timedelta(hours=5) test_job = Job( self.method, args=("o", "k"), kwargs={"c": "!"}, priority=15, eta=eta, description="My description", ) test_job.worker_pid = 99999 # normally set on "set_start" test_job.company_id = self.env.ref("base.main_company").id test_job.store() job_read = Job.load(self.env, test_job.uuid) self.assertEqual(test_job.uuid, job_read.uuid) self.assertEqual(test_job.model_name, job_read.model_name) self.assertEqual(test_job.func.__func__, job_read.func.__func__) self.assertEqual(test_job.args, job_read.args) self.assertEqual(test_job.kwargs, job_read.kwargs) self.assertEqual(test_job.method_name, job_read.method_name) self.assertEqual(test_job.description, job_read.description) self.assertEqual(test_job.state, job_read.state) self.assertEqual(test_job.priority, job_read.priority) self.assertEqual(test_job.exc_info, job_read.exc_info) self.assertEqual(test_job.result, job_read.result) self.assertEqual(test_job.user_id, job_read.user_id) self.assertEqual(test_job.company_id, job_read.company_id) self.assertEqual(test_job.worker_pid, 99999) delta = timedelta(seconds=1) # DB does not keep milliseconds self.assertAlmostEqual( test_job.date_created, job_read.date_created, delta=delta ) self.assertAlmostEqual( test_job.date_started, job_read.date_started, delta=delta ) self.assertAlmostEqual( test_job.date_enqueued, job_read.date_enqueued, delta=delta ) self.assertAlmostEqual(test_job.date_done, job_read.date_done, delta=delta) self.assertAlmostEqual(test_job.eta, job_read.eta, delta=delta) test_date = datetime(2015, 3, 15, 21, 7, 0) job_read.date_enqueued = test_date job_read.date_started = test_date job_read.date_done = test_date job_read.store() job_read = Job.load(self.env, test_job.uuid) self.assertAlmostEqual(job_read.date_started, test_date, delta=delta) self.assertAlmostEqual(job_read.date_enqueued, test_date, delta=delta) self.assertAlmostEqual(job_read.date_done, test_date, delta=delta) self.assertAlmostEqual(job_read.exec_time, 0.0) def test_job_unlinked(self): test_job = Job(self.method, args=("o", "k"), kwargs={"c": "!"}) test_job.store() stored = self.queue_job.search([("uuid", "=", test_job.uuid)]) stored.unlink() with self.assertRaises(NoSuchJobError): Job.load(self.env, test_job.uuid) def test_unicode(self): test_job = Job( self.method, args=("öô¿‽", "ñě"), kwargs={"c": "ßø"}, priority=15, description="My dé^Wdescription", ) test_job.store() job_read = Job.load(self.env, test_job.uuid) self.assertEqual(test_job.args, job_read.args) self.assertEqual(job_read.args, ("öô¿‽", "ñě")) self.assertEqual(test_job.kwargs, job_read.kwargs) self.assertEqual(job_read.kwargs, {"c": "ßø"}) self.assertEqual(test_job.description, job_read.description) self.assertEqual(job_read.description, "My dé^Wdescription") def test_accented_bytestring(self): test_job = Job( self.method, args=("öô¿‽", "ñě"), kwargs={"c": "ßø"}, priority=15, description="My dé^Wdescription", ) test_job.store() job_read = Job.load(self.env, test_job.uuid) self.assertEqual(job_read.args, ("öô¿‽", "ñě")) self.assertEqual(job_read.kwargs, {"c": "ßø"}) self.assertEqual(job_read.description, "My dé^Wdescription") def test_job_delay(self): self.cr.execute("delete from queue_job") job_ = self.env["test.queue.job"].with_delay().testing_method() stored = self.queue_job.search([]) self.assertEqual(len(stored), 1) self.assertEqual(stored.uuid, job_.uuid, "Incorrect returned Job UUID") def test_job_delay_model_method(self): self.cr.execute("delete from queue_job") delayable = self.env["test.queue.job"].with_delay() job_instance = delayable.testing_method("a", k=1) self.assertTrue(job_instance) result = job_instance.perform() self.assertEqual(result, (("a",), {"k": 1})) def test_job_identity_key_str(self): id_key = "e294e8444453b09d59bdb6efbfec1323" test_job_1 = Job( self.method, priority=15, description="Test I am the first one", identity_key=id_key, ) test_job_1.store() job1 = Job.load(self.env, test_job_1.uuid) self.assertEqual(job1.identity_key, id_key) def test_job_identity_key_func_exact(self): hasher = hashlib.sha1() hasher.update(b"test.queue.job") hasher.update(b"testing_method") hasher.update(str(sorted([])).encode("utf-8")) hasher.update(str((1, "foo")).encode("utf-8")) hasher.update(str(sorted({"bar": "baz"}.items())).encode("utf-8")) expected_key = hasher.hexdigest() test_job_1 = Job( self.method, args=[1, "foo"], kwargs={"bar": "baz"}, identity_key=identity_exact, ) self.assertEqual(test_job_1.identity_key, expected_key) test_job_1.store() job1 = Job.load(self.env, test_job_1.uuid) self.assertEqual(job1.identity_key, expected_key) class TestJobs(JobCommonCase): """Test jobs on other methods or with different job configuration""" def test_description(self): """If no description is given to the job, it should be computed from the function """ # if a docstring is defined for the function # it's used as description job_a = Job(self.env["test.queue.job"].testing_method) self.assertEqual(job_a.description, "Method used for tests") # if no docstring, the description is computed job_b = Job(self.env["test.queue.job"].no_description) self.assertEqual(job_b.description, "test.queue.job.no_description") # case when we explicitly specify the description description = "My description" job_a = Job(self.env["test.queue.job"].testing_method, description=description) self.assertEqual(job_a.description, description) def test_retry_pattern(self): """When we specify a retry pattern, the eta must follow it""" datetime_path = "odoo.addons.queue_job.job.datetime" method = self.env["test.queue.job"].job_with_retry_pattern with mock.patch(datetime_path, autospec=True) as mock_datetime: mock_datetime.now.return_value = datetime(2015, 6, 1, 15, 10, 0) test_job = Job(method, max_retries=0) test_job.retry += 1 test_job.postpone(self.env) self.assertEqual(test_job.retry, 1) self.assertEqual(test_job.eta, datetime(2015, 6, 1, 15, 11, 0)) test_job.retry += 1 test_job.postpone(self.env) self.assertEqual(test_job.retry, 2) self.assertEqual(test_job.eta, datetime(2015, 6, 1, 15, 13, 0)) test_job.retry += 1 test_job.postpone(self.env) self.assertEqual(test_job.retry, 3) self.assertEqual(test_job.eta, datetime(2015, 6, 1, 15, 10, 10)) test_job.retry += 1 test_job.postpone(self.env) self.assertEqual(test_job.retry, 4) self.assertEqual(test_job.eta, datetime(2015, 6, 1, 15, 10, 10)) test_job.retry += 1 test_job.postpone(self.env) self.assertEqual(test_job.retry, 5) self.assertEqual(test_job.eta, datetime(2015, 6, 1, 15, 15, 0)) def test_retry_pattern_no_zero(self): """When we specify a retry pattern without 0, uses RETRY_INTERVAL""" method = self.env["test.queue.job"].job_with_retry_pattern__no_zero test_job = Job(method, max_retries=0) test_job.retry += 1 self.assertEqual(test_job.retry, 1) self.assertEqual(test_job._get_retry_seconds(), RETRY_INTERVAL) test_job.retry += 1 self.assertEqual(test_job.retry, 2) self.assertEqual(test_job._get_retry_seconds(), RETRY_INTERVAL) test_job.retry += 1 self.assertEqual(test_job.retry, 3) self.assertEqual(test_job._get_retry_seconds(), 180) test_job.retry += 1 self.assertEqual(test_job.retry, 4) self.assertEqual(test_job._get_retry_seconds(), 180) def test_job_delay_model_method_multi(self): rec1 = self.env["test.queue.job"].create({"name": "test1"}) rec2 = self.env["test.queue.job"].create({"name": "test2"}) recs = rec1 + rec2 job_instance = recs.with_delay().mapped("name") self.assertTrue(job_instance) self.assertEqual(job_instance.args, ("name",)) self.assertEqual(job_instance.recordset, recs) self.assertEqual(job_instance.model_name, "test.queue.job") self.assertEqual(job_instance.method_name, "mapped") self.assertEqual(["test1", "test2"], job_instance.perform()) def test_job_identity_key_no_duplicate(self): """If a job with same identity key in queue do not add a new one""" id_key = "e294e8444453b09d59bdb6efbfec1323" rec1 = self.env["test.queue.job"].create({"name": "test1"}) job_1 = rec1.with_delay(identity_key=id_key).mapped("name") self.assertTrue(job_1) job_2 = rec1.with_delay(identity_key=id_key).mapped("name") self.assertEqual(job_2.uuid, job_1.uuid) def test_job_with_mutable_arguments(self): """Job with mutable arguments do not mutate on perform()""" delayable = self.env["test.queue.job"].with_delay() job_instance = delayable.job_alter_mutable([1], mutable_kwarg={"a": 1}) self.assertTrue(job_instance) result = job_instance.perform() self.assertEqual(result, ([1, 2], {"a": 1, "b": 2})) job_instance.set_done() # at this point, the 'args' and 'kwargs' of the job instance # might have been modified, but they must never be modified in # the queue_job table after their creation, so a new 'load' will # get the initial values. job_instance.store() # jobs are always loaded before being performed, so we simulate # this behavior here to check if we have the correct initial arguments job_instance = Job.load(self.env, job_instance.uuid) self.assertEqual(([1],), job_instance.args) self.assertEqual({"mutable_kwarg": {"a": 1}}, job_instance.kwargs) def test_store_env_su_no_sudo(self): demo_user = self.env.ref("base.user_demo") self.env = self.env(user=demo_user) delayable = self.env["test.queue.job"].with_delay() test_job = delayable.testing_method() stored = test_job.db_record() job_instance = Job.load(self.env, stored.uuid) self.assertFalse(job_instance.recordset.env.su) self.assertTrue(job_instance.user_id, demo_user) def test_store_env_su_sudo(self): demo_user = self.env.ref("base.user_demo") self.env = self.env(user=demo_user) delayable = self.env["test.queue.job"].sudo().with_delay() test_job = delayable.testing_method() stored = test_job.db_record() job_instance = Job.load(self.env, stored.uuid) self.assertTrue(job_instance.recordset.env.su) self.assertTrue(job_instance.user_id, demo_user) class TestJobModel(JobCommonCase): def test_job_change_state(self): stored = self._create_job() stored._change_job_state(DONE, result="test") self.assertEqual(stored.state, DONE) self.assertEqual(stored.result, "test") stored._change_job_state(PENDING, result="test2") self.assertEqual(stored.state, PENDING) self.assertEqual(stored.result, "test2") with self.assertRaises(ValueError): # only PENDING and DONE supported stored._change_job_state(STARTED) def test_button_done(self): stored = self._create_job() stored.button_done() self.assertEqual(stored.state, DONE) self.assertEqual( stored.result, "Manually set to done by %s" % self.env.user.name ) def test_button_done_enqueue_waiting_dependencies(self): job_root = Job(self.env["test.queue.job"].testing_method) job_child = Job(self.env["test.queue.job"].testing_method) job_child.add_depends({job_root}) DelayableGraph._ensure_same_graph_uuid([job_root, job_child]) job_root.store() job_child.store() self.assertEqual(job_child.state, WAIT_DEPENDENCIES) record_root = job_root.db_record() record_child = job_child.db_record() # Trigger button done record_root.button_done() # Check the state self.assertEqual(record_root.state, DONE) self.assertEqual(record_child.state, PENDING) def test_button_cancel_dependencies(self): job_root = Job(self.env["test.queue.job"].testing_method) job_child = Job(self.env["test.queue.job"].testing_method) job_child.add_depends({job_root}) DelayableGraph._ensure_same_graph_uuid([job_root, job_child]) job_root.store() job_child.store() self.assertEqual(job_child.state, WAIT_DEPENDENCIES) record_root = job_root.db_record() record_child = job_child.db_record() # Trigger button cancelled record_root.button_cancelled() # Check the state self.assertEqual(record_root.state, CANCELLED) self.assertEqual(record_child.state, CANCELLED) def test_requeue(self): stored = self._create_job() stored.write({"state": "failed"}) stored.requeue() self.assertEqual(stored.state, PENDING) def test_requeue_wait_dependencies_not_touched(self): job_root = Job(self.env["test.queue.job"].testing_method) job_child = Job(self.env["test.queue.job"].testing_method) job_child.add_depends({job_root}) job_root.store() job_child.store() DelayableGraph._ensure_same_graph_uuid([job_root, job_child]) record_root = job_root.db_record() record_child = job_child.db_record() self.assertEqual(record_root.state, PENDING) self.assertEqual(record_child.state, WAIT_DEPENDENCIES) record_root.write({"state": "failed"}) (record_root + record_child).requeue() self.assertEqual(record_root.state, PENDING) self.assertEqual(record_child.state, WAIT_DEPENDENCIES) def test_message_when_write_fail(self): stored = self._create_job() stored.write({"state": "failed"}) self.assertEqual(stored.state, FAILED) messages = stored.message_ids self.assertEqual(len(messages), 1) def test_follower_when_write_fail(self): """Check that inactive users doesn't are not followers even if they are linked to an active partner""" group = self.env.ref("queue_job.group_queue_job_manager") vals = { "name": "xx", "login": "xx", "groups_id": [(6, 0, [group.id])], "active": False, } inactiveusr = self.user.create(vals) inactiveusr.partner_id.active = True self.assertFalse(inactiveusr in group.users) stored = self._create_job() stored.write({"state": "failed"}) followers = stored.message_follower_ids.mapped("partner_id") self.assertFalse(inactiveusr.partner_id in followers) self.assertFalse({u.partner_id for u in group.users} - set(followers)) def test_wizard_requeue(self): stored = self._create_job() stored.write({"state": "failed"}) model = self.env["queue.requeue.job"] model = model.with_context(active_model="queue.job", active_ids=stored.ids) model.create({}).requeue() self.assertEqual(stored.state, PENDING) def test_context_uuid(self): delayable = self.env["test.queue.job"].with_delay() test_job = delayable.testing_method(return_context=True) result = test_job.perform() key_present = "job_uuid" in result self.assertTrue(key_present) self.assertEqual(result["job_uuid"], test_job._uuid) def test_override_channel(self): delayable = self.env["test.queue.job"].with_delay(channel="root.sub.sub") test_job = delayable.testing_method(return_context=True) self.assertEqual("root.sub.sub", test_job.channel) def test_job_change_user_id(self): demo_user = self.env.ref("base.user_demo") stored = self._create_job() stored.user_id = demo_user self.assertEqual(stored.records.env.uid, demo_user.id) class TestJobStorageMultiCompany(common.TransactionCase): """Test storage of jobs""" def setUp(self): super().setUp() self.queue_job = self.env["queue.job"] grp_queue_job_manager = self.ref("queue_job.group_queue_job_manager") User = self.env["res.users"] Company = self.env["res.company"] Partner = self.env["res.partner"] main_company = self.env.ref("base.main_company") self.partner_user = Partner.create( {"name": "Simple User", "email": "simple.user@example.com"} ) self.simple_user = User.create( { "partner_id": self.partner_user.id, "company_ids": [(4, main_company.id)], "login": "simple_user", "name": "simple user", "groups_id": [], } ) self.other_partner_a = Partner.create( {"name": "My Company a", "is_company": True, "email": "test@tes.ttest"} ) self.other_company_a = Company.create( { "name": "My Company a", "partner_id": self.other_partner_a.id, "currency_id": self.ref("base.EUR"), } ) self.other_user_a = User.create( { "partner_id": self.other_partner_a.id, "company_id": self.other_company_a.id, "company_ids": [(4, self.other_company_a.id)], "login": "my_login a", "name": "my user A", "groups_id": [(4, grp_queue_job_manager)], } ) self.other_partner_b = Partner.create( {"name": "My Company b", "is_company": True, "email": "test@tes.ttest"} ) self.other_company_b = Company.create( { "name": "My Company b", "partner_id": self.other_partner_b.id, "currency_id": self.ref("base.EUR"), } ) self.other_user_b = User.create( { "partner_id": self.other_partner_b.id, "company_id": self.other_company_b.id, "company_ids": [(4, self.other_company_b.id)], "login": "my_login_b", "name": "my user B", "groups_id": [(4, grp_queue_job_manager)], } ) def _create_job(self, env): self.cr.execute("delete from queue_job") env["test.queue.job"].with_delay().testing_method() stored = self.queue_job.search([]) self.assertEqual(len(stored), 1) return stored def test_job_default_company_id(self): """the default company is the one from the current user_id""" stored = self._create_job(self.env) self.assertEqual( stored.company_id.id, self.ref("base.main_company"), "Incorrect default company_id", ) env = self.env(user=self.other_user_b.id) stored = self._create_job(env) self.assertEqual( stored.company_id.id, self.other_company_b.id, "Incorrect default company_id", ) def test_job_no_company_id(self): """if we put an empty company_id in the context jobs are created without company_id """ env = self.env(context={"company_id": None}) stored = self._create_job(env) self.assertFalse(stored.company_id, "Company_id should be empty") def test_job_specific_company_id(self): """If a company_id specified in the context it's used by default for the job creation""" env = self.env(context={"company_id": self.other_company_a.id}) stored = self._create_job(env) self.assertEqual( stored.company_id.id, self.other_company_a.id, "Incorrect company_id" ) def test_job_subscription(self): # if the job is created without company_id, all members of # queue_job.group_queue_job_manager must be followers User = self.env["res.users"] no_company_context = dict(self.env.context, company_id=None) no_company_env = self.env(user=self.simple_user, context=no_company_context) stored = self._create_job(no_company_env) stored._message_post_on_failure() users = ( User.search( [("groups_id", "=", self.ref("queue_job.group_queue_job_manager"))] ) + stored.user_id ) self.assertEqual(len(stored.message_follower_ids), len(users)) expected_partners = [u.partner_id for u in users] self.assertSetEqual( set(stored.message_follower_ids.mapped("partner_id")), set(expected_partners), ) followers_id = stored.message_follower_ids.mapped("partner_id.id") self.assertIn(self.other_partner_a.id, followers_id) self.assertIn(self.other_partner_b.id, followers_id) # jobs created for a specific company_id are followed only by # company's members company_a_context = dict(self.env.context, company_id=self.other_company_a.id) company_a_env = self.env(user=self.simple_user, context=company_a_context) stored = self._create_job(company_a_env) stored.with_user(self.other_user_a.id) stored._message_post_on_failure() # 2 because simple_user (creator of job) + self.other_partner_a self.assertEqual(len(stored.message_follower_ids), 2) users = self.simple_user + self.other_user_a expected_partners = [u.partner_id for u in users] self.assertSetEqual( set(stored.message_follower_ids.mapped("partner_id")), set(expected_partners), ) followers_id = stored.message_follower_ids.mapped("partner_id.id") self.assertIn(self.other_partner_a.id, followers_id) self.assertNotIn(self.other_partner_b.id, followers_id)