Skip to content

Commit

Permalink
Feature/increase code coverage (#39)
Browse files Browse the repository at this point in the history
* Changed step function names to be suitable for testing

* Added tests for custom type steps.

* Removed unnecessary imports

* Removed unnecessary imports

* Modified some steps a bit to be more flexible for testing without using patch

* Added MockedStep and MockedWorld classes

* Added new unit tests about @given steps

* Added __init__.py into the tests/steps directory

* Fixed string formatting on one of the tests

* Fixed some of the class initiation parts on mocks. Also added self.stash on class init

* Removed unnecessary imports

* Added few more unit tests for steps

* Added one more step test case.

* Enabled coverage badge on README.md

* Added few more tests

* Bumped patch version by 1

* Removed unnecessary imports

* Added few more unit tests

* Fixed one of the steps where it was not failing on must cases

* Fixed 2 unit tests and add another one.

* Fixed a step where must and must not is not processed properly

* Added 4 more unit tests and fixed 1 unit test
  • Loading branch information
eerkunt authored Oct 31, 2018
1 parent 9f87cae commit 4747370
Show file tree
Hide file tree
Showing 10 changed files with 367 additions and 32 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,5 @@ dist
example/tf_files/*
terraform_compliance.egg-info

.DS_Store
.DS_Store
terraform-compliance.iml
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,11 @@
<img src="https://img.shields.io/travis/eerkunt/terraform-compliance/master.svg" alt="Build" />
</a>

<!-- Coverage
<!-- Coverage -->
<a href="https://hub.docker.com/r/eerkunt/terraform-compliance/">
<img src="https://coveralls.io/repos/github/eerkunt/terraform-compliance/badge.svg?branch=master" alt="coverage report" />
</a>
-->


<!-- Docker Ready -->
<a href="https://hub.docker.com/r/eerkunt/terraform-compliance/">
Expand Down
2 changes: 1 addition & 1 deletion terraform_compliance/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ def pip(action, package, params=None):


__app_name__ = "terraform-compliance"
__version__ = "0.4.4"
__version__ = "0.4.5"


class ArgHandling(object):
Expand Down
63 changes: 39 additions & 24 deletions terraform_compliance/steps/steps.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,19 @@

# New Arguments
@custom_type("ANY", r"[\.\/_\-A-Za-z0-9\s]+")
def arg_exp_for_secure_text(text):
def custom_type_any(text):
return text

@custom_type("SECTION", r"[a-z]+")
def arg_exp_for_secure_text(text):
def custom_type_section(text):
if text in ['resource', 'provider', 'data', 'module', 'output', 'terraform', 'variable']:
return text

@given(u'I have {name:ANY} {type:SECTION} configured')
def define_a_resource(step, name, type):
def i_have_name_section_configured(step, name, type, radish_world=None):
if radish_world is None:
radish_world = world

step.context.type = type
step.context.name = name

Expand All @@ -28,21 +31,24 @@ def define_a_resource(step, name, type):

step.context.resource_type = name
step.context.defined_resource = name
step.context.stash = world.config.terraform.resources(name)
step.context.stash = radish_world.config.terraform.resources(name)
else:
if name in world.config.terraform.terraform_config[type]:
step.context.stash = world.config.terraform.terraform_config[type][name]
if name in radish_world.config.terraform.terraform_config[type]:
step.context.stash = radish_world.config.terraform.terraform_config[type][name]
else:
step.context.stash = world.config.terraform.terraform_config[type]
step.context.stash = radish_world.config.terraform.terraform_config[type]

@given(u'I have {resource:ANY} defined')
def define_a_resource(step, resource):
def i_have_resource_defined(step, resource, radish_world=None):
if radish_world is None:
radish_world = world

if (resource in resource_name.keys()):
resource = resource_name[resource]

step.context.resource_type = resource
step.context.defined_resource = resource
step.context.stash = world.config.terraform.resources(resource)
step.context.stash = radish_world.config.terraform.resources(resource)


@step(u'I {action_type:ANY} them')
Expand All @@ -59,30 +65,32 @@ def i_action_them(step, action_type):


@step(u'I expect the result is {operator:ANY} than {number:d}')
def i_expect_the_result_is(step, operator, number):
def i_expect_the_result_is_operator_than_number(step, operator, number):
if hasattr(step.context.stash, 'resource_list') and not step.context.stash.resource_list:
return

value = int(step.context.stash)

if operator == "more":
assert value > number, str(value) + " is not more than " + str(number)
assert value > number, "{} is not more than {}".format(value, number)
elif operator == "more and equal":
assert value >= number, str(value) + " is not more and equal than " + str(number)
assert value >= number, "{} is not more and equal than {}".format(value, number)
elif operator == "less":
assert value < number, str(value) + " is not less than " + str(number)
assert value < number, "{} is not less than {}".format(value, number)
elif operator == "less and equal":
assert value <= number, str(value) + " is not less and equal than " + str(number)
assert value <= number, "{} is not less and equal than {}".format(value, number)
else:
AssertionError("Invalid operator: " + str(operator))
AssertionError('Invalid operator: {}'.format(operator))


@step(u'it {condition:ANY} contain {something:ANY}')
def it_contain(step, condition, something):
def it_condition_contain_something(step, condition, something,
propertylist=TerraformPropertyList, resourcelist=TerraformResourceList):

if hasattr(step.context.stash, 'resource_list') and not step.context.stash.resource_list:
return

if step.context.stash.__class__ is TerraformPropertyList:
if step.context.stash.__class__ is propertylist:
for property in step.context.stash.properties:
assert property.property_value == something, \
'{} property in {} can not be found in {} ({}). It is set to {} instead'.format(something,
Expand All @@ -91,7 +99,7 @@ def it_contain(step, condition, something):
property.resource_type,
property.property_value)

elif step.context.stash.__class__ is TerraformResourceList:
elif step.context.stash.__class__ is resourcelist:
if condition == 'must':
step.context.stash.should_have_properties(something)

Expand All @@ -108,7 +116,7 @@ def it_contain(step, condition, something):
step.context.stash = step.context.stash[something]
else:
if condition == 'must':
assert '{} does not exist.'.format(something)
assert False, '{} does not exist.'.format(something)


@step(u'encryption is enabled')
Expand All @@ -122,7 +130,7 @@ def encryption_is_enabled(step):


@step(u'its value {condition} match the "{search_regex}" regex')
def func(step, condition, search_regex):
def its_value_condition_match_the_search_regex_regex(step, condition, search_regex):
if hasattr(step.context.stash, 'resource_list') and not step.context.stash.resource_list:
return

Expand Down Expand Up @@ -155,12 +163,19 @@ def func(step, condition, search_regex):

for value in property.property_value:
matches = re.match(regex, value)
assert matches is not None, \
'{} property in {} does not match with {} regex. It is set to {} instead.'.format(property.property_name,

if condition == 'must':
assert matches is not None, \
'{} property in {} does not match with {} regex. It is set to {} instead.'.format(property.property_name,
property.resource_name,
search_regex,
value)

elif condition == 'must not':
assert matches is not None, \
'{} property in {} does not match with {} regex. It is set to {} instead.'.format(property.property_name,
property.resource_name,
search_regex,
value)

@step(u'its value must be set by a variable')
def its_value_must_be_set_by_a_variable(step):
Expand All @@ -171,7 +186,7 @@ def its_value_must_be_set_by_a_variable(step):


@step(u'it must not have {proto} protocol and port {port:d} for {cidr:ANY}')
def it_must_not_have_sg_stuff(step, proto, port, cidr):
def it_must_not_have_proto_protocol_and_port_port_for_cidr(step, proto, port, cidr):
proto = str(proto)
port = int(port)
cidr = str(cidr)
Expand Down
74 changes: 72 additions & 2 deletions tests/mocks.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
from terraform_validate.terraform_validate import TerraformSyntaxException
from os.path import exists
from os import remove, environ
from os import environ


class MockedData(object):
Expand Down Expand Up @@ -252,3 +251,74 @@ def __init__(self, directory):
else:
environ[state_key] = '1'
raise TerraformSyntaxException('detailed message')


class MockedStep(object):
def __init__(self):
self.context = MockedStepContext()

class MockedStepContext(object):
def __init__(self):
self.stash = MockedWorldConfigTerraform()


class MockedWorld(object):
def __init__(self):
self.config = MockedWorldConfig()


class MockedWorldConfig(object):
def __init__(self):
self.terraform = MockedWorldConfigTerraform()


class MockedWorldConfigTerraform(object):
def __init__(self):
self.terraform_config = {
u'resource': {
u'resource_type': {
u'resource_name': {
u'resource_property': u'resource_property_value',
u'tags': u'${module.tags.tags}'
}
},
u'aws_s3_bucket': {
u'aws_s3_bucket_name': {
u'resource_property': u'resource_property_value',
u'tags': u'${module.tags.tags}'
}
}
},

u'provider': {
u'aws': {}
},
u'something_else': {'something': 'else'}
}
def resources(self, name):
return self.terraform_config['resource'][name]


class MockedTerraformPropertyList(object):
def __init__(self):
self.properties = [MockedTerraformProperty()]


class MockedTerraformProperty(object):
def __init__(self):
self.property_value = 'test_value'
self.resource_name = 'test_resource_name'
self.resource_type = 'test_resource_type'
self.property_name = 'test_name'


class MockedTerraformResourceList(object):
def should_have_properties(self, key):
if key is None:
raise Exception('should_have_properties hit')

def property(self, key):
if key is None:
raise Exception('property hit')
self.properties = MockedTerraformPropertyList()
return self.properties
3 changes: 1 addition & 2 deletions tests/terraform_compliance/common/test_pyhcl_helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@
pad_invalid_tf_files,
pad_tf_file
)
from tests.mocks import MockedData, MockedValidator
from copy import deepcopy
from tests.mocks import MockedValidator
from os import remove, path, environ
from mock import patch

Expand Down
Empty file.
31 changes: 31 additions & 0 deletions tests/terraform_compliance/steps/test_given_steps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
from unittest import TestCase
from terraform_compliance.steps.steps import i_have_name_section_configured, i_have_resource_defined
from tests.mocks import MockedStep, MockedWorld, MockedWorldConfigTerraform


class Test_Given_Step_Cases(TestCase):

def setUp(self):
self.step = MockedStep()
self.radish_world = MockedWorld()

def test_i_have_name_section_configured(self):
i_have_name_section_configured(self.step, 'resource_type', 'resource', self.radish_world)
self.assertEqual(self.step.context.stash, MockedWorldConfigTerraform().terraform_config['resource']['resource_type'])

i_have_name_section_configured(self.step, 'aws', 'provider', self.radish_world)
self.assertEqual(self.step.context.stash, MockedWorldConfigTerraform().terraform_config['provider']['aws'])

i_have_name_section_configured(self.step, 'non_existent', 'something_else', self.radish_world)
self.assertEqual(self.step.context.stash, MockedWorldConfigTerraform().terraform_config['something_else'])

i_have_name_section_configured(self.step, 'AWS S3 Bucket', 'resource', self.radish_world)
self.assertEqual(self.step.context.stash, MockedWorldConfigTerraform().terraform_config['resource']['aws_s3_bucket'])

def test_i_have_resource_defined(self):
i_have_resource_defined(self.step, 'resource_type', self.radish_world)
self.assertEqual(self.step.context.stash, MockedWorldConfigTerraform().terraform_config['resource']['resource_type'])

i_have_resource_defined(self.step, 'AWS S3 Bucket', self.radish_world)
self.assertEqual(self.step.context.stash, MockedWorldConfigTerraform().terraform_config['resource']['aws_s3_bucket'])

Loading

0 comments on commit 4747370

Please sign in to comment.