stack_master 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (103) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +18 -0
  3. data/.rspec +2 -0
  4. data/Gemfile +4 -0
  5. data/LICENSE.txt +22 -0
  6. data/README.md +208 -0
  7. data/Rakefile +11 -0
  8. data/apply_demo.gif +0 -0
  9. data/bin/stack_master +16 -0
  10. data/example/simple/Gemfile +3 -0
  11. data/example/simple/parameters/myapp_vpc.yml +1 -0
  12. data/example/simple/parameters/myapp_web.yml +2 -0
  13. data/example/simple/stack_master.yml +13 -0
  14. data/example/simple/templates/myapp_vpc.rb +39 -0
  15. data/example/simple/templates/myapp_web.rb +16 -0
  16. data/features/apply.feature +241 -0
  17. data/features/delete.feature +43 -0
  18. data/features/diff.feature +191 -0
  19. data/features/events.feature +38 -0
  20. data/features/init.feature +6 -0
  21. data/features/outputs.feature +49 -0
  22. data/features/region_aliases.feature +66 -0
  23. data/features/resources.feature +45 -0
  24. data/features/stack_defaults.feature +88 -0
  25. data/features/status.feature +124 -0
  26. data/features/step_definitions/stack_steps.rb +50 -0
  27. data/features/support/env.rb +14 -0
  28. data/lib/stack_master.rb +81 -0
  29. data/lib/stack_master/aws_driver/cloud_formation.rb +56 -0
  30. data/lib/stack_master/cli.rb +164 -0
  31. data/lib/stack_master/command.rb +13 -0
  32. data/lib/stack_master/commands/apply.rb +104 -0
  33. data/lib/stack_master/commands/delete.rb +53 -0
  34. data/lib/stack_master/commands/diff.rb +31 -0
  35. data/lib/stack_master/commands/events.rb +39 -0
  36. data/lib/stack_master/commands/init.rb +109 -0
  37. data/lib/stack_master/commands/list_stacks.rb +16 -0
  38. data/lib/stack_master/commands/outputs.rb +27 -0
  39. data/lib/stack_master/commands/resources.rb +33 -0
  40. data/lib/stack_master/commands/status.rb +47 -0
  41. data/lib/stack_master/commands/validate.rb +17 -0
  42. data/lib/stack_master/config.rb +86 -0
  43. data/lib/stack_master/ctrl_c.rb +4 -0
  44. data/lib/stack_master/parameter_loader.rb +17 -0
  45. data/lib/stack_master/parameter_resolver.rb +45 -0
  46. data/lib/stack_master/parameter_resolvers/secret.rb +42 -0
  47. data/lib/stack_master/parameter_resolvers/security_group.rb +20 -0
  48. data/lib/stack_master/parameter_resolvers/sns_topic_name.rb +29 -0
  49. data/lib/stack_master/parameter_resolvers/stack_output.rb +53 -0
  50. data/lib/stack_master/prompter.rb +14 -0
  51. data/lib/stack_master/security_group_finder.rb +29 -0
  52. data/lib/stack_master/sns_topic_finder.rb +27 -0
  53. data/lib/stack_master/stack.rb +96 -0
  54. data/lib/stack_master/stack_definition.rb +49 -0
  55. data/lib/stack_master/stack_differ.rb +80 -0
  56. data/lib/stack_master/stack_events/fetcher.rb +45 -0
  57. data/lib/stack_master/stack_events/presenter.rb +27 -0
  58. data/lib/stack_master/stack_events/streamer.rb +55 -0
  59. data/lib/stack_master/stack_states.rb +34 -0
  60. data/lib/stack_master/template_compiler.rb +21 -0
  61. data/lib/stack_master/test_driver/cloud_formation.rb +139 -0
  62. data/lib/stack_master/testing.rb +7 -0
  63. data/lib/stack_master/utils.rb +31 -0
  64. data/lib/stack_master/validator.rb +25 -0
  65. data/lib/stack_master/version.rb +3 -0
  66. data/logo.png +0 -0
  67. data/script/buildkite/bundle.sh +5 -0
  68. data/script/buildkite/clean.sh +3 -0
  69. data/script/buildkite_rspec.sh +27 -0
  70. data/spec/fixtures/parameters/myapp_vpc.yml +1 -0
  71. data/spec/fixtures/stack_master.yml +35 -0
  72. data/spec/fixtures/templates/myapp_vpc.json +1 -0
  73. data/spec/spec_helper.rb +99 -0
  74. data/spec/stack_master/commands/apply_spec.rb +92 -0
  75. data/spec/stack_master/commands/delete_spec.rb +40 -0
  76. data/spec/stack_master/commands/init_spec.rb +17 -0
  77. data/spec/stack_master/commands/status_spec.rb +38 -0
  78. data/spec/stack_master/commands/validate_spec.rb +26 -0
  79. data/spec/stack_master/config_spec.rb +81 -0
  80. data/spec/stack_master/parameter_loader_spec.rb +81 -0
  81. data/spec/stack_master/parameter_resolver_spec.rb +58 -0
  82. data/spec/stack_master/parameter_resolvers/secret_spec.rb +66 -0
  83. data/spec/stack_master/parameter_resolvers/security_group_spec.rb +17 -0
  84. data/spec/stack_master/parameter_resolvers/sns_topic_name_spec.rb +43 -0
  85. data/spec/stack_master/parameter_resolvers/stack_output_spec.rb +77 -0
  86. data/spec/stack_master/security_group_finder_spec.rb +49 -0
  87. data/spec/stack_master/sns_topic_finder_spec.rb +25 -0
  88. data/spec/stack_master/stack_definition_spec.rb +37 -0
  89. data/spec/stack_master/stack_differ_spec.rb +34 -0
  90. data/spec/stack_master/stack_events/fetcher_spec.rb +65 -0
  91. data/spec/stack_master/stack_events/presenter_spec.rb +18 -0
  92. data/spec/stack_master/stack_events/streamer_spec.rb +33 -0
  93. data/spec/stack_master/stack_spec.rb +157 -0
  94. data/spec/stack_master/template_compiler_spec.rb +48 -0
  95. data/spec/stack_master/test_driver/cloud_formation_spec.rb +24 -0
  96. data/spec/stack_master/utils_spec.rb +30 -0
  97. data/spec/stack_master/validator_spec.rb +38 -0
  98. data/stack_master.gemspec +38 -0
  99. data/stacktemplates/parameter_region.yml +3 -0
  100. data/stacktemplates/parameter_stack_name.yml +3 -0
  101. data/stacktemplates/stack.json.erb +20 -0
  102. data/stacktemplates/stack_master.yml.erb +6 -0
  103. metadata +427 -0
@@ -0,0 +1,49 @@
1
+ RSpec.describe StackMaster::SecurityGroupFinder do
2
+ subject(:finder) { described_class.new(region) }
3
+ let(:region) { 'us-east-1' }
4
+ let(:group_name) { "our-api-BeanstalkSg-T4RKD99YOY2F" }
5
+ let(:filter) do
6
+ {
7
+ filters: [
8
+ {
9
+ name: "group-name",
10
+ values: [group_name],
11
+ },
12
+ ],
13
+ }
14
+ end
15
+
16
+ describe "#find" do
17
+ before do
18
+ allow_any_instance_of(Aws::EC2::Resource).to receive(:security_groups).with(filter).and_return(security_groups)
19
+ end
20
+
21
+ context "one sg match" do
22
+ let(:security_groups) { [
23
+ double(id: 'sg-a7d2ccc0')
24
+ ] }
25
+ it "returns the id" do
26
+ expect(finder.find(group_name)).to eq 'sg-a7d2ccc0'
27
+ end
28
+ end
29
+
30
+ context "more than one sg matches" do
31
+ let(:security_groups) { [
32
+ double(id: 'sg-a7d2ccc0'),
33
+ double(id: 'sg-a7d2ccc2'),
34
+ ] }
35
+ it "returns the id" do
36
+ err = StackMaster::SecurityGroupFinder::MultipleSecurityGroupsFound
37
+ expect { finder.find(group_name) }.to raise_error(err)
38
+ end
39
+ end
40
+
41
+ context "no matches" do
42
+ let(:security_groups) { [] }
43
+ it "returns the id" do
44
+ err = StackMaster::SecurityGroupFinder::SecurityGroupNotFound
45
+ expect { finder.find(group_name) }.to raise_error(err)
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,25 @@
1
+ RSpec.describe StackMaster::SnsTopicFinder do
2
+
3
+ subject(:finder) { described_class.new(region) }
4
+ let(:region) { 'us-east-1' }
5
+ let(:topics) do
6
+ [
7
+ double(arn: 'arn:aws:sns:us-east-1:581634149801:topic1name'),
8
+ double(arn: 'arn:aws:sns:us-east-1:581634149801:topic2name'),
9
+ ]
10
+ end
11
+ before do
12
+ allow_any_instance_of(Aws::SNS::Resource).to receive(:topics).and_return topics
13
+ end
14
+
15
+ describe '#find' do
16
+ it 'finds the topics that exist' do
17
+ expect(finder.find('topic1name')).to eq 'arn:aws:sns:us-east-1:581634149801:topic1name'
18
+ expect(finder.find('topic2name')).to eq 'arn:aws:sns:us-east-1:581634149801:topic2name'
19
+ end
20
+
21
+ it 'raises an exception for topics that do not exist' do
22
+ expect { finder.find('unknowntopics') }.to raise_error StackMaster::SnsTopicFinder::TopicNotFound
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,37 @@
1
+ RSpec.describe StackMaster::StackDefinition do
2
+ subject(:stack_definition) do
3
+ StackMaster::StackDefinition.new(
4
+ region: region,
5
+ stack_name: stack_name,
6
+ template: template,
7
+ tags: tags,
8
+ base_dir: base_dir)
9
+ end
10
+
11
+ let(:region) { 'us-east-1' }
12
+ let(:stack_name) { 'stack_name' }
13
+ let(:template) { 'template.json' }
14
+ let(:tags) { {'environment' => 'production'} }
15
+ let(:base_dir) { '/base_dir' }
16
+
17
+ it 'has default and region specific parameter file locations' do
18
+ expect(stack_definition.parameter_files).to eq([
19
+ "/base_dir/parameters/#{stack_name}.yml",
20
+ "/base_dir/parameters/#{region}/#{stack_name}.yml"
21
+ ])
22
+ end
23
+
24
+ context 'with additional parameter lookup dirs' do
25
+ before do
26
+ stack_definition.send(:additional_parameter_lookup_dirs=, ['production'])
27
+ end
28
+
29
+ it 'includes a parameter lookup dir for it' do
30
+ expect(stack_definition.parameter_files).to eq([
31
+ "/base_dir/parameters/#{stack_name}.yml",
32
+ "/base_dir/parameters/#{region}/#{stack_name}.yml",
33
+ "/base_dir/parameters/production/#{stack_name}.yml"
34
+ ])
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,34 @@
1
+ RSpec.describe StackMaster::StackDiffer do
2
+ subject(:differ) { described_class.new(proposed_stack, stack) }
3
+ let(:stack) { StackMaster::Stack.new(stack_name: stack_name,
4
+ region: region,
5
+ stack_id: 123,
6
+ template_body: '{}',
7
+ parameters: {}) }
8
+ let(:proposed_stack) { StackMaster::Stack.new(stack_name: stack_name,
9
+ region: region,
10
+ parameters: { 'param1' => 'hello'},
11
+ template_body: "{\"a\": 1}") }
12
+ let(:stack_name) { 'myapp-vpc' }
13
+ let(:region) { 'us-east-1' }
14
+
15
+ describe "#output_diff" do
16
+ context "entirely new stack" do
17
+ let(:stack) { nil }
18
+
19
+ it "outputs the entire stack" do
20
+ expect { differ.output_diff }.to output(/\+ \"a\"\: 1/).to_stdout
21
+ expect { differ.output_diff }.to output(/param1\: hello/).to_stdout
22
+ expect { differ.output_diff }.to output(/No stack found/).to_stdout
23
+ end
24
+ end
25
+
26
+ context "stack update" do
27
+ it "outputs the stack diff" do
28
+ expect { differ.output_diff }.to output(/\+ \"a\"\: 1/).to_stdout
29
+ expect { differ.output_diff }.to output(/param1\: hello/).to_stdout
30
+ expect { differ.output_diff }.to_not output(/No stack found/).to_stdout
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,65 @@
1
+ RSpec.describe StackMaster::StackEvents::Fetcher do
2
+ let(:cf) { Aws::CloudFormation::Client.new }
3
+
4
+ before do
5
+ allow(Aws::CloudFormation::Client).to receive(:new).and_return(cf)
6
+ allow(StackMaster::StackEvents::Streamer).to receive(:stream)
7
+ end
8
+
9
+ context 'with 2 stack events' do
10
+ let(:events) { [
11
+ { event_id: '1', stack_id: '1', stack_name: 'blah', timestamp: Time.now},
12
+ { event_id: '2', stack_id: '1', stack_name: 'blah', timestamp: Time.now}
13
+ ] }
14
+
15
+ before do
16
+ cf.stub_responses(:describe_stack_events, stack_events: events)
17
+ end
18
+
19
+ it 'returns stack events' do
20
+ events = StackMaster::StackEvents::Fetcher.fetch('blah', 'us-east-1')
21
+ expect(events.count).to eq 2
22
+ end
23
+ end
24
+
25
+ context 'when more than one page is present' do
26
+ let(:page_one_events) { [
27
+ { event_id: '1', stack_id: '1', stack_name: 'blah', timestamp: Time.now},
28
+ { event_id: '2', stack_id: '1', stack_name: 'blah', timestamp: Time.now}
29
+ ] }
30
+ let(:page_two_events) { [
31
+ { event_id: '3', stack_id: '1', stack_name: 'blah', timestamp: Time.now}
32
+ ] }
33
+
34
+ before do
35
+ cf.stub_responses(:describe_stack_events, { stack_events: page_one_events, next_token: 'blah' }, { stack_events: page_two_events } )
36
+ end
37
+
38
+ it 'returns all the stack events combined' do
39
+ events = StackMaster::StackEvents::Fetcher.fetch('blah', 'us-east-1')
40
+ expect(events.count).to eq 3
41
+ end
42
+ end
43
+
44
+ context 'filtering with a from timestamp' do
45
+ let(:two_pm) { Time.parse('2015-10-27 14:00') }
46
+ let(:three_pm) { Time.parse('2015-10-27 15:00') }
47
+ let(:four_pm) { Time.parse('2015-10-27 16:00') }
48
+
49
+ let(:events) {
50
+ [
51
+ { event_id: '1', stack_id: '1', stack_name: 'blah', timestamp: two_pm },
52
+ { event_id: '2', stack_id: '1', stack_name: 'blah', timestamp: four_pm },
53
+ ]
54
+ }
55
+
56
+ before do
57
+ cf.stub_responses(:describe_stack_events, stack_events: events)
58
+ end
59
+
60
+ it 'only returns events after the timestamp' do
61
+ events = StackMaster::StackEvents::Fetcher.fetch('blah', 'us-east-1', from: three_pm)
62
+ expect(events.map(&:event_id)).to eq ['2']
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,18 @@
1
+ RSpec.describe StackMaster::StackEvents::Presenter do
2
+ describe "#print_event" do
3
+ let(:time) { Time.new(2001,1,1,2,2,2) }
4
+ let(:event) do
5
+ double(:event,
6
+ timestamp: time,
7
+ logical_resource_id: 'MyAwesomeQueue',
8
+ resource_type: 'AWS::SQS::Queue',
9
+ resource_status: 'CREATE_IN_PROGRESS',
10
+ resource_status_reason: 'Resource creation Initiated')
11
+ end
12
+ subject(:print_event) { described_class.print_event($stdout, event) }
13
+
14
+ it "nicely presents event data" do
15
+ expect { print_event }.to output("\e[0;33;49m2001-01-01 02:02:02 +1100 MyAwesomeQueue AWS::SQS::Queue CREATE_IN_PROGRESS Resource creation Initiated\e[0m\n").to_stdout
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,33 @@
1
+ RSpec.describe StackMaster::StackEvents::Streamer do
2
+ let(:events_first_call) { [
3
+ OpenStruct.new(event_id: '1', resource_status: 'BLAH', timestamp: Time.now),
4
+ OpenStruct.new(event_id: '2', resource_status: 'BLAH', timestamp: Time.now),
5
+ OpenStruct.new(event_id: '3', resource_status: 'BLAH', timestamp: Time.now),
6
+ ] }
7
+ let(:events_second_call) {
8
+ events_first_call + [
9
+ OpenStruct.new(event_id: '4', resource_status: 'UPDATE_COMPLETE', resource_type: 'AWS::CloudFormation::Stack', logical_resource_id: stack_name, timestamp: Time.now)
10
+ ]
11
+ }
12
+ let(:stack_name) { 'stack-name' }
13
+ let(:region) { 'us-east-1' }
14
+ let(:now) { Time.now }
15
+
16
+ before do
17
+ allow(StackMaster::StackEvents::Fetcher).to receive(:fetch).with(stack_name, region, from: now).and_return(events_first_call, events_second_call)
18
+ allow(Time).to receive(:now).and_return(now)
19
+ end
20
+
21
+ it 'returns after seeing a finish state' do
22
+ events = []
23
+ StackMaster::StackEvents::Streamer.stream(stack_name, region, sleep_between_fetches: 0) do |event|
24
+ events << event
25
+ end
26
+ end
27
+
28
+ it 'streams events to an io object' do
29
+ io = StringIO.new
30
+ StackMaster::StackEvents::Streamer.stream(stack_name, region, sleep_between_fetches: 0, io: io)
31
+ expect(io.string).to include('UPDATE_COMPLETE')
32
+ end
33
+ end
@@ -0,0 +1,157 @@
1
+ RSpec.describe StackMaster::Stack do
2
+ let(:region) { 'us-east-1' }
3
+ let(:stack_name) { 'myapp_vpc' }
4
+ let(:stack_id) { '1' }
5
+ let(:stack_policy_body) { '{}' }
6
+ let(:cf) { Aws::CloudFormation::Client.new }
7
+ subject(:stack) { StackMaster::Stack.find(region, stack_name) }
8
+
9
+ before do
10
+ allow(Aws::CloudFormation::Client).to receive(:new).and_return(cf)
11
+ end
12
+
13
+ describe '.find' do
14
+ context 'when the stack exists in AWS' do
15
+ let(:parameters) {
16
+ [
17
+ {parameter_key: 'param1', parameter_value: 'value1'},
18
+ {parameter_key: 'param2', parameter_value: 'value2'}
19
+ ]
20
+ }
21
+ before do
22
+ cf.stub_responses(:describe_stacks, stacks: [{ stack_id: stack_id, stack_name: stack_name, creation_time: Time.now, stack_status: 'UPDATE_COMPLETE', parameters: parameters, notification_arns: ['test_arn']}])
23
+ cf.stub_responses(:get_template, template_body: "{}")
24
+ cf.stub_responses(:get_stack_policy, stack_policy_body: stack_policy_body)
25
+ end
26
+
27
+ it 'returns a stack object with a stack_id' do
28
+ expect(stack.stack_id).to eq stack_id
29
+ end
30
+
31
+ it "returns a template body" do
32
+ expect(stack.template_body).to eq "{}"
33
+ end
34
+
35
+ it 'parses parameters into a hash' do
36
+ expect(stack.parameters).to eq({'param1' => 'value1', 'param2' => 'value2'})
37
+ end
38
+
39
+ it 'sets notification_arns' do
40
+ expect(stack.notification_arns).to eq(['test_arn'])
41
+ end
42
+
43
+ it 'sets the stack policy' do
44
+ expect(stack.stack_policy_body).to eq stack_policy_body
45
+ end
46
+ end
47
+
48
+ context 'when the stack does not exist in AWS' do
49
+ before do
50
+ cf.stub_responses(:describe_stacks, Aws::CloudFormation::Errors::ValidationError.new('a', 'b'))
51
+ end
52
+
53
+ it 'returns nil' do
54
+ stack = StackMaster::Stack.find(region, stack_name)
55
+ expect(stack).to be_nil
56
+ end
57
+ end
58
+
59
+ context 'when CF returns no stacks' do
60
+ before do
61
+ cf.stub_responses(:describe_stacks, stacks: [])
62
+ end
63
+
64
+ it 'returns nil' do
65
+ stack = StackMaster::Stack.find(region, stack_name)
66
+ expect(stack).to be_nil
67
+ end
68
+ end
69
+ end
70
+
71
+ describe '.generate' do
72
+ let(:tags) { { 'tag1' => 'value1' } }
73
+ let(:stack_definition) { StackMaster::StackDefinition.new(region: region, stack_name: stack_name, tags: tags, base_dir: '/base_dir', template: template_file_name, notification_arns: ['test_arn'], stack_policy_file: 'no_replace_rds.json') }
74
+ let(:config) { StackMaster::Config.new({'stacks' => {}}, '/base_dir') }
75
+ subject(:stack) { StackMaster::Stack.generate(stack_definition, config) }
76
+ let(:parameter_hash) { { 'DbPassword' => { 'secret' => 'db_password' } } }
77
+ let(:resolved_parameters) { { 'DbPassword' => 'sdfgjkdhlfjkghdflkjghdflkjg', 'InstanceType' => 't2.medium' } }
78
+ let(:template_file_name) { 'template.rb' }
79
+ let(:template_body) { '{"Parameters": { "VpcId": { "Description": "VPC ID" }, "InstanceType": { "Description": "Instance Type", "Default": "t2.micro" }} }' }
80
+ let(:stack_policy_body) { '{}' }
81
+
82
+ before do
83
+ allow(StackMaster::ParameterLoader).to receive(:load).and_return(parameter_hash)
84
+ allow(StackMaster::ParameterResolver).to receive(:resolve).and_return(resolved_parameters)
85
+ allow(StackMaster::TemplateCompiler).to receive(:compile).with(stack_definition.template_file_path).and_return(template_body)
86
+ allow(File).to receive(:read).with(stack_definition.stack_policy_file_path).and_return(stack_policy_body)
87
+ end
88
+
89
+ it 'has the stack definitions region' do
90
+ expect(stack.region).to eq region
91
+ end
92
+
93
+ it 'has the stack definitions name' do
94
+ expect(stack.stack_name).to eq stack_name
95
+ end
96
+
97
+ it 'has the stack definitions tags' do
98
+ expect(stack.tags).to eq tags
99
+ end
100
+
101
+ it 'resolves the parameters' do
102
+ expect(stack.parameters).to eq resolved_parameters
103
+ end
104
+
105
+ it 'compiles the template body' do
106
+ expect(stack.template_body).to eq template_body
107
+ end
108
+
109
+ it 'has notification_arns' do
110
+ expect(stack.notification_arns).to eq ['test_arn']
111
+ end
112
+
113
+ it 'has the stack policy' do
114
+ expect(stack.stack_policy_body).to eq stack_policy_body
115
+ end
116
+
117
+ it 'extracts default template parameters' do
118
+ expect(stack.template_default_parameters).to eq('VpcId' => nil, 'InstanceType' => 't2.micro')
119
+ end
120
+
121
+ it 'exposes parameters with defaults taken into account' do
122
+ expect(stack.parameters_with_defaults).to eq('DbPassword' => 'sdfgjkdhlfjkghdflkjghdflkjg', 'InstanceType' => 't2.medium', 'VpcId' => nil)
123
+ end
124
+ end
125
+
126
+ describe "#maybe_compressed_template_body" do
127
+ subject(:maybe_compressed_template_body) do
128
+ stack.maybe_compressed_template_body
129
+ end
130
+ context "undersized json" do
131
+ let(:stack) { described_class.new(template_body: '{ }' ) }
132
+
133
+ it "leaves the json alone if it's not too large" do
134
+ expect(maybe_compressed_template_body).to eq('{ }')
135
+ end
136
+ end
137
+
138
+ context "oversized json" do
139
+ let(:stack) { described_class.new(template_body: "{#{' ' * 60000}}" ) }
140
+ it "compresses the json when it's overly bulbous" do
141
+ expect(maybe_compressed_template_body).to eq('{}')
142
+ end
143
+ end
144
+ end
145
+
146
+ describe '#too_big?' do
147
+ let(:big_stack) { described_class.new(template_body: "{\"a\":\"#{'x' * 60000}\"}") }
148
+ let(:little_stack) { described_class.new(template_body: "{\"a\":\"#{'x' * 1000}\"}") }
149
+
150
+ it 'returns true for big stacks' do
151
+ expect(big_stack.too_big?).to be_truthy
152
+ end
153
+ it 'returns false for small stacks' do
154
+ expect(little_stack.too_big?).to be_falsey
155
+ end
156
+ end
157
+ end
@@ -0,0 +1,48 @@
1
+ RSpec.describe StackMaster::TemplateCompiler do
2
+ describe ".compile" do
3
+ def compile
4
+ StackMaster::TemplateCompiler.compile(template_file_path)
5
+ end
6
+
7
+ context 'json template' do
8
+ let(:template_file_path) { '/base_dir/templates/template.json' }
9
+
10
+ context "small json template" do
11
+ before do
12
+ allow(File).to receive(:read).with(template_file_path).and_return('{ }')
13
+ end
14
+
15
+ it "reads from the template file path" do
16
+ expect(compile).to eq('{ }')
17
+ end
18
+ end
19
+
20
+ context 'extra big json template' do
21
+ before do
22
+ allow(File).to receive(:read).with(template_file_path).and_return("{ #{' ' * 60000} }")
23
+ end
24
+
25
+ it "reads from the template file path" do
26
+ expect(compile).to eq('{}')
27
+ end
28
+ end
29
+ end
30
+
31
+ context 'sparkleformation template' do
32
+ let(:template_file_path) { '/base_dir/templates/template.rb' }
33
+
34
+ before do
35
+ allow(SparkleFormation).to receive(:compile).with(template_file_path).and_return({})
36
+ end
37
+
38
+ it 'compiles with sparkleformation' do
39
+ expect(compile).to eq("{\n}")
40
+ end
41
+
42
+ it 'sets the appropriate sparkle_path' do
43
+ compile
44
+ expect(SparkleFormation.sparkle_path).to eq File.dirname(template_file_path)
45
+ end
46
+ end
47
+ end
48
+ end