def testWithoutParameters(self): '''APIGateway.createResponse() should create a response with statusCode 200, the Access-Control-Allow-Origin header and a body with an empty JSON if called without parameters''' event = {} sut = APIGateway(mockLoggerFactory, event) response = sut.createResponse() self.assertEqual(response, {'statusCode': 200, 'headers': {'Access-Control-Allow-Origin': '*'}, 'body': '{}'})
def testRightParameters(self): 'APIGateway.getHeader() should call APIGateway.getParameter() with the right parameters' event = {} sut = APIGateway(mockLoggerFactory, event) sut.getParameter = MagicMock() sut.getHeader('p', 'required', 'validator') sut.getParameter.assert_called_once_with('header', 'headers', 'p', 'required', 'validator')
def testReturnsTrueIfHeaderIsNone(self): 'APIGateway.wasModifiedSince() should return True if the If-Modified-Since header is None' event = {} entity = {'lastModified': datetime.datetime.now()} sut = APIGateway(mockLoggerFactory, event) wasModifiedSince = sut.wasModifiedSince(entity) self.assertTrue(wasModifiedSince)
def testValidateMissing(self): 'APIGateway.getParameter() should call the validator if passed [with missing param]' event = {} sut = APIGateway(mockLoggerFactory, event) validator = MagicMock() sut.getParameter('param', 'basePath', 'p', False, validator) validator.assert_called_once_with(None)
def test(self): 'APIGateway.createLastModifiedHeader() should return the passed date formatted according to RFC 2822' event = {} sut = APIGateway(mockLoggerFactory, event) entity = {'lastModified': datetime.datetime.now()} lastModified = sut.createLastModifiedHeader(entity) expectedHeader = formatDateRFC2822(entity['lastModified']) self.assertEqual(lastModified, {'Last-Modified': expectedHeader})
def test(self): 'APIGateway.createLocationHeader() should return getHttpResource() with the parameter appended' event = {} sut = APIGateway(mockLoggerFactory, event) sut.getHttpResource = MagicMock(return_value='http-resource') uuid = 'uuid' location = sut.createLocationHeader(uuid) self.assertEqual(location, {'Location': 'http-resource/uuid'})
def testMissing(self): 'APIGateway.getAndValidatePrincipal() should raise a 401 HttpError if principalId is missing' event = {} sut = APIGateway(mockLoggerFactory, event) with self.assertRaises(HttpError) as cm: sut.getAndValidatePrincipal() self.assertEqual(cm.exception.statusCode, 401) self.assertEqual(cm.exception.message, 'Missing principal')
def testMissingRequired(self): 'APIGateway.getParameter() should raise a 400 HttpError if the parameter is missing and required' event = {} sut = APIGateway(mockLoggerFactory, event) with self.assertRaises(HttpError) as cm: sut.getParameter('param', 'basePath', 'p', True, None) self.assertEqual(cm.exception.statusCode, 400) self.assertEqual(cm.exception.statusReason, 'Bad request') self.assertEqual(cm.exception.message, 'Missing param "p"')
def testRaises(self): 'APIGateway.wasModifiedSince() should raise if the If-Modified-Since header is not a valid RFC 2822 date' event = {'headers': {'If-Modified-Since': 'hello'}} entity = {'lastModified': datetime.datetime.now()} sut = APIGateway(mockLoggerFactory, event) with self.assertRaises(HttpError) as cm: wasModifiedSince = sut.wasModifiedSince(entity) self.assertEqual(cm.exception.statusCode, 400) self.assertEqual(cm.exception.message, 'Invalid If-Modified-Since header: "hello"') self.assertEqual(cm.exception.causes, ['ValueError(\'Unknown string format\',)'])
def testReturn(self): 'APIGateway.getParameter() should return the parameter' event = { 'basePath': { 'p': 'value' } } sut = APIGateway(mockLoggerFactory, event) param = sut.getParameter('param', 'basePath', 'p', False, None) self.assertEqual(param, 'value')
def testMissing(self): 'APIGateway.getAndValidateEntity() should raise a 400 HttpError if body is missing' event = {} sut = APIGateway(mockLoggerFactory, event) schema = {} name = 'entity' with self.assertRaises(HttpError) as cm: sut.getAndValidateEntity(schema, name) self.assertEqual(cm.exception.statusCode, 400) self.assertEqual(cm.exception.message, 'Missing entity')
def testReturnsFalseIfEqual(self): ''' APIGateway.wasModifiedSince() should return False if the If-Modified-Since header's timestamp is equal to entity.lastModified's timestamp truncated to the second ''' entity = {'lastModified': datetime.datetime.now()} event = {'headers': {'If-Modified-Since': formatDateRFC2822(entity['lastModified'])}} sut = APIGateway(mockLoggerFactory, event) wasModifiedSince = sut.wasModifiedSince(entity) self.assertFalse(wasModifiedSince)
def testValidateFound(self): 'APIGateway.getParameter() should call the validator if passed [with found param]' event = { 'basePath': { 'p': 'value' } } sut = APIGateway(mockLoggerFactory, event) validator = MagicMock() sut.getParameter('param', 'basePath', 'p', False, validator) validator.assert_called_once_with('value')
def testWithAllParameters(self): '''APIGateway.createResponse() should create a response with the passed statusCode, the Access-Control-Allow-Origin header added to the passed headers and the conversiont to json of the passed body as body''' event = {} sut = APIGateway(mockLoggerFactory, event) body = {'this': {'is': {'the': 'body'}}} response = sut.createResponse(123, {'h': 'header'}, body) self.assertEqual(response, { 'statusCode': 123, 'headers': {'Access-Control-Allow-Origin': '*', 'h': 'header'}, 'body': json.dumps(body, default=jsonutils.dumpdefault) })
def testWithHttpError(self): '''APIGateway.createErrorResponse() should create a response with the statusCode of the passed HttpError, the Access-Control-Allow-Origin header and the conversion to json of the __dict__ of the passed HttpError as body''' event = {} sut = APIGateway(mockLoggerFactory, event) error = HttpError(HttpError.NOT_FOUND, 'message') response = sut.createErrorResponse(error) self.assertEqual(response, { 'statusCode': error.statusCode, 'headers': {'Access-Control-Allow-Origin': '*'}, 'body': json.dumps(error.__dict__, default=jsonutils.dumpdefault) })
def testNonStandardHttp(self): 'APIGateway.getHttpResource() should return a standard port http resource' event = { 'headers': { 'X-Forwarded-Port': '8080', 'X-Forwarded-Proto': 'http', 'Host': 'localhost' }, 'path': '/path' } sut = APIGateway(mockLoggerFactory, event) httpResource = sut.getHttpResource() self.assertEqual(httpResource, 'http://localhost:8080/path')
def testReturnsTrueIfLess(self): ''' APIGateway.wasModifiedSince() should return True if the If-Modified-Since header's timestamp is less than entity.lastModified's timestamp truncated to the second ''' entity = {'lastModified': datetime.datetime.now()} event = { 'headers': { 'If-Modified-Since': formatDateRFC2822(entity['lastModified'] - datetime.timedelta(hours=1)) } } sut = APIGateway(mockLoggerFactory, event) wasModifiedSince = sut.wasModifiedSince(entity) self.assertTrue(wasModifiedSince)
def testNotJSON(self): 'APIGateway.getAndValidateEntity() should raise a 415 HttpError if Content-Type is not `application/json`' event = { 'headers': { 'Content-Type': 'application/xml' } } sut = APIGateway(mockLoggerFactory, event) schema = {} name = 'entity' with self.assertRaises(HttpError) as cm: sut.getAndValidateEntity(schema, name) self.assertEqual(cm.exception.statusCode, 415) self.assertEqual(cm.exception.message, 'Expected application/json Content-Type')
def testWithNspError(self): '''APIGateway.createErrorResponse() should wrap the passed NspError in an HttpError and then create an error response with it''' event = {} sut = APIGateway(mockLoggerFactory, event) error = NspError(NspError.THING_NOT_FOUND, 'message') httpError = HttpError.wrap(error) httpError.method = sut.getHttpMethod() httpError.resource = sut.getHttpResource() response = sut.createErrorResponse(error) self.assertEqual(response, { 'statusCode': httpError.statusCode, 'headers': {'Access-Control-Allow-Origin': '*'}, 'body': json.dumps(httpError.__dict__, default=jsonutils.dumpdefault) })
def testContextPath(self): 'APIGateway.getHttpResource() should insert a context path if the host is an API Gateway API' event = { 'headers': { 'X-Forwarded-Port': '443', 'X-Forwarded-Proto': 'https', 'Host': '12345678.execute-api.eu-west-1.amazonaws.com' }, 'path': '/path', 'requestContext': { 'stage': 'stage' } } sut = APIGateway(mockLoggerFactory, event) httpResource = sut.getHttpResource() self.assertEqual(httpResource, 'https://12345678.execute-api.eu-west-1.amazonaws.com/stage/path')
def testOK(self): 'APIGateway.getAndValidatePrincipal() should return the principal as a Principal instance' p = {'organizationId': 'id', 'roles': []} event = { 'requestContext': { 'authorizer': { 'principalId': json.dumps(p) } } } sut = APIGateway(mockLoggerFactory, event) principal = sut.getAndValidatePrincipal() # Non si possono confrontare i Principal se non si scrive il loro operatore, e per i test non ne vale la pena self.assertIsInstance(principal, Principal) self.assertEqual(principal.organizationId, p['organizationId']) self.assertEqual(principal.roles, set(p['roles']))
def testOK(self): 'APIGateway.getAndValidateEntity() should return the parsed entity as a dictionary with the datetimes converted' e = { 'name': 'entity', 'created': '2013-01-31T03:45:00.000Z' } expected = e.copy() expected['created'] = jsonutils.json2datetime(e['created']) event = { 'body': json.dumps(e, default=jsonutils.dumpdefault) } sut = APIGateway(mockLoggerFactory, event) schema = {'type': 'object'} name = 'entity' entity = sut.getAndValidateEntity(schema, name) self.assertEqual(entity, expected)
def testInvalid(self): 'APIGateway.getParameter() should throw a 400 HttpError with the error of the validator as cause' event = { 'basePath': { 'p': 'value' } } sut = APIGateway(mockLoggerFactory, event) validator = MagicMock() validator.side_effect = Exception('error message') with self.assertRaises(HttpError) as cm: sut.getParameter('param', 'basePath', 'p', False, validator) validator.assert_called_once_with('value') self.assertEqual(cm.exception.statusCode, 400) self.assertEqual(cm.exception.message, 'Invalid param "p"') self.assertEqual(cm.exception.causes, ['error message'])
def testInvalidJSON(self): 'APIGateway.getAndValidateEntity() should raise a 422 HttpError if body is not a valid JSON' event = { 'body': '[1]' } sut = APIGateway(mockLoggerFactory, event) schema = {'type': 'object'} name = 'entity' with self.assertRaises(HttpError) as cm: sut.getAndValidateEntity(schema, name) self.assertEqual(cm.exception.statusCode, 422) self.assertEqual(cm.exception.message, 'Invalid entity') self.assertIsInstance(cm.exception.causes, list) self.assertGreater(len(cm.exception.causes), 0) for i in range(len(cm.exception.causes)): with self.subTest(i=i): self.assertIsInstance(cm.exception.causes[i], str)
def testMalformedJSON(self): 'APIGateway.getAndValidateEntity() should raise a 400 HttpError if body is not a JSON string' event = { 'body': 'hello' } sut = APIGateway(mockLoggerFactory, event) schema = {} name = 'entity' with self.assertRaises(HttpError) as cm: sut.getAndValidateEntity(schema, name) self.assertEqual(cm.exception.statusCode, 400) self.assertEqual(cm.exception.message, 'Malformed entity JSON') self.assertIsInstance(cm.exception.causes, list) self.assertGreater(len(cm.exception.causes), 0) for i in range(len(cm.exception.causes)): with self.subTest(i=i): self.assertIsInstance(cm.exception.causes[i], str)
def testWithException(self): '''APIGateway.createErrorResponse() should wrap the passed Exception in an HttpError and the create an error response with it''' event = {} sut = APIGateway(mockLoggerFactory, event) error = KeyError('unknown') httpError = HttpError.wrap(error) httpError.method = sut.getHttpMethod() httpError.resource = sut.getHttpResource() response = sut.createErrorResponse(error) self.assertEqual(response['statusCode'], httpError.statusCode) self.assertEqual(response['headers'], {'Access-Control-Allow-Origin': '*'}) body = json.loads(response['body']) self.assertEqual(body['message'], httpError.message) self.assertEqual(body['method'], httpError.method) self.assertEqual(body['resource'], httpError.resource) self.assertEqual(body['causes'], [])
def testInvalidJSON(self): 'APIGateway.getAndValidatePrincipal() should raise a 401 HttpError if principalId is not a valid JSON' event = { 'requestContext': { 'authorizer': { 'principalId': '{}' } } } sut = APIGateway(mockLoggerFactory, event) with self.assertRaises(HttpError) as cm: sut.getAndValidatePrincipal() self.assertEqual(cm.exception.statusCode, 401) self.assertEqual(cm.exception.message, 'Invalid principal') self.assertIsInstance(cm.exception.causes, list) self.assertGreater(len(cm.exception.causes), 0) for i in range(len(cm.exception.causes)): with self.subTest(i=i): self.assertIsInstance(cm.exception.causes[i], str)
def testInvalidJSONSchema(self): ''' APIGateway.getAndValidateEntity() should raise an INTERNAL_SERVER_ERROR NspError if passed schema is not a valid JSON schema ''' event = { 'body': '[1]' } sut = APIGateway(mockLoggerFactory, event) schema = {'type': 'objective'} name = 'entity' with self.assertRaises(NspError) as cm: sut.getAndValidateEntity(schema, name) self.assertEqual(cm.exception.code, 'INTERNAL_SERVER_ERROR') self.assertEqual(cm.exception.message, 'Invalid entity JSON schema') self.assertIsInstance(cm.exception.causes, list) self.assertGreater(len(cm.exception.causes), 0) for i in range(len(cm.exception.causes)): with self.subTest(i=i): self.assertIsInstance(cm.exception.causes[i], str)
def testMissing(self): 'APIGateway.getHttpMethod() should return `UNKNOWN_METHOD` if no method is found in the event' event = {} sut = APIGateway(mockLoggerFactory, event) httpMethod = sut.getHttpMethod() self.assertEqual(httpMethod, 'UNKNOWN_METHOD')
def testFound(self): 'APIGateway.getHttpMethod() should return the method found in the event' event = {'httpMethod': 'method'} sut = APIGateway(mockLoggerFactory, event) httpMethod = sut.getHttpMethod() self.assertEqual(httpMethod, 'method')