Skip to content

Commit 126b626

Browse files
committed
test: use an uniform pattern for mocking Provider
1 parent dc405d0 commit 126b626

7 files changed

Lines changed: 100 additions & 89 deletions

File tree

stacker/tests/actions/test_build.py

Lines changed: 3 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,8 @@
22
from __future__ import division
33
from __future__ import absolute_import
44
from builtins import str
5-
import unittest
65
from collections import namedtuple
7-
6+
import unittest
87
import mock
98

109
from stacker import exceptions
@@ -18,7 +17,6 @@
1817
from stacker.blueprints.variables.types import CFNString
1918
from stacker.context import Context, Config
2019
from stacker.exceptions import StackDidNotChange, StackDoesNotExist
21-
from stacker.providers.base import BaseProvider
2220
from stacker.providers.aws.default import Provider
2321
from stacker.status import (
2422
NotSubmittedStatus,
@@ -29,7 +27,7 @@
2927
FAILED
3028
)
3129

32-
from ..factories import MockThreadingEvent, MockProviderBuilder
30+
from ..factories import MockThreadingEvent, MockProviderBuilder, mock_provider
3331

3432

3533
def mock_stack_parameters(parameters):
@@ -41,27 +39,10 @@ def mock_stack_parameters(parameters):
4139
}
4240

4341

44-
class TestProvider(BaseProvider):
45-
def __init__(self, outputs=None, *args, **kwargs):
46-
self._outputs = outputs or {}
47-
48-
def set_outputs(self, outputs):
49-
self._outputs = outputs
50-
51-
def get_stack(self, stack_name, **kwargs):
52-
if stack_name not in self._outputs:
53-
raise exceptions.StackDoesNotExist(stack_name)
54-
return {"name": stack_name, "outputs": self._outputs[stack_name]}
55-
56-
def get_outputs(self, stack_name, *args, **kwargs):
57-
stack = self.get_stack(stack_name)
58-
return stack["outputs"]
59-
60-
6142
class TestBuildAction(unittest.TestCase):
6243
def setUp(self):
6344
self.context = Context(config=Config({"namespace": "namespace"}))
64-
self.provider = TestProvider()
45+
self.provider = mock_provider()
6546
self.build_action = build.Action(
6647
self.context,
6748
provider_builder=MockProviderBuilder(self.provider))

stacker/tests/factories.py

Lines changed: 64 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,13 @@
22
from __future__ import division
33
from __future__ import absolute_import
44
from builtins import object
5-
from mock import MagicMock
5+
6+
import mock
67

78
from stacker.context import Context
89
from stacker.config import Config, Stack
9-
from stacker.lookups import Lookup
10+
from stacker.exceptions import StackDoesNotExist, StackUpdateBadStatus
11+
from stacker.providers.base import BaseProvider
1012

1113

1214
class MockThreadingEvent(object):
@@ -23,23 +25,72 @@ def build(self, region=None, profile=None):
2325
return self.provider
2426

2527

26-
def mock_provider(**kwargs):
27-
return MagicMock(**kwargs)
28+
class MockProvider(BaseProvider):
29+
def __init__(self, outputs=None):
30+
self._stacks = {}
31+
for stack_name, stack_outputs in (outputs or {}).items():
32+
self._stacks[stack_name] = {
33+
"StackName": stack_name,
34+
"Outputs": stack_outputs,
35+
"StackStatus": "CREATED"
36+
}
37+
38+
def get_stack(self, stack_name, **kwargs):
39+
try:
40+
return self._stacks[stack_name]
41+
except KeyError:
42+
raise StackDoesNotExist(stack_name)
43+
44+
def get_outputs(self, stack_name, *args, **kwargs):
45+
return self.get_stack(stack_name)["Outputs"]
46+
47+
def get_stack_status(self, stack_name, *args, **kwargs):
48+
return self.get_stack(stack_name)["StackStatus"]
49+
50+
def create_stack(self, stack_name, *args, **kwargs):
51+
try:
52+
stack = self.get_stack(stack_name)
53+
status = self.get_stack_status(stack)
54+
if status != "DELETED":
55+
raise StackUpdateBadStatus(stack_name, status, "can't create")
56+
except StackDoesNotExist:
57+
pass
58+
59+
return None
60+
61+
def update_stack(self, stack_name, *args, **kwargs):
62+
stack = self.get_stack(stack_name)
63+
status = self.get_stack_status(stack)
64+
if status == "DELETED":
65+
raise StackUpdateBadStatus(stack_name, status, "can't update")
66+
67+
stack["StackStatus"] = "UPDATED"
68+
return None
2869

70+
def destroy_stack(self, stack_name, *args, **kwargs):
71+
stack = self.get_stack(stack_name)
72+
status = self.get_stack_status(stack)
73+
if status == "DELETED":
74+
raise StackUpdateBadStatus(stack_name, status, "can't destroy")
2975

30-
def mock_context(namespace="default", extra_config_args=None, **kwargs):
76+
stack["StackStatus"] = "DELETED"
77+
return None
78+
79+
80+
def mock_provider(outputs=None, **kwargs):
81+
provider = mock.MagicMock(wraps=MockProvider(outputs), **kwargs)
82+
return provider
83+
84+
85+
def mock_context(namespace="default", extra_config_args=None,
86+
environment=None, **kwargs):
3187
config_args = {"namespace": namespace}
3288
if extra_config_args:
3389
config_args.update(extra_config_args)
90+
3491
config = Config(config_args)
35-
if kwargs.get("environment"):
36-
return Context(
37-
config=config,
38-
**kwargs)
39-
return Context(
40-
config=config,
41-
environment={},
42-
**kwargs)
92+
environment = environment or {}
93+
return Context(config=config, environment=environment, **kwargs)
4394

4495

4596
def generate_definition(base_name, stack_id, **overrides):
@@ -53,12 +104,6 @@ def generate_definition(base_name, stack_id, **overrides):
53104
return Stack(definition)
54105

55106

56-
def mock_lookup(lookup_input, lookup_type, raw=None):
57-
if raw is None:
58-
raw = "%s %s" % (lookup_type, lookup_input)
59-
return Lookup(type=lookup_type, input=lookup_input, raw=raw)
60-
61-
62107
class SessionStub(object):
63108

64109
"""Stubber class for boto3 sessions made with session_cache.get_session()

stacker/tests/lookups/handlers/test_default.py

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,18 @@
11
from __future__ import print_function
22
from __future__ import division
33
from __future__ import absolute_import
4-
from mock import MagicMock
54
import unittest
65

7-
from stacker.context import Context
86
from stacker.lookups.handlers.default import DefaultLookup
97

8+
from ...factories import mock_context, mock_provider
109

11-
class TestDefaultLookup(unittest.TestCase):
1210

11+
class TestDefaultLookup(unittest.TestCase):
1312
def setUp(self):
14-
self.provider = MagicMock()
15-
self.context = Context(
16-
environment={
17-
'namespace': 'test',
18-
'env_var': 'val_in_env'}
19-
)
13+
self.provider = mock_provider()
14+
self.context = mock_context(
15+
namespace='test', environment={'env_var': 'val_in_env'})
2016

2117
def test_env_var_present(self):
2218
lookup_val = "env_var::fallback"

stacker/tests/lookups/handlers/test_output.py

Lines changed: 11 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,25 +5,22 @@
55
import unittest
66

77
from stacker.stack import Stack
8-
from ...factories import generate_definition
98
from stacker.lookups.handlers.output import OutputLookup
109

10+
from ...factories import generate_definition, mock_context, mock_provider
1111

12-
class TestOutputHandler(unittest.TestCase):
1312

13+
class TestOutputHandler(unittest.TestCase):
1414
def setUp(self):
15-
self.context = MagicMock()
15+
stack_def = generate_definition("vpc", 1)
16+
self.context = mock_context()
17+
self.stack = Stack(definition=stack_def, context=self.context)
18+
self.context.get_stacks = MagicMock(return_value=[self.stack])
19+
self.provider = mock_provider(
20+
outputs={self.stack.fqn: {"SomeOutput": "Test Output"}})
1621

1722
def test_output_handler(self):
18-
stack = Stack(
19-
definition=generate_definition("vpc", 1),
20-
context=self.context)
21-
stack.set_outputs({
22-
"SomeOutput": "Test Output"})
23-
self.context.get_stack.return_value = stack
24-
value = OutputLookup.handle("stack-name::SomeOutput",
25-
context=self.context)
23+
value = OutputLookup.handle("{}::SomeOutput".format(self.stack.name),
24+
context=self.context,
25+
provider=self.provider)
2626
self.assertEqual(value, "Test Output")
27-
self.assertEqual(self.context.get_stack.call_count, 1)
28-
args = self.context.get_stack.call_args
29-
self.assertEqual(args[0][0], "stack-name")
Lines changed: 8 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,24 @@
11
from __future__ import print_function
22
from __future__ import division
33
from __future__ import absolute_import
4-
from mock import MagicMock
54
import unittest
65

76
from stacker.lookups.handlers.rxref import RxrefLookup
8-
from ....context import Context
9-
from ....config import Config
7+
8+
from ...factories import mock_context, mock_provider
109

1110

1211
class TestRxrefHandler(unittest.TestCase):
1312

1413
def setUp(self):
15-
self.provider = MagicMock()
16-
self.context = Context(
17-
config=Config({"namespace": "ns"})
18-
)
14+
self.context = mock_context()
15+
self.stack_name = "stack-name"
16+
self.stack_fqn = self.context.get_fqn(self.stack_name)
17+
self.provider = mock_provider(
18+
outputs={self.stack_fqn: {"SomeOutput": "Test Output"}})
1919

2020
def test_rxref_handler(self):
21-
self.provider.get_output.return_value = "Test Output"
22-
23-
value = RxrefLookup.handle("fully-qualified-stack-name::SomeOutput",
21+
value = RxrefLookup.handle("{}::SomeOutput".format(self.stack_name),
2422
provider=self.provider,
2523
context=self.context)
2624
self.assertEqual(value, "Test Output")
27-
28-
args = self.provider.get_output.call_args
29-
self.assertEqual(args[0][0], "ns-fully-qualified-stack-name")
30-
self.assertEqual(args[0][1], "SomeOutput")
Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,23 @@
11
from __future__ import print_function
22
from __future__ import division
33
from __future__ import absolute_import
4-
from mock import MagicMock
54
import unittest
65

76
from stacker.lookups.handlers.xref import XrefLookup
87

8+
from ...factories import mock_context, mock_provider
9+
910

1011
class TestXrefHandler(unittest.TestCase):
1112

1213
def setUp(self):
13-
self.provider = MagicMock()
14-
self.context = MagicMock()
14+
self.stack_fqn = "fully-qualified-stack-name"
15+
self.context = mock_context()
16+
self.provider = mock_provider(
17+
outputs={self.stack_fqn: {"SomeOutput": "Test Output"}})
1518

1619
def test_xref_handler(self):
17-
self.provider.get_output.return_value = "Test Output"
18-
value = XrefLookup.handle("fully-qualified-stack-name::SomeOutput",
20+
value = XrefLookup.handle("{}::SomeOutput".format(self.stack_fqn),
1921
provider=self.provider,
2022
context=self.context)
2123
self.assertEqual(value, "Test Output")
22-
self.assertEqual(self.context.get_fqn.call_count, 0)
23-
args = self.provider.get_output.call_args
24-
self.assertEqual(args[0][0], "fully-qualified-stack-name")
25-
self.assertEqual(args[0][1], "SomeOutput")

stacker/tests/test_variables.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
from stacker.variables import Variable
1212
from stacker.lookups import register_lookup_handler
1313

14-
from .factories import TestProvider, mock_context
14+
from .factories import mock_context, mock_provider
1515

1616

1717
class MockLookup(LookupHandler):
@@ -29,7 +29,7 @@ def handle(cls, value, context, provider):
2929
class TestVariables(unittest.TestCase):
3030

3131
def setUp(self):
32-
self.provider = TestProvider()
32+
self.provider = mock_provider()
3333
self.context = mock_context()
3434

3535
register_lookup_handler("test", MockLookup)

0 commit comments

Comments
 (0)