Merge lp:~danilo/maas/django-upgrade-legacy-migrations into lp:~maas-committers/maas/trunk
- django-upgrade-legacy-migrations
- Merge into trunk
Status: | Merged |
---|---|
Approved by: | Данило Шеган |
Approved revision: | no longer in the source branch. |
Merged at revision: | 6098 |
Proposed branch: | lp:~danilo/maas/django-upgrade-legacy-migrations |
Merge into: | lp:~maas-committers/maas/trunk |
Prerequisite: | lp:~danilo/maas/django-upgrade-method-fixes |
Diff against target: |
373 lines (+95/-97) 8 files modified
src/maasserver/djangosettings/development.py (+10/-1) src/maasserver/fields.py (+0/-17) src/maasserver/middleware.py (+54/-12) src/maasserver/tests/__init__.py (+9/-1) src/maasserver/tests/test_fields.py (+0/-7) src/maasserver/tests/test_middleware.py (+6/-6) src/maastesting/djangotestcase.py (+2/-53) src/metadataserver/tests/__init__.py (+14/-0) |
To merge this branch: | bzr merge lp:~danilo/maas/django-upgrade-legacy-migrations |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Mike Pontillo (community) | Approve | ||
Review via email: mp+325898@code.launchpad.net |
Commit message
1. Remove legacy region test setup and instead make it part of the development settings to avoid problems due to differences in import order between Django 1.8 and 1.11.
2. Replace use of build_request_repr for the DebuggingLogger
3. Stop restricting lookup types for JSONField and XMLField fields because this was of limited use and entirely changed in 1.10.
Description of the change
We also stop restricting lookup types for JSONField and XMLField fields: get_prep_lookup and get_db_prep_lookup methods are gone in 1.10, and lookups are defined in a different way since, and it's not worth it to override all existing built-in lookups for these two fields and catching weird comparisons like >= on JSON fields.
Most of the legacy test setup is now simplified, though there's still cleanup rogue DB connections that fails in 1.8 but now again works in 1.11 that we may want to re-enable.
Данило Шеган (danilo) wrote : | # |
import_
FWIW, as the comment indicates, INSTALLED_APPS is set to None just to avoid lint warnings when it's modified or accessed later on in the file.
Also, note that I have run the tests in both 1.8 and 1.11 about a gazillion times in each of my django 1.11 branches, and I never submit them before they are an improvement in 1.11 (as in a reduced count of test failures), and pass completely with 1.8.
Mike Pontillo (mpontillo) wrote : | # |
Sounds good; thanks for the explanation.
I'd be happier with this branch if you leave a comment around the INSTALLED_APPS foolishness (Django's, not yours). ;-) But I won't block you on it.
Данило Шеган (danilo) wrote : | # |
I was going to complain about Django too, but it's our code that does that in maasserver.
Thanks for the re-review!
Preview Diff
1 | === modified file 'src/maasserver/djangosettings/development.py' | |||
2 | --- src/maasserver/djangosettings/development.py 2017-05-22 13:29:04 +0000 | |||
3 | +++ src/maasserver/djangosettings/development.py 2017-06-21 15:20:56 +0000 | |||
4 | @@ -14,12 +14,19 @@ | |||
5 | 14 | ) | 14 | ) |
6 | 15 | 15 | ||
7 | 16 | # We expect the following settings to be overridden. They are mentioned here | 16 | # We expect the following settings to be overridden. They are mentioned here |
9 | 17 | # to silence lint warnings. | 17 | # to silence lint warnings: import_settings() below will actually re-set it |
10 | 18 | # to a tuple as set in settings.INSTALLED_APPS. | ||
11 | 18 | INSTALLED_APPS = None | 19 | INSTALLED_APPS = None |
12 | 19 | 20 | ||
13 | 20 | # Extend base settings. | 21 | # Extend base settings. |
14 | 21 | import_settings(settings) | 22 | import_settings(settings) |
15 | 22 | 23 | ||
16 | 24 | prevent_migrations = StringBool().to_python( | ||
17 | 25 | os.environ.get("MAAS_PREVENT_MIGRATIONS", 0)) | ||
18 | 26 | |||
19 | 27 | if prevent_migrations: | ||
20 | 28 | INSTALLED_APPS += ("maasserver.tests", "metadataserver.tests") | ||
21 | 29 | |||
22 | 23 | # Use our custom test runner, which makes sure that a local database | 30 | # Use our custom test runner, which makes sure that a local database |
23 | 24 | # cluster is running in the branch. | 31 | # cluster is running in the branch. |
24 | 25 | TEST_RUNNER = 'maastesting.djangoloader.MAASDjangoTestRunner' | 32 | TEST_RUNNER = 'maastesting.djangoloader.MAASDjangoTestRunner' |
25 | @@ -97,7 +104,9 @@ | |||
26 | 97 | 'sites': 'maastesting.empty', | 104 | 'sites': 'maastesting.empty', |
27 | 98 | 'piston3': 'maastesting.empty', | 105 | 'piston3': 'maastesting.empty', |
28 | 99 | 'maasserver': 'maastesting.empty', | 106 | 'maasserver': 'maastesting.empty', |
29 | 107 | 'maasserver.tests': 'maastesting.empty', | ||
30 | 100 | 'metadataserver': 'maastesting.empty', | 108 | 'metadataserver': 'maastesting.empty', |
31 | 109 | 'metadataserver.tests': 'maastesting.empty', | ||
32 | 101 | } | 110 | } |
33 | 102 | 111 | ||
34 | 103 | PASSWORD_HASHERS = ( | 112 | PASSWORD_HASHERS = ( |
35 | 104 | 113 | ||
36 | === modified file 'src/maasserver/fields.py' | |||
37 | --- src/maasserver/fields.py 2017-06-02 11:01:43 +0000 | |||
38 | +++ src/maasserver/fields.py 2017-06-21 15:20:56 +0000 | |||
39 | @@ -320,12 +320,6 @@ | |||
40 | 320 | def get_internal_type(self): | 320 | def get_internal_type(self): |
41 | 321 | return 'TextField' | 321 | return 'TextField' |
42 | 322 | 322 | ||
43 | 323 | def get_prep_lookup(self, lookup_type, value): | ||
44 | 324 | if lookup_type not in ['exact', 'isnull']: | ||
45 | 325 | raise TypeError("Lookup type %s is not supported." % lookup_type) | ||
46 | 326 | return super(JSONObjectField, self).get_prep_lookup( | ||
47 | 327 | lookup_type, value) | ||
48 | 328 | |||
49 | 329 | def formfield(self, form_class=None, **kwargs): | 323 | def formfield(self, form_class=None, **kwargs): |
50 | 330 | """Return a plain `forms.Field` here to avoid "helpful" conversions. | 324 | """Return a plain `forms.Field` here to avoid "helpful" conversions. |
51 | 331 | 325 | ||
52 | @@ -355,17 +349,6 @@ | |||
53 | 355 | def db_type(self, connection): | 349 | def db_type(self, connection): |
54 | 356 | return "xml" | 350 | return "xml" |
55 | 357 | 351 | ||
56 | 358 | def get_db_prep_lookup(self, lookup_type, value, **kwargs): | ||
57 | 359 | """Limit lookup types to those that work on xml. | ||
58 | 360 | |||
59 | 361 | Unlike character fields the xml type is non-comparible, see: | ||
60 | 362 | <http://www.postgresql.org/docs/devel/static/datatype-xml.html> | ||
61 | 363 | """ | ||
62 | 364 | if lookup_type != 'isnull': | ||
63 | 365 | raise TypeError("Lookup type %s is not supported." % lookup_type) | ||
64 | 366 | return super(XMLField, self).get_db_prep_lookup( | ||
65 | 367 | lookup_type, value, **kwargs) | ||
66 | 368 | |||
67 | 369 | 352 | ||
68 | 370 | class EditableBinaryField(BinaryField): | 353 | class EditableBinaryField(BinaryField): |
69 | 371 | """An editable binary field. | 354 | """An editable binary field. |
70 | 372 | 355 | ||
71 | === modified file 'src/maasserver/middleware.py' | |||
72 | --- src/maasserver/middleware.py 2017-03-14 20:50:31 +0000 | |||
73 | +++ src/maasserver/middleware.py 2017-06-21 15:20:56 +0000 | |||
74 | @@ -16,6 +16,7 @@ | |||
75 | 16 | import http.client | 16 | import http.client |
76 | 17 | import json | 17 | import json |
77 | 18 | import logging | 18 | import logging |
78 | 19 | from pprint import pformat | ||
79 | 19 | import re | 20 | import re |
80 | 20 | import sys | 21 | import sys |
81 | 21 | import traceback | 22 | import traceback |
82 | @@ -34,6 +35,8 @@ | |||
83 | 34 | HttpResponseForbidden, | 35 | HttpResponseForbidden, |
84 | 35 | HttpResponseRedirect, | 36 | HttpResponseRedirect, |
85 | 36 | ) | 37 | ) |
86 | 38 | from django.utils import six | ||
87 | 39 | from django.utils.encoding import force_str | ||
88 | 37 | from django.utils.http import urlquote_plus | 40 | from django.utils.http import urlquote_plus |
89 | 38 | from maasserver import logger | 41 | from maasserver import logger |
90 | 39 | from maasserver.bootresources import SIMPLESTREAMS_URL_REGEXP | 42 | from maasserver.bootresources import SIMPLESTREAMS_URL_REGEXP |
91 | @@ -55,16 +58,6 @@ | |||
92 | 55 | ) | 58 | ) |
93 | 56 | from provisioningserver.utils.shell import ExternalProcessError | 59 | from provisioningserver.utils.shell import ExternalProcessError |
94 | 57 | 60 | ||
95 | 58 | |||
96 | 59 | try: | ||
97 | 60 | from django.http.request import build_request_repr | ||
98 | 61 | except ImportError: | ||
99 | 62 | # build_request_repr is only used for debugging: use | ||
100 | 63 | # a degraded version if build_request_repr is not | ||
101 | 64 | # available (i.e. if Django version < 1.5). | ||
102 | 65 | build_request_repr = repr | ||
103 | 66 | |||
104 | 67 | |||
105 | 68 | # 'Retry-After' header sent for httplib.SERVICE_UNAVAILABLE | 61 | # 'Retry-After' header sent for httplib.SERVICE_UNAVAILABLE |
106 | 69 | # responses. | 62 | # responses. |
107 | 70 | RETRY_AFTER_SERVICE_UNAVAILABLE = 10 | 63 | RETRY_AFTER_SERVICE_UNAVAILABLE = 10 |
108 | @@ -272,12 +265,61 @@ | |||
109 | 272 | 265 | ||
110 | 273 | log_level = logging.DEBUG | 266 | log_level = logging.DEBUG |
111 | 274 | 267 | ||
112 | 268 | # Taken straight out of Django 1.8 django.http.request module to improve | ||
113 | 269 | # our debug output on requests (dropped in Django 1.9). | ||
114 | 270 | @classmethod | ||
115 | 271 | def _build_request_repr( | ||
116 | 272 | self, request, path_override=None, GET_override=None, | ||
117 | 273 | POST_override=None, COOKIES_override=None, META_override=None): | ||
118 | 274 | """ | ||
119 | 275 | Builds and returns the request's representation string. The request's | ||
120 | 276 | attributes may be overridden by pre-processed values. | ||
121 | 277 | """ | ||
122 | 278 | # Since this is called as part of error handling, we need to be very | ||
123 | 279 | # robust against potentially malformed input. | ||
124 | 280 | try: | ||
125 | 281 | get = (pformat(GET_override) | ||
126 | 282 | if GET_override is not None | ||
127 | 283 | else pformat(request.GET)) | ||
128 | 284 | except Exception: | ||
129 | 285 | get = '<could not parse>' | ||
130 | 286 | if request._post_parse_error: | ||
131 | 287 | post = '<could not parse>' | ||
132 | 288 | else: | ||
133 | 289 | try: | ||
134 | 290 | post = (pformat(POST_override) | ||
135 | 291 | if POST_override is not None | ||
136 | 292 | else pformat(request.POST)) | ||
137 | 293 | except Exception: | ||
138 | 294 | post = '<could not parse>' | ||
139 | 295 | try: | ||
140 | 296 | cookies = (pformat(COOKIES_override) | ||
141 | 297 | if COOKIES_override is not None | ||
142 | 298 | else pformat(request.COOKIES)) | ||
143 | 299 | except Exception: | ||
144 | 300 | cookies = '<could not parse>' | ||
145 | 301 | try: | ||
146 | 302 | meta = (pformat(META_override) | ||
147 | 303 | if META_override is not None | ||
148 | 304 | else pformat(request.META)) | ||
149 | 305 | except Exception: | ||
150 | 306 | meta = '<could not parse>' | ||
151 | 307 | path = path_override if path_override is not None else request.path | ||
152 | 308 | return force_str( | ||
153 | 309 | '<%s\npath:%s,\nGET:%s,\nPOST:%s,\nCOOKIES:%s,\nMETA:%s>' % | ||
154 | 310 | (request.__class__.__name__, | ||
155 | 311 | path, | ||
156 | 312 | six.text_type(get), | ||
157 | 313 | six.text_type(post), | ||
158 | 314 | six.text_type(cookies), | ||
159 | 315 | six.text_type(meta))) | ||
160 | 316 | |||
161 | 275 | def process_request(self, request): | 317 | def process_request(self, request): |
162 | 276 | if logger.isEnabledFor(self.log_level): | 318 | if logger.isEnabledFor(self.log_level): |
163 | 277 | header = " Request dump ".center(79, "#") | 319 | header = " Request dump ".center(79, "#") |
164 | 278 | logger.log( | 320 | logger.log( |
167 | 279 | self.log_level, | 321 | self.log_level, "%s\n%s", header, |
168 | 280 | "%s\n%s", header, build_request_repr(request)) | 322 | self._build_request_repr(request)) |
169 | 281 | return None # Allow request processing to continue unabated. | 323 | return None # Allow request processing to continue unabated. |
170 | 282 | 324 | ||
171 | 283 | def process_response(self, request, response): | 325 | def process_response(self, request, response): |
172 | 284 | 326 | ||
173 | === modified file 'src/maasserver/tests/__init__.py' | |||
174 | --- src/maasserver/tests/__init__.py 2015-12-01 18:12:59 +0000 | |||
175 | +++ src/maasserver/tests/__init__.py 2017-06-21 15:20:56 +0000 | |||
176 | @@ -3,4 +3,12 @@ | |||
177 | 3 | 3 | ||
178 | 4 | """Tests for `maasserver`.""" | 4 | """Tests for `maasserver`.""" |
179 | 5 | 5 | ||
181 | 6 | __all__ = [] | 6 | from django.apps import AppConfig |
182 | 7 | |||
183 | 8 | |||
184 | 9 | class MAASServerTestsConfig(AppConfig): | ||
185 | 10 | name = 'maasserver.tests' | ||
186 | 11 | label = 'maasserver_tests' | ||
187 | 12 | |||
188 | 13 | |||
189 | 14 | default_app_config = 'maasserver.tests.MAASServerTestsConfig' | ||
190 | 7 | 15 | ||
191 | === modified file 'src/maasserver/tests/test_fields.py' | |||
192 | --- src/maasserver/tests/test_fields.py 2017-06-02 14:37:33 +0000 | |||
193 | +++ src/maasserver/tests/test_fields.py 2017-06-21 15:20:56 +0000 | |||
194 | @@ -306,10 +306,6 @@ | |||
195 | 306 | test_instance = JSONFieldModel.objects.get(value__isnull=True) | 306 | test_instance = JSONFieldModel.objects.get(value__isnull=True) |
196 | 307 | self.assertIsNone(test_instance.value) | 307 | self.assertIsNone(test_instance.value) |
197 | 308 | 308 | ||
198 | 309 | def test_field_another_lookup_fails(self): | ||
199 | 310 | # Others lookups are not allowed. | ||
200 | 311 | self.assertRaises(TypeError, JSONFieldModel.objects.get, value__gte=3) | ||
201 | 312 | |||
202 | 313 | def test_form_field_is_a_plain_field(self): | 309 | def test_form_field_is_a_plain_field(self): |
203 | 314 | self.assertThat( | 310 | self.assertThat( |
204 | 315 | JSONObjectField().formfield(), | 311 | JSONObjectField().formfield(), |
205 | @@ -350,9 +346,6 @@ | |||
206 | 350 | test_instance = XMLFieldModel.objects.get(value__isnull=True) | 346 | test_instance = XMLFieldModel.objects.get(value__isnull=True) |
207 | 351 | self.assertIsNone(test_instance.value) | 347 | self.assertIsNone(test_instance.value) |
208 | 352 | 348 | ||
209 | 353 | def test_lookup_exact_unsupported(self): | ||
210 | 354 | self.assertRaises(TypeError, XMLFieldModel.objects.get, value="") | ||
211 | 355 | |||
212 | 356 | 349 | ||
213 | 357 | class TestEditableBinaryField(MAASServerTestCase): | 350 | class TestEditableBinaryField(MAASServerTestCase): |
214 | 358 | 351 | ||
215 | 359 | 352 | ||
216 | === modified file 'src/maasserver/tests/test_middleware.py' | |||
217 | --- src/maasserver/tests/test_middleware.py 2016-05-12 19:07:37 +0000 | |||
218 | +++ src/maasserver/tests/test_middleware.py 2017-06-21 15:20:56 +0000 | |||
219 | @@ -20,7 +20,6 @@ | |||
220 | 20 | ) | 20 | ) |
221 | 21 | from django.core.urlresolvers import reverse | 21 | from django.core.urlresolvers import reverse |
222 | 22 | from django.http import HttpResponse | 22 | from django.http import HttpResponse |
223 | 23 | from django.http.request import build_request_repr | ||
224 | 24 | from fixtures import FakeLogger | 23 | from fixtures import FakeLogger |
225 | 25 | from maasserver import middleware as middleware_module | 24 | from maasserver import middleware as middleware_module |
226 | 26 | from maasserver.components import ( | 25 | from maasserver.components import ( |
227 | @@ -239,9 +238,8 @@ | |||
228 | 239 | logger = self.useFixture(FakeLogger('maasserver', logging.INFO)) | 238 | logger = self.useFixture(FakeLogger('maasserver', logging.INFO)) |
229 | 240 | request = factory.make_fake_request("/api/2.0/nodes/") | 239 | request = factory.make_fake_request("/api/2.0/nodes/") |
230 | 241 | DebuggingLoggerMiddleware().process_request(request) | 240 | DebuggingLoggerMiddleware().process_request(request) |
234 | 242 | self.assertThat( | 241 | debug_output = DebuggingLoggerMiddleware._build_request_repr(request) |
235 | 243 | logger.output, | 242 | self.assertThat(logger.output, Not(Contains(debug_output))) |
233 | 244 | Not(Contains(build_request_repr(request)))) | ||
236 | 245 | 243 | ||
237 | 246 | def test_debugging_logger_does_not_log_response_if_info_level(self): | 244 | def test_debugging_logger_does_not_log_response_if_info_level(self): |
238 | 247 | logger = self.useFixture(FakeLogger('maasserver', logging.INFO)) | 245 | logger = self.useFixture(FakeLogger('maasserver', logging.INFO)) |
239 | @@ -250,15 +248,17 @@ | |||
240 | 250 | content="test content", | 248 | content="test content", |
241 | 251 | content_type=b"text/plain; charset=utf-8") | 249 | content_type=b"text/plain; charset=utf-8") |
242 | 252 | DebuggingLoggerMiddleware().process_response(request, response) | 250 | DebuggingLoggerMiddleware().process_response(request, response) |
243 | 251 | debug_output = DebuggingLoggerMiddleware._build_request_repr(request) | ||
244 | 253 | self.assertThat( | 252 | self.assertThat( |
246 | 254 | logger.output, Not(Contains(build_request_repr(request)))) | 253 | logger.output, Not(Contains(debug_output))) |
247 | 255 | 254 | ||
248 | 256 | def test_debugging_logger_logs_request(self): | 255 | def test_debugging_logger_logs_request(self): |
249 | 257 | logger = self.useFixture(FakeLogger('maasserver', logging.DEBUG)) | 256 | logger = self.useFixture(FakeLogger('maasserver', logging.DEBUG)) |
250 | 258 | request = factory.make_fake_request("/api/2.0/nodes/") | 257 | request = factory.make_fake_request("/api/2.0/nodes/") |
251 | 259 | request.content = "test content" | 258 | request.content = "test content" |
252 | 260 | DebuggingLoggerMiddleware().process_request(request) | 259 | DebuggingLoggerMiddleware().process_request(request) |
254 | 261 | self.assertThat(logger.output, Contains(build_request_repr(request))) | 260 | debug_output = DebuggingLoggerMiddleware._build_request_repr(request) |
255 | 261 | self.assertThat(logger.output, Contains(debug_output)) | ||
256 | 262 | 262 | ||
257 | 263 | def test_debugging_logger_logs_response(self): | 263 | def test_debugging_logger_logs_response(self): |
258 | 264 | logger = self.useFixture(FakeLogger('maasserver', logging.DEBUG)) | 264 | logger = self.useFixture(FakeLogger('maasserver', logging.DEBUG)) |
259 | 265 | 265 | ||
260 | === modified file 'src/maastesting/djangotestcase.py' | |||
261 | --- src/maastesting/djangotestcase.py 2017-01-28 00:51:47 +0000 | |||
262 | +++ src/maastesting/djangotestcase.py 2017-06-21 15:20:56 +0000 | |||
263 | @@ -14,8 +14,6 @@ | |||
264 | 14 | time, | 14 | time, |
265 | 15 | ) | 15 | ) |
266 | 16 | 16 | ||
267 | 17 | from django.apps import apps as django_apps | ||
268 | 18 | from django.core.management import call_command | ||
269 | 19 | from django.core.signals import request_started | 17 | from django.core.signals import request_started |
270 | 20 | from django.db import ( | 18 | from django.db import ( |
271 | 21 | connection, | 19 | connection, |
272 | @@ -170,45 +168,7 @@ | |||
273 | 170 | ) | 168 | ) |
274 | 171 | 169 | ||
275 | 172 | 170 | ||
315 | 173 | class InstallDjangoAppsMixin: | 171 | class DjangoTestCase(django.test.TestCase, MAASTestCase): |
277 | 174 | """Install extra applications into the Django ``INSTALLED_APPS`` setting. | ||
278 | 175 | |||
279 | 176 | :deprecated: Do NOT use in new tests. | ||
280 | 177 | """ | ||
281 | 178 | |||
282 | 179 | def _setup_apps(self, apps): | ||
283 | 180 | self._did_set_apps = False | ||
284 | 181 | if len(apps) > 0: | ||
285 | 182 | # Inject the apps into django now that the fixture is setup. | ||
286 | 183 | self._did_set_apps = True | ||
287 | 184 | current_apps = [ | ||
288 | 185 | current_app.name | ||
289 | 186 | for current_app in django_apps.get_app_configs() | ||
290 | 187 | ] | ||
291 | 188 | need_to_add = [ | ||
292 | 189 | new_app | ||
293 | 190 | for new_app in apps | ||
294 | 191 | if new_app not in current_apps | ||
295 | 192 | ] | ||
296 | 193 | for app in need_to_add: | ||
297 | 194 | current_apps.append(app) | ||
298 | 195 | # Set the installed applications in django. This requires another | ||
299 | 196 | # call to unset_installed_apps to reset to the previous value. | ||
300 | 197 | django_apps.set_installed_apps(current_apps) | ||
301 | 198 | # Call migrate that will actual perform the migrations for this | ||
302 | 199 | # applications and if no migrations exists then it will fallback | ||
303 | 200 | # to performing 'syncdb'. | ||
304 | 201 | call_command("migrate") | ||
305 | 202 | |||
306 | 203 | def _teardown_apps(self): | ||
307 | 204 | # Check that the internal __set_apps is set so that the required | ||
308 | 205 | # unset_installed_apps can be called. | ||
309 | 206 | if self._did_set_apps: | ||
310 | 207 | django_apps.unset_installed_apps() | ||
311 | 208 | |||
312 | 209 | |||
313 | 210 | class DjangoTestCase( | ||
314 | 211 | django.test.TestCase, MAASTestCase, InstallDjangoAppsMixin): | ||
316 | 212 | """A Django `TestCase` for MAAS. | 172 | """A Django `TestCase` for MAAS. |
317 | 213 | 173 | ||
318 | 214 | Generally you should NOT directly subclass this for tests; use an | 174 | Generally you should NOT directly subclass this for tests; use an |
319 | @@ -232,12 +192,7 @@ | |||
320 | 232 | # This attribute is used as a tag with Nose. | 192 | # This attribute is used as a tag with Nose. |
321 | 233 | legacy = True | 193 | legacy = True |
322 | 234 | 194 | ||
323 | 235 | def _fixture_setup(self): | ||
324 | 236 | super(DjangoTestCase, self)._fixture_setup() | ||
325 | 237 | self._setup_apps(self.apps) | ||
326 | 238 | |||
327 | 239 | def _fixture_teardown(self): | 195 | def _fixture_teardown(self): |
328 | 240 | self._teardown_apps() | ||
329 | 241 | super(DjangoTestCase, self)._fixture_teardown() | 196 | super(DjangoTestCase, self)._fixture_teardown() |
330 | 242 | # TODO blake_r: Fix so this is not disabled. Currently not | 197 | # TODO blake_r: Fix so this is not disabled. Currently not |
331 | 243 | # working with Django 1.8. | 198 | # working with Django 1.8. |
332 | @@ -245,8 +200,7 @@ | |||
333 | 245 | # check_for_rogue_database_activity(self) | 200 | # check_for_rogue_database_activity(self) |
334 | 246 | 201 | ||
335 | 247 | 202 | ||
338 | 248 | class DjangoTransactionTestCase( | 203 | class DjangoTransactionTestCase(django.test.TransactionTestCase, MAASTestCase): |
337 | 249 | django.test.TransactionTestCase, MAASTestCase, InstallDjangoAppsMixin): | ||
339 | 250 | """A Django `TransactionTestCase` for MAAS. | 204 | """A Django `TransactionTestCase` for MAAS. |
340 | 251 | 205 | ||
341 | 252 | A version of `MAASTestCase` that supports transactions. | 206 | A version of `MAASTestCase` that supports transactions. |
342 | @@ -273,12 +227,7 @@ | |||
343 | 273 | # This attribute is used as a tag with Nose. | 227 | # This attribute is used as a tag with Nose. |
344 | 274 | legacy = True | 228 | legacy = True |
345 | 275 | 229 | ||
346 | 276 | def _fixture_setup(self): | ||
347 | 277 | super(DjangoTransactionTestCase, self)._fixture_setup() | ||
348 | 278 | self._setup_apps(self.apps) | ||
349 | 279 | |||
350 | 280 | def _fixture_teardown(self): | 230 | def _fixture_teardown(self): |
351 | 281 | self._teardown_apps() | ||
352 | 282 | super(DjangoTransactionTestCase, self)._fixture_teardown() | 231 | super(DjangoTransactionTestCase, self)._fixture_teardown() |
353 | 283 | # TODO blake_r: Fix so this is not disabled. Currently not | 232 | # TODO blake_r: Fix so this is not disabled. Currently not |
354 | 284 | # working with Django 1.8. | 233 | # working with Django 1.8. |
355 | 285 | 234 | ||
356 | === modified file 'src/metadataserver/tests/__init__.py' | |||
357 | --- src/metadataserver/tests/__init__.py 2013-08-23 12:54:28 +0000 | |||
358 | +++ src/metadataserver/tests/__init__.py 2017-06-21 15:20:56 +0000 | |||
359 | @@ -0,0 +1,14 @@ | |||
360 | 1 | # Copyright 2012-2017 Canonical Ltd. This software is licensed under the | ||
361 | 2 | # GNU Affero General Public License version 3 (see the file LICENSE). | ||
362 | 3 | |||
363 | 4 | """Tests for `metadataserver`.""" | ||
364 | 5 | |||
365 | 6 | from django.apps import AppConfig | ||
366 | 7 | |||
367 | 8 | |||
368 | 9 | class MetadataServerTestsConfig(AppConfig): | ||
369 | 10 | name = 'metadataserver.tests' | ||
370 | 11 | label = 'metadataserver_tests' | ||
371 | 12 | |||
372 | 13 | |||
373 | 14 | default_app_config = 'metadataserver.tests.MetadataServerTestsConfig' |
I'm confused about how appending to INSTALLED_APPS is going to work. I see the following code in the file, when I look at the full diff context:
# We expect the following settings to be overridden. They are mentioned here
# to silence lint warnings.
INSTALLED_APPS = None
# Extend base settings. settings( settings)
import_
prevent_ migrations = StringBool( ).to_python( environ. get("MAAS_ PREVENT_ MIGRATIONS" , 0))
os.
if prevent_migrations:
INSTALLED_ APPS += ("maasserver. tests", "metadataserver .tests" )
So how is a tuple going to be appended to None?