1. import sys
    
  2. import threading
    
  3. import time
    
  4. from unittest import skipIf, skipUnless
    
  5. 
    
  6. from django.db import (
    
  7.     DatabaseError,
    
  8.     Error,
    
  9.     IntegrityError,
    
  10.     OperationalError,
    
  11.     connection,
    
  12.     transaction,
    
  13. )
    
  14. from django.test import (
    
  15.     TestCase,
    
  16.     TransactionTestCase,
    
  17.     skipIfDBFeature,
    
  18.     skipUnlessDBFeature,
    
  19. )
    
  20. 
    
  21. from .models import Reporter
    
  22. 
    
  23. 
    
  24. @skipUnlessDBFeature("uses_savepoints")
    
  25. class AtomicTests(TransactionTestCase):
    
  26.     """
    
  27.     Tests for the atomic decorator and context manager.
    
  28. 
    
  29.     The tests make assertions on internal attributes because there isn't a
    
  30.     robust way to ask the database for its current transaction state.
    
  31. 
    
  32.     Since the decorator syntax is converted into a context manager (see the
    
  33.     implementation), there are only a few basic tests with the decorator
    
  34.     syntax and the bulk of the tests use the context manager syntax.
    
  35.     """
    
  36. 
    
  37.     available_apps = ["transactions"]
    
  38. 
    
  39.     def test_decorator_syntax_commit(self):
    
  40.         @transaction.atomic
    
  41.         def make_reporter():
    
  42.             return Reporter.objects.create(first_name="Tintin")
    
  43. 
    
  44.         reporter = make_reporter()
    
  45.         self.assertSequenceEqual(Reporter.objects.all(), [reporter])
    
  46. 
    
  47.     def test_decorator_syntax_rollback(self):
    
  48.         @transaction.atomic
    
  49.         def make_reporter():
    
  50.             Reporter.objects.create(first_name="Haddock")
    
  51.             raise Exception("Oops, that's his last name")
    
  52. 
    
  53.         with self.assertRaisesMessage(Exception, "Oops"):
    
  54.             make_reporter()
    
  55.         self.assertSequenceEqual(Reporter.objects.all(), [])
    
  56. 
    
  57.     def test_alternate_decorator_syntax_commit(self):
    
  58.         @transaction.atomic()
    
  59.         def make_reporter():
    
  60.             return Reporter.objects.create(first_name="Tintin")
    
  61. 
    
  62.         reporter = make_reporter()
    
  63.         self.assertSequenceEqual(Reporter.objects.all(), [reporter])
    
  64. 
    
  65.     def test_alternate_decorator_syntax_rollback(self):
    
  66.         @transaction.atomic()
    
  67.         def make_reporter():
    
  68.             Reporter.objects.create(first_name="Haddock")
    
  69.             raise Exception("Oops, that's his last name")
    
  70. 
    
  71.         with self.assertRaisesMessage(Exception, "Oops"):
    
  72.             make_reporter()
    
  73.         self.assertSequenceEqual(Reporter.objects.all(), [])
    
  74. 
    
  75.     def test_commit(self):
    
  76.         with transaction.atomic():
    
  77.             reporter = Reporter.objects.create(first_name="Tintin")
    
  78.         self.assertSequenceEqual(Reporter.objects.all(), [reporter])
    
  79. 
    
  80.     def test_rollback(self):
    
  81.         with self.assertRaisesMessage(Exception, "Oops"):
    
  82.             with transaction.atomic():
    
  83.                 Reporter.objects.create(first_name="Haddock")
    
  84.                 raise Exception("Oops, that's his last name")
    
  85.         self.assertSequenceEqual(Reporter.objects.all(), [])
    
  86. 
    
  87.     def test_nested_commit_commit(self):
    
  88.         with transaction.atomic():
    
  89.             reporter1 = Reporter.objects.create(first_name="Tintin")
    
  90.             with transaction.atomic():
    
  91.                 reporter2 = Reporter.objects.create(
    
  92.                     first_name="Archibald", last_name="Haddock"
    
  93.                 )
    
  94.         self.assertSequenceEqual(Reporter.objects.all(), [reporter2, reporter1])
    
  95. 
    
  96.     def test_nested_commit_rollback(self):
    
  97.         with transaction.atomic():
    
  98.             reporter = Reporter.objects.create(first_name="Tintin")
    
  99.             with self.assertRaisesMessage(Exception, "Oops"):
    
  100.                 with transaction.atomic():
    
  101.                     Reporter.objects.create(first_name="Haddock")
    
  102.                     raise Exception("Oops, that's his last name")
    
  103.         self.assertSequenceEqual(Reporter.objects.all(), [reporter])
    
  104. 
    
  105.     def test_nested_rollback_commit(self):
    
  106.         with self.assertRaisesMessage(Exception, "Oops"):
    
  107.             with transaction.atomic():
    
  108.                 Reporter.objects.create(last_name="Tintin")
    
  109.                 with transaction.atomic():
    
  110.                     Reporter.objects.create(last_name="Haddock")
    
  111.                 raise Exception("Oops, that's his first name")
    
  112.         self.assertSequenceEqual(Reporter.objects.all(), [])
    
  113. 
    
  114.     def test_nested_rollback_rollback(self):
    
  115.         with self.assertRaisesMessage(Exception, "Oops"):
    
  116.             with transaction.atomic():
    
  117.                 Reporter.objects.create(last_name="Tintin")
    
  118.                 with self.assertRaisesMessage(Exception, "Oops"):
    
  119.                     with transaction.atomic():
    
  120.                         Reporter.objects.create(first_name="Haddock")
    
  121.                     raise Exception("Oops, that's his last name")
    
  122.                 raise Exception("Oops, that's his first name")
    
  123.         self.assertSequenceEqual(Reporter.objects.all(), [])
    
  124. 
    
  125.     def test_merged_commit_commit(self):
    
  126.         with transaction.atomic():
    
  127.             reporter1 = Reporter.objects.create(first_name="Tintin")
    
  128.             with transaction.atomic(savepoint=False):
    
  129.                 reporter2 = Reporter.objects.create(
    
  130.                     first_name="Archibald", last_name="Haddock"
    
  131.                 )
    
  132.         self.assertSequenceEqual(Reporter.objects.all(), [reporter2, reporter1])
    
  133. 
    
  134.     def test_merged_commit_rollback(self):
    
  135.         with transaction.atomic():
    
  136.             Reporter.objects.create(first_name="Tintin")
    
  137.             with self.assertRaisesMessage(Exception, "Oops"):
    
  138.                 with transaction.atomic(savepoint=False):
    
  139.                     Reporter.objects.create(first_name="Haddock")
    
  140.                     raise Exception("Oops, that's his last name")
    
  141.         # Writes in the outer block are rolled back too.
    
  142.         self.assertSequenceEqual(Reporter.objects.all(), [])
    
  143. 
    
  144.     def test_merged_rollback_commit(self):
    
  145.         with self.assertRaisesMessage(Exception, "Oops"):
    
  146.             with transaction.atomic():
    
  147.                 Reporter.objects.create(last_name="Tintin")
    
  148.                 with transaction.atomic(savepoint=False):
    
  149.                     Reporter.objects.create(last_name="Haddock")
    
  150.                 raise Exception("Oops, that's his first name")
    
  151.         self.assertSequenceEqual(Reporter.objects.all(), [])
    
  152. 
    
  153.     def test_merged_rollback_rollback(self):
    
  154.         with self.assertRaisesMessage(Exception, "Oops"):
    
  155.             with transaction.atomic():
    
  156.                 Reporter.objects.create(last_name="Tintin")
    
  157.                 with self.assertRaisesMessage(Exception, "Oops"):
    
  158.                     with transaction.atomic(savepoint=False):
    
  159.                         Reporter.objects.create(first_name="Haddock")
    
  160.                     raise Exception("Oops, that's his last name")
    
  161.                 raise Exception("Oops, that's his first name")
    
  162.         self.assertSequenceEqual(Reporter.objects.all(), [])
    
  163. 
    
  164.     def test_reuse_commit_commit(self):
    
  165.         atomic = transaction.atomic()
    
  166.         with atomic:
    
  167.             reporter1 = Reporter.objects.create(first_name="Tintin")
    
  168.             with atomic:
    
  169.                 reporter2 = Reporter.objects.create(
    
  170.                     first_name="Archibald", last_name="Haddock"
    
  171.                 )
    
  172.         self.assertSequenceEqual(Reporter.objects.all(), [reporter2, reporter1])
    
  173. 
    
  174.     def test_reuse_commit_rollback(self):
    
  175.         atomic = transaction.atomic()
    
  176.         with atomic:
    
  177.             reporter = Reporter.objects.create(first_name="Tintin")
    
  178.             with self.assertRaisesMessage(Exception, "Oops"):
    
  179.                 with atomic:
    
  180.                     Reporter.objects.create(first_name="Haddock")
    
  181.                     raise Exception("Oops, that's his last name")
    
  182.         self.assertSequenceEqual(Reporter.objects.all(), [reporter])
    
  183. 
    
  184.     def test_reuse_rollback_commit(self):
    
  185.         atomic = transaction.atomic()
    
  186.         with self.assertRaisesMessage(Exception, "Oops"):
    
  187.             with atomic:
    
  188.                 Reporter.objects.create(last_name="Tintin")
    
  189.                 with atomic:
    
  190.                     Reporter.objects.create(last_name="Haddock")
    
  191.                 raise Exception("Oops, that's his first name")
    
  192.         self.assertSequenceEqual(Reporter.objects.all(), [])
    
  193. 
    
  194.     def test_reuse_rollback_rollback(self):
    
  195.         atomic = transaction.atomic()
    
  196.         with self.assertRaisesMessage(Exception, "Oops"):
    
  197.             with atomic:
    
  198.                 Reporter.objects.create(last_name="Tintin")
    
  199.                 with self.assertRaisesMessage(Exception, "Oops"):
    
  200.                     with atomic:
    
  201.                         Reporter.objects.create(first_name="Haddock")
    
  202.                     raise Exception("Oops, that's his last name")
    
  203.                 raise Exception("Oops, that's his first name")
    
  204.         self.assertSequenceEqual(Reporter.objects.all(), [])
    
  205. 
    
  206.     def test_force_rollback(self):
    
  207.         with transaction.atomic():
    
  208.             Reporter.objects.create(first_name="Tintin")
    
  209.             # atomic block shouldn't rollback, but force it.
    
  210.             self.assertFalse(transaction.get_rollback())
    
  211.             transaction.set_rollback(True)
    
  212.         self.assertSequenceEqual(Reporter.objects.all(), [])
    
  213. 
    
  214.     def test_prevent_rollback(self):
    
  215.         with transaction.atomic():
    
  216.             reporter = Reporter.objects.create(first_name="Tintin")
    
  217.             sid = transaction.savepoint()
    
  218.             # trigger a database error inside an inner atomic without savepoint
    
  219.             with self.assertRaises(DatabaseError):
    
  220.                 with transaction.atomic(savepoint=False):
    
  221.                     with connection.cursor() as cursor:
    
  222.                         cursor.execute("SELECT no_such_col FROM transactions_reporter")
    
  223.             # prevent atomic from rolling back since we're recovering manually
    
  224.             self.assertTrue(transaction.get_rollback())
    
  225.             transaction.set_rollback(False)
    
  226.             transaction.savepoint_rollback(sid)
    
  227.         self.assertSequenceEqual(Reporter.objects.all(), [reporter])
    
  228. 
    
  229. 
    
  230. class AtomicInsideTransactionTests(AtomicTests):
    
  231.     """All basic tests for atomic should also pass within an existing transaction."""
    
  232. 
    
  233.     def setUp(self):
    
  234.         self.atomic = transaction.atomic()
    
  235.         self.atomic.__enter__()
    
  236. 
    
  237.     def tearDown(self):
    
  238.         self.atomic.__exit__(*sys.exc_info())
    
  239. 
    
  240. 
    
  241. class AtomicWithoutAutocommitTests(AtomicTests):
    
  242.     """All basic tests for atomic should also pass when autocommit is turned off."""
    
  243. 
    
  244.     def setUp(self):
    
  245.         transaction.set_autocommit(False)
    
  246. 
    
  247.     def tearDown(self):
    
  248.         # The tests access the database after exercising 'atomic', initiating
    
  249.         # a transaction ; a rollback is required before restoring autocommit.
    
  250.         transaction.rollback()
    
  251.         transaction.set_autocommit(True)
    
  252. 
    
  253. 
    
  254. @skipUnlessDBFeature("uses_savepoints")
    
  255. class AtomicMergeTests(TransactionTestCase):
    
  256.     """Test merging transactions with savepoint=False."""
    
  257. 
    
  258.     available_apps = ["transactions"]
    
  259. 
    
  260.     def test_merged_outer_rollback(self):
    
  261.         with transaction.atomic():
    
  262.             Reporter.objects.create(first_name="Tintin")
    
  263.             with transaction.atomic(savepoint=False):
    
  264.                 Reporter.objects.create(first_name="Archibald", last_name="Haddock")
    
  265.                 with self.assertRaisesMessage(Exception, "Oops"):
    
  266.                     with transaction.atomic(savepoint=False):
    
  267.                         Reporter.objects.create(first_name="Calculus")
    
  268.                         raise Exception("Oops, that's his last name")
    
  269.                 # The third insert couldn't be roll back. Temporarily mark the
    
  270.                 # connection as not needing rollback to check it.
    
  271.                 self.assertTrue(transaction.get_rollback())
    
  272.                 transaction.set_rollback(False)
    
  273.                 self.assertEqual(Reporter.objects.count(), 3)
    
  274.                 transaction.set_rollback(True)
    
  275.             # The second insert couldn't be roll back. Temporarily mark the
    
  276.             # connection as not needing rollback to check it.
    
  277.             self.assertTrue(transaction.get_rollback())
    
  278.             transaction.set_rollback(False)
    
  279.             self.assertEqual(Reporter.objects.count(), 3)
    
  280.             transaction.set_rollback(True)
    
  281.         # The first block has a savepoint and must roll back.
    
  282.         self.assertSequenceEqual(Reporter.objects.all(), [])
    
  283. 
    
  284.     def test_merged_inner_savepoint_rollback(self):
    
  285.         with transaction.atomic():
    
  286.             reporter = Reporter.objects.create(first_name="Tintin")
    
  287.             with transaction.atomic():
    
  288.                 Reporter.objects.create(first_name="Archibald", last_name="Haddock")
    
  289.                 with self.assertRaisesMessage(Exception, "Oops"):
    
  290.                     with transaction.atomic(savepoint=False):
    
  291.                         Reporter.objects.create(first_name="Calculus")
    
  292.                         raise Exception("Oops, that's his last name")
    
  293.                 # The third insert couldn't be roll back. Temporarily mark the
    
  294.                 # connection as not needing rollback to check it.
    
  295.                 self.assertTrue(transaction.get_rollback())
    
  296.                 transaction.set_rollback(False)
    
  297.                 self.assertEqual(Reporter.objects.count(), 3)
    
  298.                 transaction.set_rollback(True)
    
  299.             # The second block has a savepoint and must roll back.
    
  300.             self.assertEqual(Reporter.objects.count(), 1)
    
  301.         self.assertSequenceEqual(Reporter.objects.all(), [reporter])
    
  302. 
    
  303. 
    
  304. @skipUnlessDBFeature("uses_savepoints")
    
  305. class AtomicErrorsTests(TransactionTestCase):
    
  306.     available_apps = ["transactions"]
    
  307.     forbidden_atomic_msg = "This is forbidden when an 'atomic' block is active."
    
  308. 
    
  309.     def test_atomic_prevents_setting_autocommit(self):
    
  310.         autocommit = transaction.get_autocommit()
    
  311.         with transaction.atomic():
    
  312.             with self.assertRaisesMessage(
    
  313.                 transaction.TransactionManagementError, self.forbidden_atomic_msg
    
  314.             ):
    
  315.                 transaction.set_autocommit(not autocommit)
    
  316.         # Make sure autocommit wasn't changed.
    
  317.         self.assertEqual(connection.autocommit, autocommit)
    
  318. 
    
  319.     def test_atomic_prevents_calling_transaction_methods(self):
    
  320.         with transaction.atomic():
    
  321.             with self.assertRaisesMessage(
    
  322.                 transaction.TransactionManagementError, self.forbidden_atomic_msg
    
  323.             ):
    
  324.                 transaction.commit()
    
  325.             with self.assertRaisesMessage(
    
  326.                 transaction.TransactionManagementError, self.forbidden_atomic_msg
    
  327.             ):
    
  328.                 transaction.rollback()
    
  329. 
    
  330.     def test_atomic_prevents_queries_in_broken_transaction(self):
    
  331.         r1 = Reporter.objects.create(first_name="Archibald", last_name="Haddock")
    
  332.         with transaction.atomic():
    
  333.             r2 = Reporter(first_name="Cuthbert", last_name="Calculus", id=r1.id)
    
  334.             with self.assertRaises(IntegrityError):
    
  335.                 r2.save(force_insert=True)
    
  336.             # The transaction is marked as needing rollback.
    
  337.             msg = (
    
  338.                 "An error occurred in the current transaction. You can't "
    
  339.                 "execute queries until the end of the 'atomic' block."
    
  340.             )
    
  341.             with self.assertRaisesMessage(transaction.TransactionManagementError, msg):
    
  342.                 r2.save(force_update=True)
    
  343.         self.assertEqual(Reporter.objects.get(pk=r1.pk).last_name, "Haddock")
    
  344. 
    
  345.     @skipIfDBFeature("atomic_transactions")
    
  346.     def test_atomic_allows_queries_after_fixing_transaction(self):
    
  347.         r1 = Reporter.objects.create(first_name="Archibald", last_name="Haddock")
    
  348.         with transaction.atomic():
    
  349.             r2 = Reporter(first_name="Cuthbert", last_name="Calculus", id=r1.id)
    
  350.             with self.assertRaises(IntegrityError):
    
  351.                 r2.save(force_insert=True)
    
  352.             # Mark the transaction as no longer needing rollback.
    
  353.             transaction.set_rollback(False)
    
  354.             r2.save(force_update=True)
    
  355.         self.assertEqual(Reporter.objects.get(pk=r1.pk).last_name, "Calculus")
    
  356. 
    
  357.     @skipUnlessDBFeature("test_db_allows_multiple_connections")
    
  358.     def test_atomic_prevents_queries_in_broken_transaction_after_client_close(self):
    
  359.         with transaction.atomic():
    
  360.             Reporter.objects.create(first_name="Archibald", last_name="Haddock")
    
  361.             connection.close()
    
  362.             # The connection is closed and the transaction is marked as
    
  363.             # needing rollback. This will raise an InterfaceError on databases
    
  364.             # that refuse to create cursors on closed connections (PostgreSQL)
    
  365.             # and a TransactionManagementError on other databases.
    
  366.             with self.assertRaises(Error):
    
  367.                 Reporter.objects.create(first_name="Cuthbert", last_name="Calculus")
    
  368.         # The connection is usable again .
    
  369.         self.assertEqual(Reporter.objects.count(), 0)
    
  370. 
    
  371. 
    
  372. @skipUnlessDBFeature("uses_savepoints")
    
  373. @skipUnless(connection.vendor == "mysql", "MySQL-specific behaviors")
    
  374. class AtomicMySQLTests(TransactionTestCase):
    
  375.     available_apps = ["transactions"]
    
  376. 
    
  377.     @skipIf(threading is None, "Test requires threading")
    
  378.     def test_implicit_savepoint_rollback(self):
    
  379.         """MySQL implicitly rolls back savepoints when it deadlocks (#22291)."""
    
  380.         Reporter.objects.create(id=1)
    
  381.         Reporter.objects.create(id=2)
    
  382. 
    
  383.         main_thread_ready = threading.Event()
    
  384. 
    
  385.         def other_thread():
    
  386.             try:
    
  387.                 with transaction.atomic():
    
  388.                     Reporter.objects.select_for_update().get(id=1)
    
  389.                     main_thread_ready.wait()
    
  390.                     # 1) This line locks... (see below for 2)
    
  391.                     Reporter.objects.exclude(id=1).update(id=2)
    
  392.             finally:
    
  393.                 # This is the thread-local connection, not the main connection.
    
  394.                 connection.close()
    
  395. 
    
  396.         other_thread = threading.Thread(target=other_thread)
    
  397.         other_thread.start()
    
  398. 
    
  399.         with self.assertRaisesMessage(OperationalError, "Deadlock found"):
    
  400.             # Double atomic to enter a transaction and create a savepoint.
    
  401.             with transaction.atomic():
    
  402.                 with transaction.atomic():
    
  403.                     Reporter.objects.select_for_update().get(id=2)
    
  404.                     main_thread_ready.set()
    
  405.                     # The two threads can't be synchronized with an event here
    
  406.                     # because the other thread locks. Sleep for a little while.
    
  407.                     time.sleep(1)
    
  408.                     # 2) ... and this line deadlocks. (see above for 1)
    
  409.                     Reporter.objects.exclude(id=2).update(id=1)
    
  410. 
    
  411.         other_thread.join()
    
  412. 
    
  413. 
    
  414. class AtomicMiscTests(TransactionTestCase):
    
  415.     available_apps = ["transactions"]
    
  416. 
    
  417.     def test_wrap_callable_instance(self):
    
  418.         """#20028 -- Atomic must support wrapping callable instances."""
    
  419. 
    
  420.         class Callable:
    
  421.             def __call__(self):
    
  422.                 pass
    
  423. 
    
  424.         # Must not raise an exception
    
  425.         transaction.atomic(Callable())
    
  426. 
    
  427.     @skipUnlessDBFeature("can_release_savepoints")
    
  428.     def test_atomic_does_not_leak_savepoints_on_failure(self):
    
  429.         """#23074 -- Savepoints must be released after rollback."""
    
  430. 
    
  431.         # Expect an error when rolling back a savepoint that doesn't exist.
    
  432.         # Done outside of the transaction block to ensure proper recovery.
    
  433.         with self.assertRaises(Error):
    
  434.             # Start a plain transaction.
    
  435.             with transaction.atomic():
    
  436.                 # Swallow the intentional error raised in the sub-transaction.
    
  437.                 with self.assertRaisesMessage(Exception, "Oops"):
    
  438.                     # Start a sub-transaction with a savepoint.
    
  439.                     with transaction.atomic():
    
  440.                         sid = connection.savepoint_ids[-1]
    
  441.                         raise Exception("Oops")
    
  442. 
    
  443.                 # This is expected to fail because the savepoint no longer exists.
    
  444.                 connection.savepoint_rollback(sid)
    
  445. 
    
  446.     def test_mark_for_rollback_on_error_in_transaction(self):
    
  447.         with transaction.atomic(savepoint=False):
    
  448.             # Swallow the intentional error raised.
    
  449.             with self.assertRaisesMessage(Exception, "Oops"):
    
  450.                 # Wrap in `mark_for_rollback_on_error` to check if the
    
  451.                 # transaction is marked broken.
    
  452.                 with transaction.mark_for_rollback_on_error():
    
  453.                     # Ensure that we are still in a good state.
    
  454.                     self.assertFalse(transaction.get_rollback())
    
  455. 
    
  456.                     raise Exception("Oops")
    
  457. 
    
  458.                 # mark_for_rollback_on_error marked the transaction as broken …
    
  459.                 self.assertTrue(transaction.get_rollback())
    
  460. 
    
  461.             # … and further queries fail.
    
  462.             msg = "You can't execute queries until the end of the 'atomic' block."
    
  463.             with self.assertRaisesMessage(transaction.TransactionManagementError, msg):
    
  464.                 Reporter.objects.create()
    
  465. 
    
  466.         # Transaction errors are reset at the end of an transaction, so this
    
  467.         # should just work.
    
  468.         Reporter.objects.create()
    
  469. 
    
  470.     def test_mark_for_rollback_on_error_in_autocommit(self):
    
  471.         self.assertTrue(transaction.get_autocommit())
    
  472. 
    
  473.         # Swallow the intentional error raised.
    
  474.         with self.assertRaisesMessage(Exception, "Oops"):
    
  475.             # Wrap in `mark_for_rollback_on_error` to check if the transaction
    
  476.             # is marked broken.
    
  477.             with transaction.mark_for_rollback_on_error():
    
  478.                 # Ensure that we are still in a good state.
    
  479.                 self.assertFalse(transaction.get_connection().needs_rollback)
    
  480. 
    
  481.                 raise Exception("Oops")
    
  482. 
    
  483.             # Ensure that `mark_for_rollback_on_error` did not mark the transaction
    
  484.             # as broken, since we are in autocommit mode …
    
  485.             self.assertFalse(transaction.get_connection().needs_rollback)
    
  486. 
    
  487.         # … and further queries work nicely.
    
  488.         Reporter.objects.create()
    
  489. 
    
  490. 
    
  491. class NonAutocommitTests(TransactionTestCase):
    
  492.     available_apps = []
    
  493. 
    
  494.     def setUp(self):
    
  495.         transaction.set_autocommit(False)
    
  496. 
    
  497.     def tearDown(self):
    
  498.         transaction.rollback()
    
  499.         transaction.set_autocommit(True)
    
  500. 
    
  501.     def test_orm_query_after_error_and_rollback(self):
    
  502.         """
    
  503.         ORM queries are allowed after an error and a rollback in non-autocommit
    
  504.         mode (#27504).
    
  505.         """
    
  506.         r1 = Reporter.objects.create(first_name="Archibald", last_name="Haddock")
    
  507.         r2 = Reporter(first_name="Cuthbert", last_name="Calculus", id=r1.id)
    
  508.         with self.assertRaises(IntegrityError):
    
  509.             r2.save(force_insert=True)
    
  510.         transaction.rollback()
    
  511.         Reporter.objects.last()
    
  512. 
    
  513.     def test_orm_query_without_autocommit(self):
    
  514.         """#24921 -- ORM queries must be possible after set_autocommit(False)."""
    
  515.         Reporter.objects.create(first_name="Tintin")
    
  516. 
    
  517. 
    
  518. class DurableTestsBase:
    
  519.     available_apps = ["transactions"]
    
  520. 
    
  521.     def test_commit(self):
    
  522.         with transaction.atomic(durable=True):
    
  523.             reporter = Reporter.objects.create(first_name="Tintin")
    
  524.         self.assertEqual(Reporter.objects.get(), reporter)
    
  525. 
    
  526.     def test_nested_outer_durable(self):
    
  527.         with transaction.atomic(durable=True):
    
  528.             reporter1 = Reporter.objects.create(first_name="Tintin")
    
  529.             with transaction.atomic():
    
  530.                 reporter2 = Reporter.objects.create(
    
  531.                     first_name="Archibald",
    
  532.                     last_name="Haddock",
    
  533.                 )
    
  534.         self.assertSequenceEqual(Reporter.objects.all(), [reporter2, reporter1])
    
  535. 
    
  536.     def test_nested_both_durable(self):
    
  537.         msg = "A durable atomic block cannot be nested within another atomic block."
    
  538.         with transaction.atomic(durable=True):
    
  539.             with self.assertRaisesMessage(RuntimeError, msg):
    
  540.                 with transaction.atomic(durable=True):
    
  541.                     pass
    
  542. 
    
  543.     def test_nested_inner_durable(self):
    
  544.         msg = "A durable atomic block cannot be nested within another atomic block."
    
  545.         with transaction.atomic():
    
  546.             with self.assertRaisesMessage(RuntimeError, msg):
    
  547.                 with transaction.atomic(durable=True):
    
  548.                     pass
    
  549. 
    
  550.     def test_sequence_of_durables(self):
    
  551.         with transaction.atomic(durable=True):
    
  552.             reporter = Reporter.objects.create(first_name="Tintin 1")
    
  553.         self.assertEqual(Reporter.objects.get(first_name="Tintin 1"), reporter)
    
  554.         with transaction.atomic(durable=True):
    
  555.             reporter = Reporter.objects.create(first_name="Tintin 2")
    
  556.         self.assertEqual(Reporter.objects.get(first_name="Tintin 2"), reporter)
    
  557. 
    
  558. 
    
  559. class DurableTransactionTests(DurableTestsBase, TransactionTestCase):
    
  560.     pass
    
  561. 
    
  562. 
    
  563. class DurableTests(DurableTestsBase, TestCase):
    
  564.     pass