class testRepeatCalls(RESTBaseUnitTest): def initialize(self): self.config = DefaultConfig() self.config.UnitTests.templates = getWMBASE() + '/src/templates/WMCore/WebTools' self.config.Webtools.section_('server') self.config.Webtools.server.socket_timeout = 1 self.urlbase = self.config.getServerUrl() self.cache_path = tempfile.mkdtemp() def tearDown(self): shutil.rmtree(self.cache_path, ignore_errors = True) self.rt.stop() def test10Calls(self): fail_count = 0 req = Requests.Requests(self.urlbase, {'req_cache_path': self.cache_path}) for i in range(0, 5): time.sleep(i) print 'test %s starting at %s' % (i, time.time()) try: result = req.get('/', incoming_headers={'Cache-Control':'no-cache'}) self.assertEqual(False, result[3]) self.assertEqual(200, result[1]) except HTTPException, he: print 'test %s raised a %s error' % (i, he.status) fail_count += 1 except Exception, e: print 'test %s raised an unexpected exception of type %s' % (i, type(e)) print e fail_count += 1
class testRepeatCalls(RESTBaseUnitTest): def initialize(self): self.config = DefaultConfig() self.config.UnitTests.templates = getWMBASE( ) + '/src/templates/WMCore/WebTools' self.config.Webtools.section_('server') self.config.Webtools.server.socket_timeout = 1 self.urlbase = self.config.getServerUrl() self.cache_path = tempfile.mkdtemp() def tearDown(self): shutil.rmtree(self.cache_path, ignore_errors=True) self.rt.stop() def test10Calls(self): fail_count = 0 req = Requests.Requests(self.urlbase, {'req_cache_path': self.cache_path}) for i in range(0, 5): time.sleep(i) print 'test %s starting at %s' % (i, time.time()) try: result = req.get( '/', incoming_headers={'Cache-Control': 'no-cache'}) self.assertEqual(False, result[3]) self.assertEqual(200, result[1]) except HTTPException, he: print 'test %s raised a %s error' % (i, he.status) fail_count += 1 except Exception, e: print 'test %s raised an unexpected exception of type %s' % ( i, type(e)) print e fail_count += 1
class FakeRESTServer(RESTBaseUnitTest): """ Loads a the CRABRESTModelMock REST interface which emulates the CRABRESTModel class. When testing a command which requires an interaction with the server wi will contact this class. """ def initialize(self): self.config = DefaultConfig('CRABRESTModelMock') self.config.Webtools.environment = 'development' self.config.Webtools.error_log_level = logging.ERROR self.config.Webtools.access_log_level = logging.ERROR self.config.Webtools.port = 8518 self.config.Webtools.host = '127.0.0.1' self.config.UnitTests.object = 'CRABRESTModelMock' #self.config.UnitTests.views.active.rest.logLevel = 'DEBUG' self.urlbase = self.config.getServerUrl() def setUp(self): """ _setUp_ """ RESTBaseUnitTest.setUp(self) def tearDown(self): RESTBaseUnitTest.tearDown(self)
class NestedModelTest(RESTBaseUnitTest): def initialize(self): self.config = DefaultConfig('WMCore_t.WebTools_t.DummyNestedModel') do_debug = True self.config.Webtools.environment = 'development' if do_debug: self.config.Webtools.error_log_level = logging.DEBUG self.config.Webtools.access_log_level = logging.DEBUG else: self.config.Webtools.error_log_level = logging.WARNING self.config.Webtools.access_log_level = logging.WARNING self.urlbase = self.config.getServerUrl() def testOuterFooPass(self): verb = 'GET' url = self.urlbase + 'foo' output = {'code': 200, 'data': '"foo"'} expireTime = 3600 methodTest(verb, url, output=output, expireTime=expireTime) url = self.urlbase + 'foo/test' output = {'code': 200, 'data': '"foo test"'} methodTest(verb, url, output=output, expireTime=expireTime) url = self.urlbase + 'foo' request_input = {'message': 'test'} output = {'code': 200, 'data': '"foo test"'} methodTest(verb, url, request_input=request_input, output=output, expireTime=expireTime) def testInnerPingPass(self): verb = 'GET' url = self.urlbase + 'foo/ping' output = {'code': 200, 'data': '"ping"'} expireTime = 3600 methodTest(verb, url, output=output, expireTime=expireTime) def testOuterFooError(self): verb = 'GET' url = self.urlbase + 'foo/123/567' output = {'code': 400} methodTest(verb, url, output=output) # This test is flipping back and forth in Jenkins. Perhaps due to port 8888 not being available. # Disabling for now @attr("integration") def testInnerPingError(self): verb = 'GET' url = self.urlbase + 'foo/123/ping' output = {'code': 400} methodTest(verb, url, output=output) url = self.urlbase + 'foo/ping/123' methodTest(verb, url, output=output)
class NestedModelTest(RESTBaseUnitTest): def initialize(self): self.config = DefaultConfig('WMCore_t.WebTools_t.DummyNestedModel') do_debug = True self.config.Webtools.environment = 'development' if do_debug: self.config.Webtools.error_log_level = logging.DEBUG self.config.Webtools.access_log_level = logging.DEBUG else: self.config.Webtools.error_log_level = logging.WARNING self.config.Webtools.access_log_level = logging.WARNING self.urlbase = self.config.getServerUrl() def testOuterFooPass(self): verb = 'GET' url = self.urlbase + 'foo' output = {'code': 200, 'data': '"foo"'} expireTime = 3600 methodTest(verb, url, output=output, expireTime=expireTime) url = self.urlbase + 'foo/test' output = {'code': 200, 'data': '"foo test"'} methodTest(verb, url, output=output, expireTime=expireTime) url = self.urlbase + 'foo' request_input = {'message': 'test'} output = {'code': 200, 'data': '"foo test"'} methodTest(verb, url, request_input=request_input, output=output, expireTime=expireTime) def testInnerPingPass(self): verb = 'GET' url = self.urlbase + 'foo/ping' output = {'code': 200, 'data': '"ping"'} expireTime = 3600 methodTest(verb, url, output=output, expireTime=expireTime) def testOuterFooError(self): verb = 'GET' url = self.urlbase + 'foo/123/567' output = {'code': 400} methodTest(verb, url, output=output) def testInnerPingError(self): verb = 'GET' url = self.urlbase + 'foo/123/ping' output = {'code': 400} methodTest(verb, url, output=output) url = self.urlbase + 'foo/ping/123' methodTest(verb, url, output=output)
class NestedModelTest(RESTBaseUnitTest): def initialize(self): self.config = DefaultConfig('WMCore_t.WebTools_t.DummyNestedModel') do_debug = True self.config.Webtools.environment = 'development' if do_debug: self.config.Webtools.error_log_level = logging.DEBUG self.config.Webtools.access_log_level = logging.DEBUG else: self.config.Webtools.error_log_level = logging.WARNING self.config.Webtools.access_log_level = logging.WARNING self.urlbase = self.config.getServerUrl() def testOuterFooPass(self): verb ='GET' url = self.urlbase + 'foo' output={'code':200, 'data':'"foo"'} expireTime =3600 methodTest(verb, url, output=output, expireTime=expireTime) url = self.urlbase + 'foo/test' output={'code':200, 'data':'"foo test"'} methodTest(verb, url, output=output, expireTime=expireTime) url = self.urlbase + 'foo' request_input = {'message': 'test'} output={'code':200, 'data':'"foo test"'} methodTest(verb, url, request_input=request_input, output=output, expireTime=expireTime) def testInnerPingPass(self): verb ='GET' url = self.urlbase + 'foo/ping' output={'code':200, 'data':'"ping"'} expireTime =3600 methodTest(verb, url, output=output, expireTime=expireTime) def testOuterFooError(self): verb ='GET' url = self.urlbase + 'foo/123/567' output={'code':400} methodTest(verb, url, output=output) def testInnerPingError(self): verb ='GET' url = self.urlbase + 'foo/123/ping' output={'code':400} methodTest(verb, url, output=output) url = self.urlbase + 'foo/ping/123' methodTest(verb, url, output=output)
class WorkQueueTest(RESTBaseUnitTest): """ Test WorkQueue Service client It will start WorkQueue RESTService Server DB sets from environment variable. Client DB sets from environment variable. This checks whether DS call makes without error and return the results. Not the correctness of functions. That will be tested in different module. """ def initialize(self): self.config = DefaultConfig( 'WMCore.HTTPFrontEnd.WMBS.WMBSRESTModel') dbUrl = os.environ.get("DATABASE", None) self.config.setDBUrl(dbUrl) # mysql example #self.config.setDBUrl('mysql://[email protected]:3306/TestDB') #self.config.setDBSocket('/var/lib/mysql/mysql.sock') self.schemaModules = ["WMCore.WMBS", "WMCore.ResourceControl", "BossAir"] def setUp(self): """ setUP global values """ RESTBaseUnitTest.setUp(self) self.params = {} self.params['endpoint'] = self.config.getServerUrl() def tearDown(self): RESTBaseUnitTest.tearDown(self) def testWorkQueueService(self): # test getWork wmbsApi = WMBS(self.params) self.assertEqual(wmbsApi.getResourceInfo(), []) self.assertEqual(wmbsApi.getResourceInfo(tableFormat = False), {})
class RESTTest(RESTBaseUnitTest): def initialize(self): self.config = DefaultConfig('WMCore_t.WebTools_t.DummyRESTModel') self.do_debug = False self.do_production = False if self.do_production: self.config.Webtools.environment = 'production' self.config.SecurityModule.dangerously_insecure = False # not real keyfile but for the test. # file will be deleted automaticall when garbage collected. self.tempFile = NamedTemporaryFile() self.config.SecurityModule.key_file = self.tempFile.name self.config.SecurityModule.section_("default") self.config.SecurityModule.default.role = "" self.config.SecurityModule.default.group = "" self.config.SecurityModule.default.site = "" if not self.do_debug: self.config.Webtools.error_log_level = logging.WARNING self.config.Webtools.access_log_level = logging.WARNING self.urlbase = self.config.getServerUrl() def testUnsupportedFormat(self): # test not accepted type should return 406 error url = self.urlbase + 'ping' methodTest('GET', url, accept='text/das', output={'code':406}) def testGoodEcho(self): verb ='POST' url = self.urlbase + 'echo' input_data={'message': 'unit test'} output={'code':200, 'type':'text/json', 'data':'{"message": "unit test"}'} methodTest(verb, url, input_data, output=output) def testBadEchoWithPosArg(self): "Echo takes one argument (message), with the positional argument it should fail" verb ='POST' url = self.urlbase + 'echo/stuff' input_data={'message': 'unit test'} output={'code':400, 'type':'text/json'} methodTest(verb, url, input_data, output=output) def testBadMethodEcho(self): """ The echo method isn't supported by GET, so should raise a 405 """ verb ='GET' url = self.urlbase + 'echo' input={'data': 'unit test'} output={'code':405, 'type':'text/json'} methodTest(verb, url, input, output=output) def testBadVerbEcho(self): "echo is only available to GET and POST, so should raise a 501" url = self.urlbase + 'echo' input={'data': 'unit test'} output={'code':501, 'type':'text/json'} for verb in ['DELETE']: methodTest(verb, url, input, output=output) def testPing(self): verb ='GET' url = self.urlbase + 'ping' output={'code':200, 'type':'text/json', 'data':'"ping"'} expireTime =3600 methodTest(verb, url, output=output, expireTime=expireTime) def testBadPing(self): verb ='GET' url = self.urlbase + 'wrong' output={'code':404} methodTest(verb, url, output=output) url = self.urlbase + 'echo' output={'code':405} methodTest(verb, url, output=output) url = self.urlbase + 'ping/wrong' output={'code':400} methodTest(verb, url, output=output) def testException(self): """ list takes a single integer argument, querying with a string """ url = self.urlbase + 'list?int=a' self.assertRaises(urllib2.HTTPError, urllib2.urlopen, url) # urllib2,urlopen raise the error but not urllib.urlopen url = self.urlbase + 'list1?int=a' expected_data = {"exception": 400, "type": "HTTPError", "message": "Invalid input"} urllib_data = urllib.urlopen(url) if self.do_production: #production mode returns 403 error self.assertEquals(urllib_data.getcode(), 403) else: response_data = urllib_data.read() response_data = json.loads(response_data) self.assertEquals(response_data['type'], expected_data['type']) self.assertEquals(response_data['message'], expected_data['message']) self.assertEquals(urllib_data.getcode(), 400) def testList(self): verb ='GET' url = self.urlbase + 'list/' request_input = {'input_int':123, 'input_str':'abc'} output={'code':200, 'type':'text/json'} result = json.loads(methodTest(verb, url, request_input=request_input, output=output)[0]) for i in result.keys(): self.assertEqual(result[i], request_input[i], '%s does not match response' % i) def testA(self): for t in ['GET', 'POST', 'PUT', 'DELETE', 'UPDATE']: response = makeRequest(url=self.urlbase + '/', values={'value':1234}) assert response[1] == 200, 'Got a return code != 200 (got %s)' % response[1] def testSanitisePass(self): """ Emulate how CherryPy passes arguments to a method, check that the data returned is correct. No server setup required """ drm = DummyRESTModel(self.config.getModelConfig()) def func(*args, **kwargs): sanitised_input = drm._sanitise_input(args, kwargs, "list") return drm.list(**sanitised_input) # 2 positional args (e.g. url/arg1/arg2) result = func(123, 'abc') assert result == {'input_int':123, 'input_str':'abc'},\ 'list with 2 positional args failed: %s' % result # 2 query string args (e.g. url?int=arg1&str=arg2) result = func(input_int=123, input_str='abc') assert result == {'input_int':123, 'input_str':'abc'},\ 'list with 2 query string args failed: %s' % result # 1 positional, 1 keyword (e.g. url/arg1/?str=arg2) result = func(123, input_str='abc') assert result == {'input_int':123, 'input_str':'abc'},\ 'list with 1 positional, 1 keyword failed: %s' % result def testSanitisePassHTTP(self): """ Same as testSanitisePass but do it over http and check the returned http codes. """ # 2 positional args (e.g. url/arg1/arg2) url = self.urlbase + 'list/123/abc' response = makeRequest(url=url) assert response[1] == 200, \ 'list with 2 positional args failed: ' +\ '. Got a return code != 200 (got %s)' % response[1] +\ '. Returned data: %s' % response[0] # 2 query string args (e.g. url?int=arg1&str=arg2) url = self.urlbase + 'list/' response = makeRequest(url=url, values={'input_int':'123', 'input_str':'abc'}) assert response[1] == 200, \ 'list with 2 query string args failed: ' +\ '. Got a return code != 200 (got %s)' % response[1] +\ '. Returned data: %s' % response[0] # 1 positional, 1 keyword (e.g. url/arg1/?str=arg2) url = self.urlbase + 'list/123/' response = makeRequest(url=url, values={'input_str':'abc'}) assert response[1] == 200, \ 'list with 1 positional, 1 keyword failed: ' +\ '. Got a return code != 200 (got %s)' % response[1] +\ '. Returned data: %s' % response[0] def testSanitiseAssertFail(self): """ No server set up required, the purpose of the test is just demonstrating how validation is used. """ drm = DummyRESTModel(self.config.getModelConfig()) def func(*args, **kwargs): sanitised_input = drm._sanitise_input(args, kwargs, "list") return drm.list(**sanitised_input) # Wrong type for input args self.assertRaises(HTTPError, func, 123, 123) self.assertRaises(HTTPError, func, 'abc', 'abc') self.assertRaises(HTTPError, func, input_str = 'abc', input_int = 123, other='dfe') self.assertRaises(HTTPError, func, str = 123, int ='abc') self.assertRaises(HTTPError, func, str =' abc', int = 'abc') self.assertRaises(HTTPError, func, 'abc', 123) self.assertRaises(HTTPError, func, 'abc', 'abc') self.assertRaises(HTTPError, func, str = 123, int = 'abc') self.assertRaises(HTTPError, func, str =123, int = 123) self.assertRaises(HTTPError, func, str = 'abc', int ='abc') # Incorrect values for input args self.assertRaises(HTTPError, func, 1234, 'abc') self.assertRaises(HTTPError, func, 123, 'abcd') # Empty input data, when data is required self.assertRaises(HTTPError, func) def testSanitiseFailHTTP(self): """ Same as testSanitisePass but do it over http and check the returned http codes. """ # 2 positional args (e.g. url/arg1/arg2) url = self.urlbase + 'list/123/' response = makeRequest(url=url, accept='text/json') assert response[1] == 400, \ 'list with 2 positional args failed: ' +\ '. Got a return code != 400 (got %s)' % response[1] +\ '. Returned data: %s' % response[0] self.assertEqual(response[2], 'text/json', 'type is not text/json : %s' % type) # 2 query string args (e.g. url?int=arg1&str=arg2) url = self.urlbase + 'list' response = makeRequest(url=url, values={'int':'abc', 'str':'abc'}) assert response[1] == 400, \ 'list with 2 query string args failed: ' +\ '. Got a return code != 400 (got %s)' % response[1] +\ '. Returned data: %s' % response[0] # 1 positional, 1 keyword (e.g. url/arg1/?str=arg2) url = self.urlbase + 'list/abc' response = makeRequest(url=url, values={'str':'abc'}) assert response[1] == 400, \ 'list with 1 positional, 1 keyword failed: ' +\ '. Got a return code != 400 (got %s)' % response[1] +\ '. Returned data: %s' % response[0] # don't need server set up def testDAOBased(self): drm = DummyRESTModel(self.config.getModelConfig()) result = drm.methods['GET']['data1']['call']() self.assertEqual( result , 123, 'Error default value is set to 123 but returns %s' % result ) result = drm.methods['GET']['data2']['call'](456) self.assertEqual( result['num'] , 456 ) result = drm.methods['GET']['data2']['call'](num = 456) self.assertEqual( result['num'] , 456 ) result = drm.methods['GET']['data3']['call'](num = 456, thing="TEST") self.assertEqual( result['num'] == 456 and result['thing'] , "TEST" ) def testDAOBasedHTTP(self): """ Same as testSanitisePass but do it over http and check the returned http codes. """ # 2 positional args (e.g. url/arg1/arg2) url = self.urlbase + 'data1/' response = makeRequest(url=url) assert response[1] == 200, \ 'dao without args failed: ' +\ '. Got a return code != 200 (got %s)' % response[1] +\ '. Returned data: %s' % response[0] self.assertEqual( response[0] , '123', response[0]) # 2 query string args (e.g. url?int=arg1&str=arg2) url = self.urlbase + 'data2' response = makeRequest(url=url, values={'num':456}) assert response[1] == 200, \ 'dao with 1 args failed: ' +\ '. Got a return code != 200 (got %s)' % response[1] +\ '. Returned data: %s' % response[0] #Warning quotation type matters #Should use encoded and decoded format self.assertEqual( response[0] , "{'num': '456'}", "should be {'num': '456'} but got %s" % response[0] ) # 1 positional, 1 keyword (e.g. url/arg1/?str=arg2) url = self.urlbase + 'data3/123' response = makeRequest(url=url, values={'thing':'abc'}) assert response[1] == 200, \ 'dao with 1 positional, 1 keyword failed: ' +\ '. Got a return code != 200 (got %s)' % response[1] +\ '. Returned data: %s' % response[0] #Warning quotation type and order matters #Should use encoded and decoded format self.assertEqual( response[0] , "{'thing': 'abc', 'num': '123'}", "should be {'thing': 'abc', 'num': '123'} but got %s" % response[0] ) def testListTypeArgs(self): # 2 positional args (e.g. url/arg1/arg2) url = self.urlbase + 'listTypeArgs?aList=1' response = makeRequest(url=url) assert response[1] == 200 and response[0] == "[1]", \ 'list args failed: ' +\ '. Got a return code != 200 (got %s)' % response[1] +\ '. Returned data: %s' % response[0] # 2 values with the same keywords (e.g. url/arg1/arg2) url = self.urlbase + 'listTypeArgs?aList=1&aList=2' response = makeRequest(url=url) assert response[1] == 200 and response[0] == "[1, 2]", \ 'list args failed: ' +\ '. Got a return code != 200 (got %s)' % response[1] +\ '. Returned data: %s' % response[0] @cherrypySetup(secureConfig) @attr("integration") def testAuthentication(self): verb ='PUT' url = self.urlbase + 'list1' urllib_data = urllib.urlopen(url) self.assertEquals(urllib_data.getcode(), 403) # pass proper role output={'code':200} methodTest(verb, url, output=output, secure=True, secureParam={'key': secureKey, 'role': DUMMY_ROLE, 'group': DUMMY_GROUP, 'site': DUMMY_SITE})
class testRepeatCalls(RESTBaseUnitTest): def initialize(self): self.config = DefaultConfig() self.config.UnitTests.templates = getWMBASE() + '/src/templates/WMCore/WebTools' self.config.Webtools.section_('server') self.config.Webtools.server.socket_timeout = 1 self.urlbase = self.config.getServerUrl() self.cache_path = tempfile.mkdtemp() def tearDown(self): shutil.rmtree(self.cache_path, ignore_errors = True) self.rt.stop() def test10Calls(self): fail_count = 0 req = Requests.Requests(self.urlbase, {'req_cache_path': self.cache_path}) for i in range(0, 5): time.sleep(i) print('test %s starting at %s' % (i, time.time())) try: result = req.get('/', incoming_headers={'Cache-Control':'no-cache'}) self.assertEqual(False, result[3]) self.assertEqual(200, result[1]) except HTTPException as he: print('test %s raised a %s error' % (i, he.status)) fail_count += 1 except Exception as e: print('test %s raised an unexpected exception of type %s' % (i, type(e))) print(e) fail_count += 1 if fail_count > 0: raise Exception('Test did not pass!') def test10Calls_with_pycurl(self): fail_count = 0 idict = {'req_cache_path': self.cache_path, 'pycurl':1} req = Requests.Requests(self.urlbase, idict) for i in range(0, 5): time.sleep(i) print('test %s starting at %s' % (i, time.time())) try: result = req.get('/', incoming_headers={'Cache-Control':'no-cache'}, decode=False) self.assertEqual(False, result[3]) self.assertEqual(200, result[1]) except HTTPException as he: print('test %s raised a %s error' % (i, he.status)) fail_count += 1 except Exception as e: print('test %s raised an unexpected exception of type %s' % (i, type(e))) print(e) fail_count += 1 if fail_count > 0: raise Exception('Test did not pass!') def testRecoveryFromConnRefused(self): """Connections succeed after server down""" import socket self.rt.stop() req = Requests.Requests(self.urlbase, {'req_cache_path': self.cache_path}) headers = {'Cache-Control':'no-cache'} self.assertRaises(socket.error, req.get, '/', incoming_headers=headers) # now restart server and hope we can connect self.rt.start(blocking=False) result = req.get('/', incoming_headers=headers) self.assertEqual(result[3], False) self.assertEqual(result[1], 200) def testRecoveryFromConnRefused_with_pycurl(self): """Connections succeed after server down""" import pycurl self.rt.stop() idict = {'req_cache_path': self.cache_path, 'pycurl':1} req = Requests.Requests(self.urlbase, idict) headers = {'Cache-Control':'no-cache'} self.assertRaises(pycurl.error, req.get, '/', incoming_headers=headers, decode=False) # now restart server and hope we can connect self.rt.start(blocking=False) result = req.get('/', incoming_headers=headers, decode=False) self.assertEqual(result[3], False) self.assertEqual(result[1], 200)
class RESTTest(RESTBaseUnitTest): def initialize(self): self.config = DefaultConfig('WMCore_t.WebTools_t.DummyRESTModel') self.do_debug = False self.do_production = False if self.do_production: self.config.Webtools.environment = 'production' self.config.SecurityModule.dangerously_insecure = False # not real keyfile but for the test. # file will be deleted automaticall when garbage collected. self.tempFile = NamedTemporaryFile() self.config.SecurityModule.key_file = self.tempFile.name self.config.SecurityModule.section_("default") self.config.SecurityModule.default.role = "" self.config.SecurityModule.default.group = "" self.config.SecurityModule.default.site = "" if not self.do_debug: self.config.Webtools.error_log_level = logging.WARNING self.config.Webtools.access_log_level = logging.WARNING self.urlbase = self.config.getServerUrl() def testGeneratorMethod(self): # test not accepted type should return 406 error url = self.urlbase + 'gen' output={'code':200} data, _ = methodTest('GET', url, accept='text/json', output=output) data = json.loads(data) self.assertEqual(type(data), list) self.assertEqual(type(data[0]), dict) def testUnsupportedFormat(self): # test not accepted type should return 406 error url = self.urlbase + 'ping' methodTest('GET', url, accept='text/das', output={'code':406}) def testGoodEcho(self): verb ='POST' url = self.urlbase + 'echo' input_data={'message': 'unit test'} output={'code':200, 'type':'text/json', 'data':'{"message": "unit test"}'} methodTest(verb, url, input_data, output=output) def testBadEchoWithPosArg(self): "Echo takes one argument (message), with the positional argument it should fail" verb ='POST' url = self.urlbase + 'echo/stuff' input_data={'message': 'unit test'} output={'code':400, 'type':'text/json'} methodTest(verb, url, input_data, output=output) def testBadMethodEcho(self): """ The echo method isn't supported by GET, so should raise a 405 """ verb ='GET' url = self.urlbase + 'echo' input_data = {'data': 'unit test'} output = {'code':405, 'type':'text/json'} methodTest(verb, url, input_data, output=output) def testBadVerbEcho(self): "echo is only available to GET and POST, so should raise a 501" url = self.urlbase + 'echo' input_data = {'data': 'unit test'} output = {'code':501, 'type':'text/json'} for verb in ['DELETE']: methodTest(verb, url, input_data, output=output) def testPing(self): verb ='GET' url = self.urlbase + 'ping' output={'code':200, 'type':'text/json', 'data':'"ping"'} expireTime =3600 methodTest(verb, url, output=output, expireTime=expireTime) def testBadPing(self): verb ='GET' url = self.urlbase + 'wrong' output={'code':404} methodTest(verb, url, output=output) url = self.urlbase + 'echo' output={'code':405} methodTest(verb, url, output=output) url = self.urlbase + 'ping/wrong' output={'code':400} methodTest(verb, url, output=output) def testException(self): """ testException list takes a single integer argument, querying with a string """ url = self.urlbase + 'list?int=a' self.assertRaises(urllib.error.HTTPError, urllib.request.urlopen, url) try: urllib.request.urlopen(url) except urllib.error.HTTPError as e: self.assertEqual(e.code, 400) self.assertEqual(e.reason, u'Bad Request') self.assertEqual(e.msg, u'Bad Request') exception_data = json.loads(e.read()) self.assertEqual(exception_data['type'], 'HTTPError') self.assertEqual(exception_data['message'], 'Invalid input: Input arguments failed sanitation.') url = self.urlbase + 'list1?int=a' self.assertRaises(urllib.error.HTTPError, urllib.request.urlopen, url) try: urllib.request.urlopen(url) except urllib.error.HTTPError as e: self.assertEqual(e.code, 400) self.assertEqual(e.reason, u'Bad Request') self.assertEqual(e.msg, u'Bad Request') exception_data = json.loads(e.read()) self.assertEqual(exception_data['type'], 'HTTPError') self.assertEqual(exception_data['message'], 'Invalid input: Arguments added where none allowed') def testList(self): verb ='GET' url = self.urlbase + 'list/' request_input = {'input_int':123, 'input_str':'abc'} output={'code':200, 'type':'text/json'} result = json.loads(methodTest(verb, url, request_input=request_input, output=output)[0]) for i in result.keys(): self.assertEqual(result[i], request_input[i], '%s does not match response' % i) def testA(self): # This test doesn't actually use the type, just the same thing 5 times. for t in ['GET', 'POST', 'PUT', 'DELETE', 'UPDATE']: response = makeRequest(url=self.urlbase + '/', values={'value':1234}) assert response[1] == 200, 'Got a return code != 200 (got %s)' % response[1] def testSanitisePass(self): """ Emulate how CherryPy passes arguments to a method, check that the data returned is correct. No server setup required """ drm = DummyRESTModel(self.config.getModelConfig()) def func(*args, **kwargs): sanitised_input = drm._sanitise_input(args, kwargs, "list") return drm.list(**sanitised_input) # 2 positional args (e.g. url/arg1/arg2) result = func(123, 'abc') assert result == {'input_int':123, 'input_str':'abc'},\ 'list with 2 positional args failed: %s' % result # 2 query string args (e.g. url?int=arg1&str=arg2) result = func(input_int=123, input_str='abc') assert result == {'input_int':123, 'input_str':'abc'},\ 'list with 2 query string args failed: %s' % result # 1 positional, 1 keyword (e.g. url/arg1/?str=arg2) result = func(123, input_str='abc') assert result == {'input_int':123, 'input_str':'abc'},\ 'list with 1 positional, 1 keyword failed: %s' % result def testSanitisePassHTTP(self): """ Same as testSanitisePass but do it over http and check the returned http codes. """ # 2 positional args (e.g. url/arg1/arg2) url = self.urlbase + 'list/123/abc' response = makeRequest(url=url) assert response[1] == 200, \ 'list with 2 positional args failed: ' +\ '. Got a return code != 200 (got %s)' % response[1] +\ '. Returned data: %s' % response[0] # 2 query string args (e.g. url?int=arg1&str=arg2) url = self.urlbase + 'list/' response = makeRequest(url=url, values={'input_int':'123', 'input_str':'abc'}) assert response[1] == 200, \ 'list with 2 query string args failed: ' +\ '. Got a return code != 200 (got %s)' % response[1] +\ '. Returned data: %s' % response[0] # 1 positional, 1 keyword (e.g. url/arg1/?str=arg2) url = self.urlbase + 'list/123/' response = makeRequest(url=url, values={'input_str':'abc'}) assert response[1] == 200, \ 'list with 1 positional, 1 keyword failed: ' +\ '. Got a return code != 200 (got %s)' % response[1] +\ '. Returned data: %s' % response[0] def testSanitiseAssertFail(self): """ No server set up required, the purpose of the test is just demonstrating how validation is used. """ drm = DummyRESTModel(self.config.getModelConfig()) def func(*args, **kwargs): sanitised_input = drm._sanitise_input(args, kwargs, "list") return drm.list(**sanitised_input) # Wrong type for input args self.assertRaises(HTTPError, func, 123, 123) self.assertRaises(HTTPError, func, 'abc', 'abc') self.assertRaises(HTTPError, func, input_str = 'abc', input_int = 123, other='dfe') self.assertRaises(HTTPError, func, str = 123, int ='abc') self.assertRaises(HTTPError, func, str =' abc', int = 'abc') self.assertRaises(HTTPError, func, 'abc', 123) self.assertRaises(HTTPError, func, 'abc', 'abc') self.assertRaises(HTTPError, func, str = 123, int = 'abc') self.assertRaises(HTTPError, func, str =123, int = 123) self.assertRaises(HTTPError, func, str = 'abc', int ='abc') # Incorrect values for input args self.assertRaises(HTTPError, func, 1234, 'abc') self.assertRaises(HTTPError, func, 123, 'abcd') # Empty input data, when data is required self.assertRaises(HTTPError, func) def testSanitiseFailHTTP(self): """ Same as testSanitisePass but do it over http and check the returned http codes. """ # 2 positional args (e.g. url/arg1/arg2) url = self.urlbase + 'list/123/' response = makeRequest(url=url, accept='text/json') assert response[1] == 400, \ 'list with 2 positional args failed: ' +\ '. Got a return code != 400 (got %s)' % response[1] +\ '. Returned data: %s' % response[0] self.assertEqual(response[2], 'text/json', 'type is not text/json : %s' % type) # 2 query string args (e.g. url?int=arg1&str=arg2) url = self.urlbase + 'list' response = makeRequest(url=url, values={'int':'abc', 'str':'abc'}) assert response[1] == 400, \ 'list with 2 query string args failed: ' +\ '. Got a return code != 400 (got %s)' % response[1] +\ '. Returned data: %s' % response[0] # 1 positional, 1 keyword (e.g. url/arg1/?str=arg2) url = self.urlbase + 'list/abc' response = makeRequest(url=url, values={'str':'abc'}) assert response[1] == 400, \ 'list with 1 positional, 1 keyword failed: ' +\ '. Got a return code != 400 (got %s)' % response[1] +\ '. Returned data: %s' % response[0] # don't need server set up def testDAOBased(self): drm = DummyRESTModel(self.config.getModelConfig()) result = drm.methods['GET']['data1']['call']() self.assertEqual( result , 123, 'Error default value is set to 123 but returns %s' % result ) result = drm.methods['GET']['data2']['call'](456) self.assertEqual( result['num'] , 456 ) result = drm.methods['GET']['data2']['call'](num = 456) self.assertEqual( result['num'] , 456 ) result = drm.methods['GET']['data3']['call'](num = 456, thing="TEST") self.assertEqual( result['num'] == 456 and result['thing'] , "TEST" ) # This test is flipping back and forth in Jenkins. Perhaps due to port 8888 not being available. # Disabling for now @attr("integration") def testDAOBasedHTTP(self): """ Same as testSanitisePass but do it over http and check the returned http codes. """ # 2 positional args (e.g. url/arg1/arg2) url = self.urlbase + 'data1/' response = makeRequest(url=url) assert response[1] == 200, \ 'dao without args failed: ' +\ '. Got a return code != 200 (got %s)' % response[1] +\ '. Returned data: %s' % response[0] self.assertEqual( response[0] , '123', response[0]) # 2 query string args (e.g. url?int=arg1&str=arg2) url = self.urlbase + 'data2' response = makeRequest(url=url, values={'num':456}) assert response[1] == 200, \ 'dao with 1 args failed: ' +\ '. Got a return code != 200 (got %s)' % response[1] +\ '. Returned data: %s' % response[0] #Warning quotation type matters #Should use encoded and decoded format self.assertEqual( response[0] , "{'num': '456'}", "should be {'num': '456'} but got %s" % response[0] ) # 1 positional, 1 keyword (e.g. url/arg1/?str=arg2) url = self.urlbase + 'data3/123' response = makeRequest(url=url, values={'thing':'abc'}) assert response[1] == 200, \ 'dao with 1 positional, 1 keyword failed: ' +\ '. Got a return code != 200 (got %s)' % response[1] +\ '. Returned data: %s' % response[0] #Warning quotation type and order matters #Should use encoded and decoded format self.assertEqual( response[0] , "{'thing': 'abc', 'num': '123'}", "should be {'thing': 'abc', 'num': '123'} but got %s" % response[0] ) def testListTypeArgs(self): # 2 positional args (e.g. url/arg1/arg2) url = self.urlbase + 'listTypeArgs?aList=1' response = makeRequest(url=url) assert response[1] == 200 and response[0] == b"[1]", \ 'list args failed: ' +\ '. Got a return code != 200 (got %s)' % response[1] +\ '. Returned data: %s' % response[0] # 2 values with the same keywords (e.g. url/arg1/arg2) url = self.urlbase + 'listTypeArgs?aList=1&aList=2' response = makeRequest(url=url) assert response[1] == 200 and response[0] == b"[1, 2]", \ 'list args failed: ' +\ '. Got a return code != 200 (got %s)' % response[1] +\ '. Returned data: %s' % response[0] @cherrypySetup(secureConfig) @attr("integration") def testAuthentication(self): verb ='PUT' url = self.urlbase + 'list1' urllib_data = urllib.request.urlopen(url) self.assertEqual(urllib_data.getcode(), 403) # pass proper role output={'code':200} methodTest(verb, url, output=output, secure=True, secureParam={'key': secureKey, 'role': DUMMY_ROLE, 'group': DUMMY_GROUP, 'site': DUMMY_SITE})
class RESTFormatTest(RESTBaseUnitTest): def initialize(self): self.config = DefaultConfig('WMCore_t.WebTools_t.DummyRESTModel') do_debug = False self.config.Webtools.environment = 'development' if do_debug: self.config.Webtools.error_log_level = logging.DEBUG self.config.Webtools.access_log_level = logging.DEBUG else: self.config.Webtools.error_log_level = logging.WARNING self.config.Webtools.access_log_level = logging.WARNING self.urlbase = self.config.getServerUrl() def testUnsupportedFormat(self): # test not accepted type should return 406 error url = self.urlbase +'list1/' methodTest('GET', url, accept='text/das', output={'code':406}) def testSupportedFormat(self): rf = RESTFormatter(config=self.config.Webtools) url = self.urlbase +'list1/' for type in rf.supporttypes.keys(): # test accepted type should return 200 error methodTest('GET', url, accept=type, output={'code':200}) def testEncodedInput(self): type = 'text/plain' url = self.urlbase + 'list3?a=a%&b=b' methodTest('GET', url, accept=type, output={'code':200, 'data':"{'a': 'a%', 'b': 'b'}"}) request_input={'a':'%', 'b':'b'} #methodTest encoded input with urlencode url = self.urlbase +'list3' methodTest('GET', url, accept=type, request_input=request_input, output={'code':200, 'data':"{'a': '%', 'b': 'b'}"}) def testReturnFormat(self): return_type = 'application/json' url = self.urlbase +'list3?a=a%&b=b' methodTest('GET', url, accept=return_type, output={'code':200, 'data':'{"a": "a%", "b": "b"}'}) url = self.urlbase + 'list?input_int=a&input_str=a' expected_data = '''{"exception": 400, "message": "Invalid input", "type": "HTTPError"}''' methodTest('GET', url, accept=return_type, output={'code':400, 'data':expected_data}) def testNoArgMethods(self): """ list1 takes no arguments, it should raise an error if called with one. Require json output. """ return_type = 'application/json' url = self.urlbase + 'list1?int=a' expected_data = """{"exception": 400, "message": "Invalid input", "type": "HTTPError"}""" methodTest('GET', url, accept=return_type, output={'code':400, 'data':expected_data})
class RESTFormatTest(RESTBaseUnitTest): def initialize(self): self.config = DefaultConfig('WMCore_t.WebTools_t.DummyRESTModel') do_debug = False self.config.Webtools.environment = 'development' if do_debug: self.config.Webtools.error_log_level = logging.DEBUG self.config.Webtools.access_log_level = logging.DEBUG else: self.config.Webtools.error_log_level = logging.WARNING self.config.Webtools.access_log_level = logging.WARNING self.urlbase = self.config.getServerUrl() def testUnsupportedFormat(self): # test not accepted type should return 406 error url = self.urlbase +'list1/' methodTest('GET', url, accept='text/das', output={'code':406}) def testSupportedFormat(self): rf = RESTFormatter(config=self.config.Webtools) url = self.urlbase +'list1/' for type in rf.supporttypes.keys(): # test accepted type should return 200 error methodTest('GET', url, accept=type, output={'code':200}) def testEncodedInput(self): type = 'text/plain' url = self.urlbase + 'list3?a=a%&b=b' methodTest('GET', url, accept=type, output={'code':200, 'data':"{'a': 'a%', 'b': 'b'}"}) request_input={'a':'%', 'b':'b'} #methodTest encoded input with urlencode url = self.urlbase +'list3' methodTest('GET', url, accept=type, request_input=request_input, output={'code':200, 'data':"{'a': '%', 'b': 'b'}"}) def testReturnFormat(self): return_type = 'application/json' url = self.urlbase +'list3?a=a%&b=b' methodTest('GET', url, accept=return_type, output={'code':200, 'data':'{"a": "a%", "b": "b"}'}) url = self.urlbase + 'list?input_int=a&input_str=a' expected_data = '''{"exception": 400, "message": "Invalid input: Input data failed validation.", "type": "HTTPError"}''' methodTest('GET', url, accept=return_type, output={'code':400, 'data':expected_data}) def testNoArgMethods(self): """ list1 takes no arguments, it should raise an error if called with one. Require json output. """ return_type = 'application/json' url = self.urlbase + 'list1?int=a' expected_data = """{"exception": 400, "message": "Invalid input: Arguments added where none allowed", "type": "HTTPError"}""" methodTest('GET', url, accept=return_type, output={'code':400, 'data':expected_data})
class RESTFormatTest(RESTBaseUnitTest): def initialize(self): self.config = DefaultConfig('WMCore_t.WebTools_t.DummyRESTModel') do_debug = False self.config.Webtools.environment = 'development' if do_debug: self.config.Webtools.error_log_level = logging.DEBUG self.config.Webtools.access_log_level = logging.DEBUG else: self.config.Webtools.error_log_level = logging.WARNING self.config.Webtools.access_log_level = logging.WARNING self.urlbase = self.config.getServerUrl() def testUnsupportedFormat(self): # test not accepted type should return 406 error url = self.urlbase +'list1/' methodTest('GET', url, accept='text/das', output={'code':406}) def testSupportedFormat(self): rf = RESTFormatter(config=self.config.Webtools) url = self.urlbase +'list1/' for textType in rf.supporttypes.keys(): # test accepted type should return 200 error methodTest('GET', url, accept=textType, output={'code':200}) # This test is flipping back and forth in Jenkins. Perhaps due to port 8888 not being available. # Disabling for now @attr("integration") def testEncodedInput(self): textType = 'text/plain' url = self.urlbase + 'list3?a=a%&b=b' data = json.dumps({'a': 'a%', 'b': 'b'}) methodTest('GET', url, accept=textType, output={'code':200, 'data':data}) request_input={'a':'%', 'b':'b'} #methodTest encoded input with urlencode url = self.urlbase +'list3' data = json.dumps({'a': '%', 'b': 'b'}) methodTest('GET', url, accept=textType, request_input=request_input, output={'code':200, 'data':data}) def testReturnFormat(self): return_type = 'application/json' url = self.urlbase +'list3?a=a%&b=b' methodTest('GET', url, accept=return_type, output={'code':200, 'data':'{"a": "a%", "b": "b"}'}) url = self.urlbase + 'list?input_int=a&input_str=a' expected_data_py2 = '{"exception": 400, "message": "Invalid input: Input data failed validation.", "type": "HTTPError"}' expected_data_py3 = '{"exception": 400, "type": "HTTPError", "message": "Invalid input: Input data failed validation."}' expected_data = expected_data_py3 if PY3 else expected_data_py2 methodTest('GET', url, accept=return_type, output={'code':400, 'data':expected_data}) def testNoArgMethods(self): """ list1 takes no arguments, it should raise an error if called with one. Require json output. """ return_type = 'application/json' url = self.urlbase + 'list1?int=a' expected_data_py2 = '{"exception": 400, "message": "Invalid input: Arguments added where none allowed", "type": "HTTPError"}' expected_data_py3 = '{"exception": 400, "type": "HTTPError", "message": "Invalid input: Arguments added where none allowed"}' expected_data = expected_data_py3 if PY3 else expected_data_py2 methodTest('GET', url, accept=return_type, output={'code':400, 'data':expected_data}) def testGenerator(self): rf = RESTFormatter(config=self.config.Webtools) url = self.urlbase +'gen' # gen method from DummyRESTModel will return this generator gen = ({'idx':i} for i in range(10)) # the WMCore should convert it into list regardless of accept type data = rf.json(gen) methodTest('GET', url, accept='application/json', output={'code':200, 'data':data}) methodTest('GET', url, accept='*/*', output={'code':200, 'data':data})
class RESTFormatTest(RESTBaseUnitTest): def initialize(self): self.config = DefaultConfig('WMCore_t.WebTools_t.DummyRESTModel') do_debug = False self.config.Webtools.environment = 'development' if do_debug: self.config.Webtools.error_log_level = logging.DEBUG self.config.Webtools.access_log_level = logging.DEBUG else: self.config.Webtools.error_log_level = logging.WARNING self.config.Webtools.access_log_level = logging.WARNING self.urlbase = self.config.getServerUrl() def testUnsupportedFormat(self): # test not accepted type should return 406 error url = self.urlbase +'list1/' methodTest('GET', url, accept='text/das', output={'code':406}) def testSupportedFormat(self): rf = RESTFormatter(config=self.config.Webtools) url = self.urlbase +'list1/' for textType in rf.supporttypes.keys(): # test accepted type should return 200 error methodTest('GET', url, accept=textType, output={'code':200}) # This test is flipping back and forth in Jenkins. Perhaps due to port 8888 not being available. # Disabling for now @attr("integration") def testEncodedInput(self): textType = 'text/plain' url = self.urlbase + 'list3?a=a%&b=b' data = json.dumps({'a': 'a%', 'b': 'b'}) methodTest('GET', url, accept=textType, output={'code':200, 'data':data}) request_input={'a':'%', 'b':'b'} #methodTest encoded input with urlencode url = self.urlbase +'list3' data = json.dumps({'a': '%', 'b': 'b'}) methodTest('GET', url, accept=textType, request_input=request_input, output={'code':200, 'data':data}) def testReturnFormat(self): return_type = 'application/json' url = self.urlbase +'list3?a=a%&b=b' methodTest('GET', url, accept=return_type, output={'code':200, 'data':'{"a": "a%", "b": "b"}'}) url = self.urlbase + 'list?input_int=a&input_str=a' expected_data = '''{"exception": 400, "message": "Invalid input: Input data failed validation.", "type": "HTTPError"}''' methodTest('GET', url, accept=return_type, output={'code':400, 'data':expected_data}) def testNoArgMethods(self): """ list1 takes no arguments, it should raise an error if called with one. Require json output. """ return_type = 'application/json' url = self.urlbase + 'list1?int=a' expected_data = """{"exception": 400, "message": "Invalid input: Arguments added where none allowed", "type": "HTTPError"}""" methodTest('GET', url, accept=return_type, output={'code':400, 'data':expected_data}) def testGenerator(self): rf = RESTFormatter(config=self.config.Webtools) url = self.urlbase +'gen' # gen method from DummyRESTModel will return this generator gen = ({'idx':i} for i in range(10)) # the WMCore should convert it into list regardless of accept type data = rf.json(gen) methodTest('GET', url, accept='application/json', output={'code':200, 'data':data}) methodTest('GET', url, accept='*/*', output={'code':200, 'data':data})