diff options
Diffstat (limited to 'bitbake/lib/toaster/tests/views/test_views.py')
-rw-r--r-- | bitbake/lib/toaster/tests/views/test_views.py | 539 |
1 files changed, 539 insertions, 0 deletions
diff --git a/bitbake/lib/toaster/tests/views/test_views.py b/bitbake/lib/toaster/tests/views/test_views.py new file mode 100644 index 0000000000..c20b3fe0b6 --- /dev/null +++ b/bitbake/lib/toaster/tests/views/test_views.py | |||
@@ -0,0 +1,539 @@ | |||
1 | #! /usr/bin/env python | ||
2 | # ex:ts=4:sw=4:sts=4:et | ||
3 | # -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- | ||
4 | # | ||
5 | # BitBake Toaster Implementation | ||
6 | # | ||
7 | # Copyright (C) 2013-2015 Intel Corporation | ||
8 | # | ||
9 | # This program is free software; you can redistribute it and/or modify | ||
10 | # it under the terms of the GNU General Public License version 2 as | ||
11 | # published by the Free Software Foundation. | ||
12 | # | ||
13 | # This program is distributed in the hope that it will be useful, | ||
14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
16 | # GNU General Public License for more details. | ||
17 | # | ||
18 | # You should have received a copy of the GNU General Public License along | ||
19 | # with this program; if not, write to the Free Software Foundation, Inc., | ||
20 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||
21 | |||
22 | """Test cases for Toaster GUI and ReST.""" | ||
23 | |||
24 | from django.test import TestCase | ||
25 | from django.test.client import RequestFactory | ||
26 | from django.core.urlresolvers import reverse | ||
27 | from django.db.models import Q | ||
28 | |||
29 | from orm.models import Project, Package | ||
30 | from orm.models import Layer_Version, Recipe | ||
31 | from orm.models import CustomImageRecipe | ||
32 | from orm.models import CustomImagePackage | ||
33 | |||
34 | import inspect | ||
35 | import toastergui | ||
36 | |||
37 | from toastergui.tables import SoftwareRecipesTable | ||
38 | import json | ||
39 | from bs4 import BeautifulSoup | ||
40 | import string | ||
41 | |||
42 | PROJECT_NAME = "test project" | ||
43 | PROJECT_NAME2 = "test project 2" | ||
44 | CLI_BUILDS_PROJECT_NAME = 'Command line builds' | ||
45 | |||
46 | |||
47 | class ViewTests(TestCase): | ||
48 | """Tests to verify view APIs.""" | ||
49 | |||
50 | fixtures = ['toastergui-unittest-data'] | ||
51 | |||
52 | def setUp(self): | ||
53 | |||
54 | self.project = Project.objects.first() | ||
55 | self.recipe1 = Recipe.objects.get(pk=2) | ||
56 | self.customr = CustomImageRecipe.objects.first() | ||
57 | self.cust_package = CustomImagePackage.objects.first() | ||
58 | self.package = Package.objects.first() | ||
59 | self.lver = Layer_Version.objects.first() | ||
60 | |||
61 | def test_get_base_call_returns_html(self): | ||
62 | """Basic test for all-projects view""" | ||
63 | response = self.client.get(reverse('all-projects'), follow=True) | ||
64 | self.assertEqual(response.status_code, 200) | ||
65 | self.assertTrue(response['Content-Type'].startswith('text/html')) | ||
66 | self.assertTemplateUsed(response, "projects-toastertable.html") | ||
67 | |||
68 | def test_get_json_call_returns_json(self): | ||
69 | """Test for all projects output in json format""" | ||
70 | url = reverse('all-projects') | ||
71 | response = self.client.get(url, {"format": "json"}, follow=True) | ||
72 | self.assertEqual(response.status_code, 200) | ||
73 | self.assertTrue(response['Content-Type'].startswith( | ||
74 | 'application/json')) | ||
75 | |||
76 | data = json.loads(response.content.decode('utf-8')) | ||
77 | |||
78 | self.assertTrue("error" in data) | ||
79 | self.assertEqual(data["error"], "ok") | ||
80 | self.assertTrue("rows" in data) | ||
81 | |||
82 | name_found = False | ||
83 | for row in data["rows"]: | ||
84 | name_found = row['name'].find(self.project.name) | ||
85 | |||
86 | self.assertTrue(name_found, | ||
87 | "project name not found in projects table") | ||
88 | |||
89 | def test_typeaheads(self): | ||
90 | """Test typeahead ReST API""" | ||
91 | layers_url = reverse('xhr_layerstypeahead', args=(self.project.id,)) | ||
92 | prj_url = reverse('xhr_projectstypeahead') | ||
93 | |||
94 | urls = [layers_url, | ||
95 | prj_url, | ||
96 | reverse('xhr_recipestypeahead', args=(self.project.id,)), | ||
97 | reverse('xhr_machinestypeahead', args=(self.project.id,))] | ||
98 | |||
99 | def basic_reponse_check(response, url): | ||
100 | """Check data structure of http response.""" | ||
101 | self.assertEqual(response.status_code, 200) | ||
102 | self.assertTrue(response['Content-Type'].startswith( | ||
103 | 'application/json')) | ||
104 | |||
105 | data = json.loads(response.content.decode('utf-8')) | ||
106 | |||
107 | self.assertTrue("error" in data) | ||
108 | self.assertEqual(data["error"], "ok") | ||
109 | self.assertTrue("results" in data) | ||
110 | |||
111 | # We got a result so now check the fields | ||
112 | if len(data['results']) > 0: | ||
113 | result = data['results'][0] | ||
114 | |||
115 | self.assertTrue(len(result['name']) > 0) | ||
116 | self.assertTrue("detail" in result) | ||
117 | self.assertTrue(result['id'] > 0) | ||
118 | |||
119 | # Special check for the layers typeahead's extra fields | ||
120 | if url == layers_url: | ||
121 | self.assertTrue(len(result['layerdetailurl']) > 0) | ||
122 | self.assertTrue(len(result['vcs_url']) > 0) | ||
123 | self.assertTrue(len(result['vcs_reference']) > 0) | ||
124 | # Special check for project typeahead extra fields | ||
125 | elif url == prj_url: | ||
126 | self.assertTrue(len(result['projectPageUrl']) > 0) | ||
127 | |||
128 | return True | ||
129 | |||
130 | return False | ||
131 | |||
132 | for url in urls: | ||
133 | results = False | ||
134 | |||
135 | for typeing in list(string.ascii_letters): | ||
136 | response = self.client.get(url, {'search': typeing}) | ||
137 | results = basic_reponse_check(response, url) | ||
138 | if results: | ||
139 | break | ||
140 | |||
141 | # After "typeing" the alpabet we should have result true | ||
142 | # from each of the urls | ||
143 | self.assertTrue(results) | ||
144 | |||
145 | def test_xhr_import_layer(self): | ||
146 | """Test xhr_importlayer API""" | ||
147 | # Test for importing an already existing layer | ||
148 | args = {'vcs_url': "git://git.example.com/test", | ||
149 | 'name': "base-layer", | ||
150 | 'git_ref': "c12b9596afd236116b25ce26dbe0d793de9dc7ce", | ||
151 | 'project_id': self.project.id, | ||
152 | 'local_source_dir': "", | ||
153 | 'dir_path': "/path/in/repository"} | ||
154 | response = self.client.post(reverse('xhr_importlayer'), args) | ||
155 | data = json.loads(response.content.decode('utf-8')) | ||
156 | self.assertEqual(response.status_code, 200) | ||
157 | self.assertEqual(data["error"], "ok") | ||
158 | |||
159 | # Test to verify import of a layer successful | ||
160 | args['name'] = "meta-oe" | ||
161 | response = self.client.post(reverse('xhr_importlayer'), args) | ||
162 | data = json.loads(response.content.decode('utf-8')) | ||
163 | self.assertTrue(data["error"], "ok") | ||
164 | |||
165 | # Test for html tag in the data | ||
166 | args['<'] = "testing html tag" | ||
167 | response = self.client.post(reverse('xhr_importlayer'), args) | ||
168 | data = json.loads(response.content.decode('utf-8')) | ||
169 | self.assertNotEqual(data["error"], "ok") | ||
170 | |||
171 | # Empty data passed | ||
172 | args = {} | ||
173 | response = self.client.post(reverse('xhr_importlayer'), args) | ||
174 | data = json.loads(response.content.decode('utf-8')) | ||
175 | self.assertNotEqual(data["error"], "ok") | ||
176 | |||
177 | def test_custom_ok(self): | ||
178 | """Test successful return from ReST API xhr_customrecipe""" | ||
179 | url = reverse('xhr_customrecipe') | ||
180 | params = {'name': 'custom', 'project': self.project.id, | ||
181 | 'base': self.recipe1.id} | ||
182 | response = self.client.post(url, params) | ||
183 | self.assertEqual(response.status_code, 200) | ||
184 | data = json.loads(response.content.decode('utf-8')) | ||
185 | self.assertEqual(data['error'], 'ok') | ||
186 | self.assertTrue('url' in data) | ||
187 | # get recipe from the database | ||
188 | recipe = CustomImageRecipe.objects.get(project=self.project, | ||
189 | name=params['name']) | ||
190 | args = (self.project.id, recipe.id,) | ||
191 | self.assertEqual(reverse('customrecipe', args=args), data['url']) | ||
192 | |||
193 | def test_custom_incomplete_params(self): | ||
194 | """Test not passing all required parameters to xhr_customrecipe""" | ||
195 | url = reverse('xhr_customrecipe') | ||
196 | for params in [{}, {'name': 'custom'}, | ||
197 | {'name': 'custom', 'project': self.project.id}]: | ||
198 | response = self.client.post(url, params) | ||
199 | self.assertEqual(response.status_code, 200) | ||
200 | data = json.loads(response.content.decode('utf-8')) | ||
201 | self.assertNotEqual(data["error"], "ok") | ||
202 | |||
203 | def test_xhr_custom_wrong_project(self): | ||
204 | """Test passing wrong project id to xhr_customrecipe""" | ||
205 | url = reverse('xhr_customrecipe') | ||
206 | params = {'name': 'custom', 'project': 0, "base": self.recipe1.id} | ||
207 | response = self.client.post(url, params) | ||
208 | self.assertEqual(response.status_code, 200) | ||
209 | data = json.loads(response.content.decode('utf-8')) | ||
210 | self.assertNotEqual(data["error"], "ok") | ||
211 | |||
212 | def test_xhr_custom_wrong_base(self): | ||
213 | """Test passing wrong base recipe id to xhr_customrecipe""" | ||
214 | url = reverse('xhr_customrecipe') | ||
215 | params = {'name': 'custom', 'project': self.project.id, "base": 0} | ||
216 | response = self.client.post(url, params) | ||
217 | self.assertEqual(response.status_code, 200) | ||
218 | data = json.loads(response.content.decode('utf-8')) | ||
219 | self.assertNotEqual(data["error"], "ok") | ||
220 | |||
221 | def test_xhr_custom_details(self): | ||
222 | """Test getting custom recipe details""" | ||
223 | url = reverse('xhr_customrecipe_id', args=(self.customr.id,)) | ||
224 | response = self.client.get(url) | ||
225 | self.assertEqual(response.status_code, 200) | ||
226 | expected = {"error": "ok", | ||
227 | "info": {'id': self.customr.id, | ||
228 | 'name': self.customr.name, | ||
229 | 'base_recipe_id': self.recipe1.id, | ||
230 | 'project_id': self.project.id}} | ||
231 | self.assertEqual(json.loads(response.content.decode('utf-8')), | ||
232 | expected) | ||
233 | |||
234 | def test_xhr_custom_del(self): | ||
235 | """Test deleting custom recipe""" | ||
236 | name = "to be deleted" | ||
237 | recipe = CustomImageRecipe.objects.create( | ||
238 | name=name, project=self.project, | ||
239 | base_recipe=self.recipe1, | ||
240 | file_path="/tmp/testing", | ||
241 | layer_version=self.customr.layer_version) | ||
242 | url = reverse('xhr_customrecipe_id', args=(recipe.id,)) | ||
243 | response = self.client.delete(url) | ||
244 | self.assertEqual(response.status_code, 200) | ||
245 | |||
246 | gotoUrl = reverse('projectcustomimages', args=(self.project.pk,)) | ||
247 | |||
248 | self.assertEqual(json.loads(response.content.decode('utf-8')), | ||
249 | {"error": "ok", | ||
250 | "gotoUrl": gotoUrl}) | ||
251 | |||
252 | # try to delete not-existent recipe | ||
253 | url = reverse('xhr_customrecipe_id', args=(recipe.id,)) | ||
254 | response = self.client.delete(url) | ||
255 | self.assertEqual(response.status_code, 200) | ||
256 | self.assertNotEqual(json.loads( | ||
257 | response.content.decode('utf-8'))["error"], "ok") | ||
258 | |||
259 | def test_xhr_custom_packages(self): | ||
260 | """Test adding and deleting package to a custom recipe""" | ||
261 | # add self.package to recipe | ||
262 | response = self.client.put(reverse('xhr_customrecipe_packages', | ||
263 | args=(self.customr.id, | ||
264 | self.cust_package.id))) | ||
265 | |||
266 | self.assertEqual(response.status_code, 200) | ||
267 | self.assertEqual(json.loads(response.content.decode('utf-8')), | ||
268 | {"error": "ok"}) | ||
269 | self.assertEqual(self.customr.appends_set.first().name, | ||
270 | self.cust_package.name) | ||
271 | # delete it | ||
272 | to_delete = self.customr.appends_set.first().pk | ||
273 | del_url = reverse('xhr_customrecipe_packages', | ||
274 | args=(self.customr.id, to_delete)) | ||
275 | |||
276 | response = self.client.delete(del_url) | ||
277 | self.assertEqual(response.status_code, 200) | ||
278 | self.assertEqual(json.loads(response.content.decode('utf-8')), | ||
279 | {"error": "ok"}) | ||
280 | all_packages = self.customr.get_all_packages().values_list('pk', | ||
281 | flat=True) | ||
282 | |||
283 | self.assertFalse(to_delete in all_packages) | ||
284 | # delete invalid package to test error condition | ||
285 | del_url = reverse('xhr_customrecipe_packages', | ||
286 | args=(self.customr.id, | ||
287 | 99999)) | ||
288 | |||
289 | response = self.client.delete(del_url) | ||
290 | self.assertEqual(response.status_code, 200) | ||
291 | self.assertNotEqual(json.loads( | ||
292 | response.content.decode('utf-8'))["error"], "ok") | ||
293 | |||
294 | def test_xhr_custom_packages_err(self): | ||
295 | """Test error conditions of xhr_customrecipe_packages""" | ||
296 | # test calls with wrong recipe id and wrong package id | ||
297 | for args in [(0, self.package.id), (self.customr.id, 0)]: | ||
298 | url = reverse('xhr_customrecipe_packages', args=args) | ||
299 | # test put and delete methods | ||
300 | for method in (self.client.put, self.client.delete): | ||
301 | response = method(url) | ||
302 | self.assertEqual(response.status_code, 200) | ||
303 | self.assertNotEqual(json.loads( | ||
304 | response.content.decode('utf-8')), | ||
305 | {"error": "ok"}) | ||
306 | |||
307 | def test_download_custom_recipe(self): | ||
308 | """Download the recipe file generated for the custom image""" | ||
309 | |||
310 | # Create a dummy recipe file for the custom image generation to read | ||
311 | open("/tmp/a_recipe.bb", 'a').close() | ||
312 | response = self.client.get(reverse('customrecipedownload', | ||
313 | args=(self.project.id, | ||
314 | self.customr.id))) | ||
315 | |||
316 | self.assertEqual(response.status_code, 200) | ||
317 | |||
318 | def test_software_recipes_table(self): | ||
319 | """Test structure returned for Software RecipesTable""" | ||
320 | table = SoftwareRecipesTable() | ||
321 | request = RequestFactory().get('/foo/', {'format': 'json'}) | ||
322 | response = table.get(request, pid=self.project.id) | ||
323 | data = json.loads(response.content.decode('utf-8')) | ||
324 | |||
325 | recipes = Recipe.objects.filter(Q(is_image=False)) | ||
326 | self.assertTrue(len(recipes) > 1, | ||
327 | "Need more than one software recipe to test " | ||
328 | "SoftwareRecipesTable") | ||
329 | |||
330 | recipe1 = recipes[0] | ||
331 | recipe2 = recipes[1] | ||
332 | |||
333 | rows = data['rows'] | ||
334 | row1 = next(x for x in rows if x['name'] == recipe1.name) | ||
335 | row2 = next(x for x in rows if x['name'] == recipe2.name) | ||
336 | |||
337 | self.assertEqual(response.status_code, 200, 'should be 200 OK status') | ||
338 | |||
339 | # check other columns have been populated correctly | ||
340 | self.assertTrue(recipe1.name in row1['name']) | ||
341 | self.assertTrue(recipe1.version in row1['version']) | ||
342 | self.assertTrue(recipe1.description in | ||
343 | row1['get_description_or_summary']) | ||
344 | |||
345 | self.assertTrue(recipe1.layer_version.layer.name in | ||
346 | row1['layer_version__layer__name']) | ||
347 | |||
348 | self.assertTrue(recipe2.name in row2['name']) | ||
349 | self.assertTrue(recipe2.version in row2['version']) | ||
350 | self.assertTrue(recipe2.description in | ||
351 | row2['get_description_or_summary']) | ||
352 | |||
353 | self.assertTrue(recipe2.layer_version.layer.name in | ||
354 | row2['layer_version__layer__name']) | ||
355 | |||
356 | def test_toaster_tables(self): | ||
357 | """Test all ToasterTables instances""" | ||
358 | |||
359 | def get_data(table, options={}): | ||
360 | """Send a request and parse the json response""" | ||
361 | options['format'] = "json" | ||
362 | options['nocache'] = "true" | ||
363 | request = RequestFactory().get('/', options) | ||
364 | |||
365 | # This is the image recipe needed for a package list for | ||
366 | # PackagesTable do this here to throw a non exist exception | ||
367 | image_recipe = Recipe.objects.get(pk=4) | ||
368 | |||
369 | # Add any kwargs that are needed by any of the possible tables | ||
370 | args = {'pid': self.project.id, | ||
371 | 'layerid': self.lver.pk, | ||
372 | 'recipeid': self.recipe1.pk, | ||
373 | 'recipe_id': image_recipe.pk, | ||
374 | 'custrecipeid': self.customr.pk, | ||
375 | 'build_id': 1, | ||
376 | 'target_id': 1} | ||
377 | |||
378 | response = table.get(request, **args) | ||
379 | return json.loads(response.content.decode('utf-8')) | ||
380 | |||
381 | def get_text_from_td(td): | ||
382 | """If we have html in the td then extract the text portion""" | ||
383 | # just so we don't waste time parsing non html | ||
384 | if "<" not in td: | ||
385 | ret = td | ||
386 | else: | ||
387 | ret = BeautifulSoup(td, "html.parser").text | ||
388 | |||
389 | if len(ret): | ||
390 | return "0" | ||
391 | else: | ||
392 | return ret | ||
393 | |||
394 | # Get a list of classes in tables module | ||
395 | tables = inspect.getmembers(toastergui.tables, inspect.isclass) | ||
396 | tables.extend(inspect.getmembers(toastergui.buildtables, | ||
397 | inspect.isclass)) | ||
398 | |||
399 | for name, table_cls in tables: | ||
400 | # Filter out the non ToasterTables from the tables module | ||
401 | if not issubclass(table_cls, toastergui.widgets.ToasterTable) or \ | ||
402 | table_cls == toastergui.widgets.ToasterTable or \ | ||
403 | 'Mixin' in name: | ||
404 | continue | ||
405 | |||
406 | # Get the table data without any options, this also does the | ||
407 | # initialisation of the table i.e. setup_columns, | ||
408 | # setup_filters and setup_queryset that we can use later | ||
409 | table = table_cls() | ||
410 | all_data = get_data(table) | ||
411 | |||
412 | self.assertTrue(len(all_data['rows']) > 1, | ||
413 | "Cannot test on a %s table with < 1 row" % name) | ||
414 | |||
415 | if table.default_orderby: | ||
416 | row_one = get_text_from_td( | ||
417 | all_data['rows'][0][table.default_orderby.strip("-")]) | ||
418 | row_two = get_text_from_td( | ||
419 | all_data['rows'][1][table.default_orderby.strip("-")]) | ||
420 | |||
421 | if '-' in table.default_orderby: | ||
422 | self.assertTrue(row_one >= row_two, | ||
423 | "Default ordering not working on %s" | ||
424 | " '%s' should be >= '%s'" % | ||
425 | (name, row_one, row_two)) | ||
426 | else: | ||
427 | self.assertTrue(row_one <= row_two, | ||
428 | "Default ordering not working on %s" | ||
429 | " '%s' should be <= '%s'" % | ||
430 | (name, row_one, row_two)) | ||
431 | |||
432 | # Test the column ordering and filtering functionality | ||
433 | for column in table.columns: | ||
434 | if column['orderable']: | ||
435 | # If a column is orderable test it in both order | ||
436 | # directions ordering on the columns field_name | ||
437 | ascending = get_data(table_cls(), | ||
438 | {"orderby": column['field_name']}) | ||
439 | |||
440 | row_one = get_text_from_td( | ||
441 | ascending['rows'][0][column['field_name']]) | ||
442 | row_two = get_text_from_td( | ||
443 | ascending['rows'][1][column['field_name']]) | ||
444 | |||
445 | self.assertTrue(row_one <= row_two, | ||
446 | "Ascending sort applied but row 0: \"%s\"" | ||
447 | " is less than row 1: \"%s\" " | ||
448 | "%s %s " % | ||
449 | (row_one, row_two, | ||
450 | column['field_name'], name)) | ||
451 | |||
452 | descending = get_data(table_cls(), | ||
453 | {"orderby": | ||
454 | '-'+column['field_name']}) | ||
455 | |||
456 | row_one = get_text_from_td( | ||
457 | descending['rows'][0][column['field_name']]) | ||
458 | row_two = get_text_from_td( | ||
459 | descending['rows'][1][column['field_name']]) | ||
460 | |||
461 | self.assertTrue(row_one >= row_two, | ||
462 | "Descending sort applied but row 0: %s" | ||
463 | "is greater than row 1: %s" | ||
464 | "field %s table %s" % | ||
465 | (row_one, | ||
466 | row_two, | ||
467 | column['field_name'], name)) | ||
468 | |||
469 | # If the two start rows are the same we haven't actually | ||
470 | # changed the order | ||
471 | self.assertNotEqual(ascending['rows'][0], | ||
472 | descending['rows'][0], | ||
473 | "An orderby %s has not changed the " | ||
474 | "order of the data in table %s" % | ||
475 | (column['field_name'], name)) | ||
476 | |||
477 | if column['filter_name']: | ||
478 | # If a filter is available for the column get the filter | ||
479 | # info. This contains what filter actions are defined. | ||
480 | filter_info = get_data(table_cls(), | ||
481 | {"cmd": "filterinfo", | ||
482 | "name": column['filter_name']}) | ||
483 | self.assertTrue(len(filter_info['filter_actions']) > 0, | ||
484 | "Filter %s was defined but no actions " | ||
485 | "added to it" % column['filter_name']) | ||
486 | |||
487 | for filter_action in filter_info['filter_actions']: | ||
488 | # filter string to pass as the option | ||
489 | # This is the name of the filter:action | ||
490 | # e.g. project_filter:not_in_project | ||
491 | filter_string = "%s:%s" % ( | ||
492 | column['filter_name'], | ||
493 | filter_action['action_name']) | ||
494 | # Now get the data with the filter applied | ||
495 | filtered_data = get_data(table_cls(), | ||
496 | {"filter": filter_string}) | ||
497 | |||
498 | # date range filter actions can't specify the | ||
499 | # number of results they return, so their count is 0 | ||
500 | if filter_action['count'] is not None: | ||
501 | self.assertEqual( | ||
502 | len(filtered_data['rows']), | ||
503 | int(filter_action['count']), | ||
504 | "We added a table filter for %s but " | ||
505 | "the number of rows returned was not " | ||
506 | "what the filter info said there " | ||
507 | "would be" % name) | ||
508 | |||
509 | # Test search functionality on the table | ||
510 | something_found = False | ||
511 | for search in list(string.ascii_letters): | ||
512 | search_data = get_data(table_cls(), {'search': search}) | ||
513 | |||
514 | if len(search_data['rows']) > 0: | ||
515 | something_found = True | ||
516 | break | ||
517 | |||
518 | self.assertTrue(something_found, | ||
519 | "We went through the whole alphabet and nothing" | ||
520 | " was found for the search of table %s" % name) | ||
521 | |||
522 | # Test the limit functionality on the table | ||
523 | limited_data = get_data(table_cls(), {'limit': "1"}) | ||
524 | self.assertEqual(len(limited_data['rows']), | ||
525 | 1, | ||
526 | "Limit 1 set on table %s but not 1 row returned" | ||
527 | % name) | ||
528 | |||
529 | # Test the pagination functionality on the table | ||
530 | page_one_data = get_data(table_cls(), {'limit': "1", | ||
531 | "page": "1"})['rows'][0] | ||
532 | |||
533 | page_two_data = get_data(table_cls(), {'limit': "1", | ||
534 | "page": "2"})['rows'][0] | ||
535 | |||
536 | self.assertNotEqual(page_one_data, | ||
537 | page_two_data, | ||
538 | "Changed page on table %s but first row is" | ||
539 | " the same as the previous page" % name) | ||