From 0c9cc2e6cedc19e00138f5ee29afa67318fd34da Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Thu, 29 Feb 2024 17:35:11 +0530 Subject: [PATCH] test: NOWAIT functionality --- frappe/tests/test_db.py | 18 +++++++++++++++--- frappe/tests/test_document.py | 16 +++++++++++++++- frappe/tests/utils.py | 5 +++++ 3 files changed, 35 insertions(+), 4 deletions(-) diff --git a/frappe/tests/test_db.py b/frappe/tests/test_db.py index 825aff98cf..6376aff5a6 100644 --- a/frappe/tests/test_db.py +++ b/frappe/tests/test_db.py @@ -15,7 +15,7 @@ from frappe.database.utils import FallBackDateTimeStr from frappe.query_builder import Field from frappe.query_builder.functions import Concat_ws from frappe.tests.test_query_builder import db_type_is, run_only_if -from frappe.tests.utils import FrappeTestCase +from frappe.tests.utils import FrappeTestCase, timeout from frappe.utils import add_days, now, random_string, set_request from frappe.utils.testutils import clear_custom_fields @@ -58,13 +58,25 @@ class TestDB(FrappeTestCase): def test_skip_locking(self): with self.primary_connection(): - name = frappe.db.get_value("User", "Administrator", "name", for_update=True, skip_locked=True) + name = frappe.db.get_value("User", "Administrator", for_update=True, skip_locked=True) self.assertEqual(name, "Administrator") with self.secondary_connection(): - name = frappe.db.get_value("User", "Administrator", "name", for_update=True, skip_locked=True) + name = frappe.db.get_value("User", "Administrator", for_update=True, skip_locked=True) self.assertFalse(name) + @timeout(5, "Lock timeout should have been 0") + def test_no_wait(self): + with self.primary_connection(): + name = frappe.db.get_value("User", "Administrator", for_update=True) + self.assertEqual(name, "Administrator") + + with self.secondary_connection(): + self.assertRaises( + frappe.QueryTimeoutError, + lambda: frappe.db.get_value("User", "Administrator", for_update=True, wait=False), + ) + @patch.dict(frappe.conf, {"http_timeout": 20, "enable_db_statement_timeout": 1}) def test_db_timeout_computation(self): set_request(method="GET", path="/") diff --git a/frappe/tests/test_document.py b/frappe/tests/test_document.py index dd76970903..2201949fe0 100644 --- a/frappe/tests/test_document.py +++ b/frappe/tests/test_document.py @@ -9,7 +9,7 @@ from frappe.app import make_form_dict from frappe.core.doctype.doctype.test_doctype import new_doctype from frappe.desk.doctype.note.note import Note from frappe.model.naming import make_autoname, parse_naming_series, revert_series_if_last -from frappe.tests.utils import FrappeTestCase +from frappe.tests.utils import FrappeTestCase, timeout from frappe.utils import cint, now_datetime, set_request from frappe.website.serve import get_response @@ -492,6 +492,20 @@ class TestDocument(FrappeTestCase): changed_val = frappe.db.get_single_value(c.doctype, key) self.assertEqual(val, changed_val) + @timeout(5, "Deletion stuck on lock timeout") + def test_delete_race_condition(self): + note = frappe.new_doc("Note") + note.title = note.content = frappe.generate_hash() + note.insert() + frappe.db.commit() # ensure that second connection can see the document + + with self.primary_connection(): + n1 = frappe.get_doc(note.doctype, note.name) + n1.save() + + with self.secondary_connection(): + self.assertRaises(frappe.QueryTimeoutError, frappe.delete_doc, note.doctype, note.name) + class TestDocumentWebView(FrappeTestCase): def get(self, path, user="Guest"): diff --git a/frappe/tests/utils.py b/frappe/tests/utils.py index 5dea3f5630..9ea16fa3e8 100644 --- a/frappe/tests/utils.py +++ b/frappe/tests/utils.py @@ -119,6 +119,11 @@ class FrappeTestCase(unittest.TestCase): yield finally: frappe.local.db = current_conn + self.addCleanup(self._rollback_connections) + + def _rollback_connections(self): + self._primary_connection.rollback() + self._secondary_connection.rollback() def assertQueryEqual(self, first: str, second: str): self.assertEqual(self.normalize_sql(first), self.normalize_sql(second))