From eefd716f1f86e8c964f54d133b91db2a0a575b44 Mon Sep 17 00:00:00 2001 From: John Chilton Date: Thu, 1 Dec 2016 16:57:14 -0500 Subject: [PATCH] Migrate more CasperJS API tests to the API test framework. - Replace visualization tests (api-visualizations-tests.js -> test_visualizations.py). - Replace dataset API tests (api-dataset-tests.js -> test_datasets.py). - Start work on migrating history dataset API tests (api-hda-tests.js -> test_history_contents.py). --- test/api/test_datasets.py | 8 +- test/api/test_history_contents.py | 51 ++++-- test/api/test_visualizations.py | 108 ++++++++++++ test/casperjs/api-dataset-tests.js | 59 ------- test/casperjs/api-visualizations-tests.js | 199 ---------------------- 5 files changed, 151 insertions(+), 274 deletions(-) create mode 100644 test/api/test_visualizations.py delete mode 100644 test/casperjs/api-dataset-tests.js delete mode 100644 test/casperjs/api-visualizations-tests.js diff --git a/test/api/test_datasets.py b/test/api/test_datasets.py index 601c7be5d5d..709838f5316 100644 --- a/test/api/test_datasets.py +++ b/test/api/test_datasets.py @@ -15,8 +15,6 @@ class DatasetsApiTestCase( api.ApiTestCase, TestsDatasets ): def test_index( self ): index_response = self._get( "datasets" ) - print(index_response) - print(dir( index_response )) self._assert_status_code_is( index_response, 501 ) def test_show( self ): @@ -36,10 +34,10 @@ class DatasetsApiTestCase( api.ApiTestCase, TestsDatasets ): A B C D 10 20 30 40 """ ) - hda1 = self._new_dataset( self.history_id, content=contents ) + hda1 = self._new_dataset(self.history_id, content=contents) + self._wait_for_history(self.history_id) display_response = self._get( "histories/%s/contents/%s/display" % ( self.history_id, hda1[ "id" ] ), { 'raw': 'True' }) self._assert_status_code_is( display_response, 200 ) - # TODO: doesn't work - # assert display_response.text == contents + assert display_response.text == contents diff --git a/test/api/test_history_contents.py b/test/api/test_history_contents.py index 8f58bb321c7..ac2750f6a24 100644 --- a/test/api/test_history_contents.py +++ b/test/api/test_history_contents.py @@ -1,3 +1,5 @@ +# -*- coding: utf-8 -*- + import json from requests import delete, put @@ -62,17 +64,44 @@ class HistoryContentsApiTestCase( api.ApiTestCase, TestsDatasets ): self._assert_status_code_is( create_response, 200 ) assert self.__count_contents( self.history_id ) == 1 - def test_update( self ): - hda1 = self._new_dataset( self.history_id ) - self._wait_for_history( self.history_id ) - assert str( hda1[ "deleted" ] ).lower() == "false" - update_url = self._api_url( "histories/%s/contents/%s" % ( self.history_id, hda1[ "id" ] ), use_key=True ) - # Awkward json.dumps required here because of https://trello.com/c/CQwmCeG6 - body = json.dumps( dict( deleted=True ) ) - update_response = put( update_url, data=body ) - self._assert_status_code_is( update_response, 200 ) - show_response = self.__show( hda1 ) - assert str( show_response.json()[ "deleted" ] ).lower() == "true" + def test_update(self): + hda1 = self._wait_for_new_hda() + assert str(hda1["deleted"]).lower() == "false" + update_response = self._raw_update(hda1["id"], dict(deleted=True)) + self._assert_status_code_is(update_response, 200) + show_response = self.__show(hda1) + assert str(show_response.json()["deleted"]).lower() == "true" + + update_response = self._raw_update(hda1["id"], dict(name="Updated Name")) + assert self.__show(hda1).json()["name"] == "Updated Name" + + update_response = self._raw_update(hda1["id"], dict(name="Updated Name")) + assert self.__show(hda1).json()["name"] == "Updated Name" + + unicode_name = u'ржевский сапоги' + update_response = self._raw_update(hda1["id"], dict(name=unicode_name)) + updated_hda = self.__show(hda1).json() + assert updated_hda["name"] == unicode_name, updated_hda + + quoted_name = '"Mooo"' + update_response = self._raw_update(hda1["id"], dict(name=quoted_name)) + updated_hda = self.__show(hda1).json() + assert updated_hda["name"] == quoted_name, quoted_name + + def test_update_type_failures(self): + hda1 = self._wait_for_new_hda() + update_response = self._raw_update(hda1["id"], dict(deleted='not valid')) + self._assert_status_code_is(update_response, 400) + + def _wait_for_new_hda(self): + hda1 = self._new_dataset(self.history_id) + self._wait_for_history(self.history_id) + return hda1 + + def _raw_update(self, item_id, data): + update_url = self._api_url( "histories/%s/contents/%s" % (self.history_id, item_id), use_key=True) + update_response = put(update_url, json=data) + return update_response def test_delete( self ): hda1 = self._new_dataset( self.history_id ) diff --git a/test/api/test_visualizations.py b/test/api/test_visualizations.py new file mode 100644 index 00000000000..be6fb2d6e17 --- /dev/null +++ b/test/api/test_visualizations.py @@ -0,0 +1,108 @@ +import json +import uuid + +from requests import ( + put +) + +from base import api +from base.api_asserts import assert_has_keys + +INDEX_KEYS = [ + 'id', 'title', 'type', 'dbkey', 'url' +] +SHOW_KEYS = INDEX_KEYS + [ + 'user_id', 'model_class', 'revisions', 'latest_revision', 'annotation' +] +REVISION_KEYS = [ + 'id', 'title', 'visualization_id', 'dbkey', 'model_class', 'config' +] + + +class VisualizationsApiTestCase( api.ApiTestCase ): + + def test_index_and_show(self): + self._create_viz() # to ensure on exists to index + index = self._get("visualizations").json() + assert len(index) >= 1 + for viz in index: + self._verify_viz_object(viz, show=False) + first_viz = index[0] + self._show_viz(first_viz["id"]) + + def test_create(self): + viz_id, viz_request = self._create_viz() + self._show_viz(viz_id, assert_ok=True) + + def test_create_fails_without_title(self): + response = self._raw_create_viz(title='') + self._assert_status_code_is(response, 400) + + def test_create_fails_with_bad_slug(self): + response = self._raw_create_viz(slug='123_()') + self._assert_status_code_is(response, 400) + + def test_create_fails_with_invalid_config(self): + response = self._raw_create_viz(config="3 = nime") + self._assert_status_code_is(response, 400) + + def test_update_title(self): + viz_id, viz = self._create_viz() + update_url = self._api_url("visualizations/%s" % viz_id, use_key=True) + response = put(update_url, {"title": "New Name"}) + self._assert_status_code_is(response, 200) + updated_viz = self._show_viz(viz_id) + assert updated_viz["title"] == "New Name" + + def _show_viz(self, viz_id, assert_ok=True): + show_response = self._get("visualizations/%s" % viz_id) + if assert_ok: + self._assert_status_code_is(show_response, 200) + + viz = show_response.json() + + if assert_ok: + self._verify_viz_object(viz, show=True) + + return viz + + def _raw_create_viz(self, title=None, slug=None, config=None): + uuid_str = str(uuid.uuid4()) + + title = title if title is not None else 'Test Visualization' + slug = slug if slug is not None else 'test-visualization-%s' % uuid_str + config = config if config is not None else json.dumps({ + "x": 10, + "y": 12, + }) + create_payload = { + "title": title, + "slug": slug, + "type": 'test', + "dbkey": 'hg17', + "annotation": 'this is a test of the emergency visualization system', + "config": config, + } + response = self._post("visualizations", data=create_payload) + return response + + def _create_viz(self, **kwds): + response = self._raw_create_viz(**kwds) + self._assert_status_code_is(response, 200) + viz = response.json() + return viz["id"], viz + + def _verify_viz_object(self, obj, show=False): + assert_has_keys(obj, *(SHOW_KEYS if show else INDEX_KEYS)) + + if show: + assert "revisions" in obj + revisions = obj["revisions"] + assert len(revisions) >= 1 + + assert "latest_revision" in obj + latest_revision = obj["latest_revision"] + assert_has_keys(latest_revision, *REVISION_KEYS) + assert latest_revision["model_class"] == "VisualizationRevision" + + assert latest_revision["id"] in revisions diff --git a/test/casperjs/api-dataset-tests.js b/test/casperjs/api-dataset-tests.js deleted file mode 100644 index efbe6bf5099..00000000000 --- a/test/casperjs/api-dataset-tests.js +++ /dev/null @@ -1,59 +0,0 @@ -var require = patchRequire( require ), - spaceghost = require( 'spaceghost' ).fromCasper( casper ), - xpath = require( 'casper' ).selectXPath, - utils = require( 'utils' ), - format = utils.format; - -spaceghost.test.begin( 'Test the HDA API', 0, function suite( test ){ - spaceghost.start(); - - // =================================================================== SET UP - var email = spaceghost.user.getRandomEmail(), - password = '123456'; - if( spaceghost.fixtureData.testUser ){ - email = spaceghost.fixtureData.testUser.email; - password = spaceghost.fixtureData.testUser.password; - } - - var detailKeys = [ - // the following are always present regardless of datatype - 'id', 'name', 'api_type', 'model_class', - 'history_id', 'hid', - 'accessible', 'deleted', 'visible', 'purged', - 'state', 'data_type', 'file_ext', 'file_size', - 'misc_info', 'misc_blurb', - 'download_url', 'visualizations', 'display_apps', 'display_types', - 'genome_build' - ]; - - spaceghost.user.loginOrRegisterUser( email, password ); - spaceghost.openHomePage(); - spaceghost.api.tools.thenUploadToCurrent({ filepath: '../../test-data/1.bed' }); - - spaceghost.then( function(){ - // ------------------------------------------------------------------------------------------- INDEX - this.test.comment( 'index should error with not implemented' ); - this.api.assertRaises( function(){ - this.api.datasets.index(); - }, 501, 'not implemented', 'throws unimplemented' ); - - // ------------------------------------------------------------------------------------------- SHOW - this.test.comment( 'show should get an HDA details object' ); - var history = this.api.histories.show( 'most_recently_used', { keys : 'id,hdas' } ), - hdaId = history.hdas[0], - show = this.api.datasets.show( hdaId ); - this.debug( this.jsonStr( history ) ); - this.debug( this.jsonStr( show ) ); - this.test.assert( this.hasKeys( show, detailKeys ), 'Has the proper keys' ); - - // ------------------------------------------------------------------------------------------- DISPLAY - this.test.comment( 'show should get an HDA details object' ); - var fileContents = this.api.datasets.display( history.id, hdaId, { raw: 'True' }); - this.test.assert( fileContents.split( '\n' ).length === 66, '1.bed has 66 lines' ); - }); - - - // =================================================================== - spaceghost.run( function(){ test.done(); }); -}); - diff --git a/test/casperjs/api-visualizations-tests.js b/test/casperjs/api-visualizations-tests.js deleted file mode 100644 index 9f2a7915efb..00000000000 --- a/test/casperjs/api-visualizations-tests.js +++ /dev/null @@ -1,199 +0,0 @@ -var require = patchRequire( require ), - spaceghost = require( 'spaceghost' ).fromCasper( casper ), - xpath = require( 'casper' ).selectXPath, - utils = require( 'utils' ), - format = utils.format; - -spaceghost.test.begin( 'Test the visualizations API', 0, function suite( test ){ - spaceghost.start(); - - // =================================================================== SET UP - var email = spaceghost.user.getRandomEmail(), - password = '123456'; - if( spaceghost.fixtureData.testUser ){ - email = spaceghost.fixtureData.testUser.email; - password = spaceghost.fixtureData.testUser.password; - } - spaceghost.user.loginOrRegisterUser( email, password ); - - // =================================================================== TESTS - spaceghost.openHomePage().then( function(){ - var ALWAYS_CREATE = true, - indexKeys = [ - 'id', 'title', 'type', 'dbkey', 'url' - ], - showKeys = indexKeys.concat([ - 'user_id', 'model_class', 'revisions', 'latest_revision', 'annotation' - ]), - revisionKeys = [ - 'id', 'title', 'visualization_id', 'dbkey', 'model_class', 'config' - ]; - - // ------------------------------------------------------------------------------------------- set up - var visualizationIndex = this.api.visualizations.index(); - if( ALWAYS_CREATE || !visualizationIndex.length ){ - // post a visualization - this.info( 'creating new visualization for tests' ); - var testVisualization = this.api.visualizations.create({ - title : 'Test Visualization', - // needs to be unique - slug : 'test-visualization-' + Date.now(), - type : 'test', - dbkey : 'hg17', - annotation : 'this is a test of the emergency visualization system', - config : { - x : 10, - y : 12 - } - }); - this.debug( this.jsonStr( testVisualization ) ); - } - - // ------------------------------------------------------------------------------------------- INDEX - this.test.comment( 'index should get a list of visualizations' ); - visualizationIndex = this.api.visualizations.index(); - this.debug( this.jsonStr( visualizationIndex ) ); - this.test.assert( utils.isArray( visualizationIndex ), - "index returned an array: length " + visualizationIndex.length ); - this.test.assert( visualizationIndex.length >= 1, 'Has at least one visualization' ); - - var firstVisualization = visualizationIndex[0]; - this.test.assert( this.hasKeys( firstVisualization, indexKeys ), 'Has the proper keys' ); - this.test.assert( this.api.isEncodedId( firstVisualization.id ), 'Id appears well-formed' ); - - //TODO: index searching - //TODO: anon user - //TODO: admin user - - // ------------------------------------------------------------------------------------------- SHOW - this.test.comment( 'show should get a visualization details object' ); - var visualizationShow = this.api.visualizations.show( firstVisualization.id ); - this.debug( this.jsonStr( visualizationShow ) ); - this.test.assert( this.hasKeys( visualizationShow, showKeys ), 'Has the proper keys' ); - this.test.assert( visualizationShow.model_class === 'Visualization', - 'Has the proper model_class: ' + visualizationShow.model_class ); - - this.test.comment( 'a visualization details object should contain an array of revision ids' ); - var revisions = visualizationShow.revisions; - this.test.assert( utils.isArray( revisions ), 'revisions is an array' ); - this.test.assert( revisions.length >= 1, 'revisions has at least one entry' ); - var areIds = true; - revisions.forEach( function( revision ){ - if( !spaceghost.api.isEncodedId( revision ) ){ areIds = false; } - }); - this.test.assert( areIds, 'all revisions are ids' ); - - this.test.comment( 'a visualization details object should contain a subobject of the latest revision' ); - var latestRevision = visualizationShow.latest_revision; - this.test.assert( utils.isObject( latestRevision ), 'latestRevision is an object' ); - this.test.assert( this.hasKeys( latestRevision, revisionKeys ), 'latestRevision has the proper keys' ); - this.test.assert( latestRevision.model_class === 'VisualizationRevision', - 'Has the proper model_class: ' + latestRevision.model_class ); - this.test.assert( latestRevision.visualization_id === visualizationShow.id, - 'revision visualization_id matches containing visualization id: ' + latestRevision.visualization_id ); - this.test.assert( visualizationShow.revisions.indexOf( latestRevision.id ) !== -1, - 'found latest_revision id in revisions' ); - - this.test.comment( 'a visualization revision should contain a subobject for the config' ); - var config = latestRevision.config; - this.test.assert( utils.isObject( config ), 'config is an object:\n' + this.jsonStr( config ) ); - - //TODO: url in visualizationIndex == show url - //TODO: non existing id throws error - //TODO: anon user - //TODO: user1 has no permissions to show user2 - - // ------------------------------------------------------------------------------------------- CREATE - this.test.comment( 'Calling create should create a new visualization and allow setting the name' ); - var visualizationData = { - title : 'Created Visualization', - // needs to be unique - slug : 'created-visualization-' + Date.now(), - type : 'test', - dbkey : 'hg17', - annotation : 'invisible visualization', - config : { - x : 10, - y : 12 - } - }; - var created = this.api.visualizations.create( visualizationData ); - this.debug( 'returned from create:\n' + this.jsonStr( created ) ); - this.test.assert( this.api.isEncodedId( created.id ), "create returned an id: " + created.id ); - - // check v. show - visualizationShow = this.api.visualizations.show( created.id ); - this.debug( 'visualizationShow:\n' + this.jsonStr( visualizationShow ) ); - // config is re-located into a revision and won't be there - //this.test.assert( compareObjs( visualizationShow, visualizationData, [ 'config' ] ), - // "show results seem to match create data" ); - - // the following errors are produced within base.controller.UsesVisualizationsMixin._create_visualization - this.test.comment( 'Calling create with a non-unique slug will cause an API error' ); - this.api.assertRaises( function(){ - created = this.api.visualizations.create( visualizationData ); - }, 400, 'visualization identifier must be unique' ); - - this.test.comment( 'Calling create with no title will cause an API error' ); - visualizationData.title = ''; - this.api.assertRaises( function(){ - created = this.api.visualizations.create( visualizationData ); - }, 400, 'visualization name is required' ); - visualizationData.title = 'Created Visualization'; - - this.test.comment( 'Calling create with improper characters in the slug will cause an API error' ); - var oldSlug = visualizationData.slug; - visualizationData.slug = '123_()'; - this.api.assertRaises( function(){ - created = this.api.visualizations.create( visualizationData ); - }, 400, "visualization identifier must consist of only lowercase letters, numbers, and the '-' character" ); - visualizationData.slug = oldSlug; - - this.test.comment( 'Calling create with an unrecognized key will be ignored' ); - visualizationData.title = 'Unrecognized key'; - visualizationData.slug = 'unrecognized-key'; - visualizationData.bler = 'blah'; - created = this.api.visualizations.create( visualizationData ); - this.test.assert( created.bler === undefined ); - delete visualizationData.bler; - - this.test.comment( 'Calling create with an unparsable JSON config will cause an API error' ); - visualizationData.title = 'Unparsable'; - visualizationData.slug = 'unparsable'; - visualizationData.config = '3 = nime'; - this.api.assertRaises( function(){ - created = this.api.visualizations.create( visualizationData ); - }, 400, "config must be a dictionary: " ); - - // ------------------------------------------------------------------------------------------ UPDATE - // ........................................................................................... idiot proofing - //this.test.comment( 'updating using a nonsense key should fail with an error' ); - //returned = this.api.visualizations.update( created.id, { bler : 'blah' }); - ////TODO: this isn't returning an object... - //this.debug( 'returned:' + this.jsonStr( returned ) ); - //this.test.assert( returned.bler === undefined ); - - this.test.comment( 'updating by attempting to change type should cause an error' ); - this.api.assertRaises( function(){ - returned = this.api.visualizations.update( created.id, { title : 30 }); - }, 400, 'title must be a string or unicode' ); - //TODO: the other types... - - // ........................................................................................... title - //this.test.comment( 'update should create a new visualization revision' ); - // - //this.test.comment( 'updating with a new title should NOT change the visualization title...' ); - //latestRevision = this.api.visualizations.show( created.id ).latest_revision; - //returned = this.api.visualizations.update( created.id, { - // title : 'New title' - //}); - //visualizationShow = this.api.visualizations.show( created.id ); - //this.debug( this.jsonStr( visualizationShow ) ); - //this.test.assert( visualizationShow.title === visualizationData.title, - // "Title does not set via update: " + visualizationShow.title ); - - }); - - // =================================================================== - spaceghost.run( function(){ test.done(); }); -});