Merge lp:~stub/launchpad/pgbouncer-fixture-noca into lp:launchpad

Proposed by Stuart Bishop
Status: Merged
Approved by: Stuart Bishop
Approved revision: no longer in the source branch.
Merged at revision: 13863
Proposed branch: lp:~stub/launchpad/pgbouncer-fixture-noca
Merge into: lp:launchpad
Prerequisite: lp:~stub/launchpad/pgbouncer-fixture
Diff against target: 126 lines (+74/-5)
2 files modified
lib/lp/testing/fixture.py (+16/-3)
lib/lp/testing/tests/test_fixture.py (+58/-2)
To merge this branch: bzr merge lp:~stub/launchpad/pgbouncer-fixture-noca
Reviewer Review Type Date Requested Status
Gavin Panella (community) Approve
Review via email: mp+73525@code.launchpad.net

Commit message

[r=jtv,allenap][no-qa] pgbouncer test fixture allowing tests of database disconnection behavior.

Description of the change

= Summary =

PGBouncerFixture would not work in layers without the Component Architecture loaded, such as DatabaseLayer, because it would attempt to reset the Storm Stores and these were not registered.

== Proposed fix ==

Only reset the Storm Stores if they are registered with the Component Architecture.

To post a comment you must log in.
Revision history for this message
Gavin Panella (allenap) wrote :

Looks good.

[1]

You might find assertTrue to be a prettier synonym for assert_
(alternatively, both are synonyms for failUnless).

[2]

+ def is_db_available(self):

Could this method be useful elsewhere? If so, the effort to extract it
into a matcher might be worth it.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/lp/testing/fixture.py'
2--- lib/lp/testing/fixture.py 2011-09-03 02:27:01 +0000
3+++ lib/lp/testing/fixture.py 2011-09-03 02:27:03 +0000
4@@ -91,8 +91,7 @@
5
6 # reconnect_store cleanup added first so it is run last, after
7 # the environment variables have been reset.
8- from canonical.testing.layers import reconnect_stores
9- self.addCleanup(reconnect_stores)
10+ self.addCleanup(self._maybe_reconnect_stores)
11
12 # Abuse the PGPORT environment variable to get things connecting
13 # via pgbouncer. Otherwise, we would need to temporarily
14@@ -100,7 +99,21 @@
15 self.useFixture(EnvironmentVariableFixture('PGPORT', str(self.port)))
16
17 # Reset database connections so they go through pgbouncer.
18- reconnect_stores()
19+ self._maybe_reconnect_stores()
20+
21+ def _maybe_reconnect_stores(self):
22+ """Force Storm Stores to reconnect if they are registered.
23+
24+ This is a noop if the Component Architecture is not loaded,
25+ as we are using a test layer that doesn't provide database
26+ connections.
27+ """
28+ from canonical.testing.layers import (
29+ reconnect_stores,
30+ is_ca_available,
31+ )
32+ if is_ca_available():
33+ reconnect_stores()
34
35
36 class ZopeAdapterFixture(Fixture):
37
38=== modified file 'lib/lp/testing/tests/test_fixture.py'
39--- lib/lp/testing/tests/test_fixture.py 2011-09-03 02:27:01 +0000
40+++ lib/lp/testing/tests/test_fixture.py 2011-09-03 02:27:03 +0000
41@@ -8,6 +8,7 @@
42 from textwrap import dedent
43
44 from fixtures import EnvironmentVariableFixture
45+import psycopg2
46 from storm.exceptions import DisconnectionError
47 from zope.component import (
48 adapts,
49@@ -18,8 +19,13 @@
50 Interface,
51 )
52
53+from canonical.config import dbconfig
54 from canonical.launchpad.interfaces.lpstorm import IMasterStore
55-from canonical.testing.layers import BaseLayer, LaunchpadZopelessLayer
56+from canonical.testing.layers import (
57+ BaseLayer,
58+ DatabaseLayer,
59+ LaunchpadZopelessLayer,
60+ )
61 from lp.registry.model.person import Person
62 from lp.testing import TestCase
63 from lp.testing.fixture import (
64@@ -95,7 +101,11 @@
65 self.assertIs(None, queryAdapter(context, IBar))
66
67
68-class TestPGBouncerFixture(TestCase):
69+class TestPGBouncerFixtureWithCA(TestCase):
70+ """PGBouncerFixture reconnect tests for Component Architecture layers.
71+
72+ Registered Storm Stores should be reconnected through pgbouncer.
73+ """
74 layer = LaunchpadZopelessLayer
75
76 def is_connected(self):
77@@ -149,3 +159,49 @@
78
79 # Database is working again.
80 assert self.is_connected()
81+
82+
83+class TestPGBouncerFixtureWithoutCA(TestCase):
84+ """PGBouncerFixture tests for non-Component Architecture layers."""
85+ layer = DatabaseLayer
86+
87+ def is_db_available(self):
88+ # Direct connection to the DB.
89+ con_str = dbconfig.rw_main_master + ' user=launchpad_main'
90+ try:
91+ con = psycopg2.connect(con_str)
92+ cur = con.cursor()
93+ cur.execute("SELECT id FROM Person LIMIT 1")
94+ con.close()
95+ return True
96+ except psycopg2.OperationalError:
97+ return False
98+
99+ def test_install_fixture(self):
100+ self.assert_(self.is_db_available())
101+
102+ with PGBouncerFixture() as pgbouncer:
103+ self.assertTrue(self.is_db_available())
104+
105+ pgbouncer.stop()
106+ self.assertFalse(self.is_db_available())
107+
108+ # This confirms that we are again connecting directly to the
109+ # database, as the pgbouncer process was shutdown.
110+ self.assertTrue(self.is_db_available())
111+
112+ def test_install_fixture_with_restart(self):
113+ self.assert_(self.is_db_available())
114+
115+ with PGBouncerFixture() as pgbouncer:
116+ self.assertTrue(self.is_db_available())
117+
118+ pgbouncer.stop()
119+ self.assertFalse(self.is_db_available())
120+
121+ pgbouncer.start()
122+ self.assertTrue(self.is_db_available())
123+
124+ # Note that because pgbouncer was left running, we can't confirm
125+ # that we are now connecting directly to the database.
126+ self.assertTrue(self.is_db_available())