blob: 04072ab4ca5f515a6d7985975c38855c36242809 [file] [log] [blame]
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# libcloud.org licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import httplib
from cStringIO import StringIO
from urllib2 import urlparse
from cgi import parse_qs
from libcloud.base import Node, NodeImage, NodeSize, NodeLocation
from libcloud.types import NodeState
import unittest
class multipleresponse(object):
"""
A decorator that allows MockHttp objects to return multi responses
"""
count = 0
func = None
def __init__(self, f):
self.func = f
def __call__(self, *args, **kwargs):
ret = self.func(self.func.__class__, *args, **kwargs)
response = ret[self.count]
self.count = self.count + 1
return response
class MockResponse(object):
"""
A mock HTTPResponse
"""
headers = {}
body = StringIO()
status = 0
reason = ''
version = 11
def __init__(self, status, body, headers=None, reason=None):
self.status = status
self.body = StringIO(body)
self.headers = headers or self.headers
self.reason = reason or self.reason
def read(self, *args, **kwargs):
return self.body.read(*args, **kwargs)
def getheader(self, name, *args, **kwargs):
return self.headers.get(name, *args, **kwargs)
def getheaders(self):
return self.headers.items()
def msg(self):
raise NotImplemented
class MockHttp(object):
"""
A mock HTTP client/server suitable for testing purposes. This replaces
`HTTPConnection` by implementing its API and returning a mock response.
Define methods by request path, replacing slashes (/) with underscores (_).
Each of these mock methods should return a tuple of:
(int status, str body, dict headers, str reason)
>>> mock = MockHttp('localhost', 8080)
>>> mock.request('GET', '/example/')
>>> response = mock.getresponse()
>>> response.body.read()
'Hello World!'
>>> response.status
200
>>> response.getheaders()
[('X-Foo', 'libcloud')]
>>> MockHttp.type = 'fail'
>>> mock.request('GET', '/example/')
>>> response = mock.getresponse()
>>> response.body.read()
'Oh Noes!'
>>> response.status
403
>>> response.getheaders()
[('X-Foo', 'fail')]
"""
responseCls = MockResponse
host = None
port = None
response = None
type = None
use_param = None # will use this param to namespace the request function
def __init__(self, host, port, *args, **kwargs):
self.host = host
self.port = port
def request(self, method, url, body=None, headers=None):
# Find a method we can use for this request
parsed = urlparse.urlparse(url)
scheme, netloc, path, params, query, fragment = parsed
qs = parse_qs(query)
if path.endswith('/'):
path = path[:-1]
meth_name = path.replace('/','_').replace('.', '_').replace('-','_')
if self.type:
meth_name = '%s_%s' % (meth_name, self.type)
if self.use_param:
param = qs[self.use_param][0].replace('.', '_')
meth_name = '%s_%s' % (meth_name, param)
meth = getattr(self, meth_name)
status, body, headers, reason = meth(method, url, body, headers)
self.response = self.responseCls(status, body, headers, reason)
def getresponse(self):
return self.response
def connect(self):
"""
Can't think of anything to mock here.
"""
pass
def close(self):
pass
# Mock request/response example
def _example(self, method, url, body, headers):
"""
Return a simple message and header, regardless of input.
"""
return (httplib.OK, 'Hello World!', {'X-Foo': 'libcloud'},
httplib.responses[httplib.OK])
def _example_fail(self, method, url, body, headers):
return (httplib.FORBIDDEN, 'Oh Noes!', {'X-Foo': 'fail'},
httplib.responses[httplib.FORBIDDEN])
class TestCaseMixin(object):
def test_list_nodes_response(self):
nodes = self.driver.list_nodes()
self.assertTrue(isinstance(nodes, list))
for node in nodes:
self.assertTrue(isinstance(node, Node))
def test_list_sizes_response(self):
sizes = self.driver.list_sizes()
size = sizes[0]
self.assertTrue(isinstance(sizes, list))
# Check that size values are ints or None
self.assertTrue(size.ram is None or isinstance(size.ram, int))
self.assertTrue(size.disk is None or isinstance(size.disk, int))
self.assertTrue(size.bandwidth is None or
isinstance(size.bandwidth, int))
def test_list_images_response(self):
images = self.driver.list_images()
self.assertTrue(isinstance(images, list))
for image in images:
self.assertTrue(isinstance(image, NodeImage))
def test_list_images_response(self):
locations = self.driver.list_locations()
self.assertTrue(isinstance(locations, list))
for dc in locations:
self.assertTrue(isinstance(dc, NodeLocation))
def test_create_node_response(self):
# should return a node object
size = self.driver.list_sizes()[0]
image = self.driver.list_images()[0]
node = self.driver.create_node(name='node-name',
image=image,
size=size)
self.assertTrue(isinstance(node, Node))
def test_destroy_node_response(self):
# should return a node object
node = self.driver.list_nodes()[0]
ret = self.driver.destroy_node(node)
self.assertTrue(isinstance(ret, bool))
def test_reboot_node_response(self):
# should return a node object
node = self.driver.list_nodes()[0]
ret = self.driver.reboot_node(node)
self.assertTrue(isinstance(ret, bool))
if __name__ == "__main__":
import doctest
doctest.testmod()