Added gabbi API tests

Added tests on all the core API.

Change-Id: I6610ddbe5071ce1231c80319ab1b43c9cd626de4
Depends-On: I2231521708f163d80538fe2e7e5559d4c06cf0c3
Depends-On: Ib05d533eecd32159c1bc7a639db1a49df678f726
Depends-On: I3302617c266111ba1d705d04f1acf20b9348614d
Depends-On: I0ab0f1b2a75d39b8d371c54a264856db69adc2fd
This commit is contained in:
Stéphane Albert 2015-08-28 18:18:34 +02:00
parent 23458597ab
commit 04a9f0cd43
17 changed files with 1046 additions and 13 deletions

View File

@ -2,3 +2,4 @@
test_command=${PYTHON:-python} -m subunit.run discover -t ./ ./cloudkitty/tests $LISTOPT $IDOPTION
test_id_option=--load-list $IDFILE
test_list_option=--list
group_regex=gabbi\.driver\.(test_[^_]+_[^_]+)

View File

@ -48,12 +48,13 @@ auth_opts = [
api_opts = [
cfg.StrOpt('host_ip',
default="0.0.0.0",
help="Host serving the API."
),
help="Host serving the API."),
cfg.IntOpt('port',
default=8888,
help="Host port serving the API."
),
help="Host port serving the API."),
cfg.BoolOpt('pecan_debug',
default=False,
help='Toggle Pecan Debug Middleware.'),
]
CONF = cfg.CONF
@ -87,7 +88,7 @@ def setup_app(pecan_config=None, extra_hooks=None):
app_conf.app.root,
static_root=app_conf.app.static_root,
template_path=app_conf.app.template_path,
debug=CONF.debug,
debug=CONF.api.pecan_debug,
force_canonical=getattr(app_conf.app, 'force_canonical', True),
hooks=app_hooks,
guess_content_type_from_ext=False

View File

@ -102,7 +102,7 @@ class ModulesController(rest.RestController, RatingModulesMixin):
with lock:
module = self.extensions[module_id]
except KeyError:
pecan.abort(404, 'Module not found.')
pecan.abort(400, 'Module not found.')
infos = module.obj.module_info.copy()
infos['module_id'] = infos.pop('name')
return rating_models.CloudkittyModule(**infos)
@ -218,3 +218,4 @@ class RatingController(rest.RestController):
policy.enforce(pecan.request.context, 'rating:module_config', {})
self.modules.reload_extensions()
self.module_config.reload_extensions()
self.module_config.expose_modules()

View File

@ -54,25 +54,28 @@ class DataFramesController(rest.RestController):
backend = pecan.request.storage_backend
dataframes = []
try:
frames = backend.get_time_frame(begin_ts, end_ts,
frames = backend.get_time_frame(begin_ts,
end_ts,
tenant_id=tenant_id,
res_type=resource_type)
for frame in frames:
for service, data_list in frame['usage'].items():
frame_tenant = None
resources = []
for data in data_list:
desc = data['desc'] if data['desc'] else {}
price = decimal.Decimal(data['rating']['price'])
price = decimal.Decimal(str(data['rating']['price']))
resource = storage_models.RatedResource(
service=service,
desc=desc,
volume=data['vol']['qty'],
rating=price)
frame_tenant = data['tenant_id']
resources.append(resource)
dataframe = storage_models.DataFrame(
begin=ck_utils.iso2dt(frame['period']['begin']),
end=ck_utils.iso2dt(frame['period']['end']),
tenant_id=tenant_id, # FIXME
tenant_id=frame_tenant,
resources=resources)
dataframes.append(dataframe)
except ck_storage.NoTimeFrame:

View File

@ -60,7 +60,7 @@ class RatedDataFrame(Base, models.ModelBase):
# Volume informations
vol_dict = {}
vol_dict['qty'] = self.qty
vol_dict['qty'] = self.qty.normalize()
vol_dict['unit'] = self.unit
res_dict = {}

View File

@ -19,11 +19,20 @@ from oslo_config import fixture as config_fixture
from oslotest import base
import testscenarios
from cloudkitty import collector
from cloudkitty import db
from cloudkitty.db import api as ck_db_api
from cloudkitty import rating
class FakeCollectorModule(collector.BaseCollector):
collector_name = 'test_fake'
dependencies = tuple()
def __init__(self):
super(FakeCollectorModule, self).__init__([], period=3600)
class FakeRatingModule(rating.RatingProcessorBase):
module_name = 'fake'
description = 'fake rating module'

View File

View File

@ -0,0 +1,311 @@
# -*- coding: utf-8 -*-
# Copyright 2015 Objectif Libre
#
# Licensed 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.
#
# @author: Stéphane Albert
#
import abc
import decimal
import os
from gabbi import fixture
import mock
from oslo_config import cfg
from oslo_config import fixture as conf_fixture
from oslo_db.sqlalchemy import utils
import oslo_messaging as messaging
from oslo_messaging import conffixture
from oslo_policy import opts as policy_opts
import six
from stevedore import driver
from stevedore import extension
from wsme import types as wtypes
import wsmeext.pecan as wsme_pecan
from cloudkitty.api import app
from cloudkitty.common import rpc
from cloudkitty import db
from cloudkitty.db import api as ck_db_api
from cloudkitty import rating
from cloudkitty import storage
from cloudkitty.storage.sqlalchemy import models
from cloudkitty import tests
from cloudkitty import utils as ck_utils
INITIAL_TIMESTAMP = 1420070400
class UUIDFixture(fixture.GabbiFixture):
def start_fixture(self):
FAKE_UUID = '6c1b8a30-797f-4b7e-ad66-9879b79059fb'
patcher = mock.patch(
'oslo_utils.uuidutils.generate_uuid',
return_value=FAKE_UUID)
patcher.start()
self.patcher = patcher
def stop_fixture(self):
self.patcher.stop()
@six.add_metaclass(abc.ABCMeta)
class BaseExtensionFixture(fixture.GabbiFixture):
klass = None
namespace = None
stevedore_mgr = None
assert_args = {}
@abc.abstractmethod
def setup_fake_modules(self):
pass
def start_fixture(self):
fake_extensions = self.setup_fake_modules()
self.mock = mock.patch(self.klass)
fake_mgr = self.stevedore_mgr.make_test_instance(
fake_extensions,
self.namespace)
self.patch = self.mock.start()
self.patch.return_value = fake_mgr
def stop_fixture(self):
self.patch.assert_called_with(
self.namespace,
**self.assert_args)
self.mock.stop()
class CollectorExtensionsFixture(BaseExtensionFixture):
klass = 'stevedore.driver.DriverManager'
namespace = 'cloudkitty.collector.backends'
stevedore_mgr = driver.DriverManager
assert_args = {
'invoke_kwds': {'period': 3600},
'invoke_on_load': True}
def setup_fake_modules(self):
def fake_metric(start,
end=None,
project_id=None,
q_filter=None):
return None
fake_module1 = tests.FakeCollectorModule()
fake_module1.collector_name = 'fake1'
fake_module1.get_compute = fake_metric
fake_module2 = tests.FakeCollectorModule()
fake_module2.collector_name = 'fake2'
fake_module2.get_volume = fake_metric
fake_module3 = tests.FakeCollectorModule()
fake_module3.collector_name = 'fake3'
fake_module3.get_compute = fake_metric
fake_extensions = [
extension.Extension(
'fake1',
'cloudkitty.tests.FakeCollectorModule1',
None,
fake_module1),
extension.Extension(
'fake2',
'cloudkitty.tests.FakeCollectorModule2',
None,
fake_module2),
extension.Extension(
'fake3',
'cloudkitty.tests.FakeCollectorModule3',
None,
fake_module3)]
return fake_extensions[0]
class RatingModulesFixture(BaseExtensionFixture):
klass = 'stevedore.extension.ExtensionManager'
namespace = 'cloudkitty.rating.processors'
stevedore_mgr = extension.ExtensionManager
assert_args = {
'invoke_on_load': True}
def setup_fake_modules(self):
class FakeConfigController(rating.RatingRestControllerBase):
_custom_actions = {
'test': ['GET']
}
@wsme_pecan.wsexpose(wtypes.text)
def get_test(self):
"""Return the list of every mapping type available.
"""
return 'OK'
fake_module1 = tests.FakeRatingModule()
fake_module1.module_name = 'fake1'
fake_module1.set_priority(3)
fake_module2 = tests.FakeRatingModule()
fake_module2.module_name = 'fake2'
fake_module2.config_controller = FakeConfigController
fake_module2.set_priority(1)
fake_module3 = tests.FakeRatingModule()
fake_module3.module_name = 'fake3'
fake_module3.set_priority(2)
fake_extensions = [
extension.Extension(
'fake1',
'cloudkitty.tests.FakeRatingModule1',
None,
fake_module1),
extension.Extension(
'fake2',
'cloudkitty.tests.FakeRatingModule2',
None,
fake_module2),
extension.Extension(
'fake3',
'cloudkitty.tests.FakeRatingModule3',
None,
fake_module3)]
return fake_extensions
class ConfigFixture(fixture.GabbiFixture):
def start_fixture(self):
self.conf = None
conf = conf_fixture.Config().conf
policy_opts.set_defaults(conf)
msg_conf = conffixture.ConfFixture(conf)
msg_conf.transport_driver = 'fake'
conf.import_group('api', 'cloudkitty.api.app')
conf.set_override('auth_strategy', 'noauth')
conf.set_override('connection', 'sqlite:///', 'database')
conf.set_override('policy_file',
os.path.abspath('etc/cloudkitty/policy.json'),
group='oslo_policy')
conf.import_group('storage', 'cloudkitty.storage')
conf.set_override('backend', 'sqlalchemy', 'storage')
self.conf = conf
self.conn = ck_db_api.get_instance()
migration = self.conn.get_migration()
migration.upgrade('head')
def stop_fixture(self):
if self.conf:
self.conf.reset()
db.get_engine().dispose()
class BaseFakeRPC(fixture.GabbiFixture):
endpoint = None
def start_fixture(self):
rpc.init()
target = messaging.Target(topic='cloudkitty',
server=cfg.CONF.host,
version='1.0')
endpoints = [
self.endpoint()
]
self.server = rpc.get_server(target, endpoints)
self.server.start()
def stop_fixture(self):
self.server.stop()
class QuoteFakeRPC(BaseFakeRPC):
class FakeRPCEndpoint(object):
target = messaging.Target(namespace='rating',
version='1.0')
def quote(self, ctxt, res_data):
return str(1.0)
endpoint = FakeRPCEndpoint
class BaseStorageDataFixture(fixture.GabbiFixture):
def create_fake_data(self, begin, end):
data = [{
"period": {
"begin": begin,
"end": end},
"usage": {
"compute": [
{
"desc": {
"dummy": True,
"fake_meta": 1.0},
"vol": {
"qty": 1,
"unit": "nothing"},
"rating": {
"price": decimal.Decimal('1.337')}}]}}]
return data
def start_fixture(self):
self.storage = storage.get_storage()
self.storage.init()
self.initialize_data()
def stop_fixture(self):
model = models.RatedDataFrame
session = db.get_session()
q = utils.model_query(
model,
session)
q.delete()
class StorageDataFixture(BaseStorageDataFixture):
def initialize_data(self):
nodata_duration = (24 * 3 + 12) * 3600
tenant_list = ['8f82cc70-e50c-466e-8624-24bdea811375',
'7606a24a-b8ad-4ae0-be6c-3d7a41334a2e']
for tenant in tenant_list:
for i in range(INITIAL_TIMESTAMP,
INITIAL_TIMESTAMP + nodata_duration,
3600):
self.storage.nodata(i, i + 3600, tenant)
data_ts = INITIAL_TIMESTAMP + nodata_duration + 3600
data_duration = (24 * 2 + 8) * 3600
for i in range(data_ts,
data_ts + data_duration,
3600):
data = self.create_fake_data(i, i + 3600)
self.storage.append(data, tenant_list[0])
half_duration = int(data_duration / 2)
for i in range(data_ts,
data_ts + half_duration,
3600):
data = self.create_fake_data(i, i + 3600)
self.storage.append(data, tenant_list[1])
for i in range(data_ts + half_duration + 3600,
data_ts + data_duration,
3600):
self.storage.nodata(i, i + 3600, tenant_list[1])
class NowStorageDataFixture(BaseStorageDataFixture):
def initialize_data(self):
begin = ck_utils.get_month_start_timestamp()
for i in range(begin,
begin + 3600 * 12,
3600):
data = self.create_fake_data(i, i + 3600)
self.storage.append(data,
'3d9a1b33-482f-42fd-aef9-b575a3da9369')
def setup_app():
rpc.init()
return app.setup_app()

View File

@ -0,0 +1,158 @@
fixtures:
- ConfigFixture
- RatingModulesFixture
- QuoteFakeRPC
tests:
- name: reload list of modules available
url: /v1/billing/reload_modules
status: 204
- name: list all modules available
url: /v1/billing/modules
status: 200
response_json_paths:
$.modules.`len`: 3
$.modules[0].priority: 3
$.modules[0].module_id: "fake1"
$.modules[0].enabled: false
$.modules[0].description: "fake rating module"
$.modules[0].hot-config: false
$.modules[1].priority: 1
$.modules[1].module_id: "fake2"
$.modules[1].enabled: false
$.modules[1].description: "fake rating module"
$.modules[1].hot-config: false
$.modules[2].priority: 2
$.modules[2].module_id: "fake3"
$.modules[2].enabled: false
$.modules[2].description: "fake rating module"
$.modules[2].hot-config: false
- name: get information of one module
url: /v1/billing/modules/fake2
status: 200
response_json_paths:
$.priority: 1
$.module_id: "fake2"
$.enabled: false
$.description: "fake rating module"
$.hot-config: false
- name: get information of a unknown module
url: /v1/billing/modules/fakb
status: 400
response_strings:
- "Module not found."
- name: change priority of a module
url: /v1/billing/modules/fake3
method: PUT
request_headers:
content-type: application/json
x-roles: admin
data:
module_id: "fake3"
priority: 5
status: 302
response_headers:
location: "$SCHEME://$NETLOC/v1/billing/modules/fake3"
- name: get information of the modified module (priority)
url: $LOCATION
status: 200
response_json_paths:
$.priority: 5
$.module_id: "fake3"
$.enabled: false
$.description: "fake rating module"
$.hot-config: false
- name: change enabled status of a module
url: /v1/billing/modules/fake3
method: PUT
request_headers:
content-type: application/json
x-roles: admin
data:
module_id: "fake3"
enabled: true
status: 302
response_headers:
location: "$SCHEME://$NETLOC/v1/billing/modules/fake3"
- name: get information of the modified module (status)
url: $LOCATION
status: 200
response_json_paths:
$.priority: 5
$.module_id: "fake3"
$.enabled: true
$.description: "fake rating module"
$.hot-config: false
- name: change status and priority of a module
url: /v1/billing/modules/fake3
method: PUT
request_headers:
content-type: application/json
x-roles: admin
data:
module_id: "fake3"
priority: 3
enabled: false
status: 302
response_headers:
location: "$SCHEME://$NETLOC/v1/billing/modules/fake3"
- name: get information of the modified module (both)
url: $LOCATION
status: 200
response_json_paths:
$.priority: 3
$.module_id: "fake3"
$.enabled: false
$.description: "fake rating module"
$.hot-config: false
- name: get a quote for a resource description
url: /v1/billing/quote
method: POST
request_headers:
content-type: application/json
x-roles: admin
data:
resources:
- service: "compute"
volume: "1.0"
desc:
test: 1
status: 200
response_strings:
- "1.0"
- name: module without custom API should use notconfigurable controller (GET)
url: /v1/billing/module_config/fake1
status: 409
response_strings:
- "Module is not configurable"
- name: module without custom API should use notconfigurable controller (POST)
url: /v1/billing/module_config/fake1
method: POST
status: 409
response_strings:
- "Module is not configurable"
- name: module without custom API should use notconfigurable controller (PUT)
url: /v1/billing/module_config/fake1
method: PUT
status: 409
response_strings:
- "Module is not configurable"
- name: verify module exposes its custom API
url: /v1/billing/module_config/fake2/test
status: 200
response_strings:
- "OK"

View File

@ -0,0 +1,138 @@
fixtures:
- ConfigFixture
tests:
# States
- name: check collector is disabled by default
url: /v1/collector/fake1/states
status: 200
response_json_paths:
$.enabled: false
$.name: "fake1"
- name: enable collector
url: /v1/collector/fake1/states
method: PUT
request_headers:
content-type: application/json
x-roles: admin
data:
name: "fake1"
enabled: true
status: 200
response_json_paths:
$.enabled: true
$.name: "fake1"
- name: check collector state isolation
url: /v1/collector/fake2/states
status: 200
response_json_paths:
$.enabled: false
$.name: "fake2"
- name: disable collector
url: /v1/collector/fake1/states
method: PUT
request_headers:
content-type: application/json
x-roles: admin
data:
name: "fake1"
enabled: false
status: 200
response_json_paths:
$.enabled: false
$.name: "fake1"
# Mappings
- name: get all mappings (empty)
url: /v1/collector/mappings
status: 200
response_json_paths:
$.mappings: []
- name: try to get an unknown mapping
url: /v1/collector/mappings/notfound
status: 400
response_strings:
- "No mapping for service: notfound"
- name: try to delete an unknown mapping
url: /v1/collector/mappings/notfound
method: DELETE
status: 400
response_strings:
- "No mapping for service: notfound"
- name: create mapping
url: /v1/collector/mappings/fake1/metric1
method: POST
request_headers:
content-type: application/json
x-roles: admin
status: 200
response_json_paths:
$.collector: "fake1"
$.service: "metric1"
- name: get all mappings
url: /v1/collector/mappings
status: 200
response_json_paths:
$.mappings[0].collector: "fake1"
$.mappings[0].service: "metric1"
- name: create second mapping
url: /v1/collector/mappings/fake2/metric8
method: POST
request_headers:
content-type: application/json
x-roles: admin
status: 200
response_json_paths:
$.collector: "fake2"
$.service: "metric8"
- name: get all mappings filtering on collector fake1
url: /v1/collector/mappings?collector=fake1
status: 200
response_json_paths:
$.mappings[0].collector: "fake1"
$.mappings[0].service: "metric1"
- name: get all mappings filtering on collector fake2
url: /v1/collector/mappings?collector=fake2
status: 200
response_json_paths:
$.mappings[0].collector: "fake2"
$.mappings[0].service: "metric8"
- name: get all mappings with no filtering
url: /v1/collector/mappings
status: 200
response_json_paths:
$.mappings.`len`: 2
$.mappings[0].collector: "fake1"
$.mappings[0].service: "metric1"
$.mappings[1].collector: "fake2"
$.mappings[1].service: "metric8"
- name: get a mapping filtering on service metric8
url: /v1/collector/mappings/metric8
status: 200
response_json_paths:
$.collector: "fake2"
$.service: "metric8"
- name: delete a mapping
url: /v1/collector/mappings/metric1
method: DELETE
status: 204
- name: check the mapping got deleted
url: /v1/collector/mappings/metric1
status: 400
response_strings:
- "No mapping for service: metric1"

View File

@ -0,0 +1,158 @@
fixtures:
- ConfigFixture
- RatingModulesFixture
- QuoteFakeRPC
tests:
- name: reload list of modules available
url: /v1/rating/reload_modules
status: 204
- name: list all modules available
url: /v1/rating/modules
status: 200
response_json_paths:
$.modules.`len`: 3
$.modules[0].priority: 3
$.modules[0].module_id: "fake1"
$.modules[0].enabled: false
$.modules[0].description: "fake rating module"
$.modules[0].hot-config: false
$.modules[1].priority: 1
$.modules[1].module_id: "fake2"
$.modules[1].enabled: false
$.modules[1].description: "fake rating module"
$.modules[1].hot-config: false
$.modules[2].priority: 2
$.modules[2].module_id: "fake3"
$.modules[2].enabled: false
$.modules[2].description: "fake rating module"
$.modules[2].hot-config: false
- name: get information of one module
url: /v1/rating/modules/fake2
status: 200
response_json_paths:
$.priority: 1
$.module_id: "fake2"
$.enabled: false
$.description: "fake rating module"
$.hot-config: false
- name: get information of a unknown module
url: /v1/rating/modules/fakb
status: 400
response_strings:
- "Module not found."
- name: change priority of a module
url: /v1/rating/modules/fake3
method: PUT
request_headers:
content-type: application/json
x-roles: admin
data:
module_id: "fake3"
priority: 5
status: 302
response_headers:
location: "$SCHEME://$NETLOC/v1/rating/modules/fake3"
- name: get information of the modified module (priority)
url: $LOCATION
status: 200
response_json_paths:
$.priority: 5
$.module_id: "fake3"
$.enabled: false
$.description: "fake rating module"
$.hot-config: false
- name: change enabled status of a module
url: /v1/rating/modules/fake3
method: PUT
request_headers:
content-type: application/json
x-roles: admin
data:
module_id: "fake3"
enabled: true
status: 302
response_headers:
location: "$SCHEME://$NETLOC/v1/rating/modules/fake3"
- name: get information of the modified module (status)
url: $LOCATION
status: 200
response_json_paths:
$.priority: 5
$.module_id: "fake3"
$.enabled: true
$.description: "fake rating module"
$.hot-config: false
- name: change status and priority of a module
url: /v1/rating/modules/fake3
method: PUT
request_headers:
content-type: application/json
x-roles: admin
data:
module_id: "fake3"
priority: 3
enabled: false
status: 302
response_headers:
location: "$SCHEME://$NETLOC/v1/rating/modules/fake3"
- name: get information of the modified module (both)
url: $LOCATION
status: 200
response_json_paths:
$.priority: 3
$.module_id: "fake3"
$.enabled: false
$.description: "fake rating module"
$.hot-config: false
- name: get a quote for a resource description
url: /v1/rating/quote
method: POST
request_headers:
content-type: application/json
x-roles: admin
data:
resources:
- service: "compute"
volume: "1.0"
desc:
test: 1
status: 200
response_strings:
- "1.0"
- name: module without custom API should use notconfigurable controller (GET)
url: /v1/rating/module_config/fake1
status: 409
response_strings:
- "Module is not configurable"
- name: module without custom API should use notconfigurable controller (POST)
url: /v1/rating/module_config/fake1
method: POST
status: 409
response_strings:
- "Module is not configurable"
- name: module without custom API should use notconfigurable controller (PUT)
url: /v1/rating/module_config/fake1
method: PUT
status: 409
response_strings:
- "Module is not configurable"
- name: verify module exposes its custom API
url: /v1/rating/module_config/fake2/test
status: 200
response_strings:
- "OK"

View File

@ -0,0 +1,68 @@
fixtures:
- ConfigFixture
- StorageDataFixture
- NowStorageDataFixture
tests:
- name: get period with two tenants
url: /v1/report/tenants
query_parameters:
begin: "2015-01-01T00:00:00"
end: "2015-01-04T00:00:00"
status: 200
response_strings:
- "8f82cc70-e50c-466e-8624-24bdea811375"
- "7606a24a-b8ad-4ae0-be6c-3d7a41334a2e"
- name: by default give tenants for the current month
url: /v1/report/tenants
status: 200
response_strings:
- "3d9a1b33-482f-42fd-aef9-b575a3da9369"
- name: get period with no tenants
url: /v1/report/tenants
query_parameters:
begin: "2015-02-01T00:00:00"
end: "2015-02-02T00:00:00"
status: 200
response_strings:
- "[]"
- name: get total for a period
url: /v1/report/total
query_parameters:
begin: "2015-01-01T00:00:00"
end: "2015-02-04T00:00:00"
status: 200
response_strings:
- "110.971"
- name: get total for a period filtering on first tenant
url: /v1/report/total
query_parameters:
begin: "2015-01-01T00:00:00"
end: "2015-02-04T00:00:00"
tenant_id: "8f82cc70-e50c-466e-8624-24bdea811375"
status: 200
response_strings:
- "73.535"
- name: get total for a period filtering on second tenant
url: /v1/report/total
query_parameters:
begin: "2015-01-01T00:00:00"
end: "2015-02-04T00:00:00"
tenant_id: "7606a24a-b8ad-4ae0-be6c-3d7a41334a2e"
status: 200
response_strings:
- "37.436"
- name: get total for a period with no data
url: /v1/report/total
query_parameters:
begin: "2015-02-01T00:00:00"
end: "2015-02-02T00:00:00"
status: 200
response_strings:
- "0"

View File

@ -0,0 +1,135 @@
fixtures:
- ConfigFixture
- StorageDataFixture
tests:
- name: fetch period with no data
url: /v1/storage/dataframes
query_parameters:
begin: "2015-01-01T00:00:00"
end: "2015-01-04T00:00:00"
status: 200
response_json_paths:
$.dataframes.`len`: 0
- name: fetch period with no data filtering on tenant_id
url: /v1/storage/dataframes
query_parameters:
begin: "2015-01-01T00:00:00"
end: "2015-01-04T00:00:00"
tenant_id: "8f82cc70-e50c-466e-8624-24bdea811375"
status: 200
response_json_paths:
$.dataframes.`len`: 0
- name: check begin is mandatory for dataframes
url: /v1/storage/dataframes
query_parameters:
end: "2015-01-04T00:00:00"
status: 400
response_strings:
- "Missing argument: \\\"begin\\\""
- name: check end is mandatory for dataframes
url: /v1/storage/dataframes
query_parameters:
begin: "2015-01-04T00:00:00"
status: 400
response_strings:
- "Missing argument: \\\"end\\\""
- name: fetch data for the first tenant
url: /v1/storage/dataframes
query_parameters:
begin: "2015-01-04T13:00:00"
end: "2015-01-04T14:00:00"
tenant_id: "8f82cc70-e50c-466e-8624-24bdea811375"
status: 200
response_json_paths:
$.dataframes.`len`: 1
$.dataframes[0].tenant_id: "8f82cc70-e50c-466e-8624-24bdea811375"
$.dataframes[0].begin: "2015-01-04T13:00:00"
$.dataframes[0].end: "2015-01-04T14:00:00"
$.dataframes[0].resources.`len`: 1
$.dataframes[0].resources[0].volume: "1"
$.dataframes[0].resources[0].rating: "1.337"
$.dataframes[0].resources[0].service: "compute"
$.dataframes[0].resources[0].desc.dummy: true
$.dataframes[0].resources[0].desc.fake_meta: 1.0
- name: fetch data for the second tenant
url: /v1/storage/dataframes
query_parameters:
begin: "2015-01-04T13:00:00"
end: "2015-01-04T14:00:00"
tenant_id: "7606a24a-b8ad-4ae0-be6c-3d7a41334a2e"
status: 200
response_json_paths:
$.dataframes.`len`: 1
$.dataframes[0].tenant_id: "7606a24a-b8ad-4ae0-be6c-3d7a41334a2e"
$.dataframes[0].begin: "2015-01-04T13:00:00"
$.dataframes[0].end: "2015-01-04T14:00:00"
$.dataframes[0].resources.`len`: 1
$.dataframes[0].resources[0].volume: "1"
$.dataframes[0].resources[0].rating: "1.337"
$.dataframes[0].resources[0].service: "compute"
$.dataframes[0].resources[0].desc.dummy: true
$.dataframes[0].resources[0].desc.fake_meta: 1.0
- name: fetch data for multiple tenants
url: /v1/storage/dataframes
query_parameters:
begin: "2015-01-04T13:00:00"
end: "2015-01-04T14:00:00"
status: 200
response_json_paths:
$.dataframes.`len`: 2
$.dataframes[0].tenant_id: "8f82cc70-e50c-466e-8624-24bdea811375"
$.dataframes[0].begin: "2015-01-04T13:00:00"
$.dataframes[0].end: "2015-01-04T14:00:00"
$.dataframes[0].resources.`len`: 1
$.dataframes[0].resources[0].volume: "1"
$.dataframes[0].resources[0].rating: "1.337"
$.dataframes[0].resources[0].service: "compute"
$.dataframes[0].resources[0].desc.dummy: true
$.dataframes[0].resources[0].desc.fake_meta: 1.0
$.dataframes[1].tenant_id: "7606a24a-b8ad-4ae0-be6c-3d7a41334a2e"
$.dataframes[1].begin: "2015-01-04T13:00:00"
$.dataframes[1].end: "2015-01-04T14:00:00"
$.dataframes[1].resources.`len`: 1
$.dataframes[1].resources[0].volume: "1"
$.dataframes[1].resources[0].rating: "1.337"
$.dataframes[1].resources[0].service: "compute"
$.dataframes[1].resources[0].desc.dummy: true
$.dataframes[1].resources[0].desc.fake_meta: 1.0
- name: fetch data filtering on service and tenant
url: /v1/storage/dataframes
query_parameters:
begin: "2015-01-04T13:00:00"
end: "2015-01-04T14:00:00"
resource_type: "compute"
tenant_id: "7606a24a-b8ad-4ae0-be6c-3d7a41334a2e"
status: 200
response_json_paths:
$.dataframes.`len`: 1
$.dataframes[0].tenant_id: "7606a24a-b8ad-4ae0-be6c-3d7a41334a2e"
$.dataframes[0].begin: "2015-01-04T13:00:00"
$.dataframes[0].end: "2015-01-04T14:00:00"
$.dataframes[0].resources.`len`: 1
$.dataframes[0].resources[0].volume: "1"
$.dataframes[0].resources[0].rating: "1.337"
$.dataframes[0].resources[0].service: "compute"
$.dataframes[0].resources[0].desc.dummy: true
$.dataframes[0].resources[0].desc.fake_meta: 1.0
- name: fetch data filtering on service with no data and tenant
url: /v1/storage/dataframes
query_parameters:
begin: "2015-01-04T13:00:00"
end: "2015-01-04T14:00:00"
resource_type: "image"
tenant_id: "7606a24a-b8ad-4ae0-be6c-3d7a41334a2e"
status: 200
response_json_paths:
$.dataframes.`len`: 0

View File

@ -0,0 +1,33 @@
# -*- coding: utf-8 -*-
# Copyright 2015 Objectif Libre
#
# Licensed 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.
#
# @author: Stéphane Albert
#
import os
from gabbi import driver
from cloudkitty.tests.gabbi import fixtures
TESTS_DIR = 'gabbits'
def load_tests(loader, tests, pattern):
test_dir = os.path.join(os.path.dirname(__file__), TESTS_DIR)
return driver.build_tests(test_dir,
loader,
host=None,
intercept=fixtures.setup_app,
fixture_module=fixtures)

View File

@ -26,6 +26,7 @@ import datetime
import sys
from oslo_utils import timeutils
from six import moves
from stevedore import extension
@ -128,8 +129,20 @@ def get_next_month_timestamp(dt=None):
def refresh_stevedore(namespace=None):
# Trigger reload of entry points
reload(sys.modules['pkg_resources'])
"""Trigger reload of entry points.
Useful to have dynamic loading/unloading of stevedore modules.
"""
# NOTE(sheeprine): pkg_resources doesn't support reload on python3 due to
# defining basestring which is still there on reload hence executing
# python2 related code.
try:
del sys.modules['pkg_resources'].basestring
except AttributeError:
# python2, do nothing
pass
# Force working_set reload
moves.reload_module(sys.modules['pkg_resources'])
# Clear stevedore cache
cache = extension.ExtensionManager.ENTRY_POINT_CACHE
if namespace:

View File

@ -1,6 +1,9 @@
[DEFAULT]
output_file = etc/cloudkitty/cloudkitty.conf.sample
namespace = cloudkitty.common.config
namespace = oslo.messaging
namespace = oslo.concurrency
namespace = oslo.db
namespace = oslo.log
namespace = oslo.messaging
namespace = oslo.policy
namespace = keystonemiddleware.auth_token

View File

@ -4,6 +4,7 @@
hacking<0.10,>=0.9.2
coverage>=3.6
discover
gabbi>=0.12.0 # Apache-2.0
testscenarios>=0.4
testrepository>=0.0.18
mock>=1.2