Merge branch 'master' of github.com:NOPping/gstack
diff --git a/.gitignore b/.gitignore
index 5a2be01..c5815a1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,6 +2,7 @@
 
 *.py[cod]
 *~
+*.db
 
 # C extensions
 *.so
@@ -26,6 +27,7 @@
 
 # Unit test / coverage reports
 .coverage
+cover
 .tox
 nosetests.xml
 
diff --git a/.travis.yml b/.travis.yml
index d874e9d..d799879 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -4,9 +4,11 @@
 install:
   - pip install pylint
   - pip install pep8
+  - pip install coveralls
   - pip install -e . --use-mirrors
 script:
   - pep8 --ignore=E501 *.py gstack
   - pylint --rcfile=pylint.rc *.py gstack
-
-# Force travis-ci build
+  - nosetests --with-coverage --cover-erase --cover-package=gstack
+after_success:
+  - coveralls
diff --git a/README.rst b/README.rst
index af6f280..a9190a5 100644
--- a/README.rst
+++ b/README.rst
@@ -134,6 +134,43 @@
 Sandbox-simulator     UP       None scheduled   
 ==================   ========  ====================
 
+Running The Tests
+##################
+
+To run the included tests the following software is required:
+
+   pep8
+   
+   pylint
+   
+   nose
+   
+   mock
+   
+   coverage
+
+These can be installed via the Python Package Index:
+
+   pip install pep8 pylint nose mock coverage
+
+Tests can be executed from the root of the code base as follows:
+
+Style Check
+___________
+
+   pep8 --ignore=E501 *.py gstack
+
+Lint
+____
+
+   pylint --rcfile=pylint.rc *.py gstack
+
+Unit Tests
+___________
+
+   nosetests --with-coverage  --cover-erase --cover-package=gstack --cover-html
+
+A HTML base coverage report will be placed in ./cover
 
 Trouble shooting
 #################
diff --git a/gstack/__init__.py b/gstack/__init__.py
index 2627d2b..acc141d 100644
--- a/gstack/__init__.py
+++ b/gstack/__init__.py
@@ -36,10 +36,15 @@
     return config_file
 
 
-def configure_app():
-    config_file = _load_config_file()
-    app.config.from_pyfile(config_file)
+def configure_app(settings=None):
     app.config['DATA'] = os.path.abspath(os.path.dirname(__file__)) + '/data'
+
+    if settings:
+        app.config.from_object(settings)
+    else:
+        config_file = _load_config_file()
+        app.config.from_pyfile(config_file)
+
     app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///' + \
         os.path.join(app.config['DATA'], 'app.db')
 
diff --git a/gstack/controllers/disks.py b/gstack/controllers/disks.py
index 4c8d8a4..0743a48 100644
--- a/gstack/controllers/disks.py
+++ b/gstack/controllers/disks.py
@@ -204,6 +204,7 @@
         func_route = url_for(
             'getdisk',
             projectid=projectid,
+            zone=zone,
             disk=disk
         )
         return errors.resource_not_found(func_route)
diff --git a/gstack/controllers/firewalls.py b/gstack/controllers/firewalls.py
index ce162e4..34fb736 100755
--- a/gstack/controllers/firewalls.py
+++ b/gstack/controllers/firewalls.py
@@ -26,38 +26,39 @@
 
 
 def _cloudstack_securitygroup_to_gce(response_item):
-    rules = response_item['ingressrule']
-    allowed = []
-    sourceranges = []
-    for rule in rules:
-        ports = []
-        for i in range(rule['startport'], rule['endport'] + 1):
-            ports.append(str(i))
-        allowed.append({
-            "IPProtocol": rule['protocol'],
-            "ports": ports
+    if 'ingressrule' in response_item:
+        rules = response_item['ingressrule']
+        allowed = []
+        sourceranges = []
+        for rule in rules:
+            ports = []
+            if 'startport' in rule:
+                for i in range(rule['startport'], rule['endport'] + 1):
+                    ports.append(str(i))
+            allowed.append({
+                "IPProtocol": rule['protocol'],
+                "ports": ports
+            })
+            if 'cidr' in rule.keys():
+                sourceranges.append(rule['cidr'])
+        return ({
+            "kind": "compute#firewall",
+            "selfLink": '',
+            "id": response_item['id'],
+            "creationTimestamp": '',
+            "name": response_item['name'],
+            "description": response_item['description'],
+            "network": '',
+            "sourceRanges": sourceranges,
+            "sourceTags": [
+                ''
+            ],
+            "targetTags": response_item['tags'],
+            "allowed": allowed
         })
-        if 'cidr' in rule.keys():
-            sourceranges.append(rule['cidr'])
-    return ({
-        "kind": "compute#firewall",
-        "selfLink": '',
-        "id": response_item['id'],
-        "creationTimestamp": '',
-        "name": response_item['name'],
-        "description": response_item['description'],
-        "network": '',
-        "sourceRanges": sourceranges,
-        "sourceTags": [
-            ''
-        ],
-        "targetTags": response_item['tags'],
-        "allowed": allowed
-    })
 
 
-@app.route('/compute/v1/projects/<projectid>/global/firewalls',
-           methods=['GET'])
+@app.route('/compute/v1/projects/<projectid>/global/firewalls', methods=['GET'])
 @authentication.required
 def listsecuritygroups(projectid, authorization):
     command = 'listSecurityGroups'
@@ -121,7 +122,7 @@
         json.dumps(cloudstack_response, indent=4, separators=(',', ': '))
     )
 
-    if cloudstack_response['listsecuritygroupsresponse']:
+    if cloudstack_response['listsecuritygroupsresponse']['securitygroup']:
         response_item = cloudstack_response[
             'listsecuritygroupsresponse']['securitygroup'][0]
         firewall = _cloudstack_securitygroup_to_gce(response_item)
diff --git a/gstack/controllers/index.py b/gstack/controllers/index.py
index 6d76357..293c6ac 100755
--- a/gstack/controllers/index.py
+++ b/gstack/controllers/index.py
@@ -28,8 +28,7 @@
     with open(app.config['DATA'] + '/v1.json') as template:
         discovery_template = json.loads(template.read())
 
-    discovery_template[
-        'baseUrl'] = helper.get_root_url() + '/' + app.config['PATH']
+    discovery_template['baseUrl'] = helper.get_root_url() + '/' + app.config['PATH']
     discovery_template['basePath'] = '/' + app.config['PATH']
     discovery_template['rootUrl'] = helper.get_root_url() + '/'
     discovery_template['servicePath'] = app.config['PATH']
diff --git a/gstack/controllers/instances.py b/gstack/controllers/instances.py
index f2bee6d..032ba0e 100755
--- a/gstack/controllers/instances.py
+++ b/gstack/controllers/instances.py
@@ -118,17 +118,17 @@
     response['disks'] = []
 
     networking = {}
-    if cloudstack_response['securitygroup']:
+    accessconfig = {}
+    if 'securitygroup' in cloudstack_response:
         networking['network'] = cloudstack_response['securitygroup'][0]['name']
         networking['networkIP'] = cloudstack_response['nic'][0]['ipaddress']
         networking['name'] = cloudstack_response['nic'][0]['id']
+        accessconfig['natIP'] = cloudstack_response['nic'][0]['ipaddress']
         networking['accessConfigs'] = []
 
-    accessconfig = {}
     accessconfig['kind'] = 'compute#accessConfig'
     accessconfig['type'] = 'ONE_TO_ONE_NAT'
     accessconfig['name'] = 'External NAT'
-    accessconfig['natIP'] = cloudstack_response['nic'][0]['ipaddress']
 
     networking['accessConfigs'] = accessconfig
 
@@ -155,8 +155,7 @@
 
     if virtual_machine_list['listvirtualmachinesresponse']:
         response = helper.filter_by_name(
-            data=virtual_machine_list[
-                'listvirtualmachinesresponse']['virtualmachine'],
+            data=virtual_machine_list['listvirtualmachinesresponse']['virtualmachine'],
             name=instance
         )
         return response
@@ -282,6 +281,7 @@
         function_route = url_for(
             'getinstance',
             projectid=projectid,
+            zone=zone,
             instance=instance)
         return errors.resource_not_found(function_route)
 
diff --git a/gstack/controllers/machine_type.py b/gstack/controllers/machine_type.py
index f2bdd26..f55e211 100755
--- a/gstack/controllers/machine_type.py
+++ b/gstack/controllers/machine_type.py
@@ -126,7 +126,7 @@
             authorization=authorization,
             args={'keyword': machinetype}
         )
-        if machinetype_list['listvolumesresponse']:
+        if machinetype_list['listserviceofferingsresponse']:
             machinetype = helper.filter_by_name(
                 data=machinetype_list['listserviceofferingsresponse'][
                     'serviceoffering'],
diff --git a/gstack/controllers/networks.py b/gstack/controllers/networks.py
index df69812..7fba7ab 100644
--- a/gstack/controllers/networks.py
+++ b/gstack/controllers/networks.py
@@ -49,8 +49,7 @@
 
     if securitygroup_list['listsecuritygroupsresponse']:
         response = helper.filter_by_name(
-            data=securitygroup_list[
-                'listsecuritygroupsresponse']['securitygroup'],
+            data=securitygroup_list['listsecuritygroupsresponse']['securitygroup'],
             name=securitygroup
         )
         return response
@@ -62,6 +61,7 @@
     command = 'createSecurityGroup'
     if not args:
         args = {}
+
     cloudstack_response = requester.make_request(
         command,
         args,
@@ -123,8 +123,7 @@
     return populated_response
 
 
-@app.route(
-    '/compute/v1/projects/<projectid>/global/networks', methods=['GET'])
+@app.route('/compute/v1/projects/<projectid>/global/networks', methods=['GET'])
 @authentication.required
 def listnetworks(projectid, authorization):
     securitygroup_list = _get_networks(
@@ -185,7 +184,7 @@
             'error': {
                 'errors': [{
                     'code': 'RESOURCE_ALREADY_EXISTS',
-                    'message': 'the resource \'projects/\'' + projectid + '/global/networks/' + args['name']
+                    'message': 'The resource \'projects/\'' + projectid + '/global/networks/' + args['name']
                 }]
             }
         }
diff --git a/gstack/controllers/operations.py b/gstack/controllers/operations.py
index e8c1d1e..faa7ad6 100644
--- a/gstack/controllers/operations.py
+++ b/gstack/controllers/operations.py
@@ -51,12 +51,10 @@
     }
 
     if async_result['jobstatus'] is 0:
-        # handle pending case
         populated_response['targetLink'] = ''
         populated_response['status'] = 'PENDING'
         populated_response['progress'] = 0
     elif async_result['jobstatus'] is 1:
-        # handle successful case
         populated_response['status'] = 'DONE'
         populated_response['zone'] = urllib.unquote_plus(
             helper.get_root_url() +
@@ -71,9 +69,7 @@
                 'getinstance',
                 projectid=projectid,
                 zone=async_result['jobresult']['virtualmachine']['zonename'],
-                instance=async_result['jobresult']['virtualmachine']['displayname']))
-
-    # need to add a case here for error handling, its job status 2
+                instance=async_result['jobresult']['virtualmachine']['name']))
 
     return populated_response
 
diff --git a/gstack/controllers/project.py b/gstack/controllers/project.py
index b18eeba..d6e0776 100755
--- a/gstack/controllers/project.py
+++ b/gstack/controllers/project.py
@@ -156,7 +156,7 @@
     return response
 
 
-@app.route('//compute/v1/projects/<projectid>', methods=['GET'])
+@app.route('/compute/v1/projects/<projectid>', methods=['GET'])
 @authentication.required
 def getproject(authorization, projectid):
     project = _get_account_by_name(authorization, projectid)
@@ -178,7 +178,7 @@
     return res
 
 
-@app.route('//compute/v1/projects/<projectid>/setCommonInstanceMetadata', methods=['POST'])
+@app.route('/compute/v1/projects/<projectid>/setCommonInstanceMetadata', methods=['POST'])
 @authentication.required
 def setglobalmetadata(projectid, authorization):
     data = json.loads(request.data)
diff --git a/gstack/controllers/regions.py b/gstack/controllers/regions.py
index a25bdc1..50f3e5d 100755
--- a/gstack/controllers/regions.py
+++ b/gstack/controllers/regions.py
@@ -76,10 +76,12 @@
         args={'name': region}
     )
 
-    if cloudstack_response['listregionsresponse']:
-        cloudstack_response = _cloudstack_region_to_gce(
-            cloudstack_response['listregionsresponse']['region'][0])
-        return helper.create_response(data=cloudstack_response)
-
-    function_route = url_for('getimage', projectid=projectid, image=region)
-    return errors.resource_not_found(function_route)
+    if region == cloudstack_response['listregionsresponse']['region'][0]['name']:
+        return helper.create_response(
+            data=_cloudstack_region_to_gce(
+                cloudstack_response['listregionsresponse']['region'][0]
+            )
+        )
+    else:
+        function_route = url_for('getregion', projectid=projectid, region=region)
+        return errors.resource_not_found(function_route)
diff --git a/gstack/controllers/zones.py b/gstack/controllers/zones.py
index 9ff90bd..8e433f3 100755
--- a/gstack/controllers/zones.py
+++ b/gstack/controllers/zones.py
@@ -58,6 +58,8 @@
 def get_zone_names(authorization):
     zone_list = _get_zones(authorization)
 
+    print zone_list
+
     zones = []
     if zone_list['listzonesresponse']:
         for zone in zone_list['listzonesresponse']['zone']:
diff --git a/gstack/helpers.py b/gstack/helpers.py
new file mode 100644
index 0000000..d8dddd9
--- /dev/null
+++ b/gstack/helpers.py
@@ -0,0 +1,31 @@
+#!/usr/bin/env python
+# encoding: utf-8
+# 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.  The ASF 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 os
+
+
+def read_file(name):
+    filepath = os.path.join(
+        os.path.dirname(os.path.realpath(__file__)),
+        '../',
+        name
+    )
+
+    data = open(filepath)
+    return data.read()
diff --git a/tests/__init__.py b/tests/__init__.py
new file mode 100644
index 0000000..9ce950c
--- /dev/null
+++ b/tests/__init__.py
@@ -0,0 +1,59 @@
+#!/usr/bin/env python
+# encoding: utf-8
+
+from unittest import TestCase
+
+import mock
+
+import json
+
+from gstack import app, configure_app
+from gstack.helpers import read_file
+from . import settings
+from .utils import FlaskTestCaseMixin
+
+class GStackTestCase(TestCase):
+    pass
+
+class GStackAppTestCase(FlaskTestCaseMixin, GStackTestCase):
+
+    access_token = ""
+
+    def _configure_app(self):
+        configure_app(settings=settings)
+
+    def _auth_example_user(self):
+        data = {}
+        data['code'] = 'hjrZryvgLYo3R833NkHHV8jYmxQhsD8TjKWzOm2f'
+        data['grant_type'] = 'authorization_code'
+        data['client_id'] = 'ExampleAPIKey'
+        data['client_secret'] = 'eXmaPlEm8XQwezvLOd10Qt3wXH7j9mRgaKbEg3nRDnj7FtlF3yx54EWd9mR5sB1ec5LQDV6gjpy6sfDo6ndUeeww'
+        data['redirect_uri'] = 'http://localhost:8000'
+
+        get = mock.Mock()
+        get.return_value.text = read_file('tests/data/list_capabilities.json')
+        get.return_value.status_code = 200
+
+        with mock.patch('requests.get', get):
+            self.get('/oauth2/auth?scope=example&redirect_uri=http://127.0.0.1:9999&response_type=code&client_id=ExampleAPIKey&access_type=offline')
+            response = self.post('/oauth2/token', data=data)
+
+        GStackAppTestCase.access_token = json.loads(response.data)['access_token']
+
+        self.assert_ok(response)
+
+
+
+    def setUp(self):
+        super(GStackTestCase, self).setUp()
+        self._configure_app()
+        self.app = app
+        self.client = self.app.test_client()
+        self._auth_example_user()
+        self.app_context = self.app.app_context()
+        self.app_context.push()
+
+    def tearDown(self):
+        super(GStackTestCase, self).tearDown()
+        self.app_context.pop()
+
diff --git a/tests/data/authorize_security_group_ingress.json b/tests/data/authorize_security_group_ingress.json
new file mode 100644
index 0000000..5b20e71
--- /dev/null
+++ b/tests/data/authorize_security_group_ingress.json
@@ -0,0 +1,32 @@
+{ "queryasyncjobresultresponse": {
+    "accountid": "2edb0c28-95e4-11e3-b2e4-d19c9d3e5e1d",
+    "userid": "2edb33ec-95e4-11e3-b2e4-d19c9d3e5e1d",
+    "cmd": "org.apache.cloudstack.api.command.user.securitygroup.AuthorizeSecurityGroupIngressCmd",
+    "jobstatus": 1,
+    "jobprocstatus": 0,
+    "jobresultcode": 0,
+    "jobresulttype": "object",
+    "jobresult": {
+        "securitygroup": {
+            "id": "01104a6a-bd3c-4804-86fd-1ca6231f128d",
+            "name": "test5",
+            "description": "test5",
+            "account": "test",
+            "domainid": "2edae3e4-95e4-11e3-b2e4-d19c9d3e5e1d",
+            "domain": "ROOT",
+            "ingressrule": [
+                {
+                    "ruleid": "739f53a3-ce50-4790-a86d-1051f77dd3d2",
+                    "protocol": "tcp",
+                    "startport": 1000,
+                    "endport": 1024,
+                    "cidr": "0.0.0.0/0"
+                }
+            ],
+            "egressrule": [],
+            "tags": []
+        }
+    },
+    "created": "2014-02-26T23:05:55+0000",
+    "jobid": "ed5d7cc6-776a-4fda-b4c4-b71421b56c82"
+} }
diff --git a/tests/data/create_volume_response.json b/tests/data/create_volume_response.json
new file mode 100644
index 0000000..79826ba
--- /dev/null
+++ b/tests/data/create_volume_response.json
@@ -0,0 +1,38 @@
+{
+    "queryasyncjobresultresponse": {
+        "jobprocstatus": 0,
+        "created": "2014-02-25T19:46:35+0000",
+        "cmd": "org.apache.cloudstack.api.command.user.volume.CreateVolumeCmd",
+        "userid": "2edb33ec-95e4-11e3-b2e4-d19c9d3e5e1d",
+        "jobstatus": 1,
+        "jobid": "1f32404b-d067-43e8-8835-4cc1c55d737a",
+        "jobresultcode": 0,
+        "jobresulttype": "object",
+        "jobresult": {
+            "volume": {
+                "size": 10737418240,
+                "diskofferingname": "Custom",
+                "account": "admin",
+                "domainid": "2edae3e4-95e4-11e3-b2e4-d19c9d3e5e1d",
+                "name": "f0e2a699-1196-49bd-90cd-af809f4c32ea",
+                "created": "2014-02-25T19:46:34+0000",
+                "storagetype": "shared",
+                "type": "DATADISK",
+                "tags": [],
+                "domain": "ROOT",
+                "jobstatus": 0,
+                "jobid": "1f32404b-d067-43e8-8835-4cc1c55d737a",
+                "destroyed": false,
+                "state": "Allocated",
+                "diskofferingdisplaytext": "Custom Disk",
+                "isextractable": true,
+                "displayvolume": true,
+                "zoneid": "1e47a2fc-61c7-401c-b90e-416b472ada64",
+                "diskofferingid": "46f0a62e-ba3c-4a49-b619-877a8b2a07ed",
+                "id": "8552cdce-879a-42f3-bdb5-2e7f55a38996",
+                "zonename": "Sandbox-simulator"
+            }
+        },
+        "accountid": "2edb0c28-95e4-11e3-b2e4-d19c9d3e5e1d"
+    }
+}
diff --git a/tests/data/delete_keypair.json b/tests/data/delete_keypair.json
new file mode 100644
index 0000000..7c3d5a3
--- /dev/null
+++ b/tests/data/delete_keypair.json
@@ -0,0 +1,3 @@
+{ "deletesshkeypairresponse": {
+    "success": "true"
+}  }
diff --git a/tests/data/empty_describe_disk_offerings.json b/tests/data/empty_describe_disk_offerings.json
new file mode 100644
index 0000000..27d411d
--- /dev/null
+++ b/tests/data/empty_describe_disk_offerings.json
@@ -0,0 +1,3 @@
+{
+    "listdiskofferingsresponse": {}
+}
diff --git a/tests/data/empty_describe_images.json b/tests/data/empty_describe_images.json
new file mode 100644
index 0000000..c09958e
--- /dev/null
+++ b/tests/data/empty_describe_images.json
@@ -0,0 +1,5 @@
+{
+    "listtemplatesresponse": {
+        "template": {}
+    }
+}
diff --git a/tests/data/empty_describe_instances.json b/tests/data/empty_describe_instances.json
new file mode 100644
index 0000000..10f969e
--- /dev/null
+++ b/tests/data/empty_describe_instances.json
@@ -0,0 +1,5 @@
+{
+    "listvirtualmachinesresponse": {
+        "virtualmachine": {}
+    }
+}
diff --git a/tests/data/empty_describe_key_pairs.json b/tests/data/empty_describe_key_pairs.json
new file mode 100644
index 0000000..6c70975
--- /dev/null
+++ b/tests/data/empty_describe_key_pairs.json
@@ -0,0 +1,5 @@
+{
+    "listsshkeypairsresponse": {
+        "sshkeypair": {}
+    }
+}
diff --git a/tests/data/empty_describe_security_groups.json b/tests/data/empty_describe_security_groups.json
new file mode 100644
index 0000000..6fbea9f
--- /dev/null
+++ b/tests/data/empty_describe_security_groups.json
@@ -0,0 +1,5 @@
+{
+    "listsecuritygroupsresponse": {
+        "securitygroup": {}
+    }
+}
diff --git a/tests/data/empty_describe_volumes.json b/tests/data/empty_describe_volumes.json
new file mode 100644
index 0000000..2cd5f48
--- /dev/null
+++ b/tests/data/empty_describe_volumes.json
@@ -0,0 +1,5 @@
+{
+    "listvolumesresponse": {
+        "volume": {}
+    }
+}
diff --git a/tests/data/empty_describe_zone.json b/tests/data/empty_describe_zone.json
new file mode 100644
index 0000000..c575b3d
--- /dev/null
+++ b/tests/data/empty_describe_zone.json
@@ -0,0 +1,5 @@
+{
+    "listzonesresponse": {
+        "zone": {}
+    }
+}
diff --git a/tests/data/invalid_image_id.json b/tests/data/invalid_image_id.json
new file mode 100644
index 0000000..0ca8f90
--- /dev/null
+++ b/tests/data/invalid_image_id.json
@@ -0,0 +1,8 @@
+{
+    "listtemplatesresponse": {
+        "errorcode": 431,
+        "uuidlist": [],
+        "cserrorcode": 4350,
+        "errortext": "Please specify a valid template ID."
+    }
+}
diff --git a/tests/data/list_capabilities.json b/tests/data/list_capabilities.json
new file mode 100644
index 0000000..4818292
--- /dev/null
+++ b/tests/data/list_capabilities.json
@@ -0,0 +1,15 @@
+{
+    "listcapabilitiesresponse": {
+        "capability": {
+            "customdiskofferingmaxsize": 1024,
+            "cloudstackversion": "4.3.0",
+            "projectinviterequired": false,
+            "securitygroupsenabled": true,
+            "regionsecondaryenabled": false,
+            "userpublictemplateenabled": false,
+            "supportELB": "false",
+            "allowusercreateprojects": false,
+            "kvmsnapshotenabled": true
+        }
+    }
+}
diff --git a/tests/data/list_zone_by_name_response.json b/tests/data/list_zone_by_name_response.json
new file mode 100644
index 0000000..8fb517c
--- /dev/null
+++ b/tests/data/list_zone_by_name_response.json
@@ -0,0 +1,19 @@
+{
+    "listzonesresponse": {
+        "count": 1,
+        "zone": [
+            {
+                "localstorageenabled": false,
+                "name": "Sandbox-simulator",
+                "zonetoken": "038bf1a4-c55c-3d1e-9176-b42e22ee00c1",
+                "dns1": "8.8.8.8",
+                "securitygroupsenabled": true,
+                "allocationstate": "Enabled",
+                "internaldns1": "8.8.8.8",
+                "dhcpprovider": "VirtualRouter",
+                "networktype": "Basic",
+                "id": "1e47a2fc-61c7-401c-b90e-416b472ada64"
+            }
+        ]
+    }
+}
diff --git a/tests/data/valid_create_keypair.json b/tests/data/valid_create_keypair.json
new file mode 100644
index 0000000..f5383fb
--- /dev/null
+++ b/tests/data/valid_create_keypair.json
@@ -0,0 +1,7 @@
+{ "createsshkeypairresponse": {
+    "keypair": {
+        "name": "Test",
+        "fingerprint": "f1:85:d8:d6:54:3c:3e:41:49:52:82:1b:a6:4e:7e:31",
+        "privateKey": "-----BEGIN RSA PRIVATE KEY-----\nMIICWwIBAAKBgQCLFWb+Y+zxkDMSTmLu7XzOIFccIe6/TWG0BLA53xOPyoRTM4j0\n1cGF2m4VSQ8QLOXztqO22O+zOU5eOtps5kZKk0IHpVGiTeNEfovogVnQx4A3qcC+\nwKHFvjidlT6Cdy8av6erBS18W2NEtkipPEN1Kyirf5EDCz8gMcqRFxvIIQIDAQAB\nAoGAK8Dy4qp62s97UZH5S6LIdWv1G3ONUP898kzbR4lm9QBHuojm1+b692ns4aNX\nKsaFHLNjM11xotcvUTOAjWuvxsDaiE8wAxJI8Zv40QjJ3YO9zbrsbYoysXKJCVLx\nEDB+HWaAb6gRprffuxu50py54RwTyXmhsuuIFcpKn07p32UCQQD42dCvq9p2Qu4Y\nLuVn88kukc7wE3u/phLBaGGxAOEYvXH19rByKgzNYcKeZkrOrqbFEd7YiH/rsJz5\nuY1Jh/OPAkEAjxRMiy5G3SbToC8IOEsLPY0USyOmx1Domd6HEGDOT7OIUoP9gsSr\nuH23C73IzBFstxxhU+MkjZIY2dEY8ULxTwJAbNUl9Y5dTtdatezcm6f81ociT9DV\nkC2bikaSYw0VZPKFgqLO7D8DtlcI/KmUEexEN2/nXB/mgjeNj5Hc/smcdQJATEfK\nNznI1gbpNLFedIStzXb1psmvFPxxxfb5kyXJWHyi5TsxYRJxar67ZCsebo2rpEQh\nL5Qd3MxTK21rGtVRyQJAM4HcYNW/VbH0SrhbtkLoHabjPkb3H0g05THq1ZTohWa0\n1UckpzFX46gc2as+ooHGH0KsA7prUqI9AOmAwc28yw==\n-----END RSA PRIVATE KEY-----\n"
+    }
+}  }
diff --git a/tests/data/valid_create_security_group.json b/tests/data/valid_create_security_group.json
new file mode 100644
index 0000000..aea931f
--- /dev/null
+++ b/tests/data/valid_create_security_group.json
@@ -0,0 +1,12 @@
+{
+    "createsecuritygroupresponse": {
+        "securitygroup": {
+            "account": "account",
+            "domainid": "66d69e46-a95b-437b-ac6c-bcaa5331999d",
+            "name": "securitygroupname",
+            "domain": "domain",
+            "id": "9684da98-3f7c-4a0b-b968-3ad143f7650d",
+            "description": "security group description"
+        }
+    }
+}
diff --git a/tests/data/valid_delete_security_group.json b/tests/data/valid_delete_security_group.json
new file mode 100644
index 0000000..8617175
--- /dev/null
+++ b/tests/data/valid_delete_security_group.json
@@ -0,0 +1,5 @@
+{
+    "deletesecuritygroupresponse": {
+        "success": "true"
+    }
+}
diff --git a/tests/data/valid_delete_volume.json b/tests/data/valid_delete_volume.json
new file mode 100644
index 0000000..ff38c40
--- /dev/null
+++ b/tests/data/valid_delete_volume.json
@@ -0,0 +1,5 @@
+{
+    "deletevolumeresponse": {
+        "success": "true"
+    }
+}
\ No newline at end of file
diff --git a/tests/data/valid_describe_image.json b/tests/data/valid_describe_image.json
new file mode 100644
index 0000000..a3ef244
--- /dev/null
+++ b/tests/data/valid_describe_image.json
@@ -0,0 +1,35 @@
+{
+    "listtemplatesresponse": {
+        "count": 1,
+        "template": [
+            {
+                "domain": "ROOT",
+                "domainid": "2edae3e4-95e4-11e3-b2e4-d19c9d3e5e1d",
+                "ostypename": "CentOS 5.3 (32-bit)",
+                "zoneid": "1e47a2fc-61c7-401c-b90e-416b472ada64",
+                "displaytext": "CentOS 5.3(64-bit) no GUI (Simulator)",
+                "ostypeid": "2e678976-95e4-11e3-b2e4-d19c9d3e5e1d",
+                "passwordenabled": false,
+                "id": "a32d70ee-95e4-11e3-b2e4-d19c9d3e5e1d",
+                "size": 2147483648,
+                "isready": true,
+                "format": "VHD",
+                "templatetype": "BUILTIN",
+                "crosszones": true,
+                "zonename": "Sandbox-simulator",
+                "status": "Download Complete",
+                "isdynamicallyscalable": false,
+                "tags": [],
+                "isfeatured": true,
+                "sshkeyenabled": false,
+                "isextractable": false,
+                "account": "system",
+                "name": "CentOS 5.3(64-bit) no GUI (Simulator)",
+                "created": "2014-02-15T02:50:13+0000",
+                "hypervisor": "Simulator",
+                "ispublic": true,
+                "checksum": ""
+            }
+        ]
+    }
+}
diff --git a/tests/data/valid_describe_images.json b/tests/data/valid_describe_images.json
new file mode 100644
index 0000000..79f78ea
--- /dev/null
+++ b/tests/data/valid_describe_images.json
@@ -0,0 +1,35 @@
+{
+    "listtemplatesresponse": {
+        "count": 1,
+        "template": [
+            {
+                "domain": "ROOT",
+                "domainid": "2edae3e4-95e4-11e3-b2e4-d19c9d3e5e1d",
+                "ostypename": "CentOS 5.3 (32-bit)",
+                "zoneid": "1e47a2fc-61c7-401c-b90e-416b472ada64",
+                "displaytext": "CentOS 5.3(64-bit) no GUI (Simulator)",
+                "ostypeid": "2e678976-95e4-11e3-b2e4-d19c9d3e5e1d",
+                "passwordenabled": false,
+                "id": "a32d70ee-95e4-11e3-b2e4-d19c9d3e5e1d",
+                "size": 2147483648,
+                "isready": true,
+                "format": "VHD",
+                "templatetype": "BUILTIN",
+                "crosszones": true,
+                "zonename": "Sandbox-simulator",
+                "status": "Download Complete",
+                "isdynamicallyscalable": false,
+                "tags": [],
+                "isfeatured": true,
+                "sshkeyenabled": false,
+                "isextractable": false,
+                "account": "system",
+                "name": "imagename",
+                "created": "2014-02-15T02:50:13+0000",
+                "hypervisor": "Simulator",
+                "ispublic": true,
+                "checksum": ""
+            }
+        ]
+    }
+}
diff --git a/tests/data/valid_describe_instance.json b/tests/data/valid_describe_instance.json
new file mode 100644
index 0000000..4294fa1
--- /dev/null
+++ b/tests/data/valid_describe_instance.json
@@ -0,0 +1,111 @@
+{
+    "listvirtualmachinesresponse": {
+        "count": 1,
+        "virtualmachine": [
+            {
+                "domain": "brogand93@darrenbrogan.ie",
+                "domainid": "42f2b0d0-3953-485f-984d-b8d67185d358",
+                "haenable": false,
+                "templatename": "Linux CentOS 6.5 64-bit",
+                "securitygroup": [
+                    {
+                        "egressrule": [],
+                        "account": "brogand93@darrenbrogan.ie",
+                        "description": "Default Security Group",
+                        "tags": [],
+                        "ingressrule": [],
+                        "id": "6033ff41-53ff-4443-b0bb-f6c5c0191c34",
+                        "name": "default"
+                    }
+                ],
+                "zoneid": "1128bd56-b4d9-4ac6-a7b9-c715b187ce11",
+                "keypair": "brogand93@darrenbrogan.ie",
+                "cpunumber": 1,
+                "passwordenabled": true,
+                "id": "71f13c6d-1590-4e82-9cdd-22eb9bcad0db",
+                "displayvm": true,
+                "state": "Running",
+                "guestosid": "113038d0-a8cd-4d20-92be-ea313f87c3ac",
+                "memory": 1024,
+                "serviceofferingid": "b6cd1ff5-3a2f-4e9d-a4d1-8988c1191fe8",
+                "zonename": "ch-gva-2",
+                "isdynamicallyscalable": false,
+                "displayname": "foobar",
+                "tags": [
+                    {
+                        "account": "brogand93@darrenbrogan.ie",
+                        "domainid": "42f2b0d0-3953-485f-984d-b8d67185d358",
+                        "resourcetype": "UserVm",
+                        "resourceid": "71f13c6d-1590-4e82-9cdd-22eb9bcad0db",
+                        "domain": "brogand93@darrenbrogan.ie",
+                        "value": "root:ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDqAuui+xCVPXaFD4cP2MuWnDlktg9vMT/SNzF17UiAzKEbxT/mNayTDAr",
+                        "key": "0-sshkey-segment"
+                    },
+                    {
+                        "account": "brogand93@darrenbrogan.ie",
+                        "domainid": "42f2b0d0-3953-485f-984d-b8d67185d358",
+                        "resourcetype": "UserVm",
+                        "resourceid": "71f13c6d-1590-4e82-9cdd-22eb9bcad0db",
+                        "domain": "brogand93@darrenbrogan.ie",
+                        "value": "DbY/BgGYC5bHuGlb/eE1r4EGpwSXZitGkTI4ThldrSp0Em7psO8AibdpYrFxlOmDFp9wKVD6xbY2HT1ySwvKi+ZwSR5yHcEKq15e",
+                        "key": "1-sshkey-segment"
+                    },
+                    {
+                        "account": "brogand93@darrenbrogan.ie",
+                        "domainid": "42f2b0d0-3953-485f-984d-b8d67185d358",
+                        "resourcetype": "UserVm",
+                        "resourceid": "71f13c6d-1590-4e82-9cdd-22eb9bcad0db",
+                        "domain": "brogand93@darrenbrogan.ie",
+                        "value": "V4eez/3qC1vIcssKmwu5+ZBneZAvWAfxHEKsQU0dsCVvHdn8g7tFXXtg4QCGtE4yzK5v3/+f1AdtIi4hvJoMyi8MV0KSa8e/ravd",
+                        "key": "2-sshkey-segment"
+                    },
+                    {
+                        "account": "brogand93@darrenbrogan.ie",
+                        "domainid": "42f2b0d0-3953-485f-984d-b8d67185d358",
+                        "resourcetype": "UserVm",
+                        "resourceid": "71f13c6d-1590-4e82-9cdd-22eb9bcad0db",
+                        "domain": "brogand93@darrenbrogan.ie",
+                        "value": "Hbgj44PncFBB8O6epVdXPbClZwtkz9D6GEQaOArxk9tX8YEgTFnmsnNuaoZgs7giMj2N7jQe2qXh5R0nsTTuH brogand@microv",
+                        "key": "3-sshkey-segment"
+                    },
+                    {
+                        "account": "brogand93@darrenbrogan.ie",
+                        "domainid": "42f2b0d0-3953-485f-984d-b8d67185d358",
+                        "resourcetype": "UserVm",
+                        "resourceid": "71f13c6d-1590-4e82-9cdd-22eb9bcad0db",
+                        "domain": "brogand93@darrenbrogan.ie",
+                        "value": "ac",
+                        "key": "4-sshkey-segment"
+                    }
+                ],
+                "nic": [
+                    {
+                        "networkid": "00304a04-c7ea-4e77-a786-18bc64347bf7",
+                        "macaddress": "06:b0:2e:00:01:04",
+                        "isolationuri": "ec2://untagged",
+                        "networkname": "guestNetworkForBasicZone",
+                        "gateway": "185.19.28.1",
+                        "traffictype": "Guest",
+                        "broadcasturi": "vlan://untagged",
+                        "netmask": "255.255.254.0",
+                        "type": "Shared",
+                        "ipaddress": "185.19.28.199",
+                        "id": "2b5cc781-d310-43cf-9d68-66719f43855d",
+                        "isdefault": true
+                    }
+                ],
+                "cpuspeed": 2198,
+                "templateid": "c34bf3f0-318b-4d77-b0ca-f20585d05d32",
+                "affinitygroup": [],
+                "account": "brogand93@darrenbrogan.ie",
+                "name": "instancename",
+                "created": "2014-06-02T21:11:52+0200",
+                "hypervisor": "KVM",
+                "rootdevicetype": "ROOT",
+                "rootdeviceid": 0,
+                "serviceofferingname": "Tiny",
+                "templatedisplaytext": "Linux CentOS 6.5 64-bit 10GB Disk"
+            }
+        ]
+    }
+}
diff --git a/tests/data/valid_describe_instances.json b/tests/data/valid_describe_instances.json
new file mode 100644
index 0000000..998904a
--- /dev/null
+++ b/tests/data/valid_describe_instances.json
@@ -0,0 +1,77 @@
+{
+    "listvirtualmachinesresponse": {
+        "count": 2,
+        "virtualmachine": [
+            {
+                "domain": "ROOT",
+                "domainid": "2edae3e4-95e4-11e3-b2e4-d19c9d3e5e1d",
+                "haenable": false,
+                "templatename": "CentOS 5.3(64-bit) no GUI (Simulator)",
+                "zoneid": "1e47a2fc-61c7-401c-b90e-416b472ada64",
+                "cpunumber": 1,
+                "passwordenabled": false,
+                "instancename": "i-2-5-QA",
+                "id": "43791f77-26f8-48ca-b557-3a9392f735ae",
+                "networkkbswrite": 21184512,
+                "hostname": "SimulatedAgent.74a0cc00-96c0-4b60-a170-7c749c6e2f10",
+                "displayvm": true,
+                "state": "Running",
+                "guestosid": "2e678976-95e4-11e3-b2e4-d19c9d3e5e1d",
+                "cpuused": "10%",
+                "memory": 512,
+                "serviceofferingid": "739292ce-3e12-4643-8ca1-abfdc2b2eb76",
+                "zonename": "Sandbox-simulator",
+                "isdynamicallyscalable": false,
+                "tags": [],
+                "cpuspeed": 500,
+                "templateid": "a32d70ee-95e4-11e3-b2e4-d19c9d3e5e1d",
+                "affinitygroup": [],
+                "account": "admin",
+                "hostid": "e880bd0f-1597-4cec-b108-c61dd02d78fd",
+                "name": "43791f77-26f8-48ca-b557-3a9392f735ae",
+                "networkkbsread": 42369024,
+                "created": "2014-02-21T17:28:40+0000",
+                "hypervisor": "Simulator",
+                "rootdevicetype": "ROOT",
+                "rootdeviceid": 0,
+                "serviceofferingname": "Small Instance",
+                "templatedisplaytext": "CentOS 5.3(64-bit) no GUI (Simulator)"
+            },
+            {
+                "domain": "ROOT",
+                "domainid": "2edae3e4-95e4-11e3-b2e4-d19c9d3e5e1d",
+                "haenable": false,
+                "templatename": "CentOS 5.3(64-bit) no GUI (Simulator)",
+                "zoneid": "1e47a2fc-61c7-401c-b90e-416b472ada64",
+                "cpunumber": 1,
+                "passwordenabled": false,
+                "instancename": "i-2-3-QA",
+                "id": "aa10a43e-56db-4a34-88bd-1c2a51c0bc04",
+                "networkkbswrite": 119291904,
+                "hostname": "SimulatedAgent.74a0cc00-96c0-4b60-a170-7c749c6e2f10",
+                "displayvm": true,
+                "state": "Running",
+                "guestosid": "2e678976-95e4-11e3-b2e4-d19c9d3e5e1d",
+                "cpuused": "10%",
+                "memory": 512,
+                "serviceofferingid": "739292ce-3e12-4643-8ca1-abfdc2b2eb76",
+                "zonename": "Sandbox-simulator",
+                "isdynamicallyscalable": false,
+                "tags": [],
+                "cpuspeed": 500,
+                "templateid": "a32d70ee-95e4-11e3-b2e4-d19c9d3e5e1d",
+                "affinitygroup": [],
+                "account": "admin",
+                "hostid": "e880bd0f-1597-4cec-b108-c61dd02d78fd",
+                "name": "aa10a43e-56db-4a34-88bd-1c2a51c0bc04",
+                "networkkbsread": 238583808,
+                "created": "2014-02-15T20:04:05+0000",
+                "hypervisor": "Simulator",
+                "rootdevicetype": "ROOT",
+                "rootdeviceid": 0,
+                "serviceofferingname": "Small Instance",
+                "templatedisplaytext": "CentOS 5.3(64-bit) no GUI (Simulator)"
+            }
+        ]
+    }
+}
diff --git a/tests/data/valid_describe_key_pairs.json b/tests/data/valid_describe_key_pairs.json
new file mode 100644
index 0000000..17ad4af
--- /dev/null
+++ b/tests/data/valid_describe_key_pairs.json
@@ -0,0 +1,11 @@
+{
+    "listsshkeypairsresponse": {
+        "count": 1,
+        "sshkeypair": [
+            {
+                "name": "test",
+                "fingerprint": "89:e8:53:79:81:75:ac:ae:c3:95:e0:ee:e0:e7:c0:06"
+            }
+        ]
+    }
+}
diff --git a/tests/data/valid_describe_regions.json b/tests/data/valid_describe_regions.json
new file mode 100644
index 0000000..d2c2581
--- /dev/null
+++ b/tests/data/valid_describe_regions.json
@@ -0,0 +1,14 @@
+{
+    "listregionsresponse": {
+        "count": 1,
+        "region": [
+            {
+                "portableipserviceenabled": false,
+                "gslbserviceenabled": true,
+                "endpoint": "http://localhost:8080/client/",
+                "id": 1,
+                "name": "regionname"
+            }
+        ]
+    }
+}
\ No newline at end of file
diff --git a/tests/data/valid_describe_security_group.json b/tests/data/valid_describe_security_group.json
new file mode 100644
index 0000000..7e95b07
--- /dev/null
+++ b/tests/data/valid_describe_security_group.json
@@ -0,0 +1,40 @@
+{
+    "listsecuritygroupsresponse": {
+        "count": 1,
+        "securitygroup": [
+            {
+                "egressrule": [],
+                "account": "example-account",
+                "domainid": "66d69e46-a95b-437b-ac6c-bcaa5331999d",
+                "description": "Default Security Group",
+                "tags": [],
+                "domain": "example-account",
+                "ingressrule": [
+                    {
+                        "protocol": "tcp",
+                        "cidr": "0.0.0.0/0",
+                        "startport": 22,
+                        "endport": 22,
+                        "ruleid": "3d92cc70-8c84-4e8a-9989-6efcd7ff7905"
+                    },
+                    {
+                        "protocol": "icmp",
+                        "cidr": "0.0.0.0/0",
+                        "ruleid": "2ba7dd7b-13b2-49ae-bf8e-26ffadd32c9e",
+                        "icmpcode": 0,
+                        "icmptype": 0
+                    },
+                    {
+                        "protocol": "tcp",
+                        "cidr": "0.0.0.0/0",
+                        "startport": 8080,
+                        "endport": 8080,
+                        "ruleid": "c4562b3c-d1b0-4844-a771-3c3434e1a5d0"
+                    }
+                ],
+                "id": "1f95ee9b-b291-48c1-9492-0eee632677e3",
+                "name": "networkname"
+            }
+        ]
+    }
+}
diff --git a/tests/data/valid_describe_security_groups.json b/tests/data/valid_describe_security_groups.json
new file mode 100644
index 0000000..a374056
--- /dev/null
+++ b/tests/data/valid_describe_security_groups.json
@@ -0,0 +1,67 @@
+{
+    "listsecuritygroupsresponse": {
+        "count": 3,
+        "securitygroup": [
+            {
+                "egressrule": [],
+                "account": "example-account",
+                "domainid": "66d69e46-a95b-437b-ac6c-bcaa5331999d",
+                "description": "Default Security Group",
+                "tags": [],
+                "domain": "example-account",
+                "ingressrule": [
+                    {
+                        "protocol": "tcp",
+                        "cidr": "0.0.0.0/0",
+                        "startport": 22,
+                        "endport": 22,
+                        "ruleid": "3d92cc70-8c84-4e8a-9989-6efcd7ff7905"
+                    },
+                    {
+                        "protocol": "icmp",
+                        "cidr": "0.0.0.0/0",
+                        "ruleid": "2ba7dd7b-13b2-49ae-bf8e-26ffadd32c9e",
+                        "icmpcode": 0,
+                        "icmptype": 0
+                    },
+                    {
+                        "protocol": "tcp",
+                        "cidr": "0.0.0.0/0",
+                        "startport": 8080,
+                        "endport": 8080,
+                        "ruleid": "c4562b3c-d1b0-4844-a771-3c3434e1a5d0"
+                    }
+                ],
+                "id": "1f95ee9b-b291-48c1-9492-0eee632677e3",
+                "name": "default"
+            },
+            {
+                "egressrule": [],
+                "account": "example-account",
+                "domainid": "66d69e46-a95b-437b-ac6c-bcaa5331999d",
+                "description": "test",
+                "tags": [],
+                "domain": "example-account",
+                "ingressrule": [
+                    {
+                        "protocol": "tcp",
+                        "cidr": "192.168.0.0/24",
+                        "startport": 1000,
+                        "endport": 1200,
+                        "ruleid": "c2e446a1-3778-40cf-a5ff-50ec66a395b6"
+                    }
+                ],
+                "id": "7ae5b92f-3a0d-4977-bc33-f1aaecee5776",
+                "name": "test"
+            },
+            {
+                "account": "example-account",
+                "domainid": "66d69e46-a95b-437b-ac6c-bcaa5331999d",
+                "name": "helloworld",
+                "domain": "example-account",
+                "id": "3b637c2e-b0a8-40ae-a7a3-2bef2871d36d",
+                "description": "helloworld"
+            }
+        ]
+    }
+}
diff --git a/tests/data/valid_describe_service_offering.json b/tests/data/valid_describe_service_offering.json
new file mode 100644
index 0000000..fd0f1df
--- /dev/null
+++ b/tests/data/valid_describe_service_offering.json
@@ -0,0 +1,23 @@
+{
+    "listserviceofferingsresponse": {
+        "count": 1,
+        "serviceoffering": [
+            {
+                "iscustomized": false,
+                "name": "machinetypename",
+                "created": "2013-02-08T17:20:17+0100",
+                "storagetype": "local",
+                "limitcpuuse": false,
+                "cpuspeed": 2198,
+                "offerha": false,
+                "isvolatile": false,
+                "cpunumber": 1,
+                "memory": 512,
+                "displaytext": "Micro 512mb 1cpu",
+                "issystem": false,
+                "id": "71004023-bb72-4a97-b1e9-bc66dfce9470",
+                "defaultuse": false
+            }
+        ]
+    }
+}
diff --git a/tests/data/valid_describe_service_offerings.json b/tests/data/valid_describe_service_offerings.json
new file mode 100644
index 0000000..10237d0
--- /dev/null
+++ b/tests/data/valid_describe_service_offerings.json
@@ -0,0 +1,119 @@
+{
+    "listserviceofferingsresponse": {
+        "count": 7,
+        "serviceoffering": [
+            {
+                "iscustomized": false,
+                "name": "Micro",
+                "created": "2013-02-08T17:20:17+0100",
+                "storagetype": "local",
+                "limitcpuuse": false,
+                "cpuspeed": 2198,
+                "offerha": false,
+                "isvolatile": false,
+                "cpunumber": 1,
+                "memory": 512,
+                "displaytext": "Micro 512mb 1cpu",
+                "issystem": false,
+                "id": "71004023-bb72-4a97-b1e9-bc66dfce9470",
+                "defaultuse": false
+            },
+            {
+                "iscustomized": false,
+                "name": "Tiny",
+                "created": "2013-02-08T17:20:39+0100",
+                "storagetype": "local",
+                "limitcpuuse": false,
+                "cpuspeed": 2198,
+                "offerha": false,
+                "isvolatile": false,
+                "cpunumber": 1,
+                "memory": 1024,
+                "displaytext": "Tiny 1024mb 1cpu",
+                "issystem": false,
+                "id": "b6cd1ff5-3a2f-4e9d-a4d1-8988c1191fe8",
+                "defaultuse": false
+            },
+            {
+                "iscustomized": false,
+                "name": "Small",
+                "created": "2013-02-08T17:21:05+0100",
+                "storagetype": "local",
+                "limitcpuuse": false,
+                "cpuspeed": 2198,
+                "offerha": false,
+                "isvolatile": false,
+                "cpunumber": 2,
+                "memory": 2048,
+                "displaytext": "Small 2048mb 2cpu",
+                "issystem": false,
+                "id": "21624abb-764e-4def-81d7-9fc54b5957fb",
+                "defaultuse": false
+            },
+            {
+                "iscustomized": false,
+                "name": "Medium",
+                "created": "2013-02-08T17:21:43+0100",
+                "storagetype": "local",
+                "limitcpuuse": false,
+                "cpuspeed": 2198,
+                "offerha": false,
+                "isvolatile": false,
+                "cpunumber": 2,
+                "memory": 4096,
+                "displaytext": "Medium 4096mb 2cpu",
+                "issystem": false,
+                "id": "b6e9d1e8-89fc-4db3-aaa4-9b4c5b1d0844",
+                "defaultuse": false
+            },
+            {
+                "iscustomized": false,
+                "name": "Large",
+                "created": "2013-02-08T17:22:27+0100",
+                "storagetype": "local",
+                "limitcpuuse": false,
+                "cpuspeed": 2198,
+                "offerha": false,
+                "isvolatile": false,
+                "cpunumber": 4,
+                "memory": 8182,
+                "displaytext": "Large 8192mb 4cpu",
+                "issystem": false,
+                "id": "c6f99499-7f59-4138-9427-a09db13af2bc",
+                "defaultuse": false
+            },
+            {
+                "iscustomized": false,
+                "name": "Extra-large",
+                "created": "2013-02-08T17:23:38+0100",
+                "storagetype": "local",
+                "limitcpuuse": false,
+                "cpuspeed": 2198,
+                "offerha": false,
+                "isvolatile": false,
+                "cpunumber": 4,
+                "memory": 16384,
+                "displaytext": "Extra-large 16384mb 4cpu",
+                "issystem": false,
+                "id": "350dc5ea-fe6d-42ba-b6c0-efb8b75617ad",
+                "defaultuse": false
+            },
+            {
+                "iscustomized": false,
+                "name": "Huge",
+                "created": "2013-02-08T17:24:10+0100",
+                "storagetype": "local",
+                "limitcpuuse": false,
+                "cpuspeed": 2198,
+                "offerha": false,
+                "isvolatile": false,
+                "cpunumber": 8,
+                "memory": 32184,
+                "displaytext": "Huge 32184mb 8cpu",
+                "issystem": false,
+                "id": "a216b0d1-370f-4e21-a0eb-3dfc6302b564",
+                "defaultuse": false
+            }
+        ]
+    }
+}
diff --git a/tests/data/valid_describe_volume.json b/tests/data/valid_describe_volume.json
new file mode 100644
index 0000000..0c96815
--- /dev/null
+++ b/tests/data/valid_describe_volume.json
@@ -0,0 +1,28 @@
+{
+    "listvolumesresponse": {
+        "count": 1,
+        "volume": [
+            {
+                "diskofferingname": "Custom",
+                "account": "admin",
+                "domainid": "2edae3e4-95e4-11e3-b2e4-d19c9d3e5e1d",
+                "name": "volumename",
+                "created": "2014-02-22T13:25:12+0000",
+                "storagetype": "shared",
+                "type": "DATADISK",
+                "tags": [],
+                "domain": "ROOT",
+                "isextractable": true,
+                "zoneid": "1e47a2fc-61c7-401c-b90e-416b472ada64",
+                "destroyed": false,
+                "state": "Allocated",
+                "diskofferingdisplaytext": "Custom Disk",
+                "displayvolume": true,
+                "size": 10737418240,
+                "diskofferingid": "46f0a62e-ba3c-4a49-b619-877a8b2a07ed",
+                "id": "aa903a80-efa9-4fac-b424-544cbda9fce9",
+                "zonename": "Sandbox-simulator"
+            }
+        ]
+    }
+}
diff --git a/tests/data/valid_describe_volumes.json b/tests/data/valid_describe_volumes.json
new file mode 100644
index 0000000..9a95d87
--- /dev/null
+++ b/tests/data/valid_describe_volumes.json
@@ -0,0 +1,55 @@
+{
+    "listvolumesresponse": {
+        "count": 21,
+        "volume": [
+            {
+                "diskofferingname": "Custom",
+                "account": "admin",
+                "domainid": "2edae3e4-95e4-11e3-b2e4-d19c9d3e5e1d",
+                "name": "6f78a7d4-8863-40da-96f8-0e10ad4ea94e",
+                "created": "2014-02-22T13:25:12+0000",
+                "storagetype": "shared",
+                "type": "DATADISK",
+                "tags": [],
+                "domain": "ROOT",
+                "isextractable": true,
+                "zoneid": "1e47a2fc-61c7-401c-b90e-416b472ada64",
+                "destroyed": false,
+                "state": "Allocated",
+                "diskofferingdisplaytext": "Custom Disk",
+                "displayvolume": true,
+                "size": 10737418240,
+                "diskofferingid": "46f0a62e-ba3c-4a49-b619-877a8b2a07ed",
+                "id": "aa903a80-efa9-4fac-b424-544cbda9fce9",
+                "zonename": "Sandbox-simulator"
+            },
+            {
+                "domain": "ROOT",
+                "domainid": "2edae3e4-95e4-11e3-b2e4-d19c9d3e5e1d",
+                "zoneid": "1e47a2fc-61c7-401c-b90e-416b472ada64",
+                "vmname": "43791f77-26f8-48ca-b557-3a9392f735ae",
+                "id": "de2d8297-eaaf-4e81-8ffe-97f37ddbbde5",
+                "size": 5368709120,
+                "diskofferingname": "Small",
+                "diskofferingdisplaytext": "Small Disk, 5 GB",
+                "storage": "PS0",
+                "displayvolume": false,
+                "destroyed": false,
+                "state": "Ready",
+                "type": "DATADISK",
+                "zonename": "Sandbox-simulator",
+                "tags": [],
+                "isextractable": true,
+                "account": "admin",
+                "name": "DATA-5",
+                "virtualmachineid": "43791f77-26f8-48ca-b557-3a9392f735ae",
+                "storagetype": "shared",
+                "hypervisor": "Simulator",
+                "created": "2014-02-21T17:28:41+0000",
+                "deviceid": 1,
+                "diskofferingid": "8fade6f4-13ea-490f-aff2-65c9b37b7651",
+                "vmstate": "Running"
+            }
+        ]
+    }
+}
diff --git a/tests/data/valid_describe_zone.json b/tests/data/valid_describe_zone.json
new file mode 100644
index 0000000..4df5d0c
--- /dev/null
+++ b/tests/data/valid_describe_zone.json
@@ -0,0 +1,17 @@
+{
+    "listzonesresponse": {
+        "count": 1,
+        "zone": [
+            {
+                "localstorageenabled": true,
+                "name": "zonename",
+                "zonetoken": "ccb0a60c-79c8-3230-ab8b-8bdbe8c45bb7",
+                "securitygroupsenabled": true,
+                "allocationstate": "Enabled",
+                "dhcpprovider": "VirtualRouter",
+                "networktype": "Basic",
+                "id": "1128bd56-b4d9-4ac6-a7b9-c715b187ce11"
+            }
+        ]
+    }
+}
diff --git a/tests/data/valid_detach_volume.json b/tests/data/valid_detach_volume.json
new file mode 100644
index 0000000..6ad6a0b
--- /dev/null
+++ b/tests/data/valid_detach_volume.json
@@ -0,0 +1,43 @@
+{
+    "queryasyncjobresultresponse": {
+        "jobprocstatus": 0,
+        "created": "2014-03-03T16:57:26+0000",
+        "cmd": "org.apache.cloudstack.api.command.user.volume.DetachVolumeCmd",
+        "userid": "a5fa60a6-a2d5-11e3-8f5e-e2e3ca4a127e",
+        "jobstatus": 1,
+        "jobid": "436927af-f3ed-43ec-96d9-0a63eb97c566",
+        "jobresultcode": 0,
+        "jobresulttype": "object",
+        "jobresult": {
+            "volume": {
+                "domain": "ROOT",
+                "domainid": "a5f9ee28-a2d5-11e3-8f5e-e2e3ca4a127e",
+                "jobstatus": 0,
+                "zoneid": "426baaba-224d-419d-aea1-d65cb21e68ea",
+                "storageid": "Local Storage",
+                "id": "0896ccff-1b7a-4c17-8390-02a602de2efe",
+                "size": 1073741824,
+                "diskofferingname": "Custom",
+                "diskofferingdisplaytext": "Custom Disk",
+                "storage": "devcloud Local Storage",
+                "displayvolume": true,
+                "destroyed": false,
+                "state": "Ready",
+                "type": "DATADISK",
+                "zonename": "devcloud",
+                "tags": [],
+                "isextractable": true,
+                "path": "35e692a8-1135-414f-9c84-9f74e9faa8a4",
+                "account": "admin",
+                "name": "test",
+                "created": "2014-03-03T14:37:47+0000",
+                "storagetype": "local",
+                "hypervisor": "XenServer",
+                "jobid": "436927af-f3ed-43ec-96d9-0a63eb97c566",
+                "diskofferingid": "5ce6263f-ee4b-420c-a921-04570b7bc526",
+                "quiescevm": false
+            }
+        },
+        "accountid": "a5fa2942-a2d5-11e3-8f5e-e2e3ca4a127e"
+    }
+}
diff --git a/tests/data/valid_get_instance_by_id.json b/tests/data/valid_get_instance_by_id.json
new file mode 100644
index 0000000..6d0f023
--- /dev/null
+++ b/tests/data/valid_get_instance_by_id.json
@@ -0,0 +1,58 @@
+{
+    "domain": "ROOT",
+    "domainid": "2edae3e4-95e4-11e3-b2e4-d19c9d3e5e1d",
+    "haenable": false,
+    "templatename": "CentOS 5.3(64-bit) no GUI (Simulator)",
+    "securitygroup": [
+        {
+            "egressrule": [],
+            "account": "admin",
+            "description": "Default Security Group",
+            "tags": [],
+            "ingressrule": [],
+            "id": "1cc5720a-95e5-11e3-b2e4-d19c9d3e5e1d",
+            "name": "default"
+        }
+    ],
+    "zoneid": "1e47a2fc-61c7-401c-b90e-416b472ada64",
+    "cpunumber": 1,
+    "passwordenabled": false,
+    "instancename": "i-2-18-QA",
+    "id": "c3331b5e-b244-4654-8991-a1a019db6168",
+    "displayvm": true,
+    "state": "Stopped",
+    "guestosid": "2e678976-95e4-11e3-b2e4-d19c9d3e5e1d",
+    "cpuspeed": 500,
+    "serviceofferingid": "739292ce-3e12-4643-8ca1-abfdc2b2eb76",
+    "zonename": "Sandbox-simulator",
+    "isdynamicallyscalable": false,
+    "tags": [],
+    "nic": [
+        {
+            "networkid": "344ef1e8-e59e-416b-aa32-9bef9c352903",
+            "macaddress": "06:38:72:00:01:02",
+            "isolationuri": "ec2://untagged",
+            "type": "Shared",
+            "broadcasturi": "vlan://untagged",
+            "traffictype": "Guest",
+            "netmask": "255.255.255.0",
+            "ipaddress": "60.147.41.6",
+            "id": "51226be8-445d-4201-9910-79fc4ff8e837",
+            "networkname": "guestNetworkForBasicZone",
+            "gateway": "60.147.41.1",
+            "isdefault": true
+        }
+    ],
+    "memory": 512,
+    "templateid": "a32d70ee-95e4-11e3-b2e4-d19c9d3e5e1d",
+    "affinitygroup": [],
+    "account": "admin",
+    "name": "c3331b5e-b244-4654-8991-a1a019db6168",
+    "created": "2014-03-03T15:06:55+0000",
+    "hypervisor": "Simulator",
+    "rootdevicetype": "ROOT",
+    "rootdeviceid": 0,
+    "serviceofferingname": "Small Instance",
+    "templatedisplaytext": "CentOS 5.3(64-bit) no GUI (Simulator)"
+}
+
diff --git a/tests/data/valid_import_keypair.json b/tests/data/valid_import_keypair.json
new file mode 100644
index 0000000..6bfcbf9
--- /dev/null
+++ b/tests/data/valid_import_keypair.json
@@ -0,0 +1,6 @@
+{ "registersshkeypairresponse": {
+    "keypair": {
+        "name": "Test22",
+        "fingerprint": "07:69:0e:64:f1:b5:f8:c1:10:25:55:73:1a:5c:39:1d"
+    }
+}  }
diff --git a/tests/data/valid_reboot_instance.json b/tests/data/valid_reboot_instance.json
new file mode 100644
index 0000000..6862564
--- /dev/null
+++ b/tests/data/valid_reboot_instance.json
@@ -0,0 +1,79 @@
+{
+    "queryasyncjobresultresponse": {
+        "jobprocstatus": 0,
+        "created": "2014-03-03T15:44:12+0000",
+        "cmd": "org.apache.cloudstack.api.command.user.vm.RebootVMCmd",
+        "userid": "2edb33ec-95e4-11e3-b2e4-d19c9d3e5e1d",
+        "jobstatus": 1,
+        "jobid": "b6b87bc6-47fc-4973-8323-31be506299e5",
+        "jobresultcode": 0,
+        "jobresulttype": "object",
+        "jobresult": {
+            "virtualmachine": {
+                "domain": "ROOT",
+                "domainid": "2edae3e4-95e4-11e3-b2e4-d19c9d3e5e1d",
+                "haenable": false,
+                "templatename": "CentOS 5.3(64-bit) no GUI (Simulator)",
+                "securitygroup": [
+                    {
+                        "egressrule": [],
+                        "account": "admin",
+                        "description": "Default Security Group",
+                        "tags": [],
+                        "ingressrule": [],
+                        "id": "1cc5720a-95e5-11e3-b2e4-d19c9d3e5e1d",
+                        "name": "default"
+                    }
+                ],
+                "zoneid": "1e47a2fc-61c7-401c-b90e-416b472ada64",
+                "cpunumber": 1,
+                "passwordenabled": false,
+                "instancename": "i-2-19-QA",
+                "id": "bc4100f2-e34e-47b0-ba6f-34cfe6d09fec",
+                "networkkbswrite": 344064,
+                "hostname": "SimulatedAgent.74a0cc00-96c0-4b60-a170-7c749c6e2f10",
+                "displayvm": true,
+                "state": "Running",
+                "guestosid": "2e678976-95e4-11e3-b2e4-d19c9d3e5e1d",
+                "cpuused": "10%",
+                "memory": 512,
+                "serviceofferingid": "739292ce-3e12-4643-8ca1-abfdc2b2eb76",
+                "zonename": "Sandbox-simulator",
+                "isdynamicallyscalable": false,
+                "tags": [],
+                "nic": [
+                    {
+                        "networkid": "344ef1e8-e59e-416b-aa32-9bef9c352903",
+                        "macaddress": "06:49:26:00:01:03",
+                        "isolationuri": "ec2://untagged",
+                        "type": "Shared",
+                        "broadcasturi": "vlan://untagged",
+                        "traffictype": "Guest",
+                        "netmask": "255.255.255.0",
+                        "ipaddress": "60.147.41.7",
+                        "id": "81b2515b-e6c3-4309-9af8-4eddc376a2c1",
+                        "networkname": "guestNetworkForBasicZone",
+                        "gateway": "60.147.41.1",
+                        "isdefault": true
+                    }
+                ],
+                "cpuspeed": 500,
+                "jobstatus": 0,
+                "templateid": "a32d70ee-95e4-11e3-b2e4-d19c9d3e5e1d",
+                "affinitygroup": [],
+                "account": "admin",
+                "hostid": "e880bd0f-1597-4cec-b108-c61dd02d78fd",
+                "name": "bc4100f2-e34e-47b0-ba6f-34cfe6d09fec",
+                "networkkbsread": 688128,
+                "created": "2014-03-03T15:15:45+0000",
+                "hypervisor": "Simulator",
+                "jobid": "b6b87bc6-47fc-4973-8323-31be506299e5",
+                "rootdevicetype": "ROOT",
+                "rootdeviceid": 0,
+                "serviceofferingname": "Small Instance",
+                "templatedisplaytext": "CentOS 5.3(64-bit) no GUI (Simulator)"
+            }
+        },
+        "accountid": "2edb0c28-95e4-11e3-b2e4-d19c9d3e5e1d"
+    }
+}
diff --git a/tests/data/valid_run_instance.json b/tests/data/valid_run_instance.json
new file mode 100644
index 0000000..4eac3f5
--- /dev/null
+++ b/tests/data/valid_run_instance.json
@@ -0,0 +1,76 @@
+{
+    "queryasyncjobresultresponse": {
+        "jobprocstatus": 0,
+        "created": "2014-03-03T21:32:17+0000",
+        "cmd": "org.apache.cloudstack.api.command.user.vm.DeployVMCmd",
+        "userid": "2edb33ec-95e4-11e3-b2e4-d19c9d3e5e1d",
+        "jobstatus": 1,
+        "jobid": "e0b2d780-4d9e-4a4a-8429-fb28f53807dc",
+        "jobresultcode": 0,
+        "jobresulttype": "object",
+        "jobresult": {
+            "virtualmachine": {
+                "domain": "ROOT",
+                "domainid": "2edae3e4-95e4-11e3-b2e4-d19c9d3e5e1d",
+                "haenable": false,
+                "templatename": "CentOS 5.3(64-bit) no GUI (Simulator)",
+                "jobstatus": 0,
+                "zoneid": "1e47a2fc-61c7-401c-b90e-416b472ada64",
+                "cpunumber": 1,
+                "passwordenabled": false,
+                "instancename": "i-2-20-QA",
+                "id": "e8dc9242-ab36-4889-a797-21e22b528fc3",
+                "hostname": "SimulatedAgent.74a0cc00-96c0-4b60-a170-7c749c6e2f10",
+                "displayvm": true,
+                "state": "Running",
+                "guestosid": "2e678976-95e4-11e3-b2e4-d19c9d3e5e1d",
+                "cpuspeed": 500,
+                "serviceofferingid": "739292ce-3e12-4643-8ca1-abfdc2b2eb76",
+                "zonename": "Sandbox-simulator",
+                "isdynamicallyscalable": false,
+                "tags": [],
+                "nic": [
+                    {
+                        "networkid": "344ef1e8-e59e-416b-aa32-9bef9c352903",
+                        "macaddress": "06:4f:98:00:01:03",
+                        "isolationuri": "ec2://untagged",
+                        "type": "Shared",
+                        "broadcasturi": "vlan://untagged",
+                        "traffictype": "Guest",
+                        "netmask": "255.255.255.0",
+                        "ipaddress": "60.147.41.7",
+                        "id": "f148a5d7-f43c-4afd-8987-4ebaea370e59",
+                        "networkname": "guestNetworkForBasicZone",
+                        "gateway": "60.147.41.1",
+                        "isdefault": true
+                    }
+                ],
+                "memory": 512,
+                "securitygroup": [
+                    {
+                        "egressrule": [],
+                        "account": "admin",
+                        "description": "Default Security Group",
+                        "tags": [],
+                        "ingressrule": [],
+                        "id": "1cc5720a-95e5-11e3-b2e4-d19c9d3e5e1d",
+                        "name": "default"
+                    }
+                ],
+                "templateid": "a32d70ee-95e4-11e3-b2e4-d19c9d3e5e1d",
+                "affinitygroup": [],
+                "account": "admin",
+                "hostid": "e880bd0f-1597-4cec-b108-c61dd02d78fd",
+                "name": "e8dc9242-ab36-4889-a797-21e22b528fc3",
+                "created": "2014-03-03T21:32:17+0000",
+                "hypervisor": "Simulator",
+                "jobid": "e0b2d780-4d9e-4a4a-8429-fb28f53807dc",
+                "rootdevicetype": "ROOT",
+                "rootdeviceid": 0,
+                "serviceofferingname": "Small Instance",
+                "templatedisplaytext": "CentOS 5.3(64-bit) no GUI (Simulator)"
+            }
+        },
+        "accountid": "2edb0c28-95e4-11e3-b2e4-d19c9d3e5e1d"
+    }
+}
diff --git a/tests/data/valid_start_instance.json b/tests/data/valid_start_instance.json
new file mode 100644
index 0000000..697aec3
--- /dev/null
+++ b/tests/data/valid_start_instance.json
@@ -0,0 +1,79 @@
+{
+    "queryasyncjobresultresponse": {
+        "jobprocstatus": 0,
+        "created": "2014-03-03T15:34:40+0000",
+        "cmd": "org.apache.cloudstack.api.command.user.vm.StartVMCmd",
+        "userid": "2edb33ec-95e4-11e3-b2e4-d19c9d3e5e1d",
+        "jobstatus": 1,
+        "jobid": "f2687d7c-e8e2-4dec-8ee9-ce7a1643c93f",
+        "jobresultcode": 0,
+        "jobresulttype": "object",
+        "jobresult": {
+            "virtualmachine": {
+                "domain": "ROOT",
+                "domainid": "2edae3e4-95e4-11e3-b2e4-d19c9d3e5e1d",
+                "haenable": false,
+                "templatename": "CentOS 5.3(64-bit) no GUI (Simulator)",
+                "securitygroup": [
+                    {
+                        "egressrule": [],
+                        "account": "admin",
+                        "description": "Default Security Group",
+                        "tags": [],
+                        "ingressrule": [],
+                        "id": "1cc5720a-95e5-11e3-b2e4-d19c9d3e5e1d",
+                        "name": "default"
+                    }
+                ],
+                "zoneid": "1e47a2fc-61c7-401c-b90e-416b472ada64",
+                "cpunumber": 1,
+                "passwordenabled": false,
+                "instancename": "i-2-19-QA",
+                "id": "bc4100f2-e34e-47b0-ba6f-34cfe6d09fec",
+                "networkkbswrite": 294912,
+                "hostname": "SimulatedAgent.74a0cc00-96c0-4b60-a170-7c749c6e2f10",
+                "displayvm": true,
+                "state": "Running",
+                "guestosid": "2e678976-95e4-11e3-b2e4-d19c9d3e5e1d",
+                "cpuused": "10%",
+                "memory": 512,
+                "serviceofferingid": "739292ce-3e12-4643-8ca1-abfdc2b2eb76",
+                "zonename": "Sandbox-simulator",
+                "isdynamicallyscalable": false,
+                "tags": [],
+                "nic": [
+                    {
+                        "networkid": "344ef1e8-e59e-416b-aa32-9bef9c352903",
+                        "macaddress": "06:49:26:00:01:03",
+                        "isolationuri": "ec2://untagged",
+                        "type": "Shared",
+                        "broadcasturi": "vlan://untagged",
+                        "traffictype": "Guest",
+                        "netmask": "255.255.255.0",
+                        "ipaddress": "60.147.41.7",
+                        "id": "81b2515b-e6c3-4309-9af8-4eddc376a2c1",
+                        "networkname": "guestNetworkForBasicZone",
+                        "gateway": "60.147.41.1",
+                        "isdefault": true
+                    }
+                ],
+                "cpuspeed": 500,
+                "jobstatus": 0,
+                "templateid": "a32d70ee-95e4-11e3-b2e4-d19c9d3e5e1d",
+                "affinitygroup": [],
+                "account": "admin",
+                "hostid": "e880bd0f-1597-4cec-b108-c61dd02d78fd",
+                "name": "bc4100f2-e34e-47b0-ba6f-34cfe6d09fec",
+                "networkkbsread": 589824,
+                "created": "2014-03-03T15:15:45+0000",
+                "hypervisor": "Simulator",
+                "jobid": "f2687d7c-e8e2-4dec-8ee9-ce7a1643c93f",
+                "rootdevicetype": "ROOT",
+                "rootdeviceid": 0,
+                "serviceofferingname": "Small Instance",
+                "templatedisplaytext": "CentOS 5.3(64-bit) no GUI (Simulator)"
+            }
+        },
+        "accountid": "2edb0c28-95e4-11e3-b2e4-d19c9d3e5e1d"
+    }
+}
diff --git a/tests/data/valid_stop_instance.json b/tests/data/valid_stop_instance.json
new file mode 100644
index 0000000..897e59b
--- /dev/null
+++ b/tests/data/valid_stop_instance.json
@@ -0,0 +1,77 @@
+{
+    "queryasyncjobresultresponse": {
+        "jobprocstatus": 0,
+        "created": "2014-03-03T15:38:21+0000",
+        "cmd": "org.apache.cloudstack.api.command.user.vm.StopVMCmd",
+        "userid": "2edb33ec-95e4-11e3-b2e4-d19c9d3e5e1d",
+        "jobstatus": 1,
+        "jobid": "869b7256-5158-4ed5-b271-2d0f82b1d7f4",
+        "jobresultcode": 0,
+        "jobresulttype": "object",
+        "jobresult": {
+            "virtualmachine": {
+                "domain": "ROOT",
+                "domainid": "2edae3e4-95e4-11e3-b2e4-d19c9d3e5e1d",
+                "haenable": false,
+                "templatename": "CentOS 5.3(64-bit) no GUI (Simulator)",
+                "securitygroup": [
+                    {
+                        "egressrule": [],
+                        "account": "admin",
+                        "description": "Default Security Group",
+                        "tags": [],
+                        "ingressrule": [],
+                        "id": "1cc5720a-95e5-11e3-b2e4-d19c9d3e5e1d",
+                        "name": "default"
+                    }
+                ],
+                "zoneid": "1e47a2fc-61c7-401c-b90e-416b472ada64",
+                "cpunumber": 1,
+                "passwordenabled": false,
+                "instancename": "i-2-19-QA",
+                "id": "bc4100f2-e34e-47b0-ba6f-34cfe6d09fec",
+                "networkkbswrite": 344064,
+                "displayvm": true,
+                "state": "Stopped",
+                "guestosid": "2e678976-95e4-11e3-b2e4-d19c9d3e5e1d",
+                "cpuused": "10%",
+                "cpuspeed": 500,
+                "serviceofferingid": "739292ce-3e12-4643-8ca1-abfdc2b2eb76",
+                "zonename": "Sandbox-simulator",
+                "isdynamicallyscalable": false,
+                "tags": [],
+                "nic": [
+                    {
+                        "networkid": "344ef1e8-e59e-416b-aa32-9bef9c352903",
+                        "macaddress": "06:49:26:00:01:03",
+                        "isolationuri": "ec2://untagged",
+                        "type": "Shared",
+                        "broadcasturi": "vlan://untagged",
+                        "traffictype": "Guest",
+                        "netmask": "255.255.255.0",
+                        "ipaddress": "60.147.41.7",
+                        "id": "81b2515b-e6c3-4309-9af8-4eddc376a2c1",
+                        "networkname": "guestNetworkForBasicZone",
+                        "gateway": "60.147.41.1",
+                        "isdefault": true
+                    }
+                ],
+                "memory": 512,
+                "jobstatus": 0,
+                "templateid": "a32d70ee-95e4-11e3-b2e4-d19c9d3e5e1d",
+                "affinitygroup": [],
+                "account": "admin",
+                "name": "bc4100f2-e34e-47b0-ba6f-34cfe6d09fec",
+                "networkkbsread": 688128,
+                "created": "2014-03-03T15:15:45+0000",
+                "hypervisor": "Simulator",
+                "jobid": "869b7256-5158-4ed5-b271-2d0f82b1d7f4",
+                "rootdevicetype": "ROOT",
+                "rootdeviceid": 0,
+                "serviceofferingname": "Small Instance",
+                "templatedisplaytext": "CentOS 5.3(64-bit) no GUI (Simulator)"
+            }
+        },
+        "accountid": "2edb0c28-95e4-11e3-b2e4-d19c9d3e5e1d"
+    }
+}
diff --git a/tests/data/valid_terminate_instance.json b/tests/data/valid_terminate_instance.json
new file mode 100644
index 0000000..a435be9
--- /dev/null
+++ b/tests/data/valid_terminate_instance.json
@@ -0,0 +1,77 @@
+{
+    "queryasyncjobresultresponse": {
+        "jobprocstatus": 0,
+        "created": "2014-03-03T15:44:44+0000",
+        "cmd": "org.apache.cloudstack.api.command.user.vm.DestroyVMCmd",
+        "userid": "2edb33ec-95e4-11e3-b2e4-d19c9d3e5e1d",
+        "jobstatus": 1,
+        "jobid": "919ad844-7cd5-4e8a-a60f-8707ef72446c",
+        "jobresultcode": 0,
+        "jobresulttype": "object",
+        "jobresult": {
+            "virtualmachine": {
+                "domain": "ROOT",
+                "domainid": "2edae3e4-95e4-11e3-b2e4-d19c9d3e5e1d",
+                "haenable": false,
+                "templatename": "CentOS 5.3(64-bit) no GUI (Simulator)",
+                "securitygroup": [
+                    {
+                        "egressrule": [],
+                        "account": "admin",
+                        "description": "Default Security Group",
+                        "tags": [],
+                        "ingressrule": [],
+                        "id": "1cc5720a-95e5-11e3-b2e4-d19c9d3e5e1d",
+                        "name": "default"
+                    }
+                ],
+                "zoneid": "1e47a2fc-61c7-401c-b90e-416b472ada64",
+                "cpunumber": 1,
+                "passwordenabled": false,
+                "instancename": "i-2-19-QA",
+                "id": "bc4100f2-e34e-47b0-ba6f-34cfe6d09fec",
+                "networkkbswrite": 360448,
+                "displayvm": true,
+                "state": "Destroyed",
+                "guestosid": "2e678976-95e4-11e3-b2e4-d19c9d3e5e1d",
+                "cpuused": "10%",
+                "cpuspeed": 500,
+                "serviceofferingid": "739292ce-3e12-4643-8ca1-abfdc2b2eb76",
+                "zonename": "Sandbox-simulator",
+                "isdynamicallyscalable": false,
+                "tags": [],
+                "nic": [
+                    {
+                        "networkid": "344ef1e8-e59e-416b-aa32-9bef9c352903",
+                        "macaddress": "06:49:26:00:01:03",
+                        "isolationuri": "ec2://untagged",
+                        "type": "Shared",
+                        "broadcasturi": "vlan://untagged",
+                        "traffictype": "Guest",
+                        "netmask": "255.255.255.0",
+                        "ipaddress": "60.147.41.7",
+                        "id": "81b2515b-e6c3-4309-9af8-4eddc376a2c1",
+                        "networkname": "guestNetworkForBasicZone",
+                        "gateway": "60.147.41.1",
+                        "isdefault": true
+                    }
+                ],
+                "memory": 512,
+                "jobstatus": 0,
+                "templateid": "a32d70ee-95e4-11e3-b2e4-d19c9d3e5e1d",
+                "affinitygroup": [],
+                "account": "admin",
+                "name": "bc4100f2-e34e-47b0-ba6f-34cfe6d09fec",
+                "networkkbsread": 720896,
+                "created": "2014-03-03T15:15:45+0000",
+                "hypervisor": "Simulator",
+                "jobid": "919ad844-7cd5-4e8a-a60f-8707ef72446c",
+                "rootdevicetype": "ROOT",
+                "rootdeviceid": 0,
+                "serviceofferingname": "Small Instance",
+                "templatedisplaytext": "CentOS 5.3(64-bit) no GUI (Simulator)"
+            }
+        },
+        "accountid": "2edb0c28-95e4-11e3-b2e4-d19c9d3e5e1d"
+    }
+}
diff --git a/tests/data/zones_search.json b/tests/data/zones_search.json
new file mode 100644
index 0000000..c1028d6
--- /dev/null
+++ b/tests/data/zones_search.json
@@ -0,0 +1,12 @@
+{
+    "localstorageenabled": false,
+    "name": "Sandbox-simulator",
+    "zonetoken": "038bf1a4-c55c-3d1e-9176-b42e22ee00c1",
+    "dns1": "8.8.8.8",
+    "securitygroupsenabled": true,
+    "allocationstate": "Enabled",
+    "internaldns1": "8.8.8.8",
+    "dhcpprovider": "VirtualRouter",
+    "networktype": "Basic",
+    "id": "1e47a2fc-61c7-401c-b90e-416b472ada64"
+}
diff --git a/tests/discovery_tests.py b/tests/discovery_tests.py
new file mode 100644
index 0000000..a35724d
--- /dev/null
+++ b/tests/discovery_tests.py
@@ -0,0 +1,11 @@
+#!/usr/bin/env python
+# encoding: utf-8
+
+from . import GStackAppTestCase
+
+class DiscoveryTestCase(GStackAppTestCase):
+
+    def test_discovery(self):
+        response = self.get('/discovery/v1/apis/compute/v1/rest')
+
+        self.assert_ok(response)
\ No newline at end of file
diff --git a/tests/disks_tests.py b/tests/disks_tests.py
new file mode 100644
index 0000000..f9635a3
--- /dev/null
+++ b/tests/disks_tests.py
@@ -0,0 +1,105 @@
+#!/usr/bin/env python
+# encoding: utf-8
+
+import mock
+import json
+
+from gstack.helpers import read_file
+from . import GStackAppTestCase
+
+class DisksTestCase(GStackAppTestCase):
+
+    def test_list_disks(self):
+
+        get = mock.Mock()
+        get.return_value.text = read_file('tests/data/valid_describe_volumes.json')
+        get.return_value.status_code = 200
+
+        with mock.patch('requests.get', get):
+            headers = {'authorization': 'Bearer ' + str(GStackAppTestCase.access_token)}
+            response = self.get('/compute/v1/projects/exampleproject/zones/examplezone/disks', headers=headers)
+
+        self.assert_ok(response)
+
+    def test_list_disks_with_name_filter(self):
+
+        get = mock.Mock()
+        get.return_value.text = read_file('tests/data/valid_describe_volumes.json')
+        get.return_value.status_code = 200
+
+        with mock.patch('requests.get', get):
+
+            headers = {'authorization': 'Bearer ' + str(GStackAppTestCase.access_token)}
+            response = self.get(
+                '/compute/v1/projects/projectid/zones/zonename/disks?filter=name+eq+volumename',
+                headers=headers)
+
+        self.assert_ok(response)
+
+    def test_aggregated_list_disks(self):
+
+        get = mock.Mock()
+        get.return_value.text = read_file('tests/data/valid_describe_volumes.json')
+        get.return_value.status_code = 200
+
+        get_zones = mock.Mock()
+        get_zones.return_value = json.loads(read_file('tests/data/valid_describe_zone.json'))
+
+        with mock.patch('requests.get', get):
+            with mock.patch(
+                'gstack.controllers.zones._get_zones',
+                get_zones
+            ):
+                headers = {'authorization': 'Bearer ' + str(GStackAppTestCase.access_token)}
+                response = self.get('/compute/v1/projects/projectid/aggregated/disks', headers=headers)
+
+        self.assert_ok(response)
+
+    def test_aggregated_list_disks_with_name_filter(self):
+
+        get = mock.Mock()
+        get.return_value.text = read_file('tests/data/valid_describe_volume.json')
+        get.return_value.status_code = 200
+
+        get_zones = mock.Mock()
+        get_zones.return_value = json.loads(read_file('tests/data/valid_describe_zone.json'))
+
+        with mock.patch('requests.get', get):
+            with mock.patch(
+                'gstack.controllers.zones._get_zones',
+                get_zones
+            ):
+                headers = {'authorization': 'Bearer ' + str(GStackAppTestCase.access_token)}
+                response = self.get(
+                    '/compute/v1/projects/projectid/aggregated/disks?filter=name+eq+volumename',
+                    headers=headers)
+
+        self.assert_ok(response)
+
+    def test_get_disk(self):
+
+        get = mock.Mock()
+        get.return_value.text = read_file('tests/data/valid_describe_volume.json')
+        get.return_value.status_code = 200
+
+        with mock.patch('requests.get', get):
+            headers = {'authorization': 'Bearer ' + str(GStackAppTestCase.access_token)}
+            response = self.get('/compute/v1/projects/exampleproject/zones/examplezone/disks/volumename', headers=headers)
+
+        self.assert_ok(response)
+
+    def test_get_disk_disk_not_found(self):
+
+        get = mock.Mock()
+        get.return_value.text = read_file('tests/data/empty_describe_volumes.json')
+        get.return_value.status_code = 200
+
+        with mock.patch('requests.get', get):
+            headers = {'authorization': 'Bearer ' + str(GStackAppTestCase.access_token)}
+            response = self.get('/compute/v1/projects/exampleproject/zones/examplezone/disks/volumename', headers=headers)
+
+        self.assert_not_found(response)
+
+        assert 'The resource \'/compute/v1/projects/exampleproject/zones/examplezone/disks/volumename\' was not found' \
+                in response.data
+
diff --git a/tests/firewalls_tests.py b/tests/firewalls_tests.py
new file mode 100644
index 0000000..28f7607
--- /dev/null
+++ b/tests/firewalls_tests.py
@@ -0,0 +1,49 @@
+#!/usr/bin/env python
+# encoding: utf-8
+
+import mock
+import json
+
+from gstack.helpers import read_file
+from . import GStackAppTestCase
+
+class FirewallsTestCase(GStackAppTestCase):
+
+    def test_list_firewalls(self):
+
+        get = mock.Mock()
+        get.return_value.text = read_file('tests/data/valid_describe_security_groups.json')
+        get.return_value.status_code = 200
+
+        with mock.patch('requests.get', get):
+            headers = {'authorization': 'Bearer ' + str(GStackAppTestCase.access_token)}
+            response = self.get('/compute/v1/projects/exampleproject/global/firewalls', headers=headers)
+
+        self.assert_ok(response)
+
+
+    def test_get_firewall(self):
+
+        get = mock.Mock()
+        get.return_value.text = read_file('tests/data/valid_describe_security_group.json')
+        get.return_value.status_code = 200
+
+        with mock.patch('requests.get', get):
+            headers = {'authorization': 'Bearer ' + str(GStackAppTestCase.access_token)}
+            response = self.get('/compute/v1/projects/exampleproject/global/firewalls/securitygroupname', headers=headers)
+
+        self.assert_ok(response)
+
+    def test_get_firewall_firewall_not_found(self):
+
+        get = mock.Mock()
+        get.return_value.text = read_file('tests/data/empty_describe_security_groups.json')
+        get.return_value.status_code = 200
+
+        with mock.patch('requests.get', get):
+            headers = {'authorization': 'Bearer ' + str(GStackAppTestCase.access_token)}
+            response = self.get('/compute/v1/projects/exampleproject/global/firewalls/securitygroupname', headers=headers)
+
+        self.assert_not_found(response)
+        assert 'The resource \'/compute/v1/projects/exampleproject/global/firewalls/securitygroupname\' was not found' \
+                in response.data
diff --git a/tests/images_tests.py b/tests/images_tests.py
new file mode 100644
index 0000000..8a9f2c0
--- /dev/null
+++ b/tests/images_tests.py
@@ -0,0 +1,47 @@
+#!/usr/bin/env python
+# encoding: utf-8
+
+import mock
+
+from gstack.helpers import read_file
+from . import GStackAppTestCase
+
+class ImagesTestCase(GStackAppTestCase):
+
+    def test_list_images(self):
+
+        get = mock.Mock()
+        get.return_value.text = read_file('tests/data/valid_describe_images.json')
+        get.return_value.status_code = 200
+
+        with mock.patch('requests.get', get):
+            headers = {'authorization': 'Bearer ' + str(GStackAppTestCase.access_token)}
+            response = self.get('/compute/v1/projects/exampleproject/global/images', headers=headers)
+
+        self.assert_ok(response)
+
+    def test_get_image(self):
+
+        get = mock.Mock()
+        get.return_value.text = read_file('tests/data/valid_describe_images.json')
+        get.return_value.status_code = 200
+
+        with mock.patch('requests.get', get):
+            headers = {'authorization': 'Bearer ' + str(GStackAppTestCase.access_token)}
+            response = self.get('/compute/v1/projects/exampleproject/global/images/imagename', headers=headers)
+
+        self.assert_ok(response)
+
+    def test_get_image_image_not_found(self):
+
+        get = mock.Mock()
+        get.return_value.text = read_file('tests/data/empty_describe_images.json')
+        get.return_value.status_code = 200
+
+        with mock.patch('requests.get', get):
+            headers = {'authorization': 'Bearer ' + str(GStackAppTestCase.access_token)}
+            response = self.get('/compute/v1/projects/exampleproject/global/images/imagename', headers=headers)
+
+        self.assert_not_found(response)
+        assert 'The resource \'/compute/v1/projects/exampleproject/global/images/imagename\' was not found' \
+                in response.data
diff --git a/tests/instances_tests.py b/tests/instances_tests.py
new file mode 100644
index 0000000..47e71c9
--- /dev/null
+++ b/tests/instances_tests.py
@@ -0,0 +1,119 @@
+#!/usr/bin/env python
+# encoding: utf-8
+
+import mock
+import json
+
+from gstack.helpers import read_file
+from . import GStackAppTestCase
+
+class InstancesTestCase(GStackAppTestCase):
+
+    def test_list_instances(self):
+
+        get = mock.Mock()
+        get.return_value.text = read_file('tests/data/valid_describe_instances.json')
+        get.return_value.status_code = 200
+
+        with mock.patch('requests.get', get):
+            headers = {'authorization': 'Bearer ' + str(GStackAppTestCase.access_token)}
+            response = self.get('/compute/v1/projects/exampleproject/zones/examplezone/instances', headers=headers)
+
+        self.assert_ok(response)
+
+    def test_aggregated_list_instances(self):
+
+        get = mock.Mock()
+        get.return_value.text = read_file('tests/data/valid_describe_instances.json')
+        get.return_value.status_code = 200
+
+        get_zones = mock.Mock()
+        get_zones.return_value = json.loads(read_file('tests/data/valid_describe_zone.json'))
+
+        with mock.patch('requests.get', get):
+            with mock.patch(
+                'gstack.controllers.zones._get_zones',
+                get_zones
+            ):
+                headers = {'authorization': 'Bearer ' + str(GStackAppTestCase.access_token)}
+                response = self.get('/compute/v1/projects/projectid/aggregated/instances', headers=headers)
+
+        self.assert_ok(response)
+
+    def test_list_instances_with_name_filter(self):
+
+        get = mock.Mock()
+        get.return_value.text = read_file('tests/data/valid_describe_instance.json')
+        get.return_value.status_code = 200
+
+        with mock.patch('requests.get', get):
+            headers = {'authorization': 'Bearer ' + str(GStackAppTestCase.access_token)}
+            response = self.get(
+                '/compute/v1/projects/projectid/zones/zonename/instances?filter=name+eq+instancename',
+                headers=headers)
+
+        self.assert_ok(response)
+
+    def test_aggregated_list_instances_with_name_filter(self):
+
+        get = mock.Mock()
+        get.return_value.text = read_file('tests/data/valid_describe_instance.json')
+        get.return_value.status_code = 200
+
+        get_zones = mock.Mock()
+        get_zones.return_value = json.loads(read_file('tests/data/valid_describe_zone.json'))
+
+        with mock.patch('requests.get', get):
+            with mock.patch(
+                'gstack.controllers.zones._get_zones',
+                get_zones
+            ):
+                headers = {'authorization': 'Bearer ' + str(GStackAppTestCase.access_token)}
+                response = self.get(
+                    '/compute/v1/projects/projectid/aggregated/instances?filter=name+eq+instancename',
+                    headers=headers)
+
+        self.assert_ok(response)
+
+    def test_get_instance(self):
+
+        get = mock.Mock()
+        get.return_value.text = read_file('tests/data/valid_describe_instance.json')
+        get.return_value.status_code = 200
+
+        with mock.patch('requests.get', get):
+            headers = {'authorization': 'Bearer ' + str(GStackAppTestCase.access_token)}
+            response = self.get('/compute/v1/projects/exampleproject/zones/examplezone/instances/instancename', headers=headers)
+
+        self.assert_ok(response)
+
+    def test_get_instance_instance_not_found(self):
+
+        get = mock.Mock()
+        get.return_value.text = read_file('tests/data/empty_describe_instances.json')
+        get.return_value.status_code = 200
+
+        with mock.patch('requests.get', get):
+            headers = {'authorization': 'Bearer ' + str(GStackAppTestCase.access_token)}
+            response = self.get('/compute/v1/projects/exampleproject/zones/examplezone/instances/instancename', headers=headers)
+
+        self.assert_not_found(response)
+        assert 'The resource \'/compute/v1/projects/exampleproject/zones/examplezone/instances/instancename\' was not found' \
+                in response.data
+
+    #def test_delete_instance(self):
+
+    #    get = mock.Mock()
+    #    get.return_value.text = read_file('tests/data/valid_terminate_instance.json')
+    #    get.return_value.status_code = 200
+
+    #    get_instance_id = mock.Mock()
+    #    get_instance_id.return_value = {'id':'virtualmachineid'}
+
+    #    with mock.patch('requests.get', get):
+    #        with mock.patch('gstack.controllers.instances._get_virtual_machine_by_name',get_instance_id):
+    #            headers = {'authorization': 'Bearer ' + str(GStackAppTestCase.access_token)}
+    #            response = self.delete('/compute/v1/projects/exampleproject/zones/examplezone/instances/instancename', headers=headers)
+
+    #    self.assert_ok(response)
+
diff --git a/tests/machine_type_tests.py b/tests/machine_type_tests.py
new file mode 100644
index 0000000..f6e882d
--- /dev/null
+++ b/tests/machine_type_tests.py
@@ -0,0 +1,83 @@
+#!/usr/bin/env python
+# encoding: utf-8
+
+import mock
+import json
+
+from gstack.helpers import read_file
+from . import GStackAppTestCase
+
+class MachineTypesTestCase(GStackAppTestCase):
+
+    def test_list_machine_types(self):
+
+        get = mock.Mock()
+        get.return_value.text = read_file('tests/data/valid_describe_service_offerings.json')
+        get.return_value.status_code = 200
+
+        with mock.patch('requests.get', get):
+            headers = {'authorization': 'Bearer ' + str(GStackAppTestCase.access_token)}
+            response = self.get('/compute/v1/projects/exampleproject/zones/examplezone/machineTypes', headers=headers)
+
+        self.assert_ok(response)
+
+    def test_aggregated_list_machine_types(self):
+
+        get = mock.Mock()
+        get.return_value.text = read_file('tests/data/valid_describe_service_offerings.json')
+        get.return_value.status_code = 200
+
+        get_zones = mock.Mock()
+        get_zones.return_value = json.loads(read_file('tests/data/valid_describe_zone.json'))
+
+        with mock.patch('requests.get', get):
+            with mock.patch(
+                'gstack.controllers.zones._get_zones',
+                get_zones
+            ):
+                headers = {'authorization': 'Bearer ' + str(GStackAppTestCase.access_token)}
+                response = self.get('/compute/v1/projects/projectid/aggregated/machineTypes', headers=headers)
+
+        self.assert_ok(response)
+
+    def test_list_machine_types_with_name_filter(self):
+
+        get = mock.Mock()
+        get.return_value.text = read_file('tests/data/valid_describe_service_offering.json')
+        get.return_value.status_code = 200
+
+        with mock.patch('requests.get', get):
+            headers = {'authorization': 'Bearer ' + str(GStackAppTestCase.access_token)}
+            response = self.get(
+                '/compute/v1/projects/projectid/zones/zonename/machineTypes?filter=name+eq+machinetypename',
+                headers=headers)
+
+        self.assert_ok(response)
+
+
+    def test_get_machine_type(self):
+
+        get = mock.Mock()
+        get.return_value.text = read_file('tests/data/valid_describe_service_offering.json')
+        get.return_value.status_code = 200
+
+        with mock.patch('requests.get', get):
+            headers = {'authorization': 'Bearer ' + str(GStackAppTestCase.access_token)}
+            response = self.get('/compute/v1/projects/exampleproject/zones/examplezone/machineTypes/machinetypename', headers=headers)
+
+        self.assert_ok(response)
+
+    def test_get_machine_type_machine_type_not_found(self):
+
+        get = mock.Mock()
+        get.return_value.text = read_file('tests/data/valid_describe_service_offering.json')
+        get.return_value.status_code = 200
+
+        with mock.patch('requests.get', get):
+            headers = {'authorization': 'Bearer ' + str(GStackAppTestCase.access_token)}
+            response = self.get('/compute/v1/projects/exampleproject/zones/examplezone/machineTypes/invalidmachinetypename', headers=headers)
+
+        self.assert_not_found(response)
+        assert 'The resource \'/compute/v1/projects/exampleproject/zones/examplezone/machineTypes/invalidmachinetypename\' was not found' \
+                in response.data
+
diff --git a/tests/networks_tests.py b/tests/networks_tests.py
new file mode 100644
index 0000000..2f2f533
--- /dev/null
+++ b/tests/networks_tests.py
@@ -0,0 +1,47 @@
+#!/usr/bin/env python
+# encoding: utf-8
+
+import mock
+
+from gstack.helpers import read_file
+from . import GStackAppTestCase
+
+class NetworksTestCase(GStackAppTestCase):
+
+    def test_list_networks(self):
+
+        get = mock.Mock()
+        get.return_value.text = read_file('tests/data/valid_describe_security_groups.json')
+        get.return_value.status_code = 200
+
+        with mock.patch('requests.get', get):
+            headers = {'authorization': 'Bearer ' + str(GStackAppTestCase.access_token)}
+            response = self.get('/compute/v1/projects/exampleproject/global/networks', headers=headers)
+
+        self.assert_ok(response)
+
+    def test_get_network(self):
+
+        get = mock.Mock()
+        get.return_value.text = read_file('tests/data/valid_describe_security_group.json')
+        get.return_value.status_code = 200
+
+        with mock.patch('requests.get', get):
+            headers = {'authorization': 'Bearer ' + str(GStackAppTestCase.access_token)}
+            response = self.get('/compute/v1/projects/exampleproject/global/networks/networkname', headers=headers)
+
+        self.assert_ok(response)
+
+    def test_get_network_network_not_found(self):
+
+        get = mock.Mock()
+        get.return_value.text = read_file('tests/data/empty_describe_security_groups.json')
+        get.return_value.status_code = 200
+
+        with mock.patch('requests.get', get):
+            headers = {'authorization': 'Bearer ' + str(GStackAppTestCase.access_token)}
+            response = self.get('/compute/v1/projects/exampleproject/global/networks/networkname', headers=headers)
+
+        self.assert_not_found(response)
+        assert 'The resource \'/compute/v1/projects/exampleproject/global/networks/networkname\'' \
+                in response.data
diff --git a/tests/regions_tests.py b/tests/regions_tests.py
new file mode 100644
index 0000000..93f0774
--- /dev/null
+++ b/tests/regions_tests.py
@@ -0,0 +1,49 @@
+#!/usr/bin/env python
+# encoding: utf-8
+
+import mock
+
+from gstack.helpers import read_file
+from . import GStackAppTestCase
+
+class RegionsTestCase(GStackAppTestCase):
+
+    def test_list_regions(self):
+
+        get = mock.Mock()
+        get.return_value.text = read_file('tests/data/valid_describe_regions.json')
+        get.return_value.status_code = 200
+
+        with mock.patch('requests.get', get):
+            headers = {'authorization': 'Bearer ' + str(GStackAppTestCase.access_token)}
+            response = self.get('/compute/v1/projects/exampleproject/regions', headers=headers)
+
+        self.assert_ok(response)
+
+
+    def test_get_region(self):
+
+        get = mock.Mock()
+        get.return_value.text = read_file('tests/data/valid_describe_regions.json')
+        get.return_value.status_code = 200
+
+        with mock.patch('requests.get', get):
+            headers = {'authorization': 'Bearer ' + str(GStackAppTestCase.access_token)}
+            response = self.get('/compute/v1/projects/exampleproject/regions/regionname', headers=headers)
+
+        self.assert_ok(response)
+
+    def test_get_region_region_not_found(self):
+
+        get = mock.Mock()
+        get.return_value.text = read_file('tests/data/valid_describe_regions.json')
+        get.return_value.status_code = 200
+
+        with mock.patch('requests.get', get):
+            headers = {'authorization': 'Bearer ' + str(GStackAppTestCase.access_token)}
+            response = self.get('/compute/v1/projects/exampleproject/regions/invalidregionname', headers=headers)
+
+        self.assert_not_found(response)
+        assert 'The resource \'/compute/v1/projects/exampleproject/regions/invalidregionname\' was not found' \
+                in response.data
+
diff --git a/tests/settings.py b/tests/settings.py
new file mode 100644
index 0000000..867bf91
--- /dev/null
+++ b/tests/settings.py
@@ -0,0 +1,11 @@
+PATH = 'compute/v1/projects/'
+GSTACK_BIND_ADDRESS = 'localhost'
+GSTACK_PORT = '5000'
+CLOUDSTACK_HOST = 'api.exoscale.ch'
+CLOUDSTACK_PORT = '443'
+CLOUDSTACK_PROTOCOL = 'https'
+CLOUDSTACK_PATH = '/compute'
+
+DEBUG=False
+
+TESTING = True
diff --git a/tests/utils.py b/tests/utils.py
new file mode 100644
index 0000000..8101ea7
--- /dev/null
+++ b/tests/utils.py
@@ -0,0 +1,47 @@
+#!/usr/bin/env python
+# encoding: utf-8
+"""
+    tests.utils
+    ~~~~~~~~~~~
+
+    test utilities
+"""
+
+
+class FlaskTestCaseMixin(object):
+
+    @staticmethod
+    def _html_data(kwargs):
+        if not kwargs.get('content_type'):
+            kwargs['content_type'] = 'application/x-www-form-urlencoded'
+
+        return kwargs
+
+    @staticmethod
+    def _request(method, *args, **kwargs):
+        return method(*args, **kwargs)
+
+    def post(self, *args, **kwargs):
+        return (
+            self._request(self.client.post, *args, **self._html_data(kwargs))
+        )
+
+    def get(self, *args, **kwargs):
+        return self._request(self.client.get, *args, **kwargs)
+
+    def delete(self, *args, **kwargs):
+        return self._request(self.client.delete, *args, **kwargs)
+
+    def assert_status_code(self, response, status_code):
+        self.assertEquals(status_code, response.status_code)
+        return response
+
+    def assert_ok(self, response):
+        return self.assert_status_code(response, 200)
+
+    def assert_bad_request(self, response):
+        return self.assert_status_code(response, 400)
+
+    def assert_not_found(self, response):
+        return self.assert_status_code(response, 404)
+
diff --git a/tests/zones_tests.py b/tests/zones_tests.py
new file mode 100644
index 0000000..fa89ad8
--- /dev/null
+++ b/tests/zones_tests.py
@@ -0,0 +1,47 @@
+#!/usr/bin/env python
+# encoding: utf-8
+
+import mock
+
+from gstack.helpers import read_file
+from . import GStackAppTestCase
+
+class ZonesTestCase(GStackAppTestCase):
+
+    def test_list_zones(self):
+
+        get = mock.Mock()
+        get.return_value.text = read_file('tests/data/valid_describe_zone.json')
+        get.return_value.status_code = 200
+
+        with mock.patch('requests.get', get):
+            headers = {'authorization': 'Bearer ' + str(GStackAppTestCase.access_token)}
+            response = self.get('/compute/v1/projects/exampleproject/zones', headers=headers)
+
+        self.assert_ok(response)
+
+    def test_get_zone(self):
+
+        get = mock.Mock()
+        get.return_value.text = read_file('tests/data/valid_describe_zone.json')
+        get.return_value.status_code = 200
+
+        with mock.patch('requests.get', get):
+            headers = {'authorization': 'Bearer ' + str(GStackAppTestCase.access_token)}
+            response = self.get('/compute/v1/projects/exampleproject/zones/zonename', headers=headers)
+
+        self.assert_ok(response)
+
+    def test_get_zone_zone_not_found(self):
+
+        get = mock.Mock()
+        get.return_value.text = read_file('tests/data/empty_describe_zone.json')
+        get.return_value.status_code = 200
+
+        with mock.patch('requests.get', get):
+            headers = {'authorization': 'Bearer ' + str(GStackAppTestCase.access_token)}
+            response = self.get('/compute/v1/projects/exampleproject/zones/zonename', headers=headers)
+
+        self.assert_not_found(response)
+        assert 'The resource \'/compute/v1/projects/exampleproject/zones/zonename\' was not found' \
+                in response.data