spectre-core 1.12.0 → 1.12.3

Sign up to get free protection for your applications and to get access to all the features.
data/exe/spectre CHANGED
@@ -1,516 +1,545 @@
1
- #! /usr/bin/ruby
2
- # frozen_string_literal: true
3
-
4
- require 'yaml'
5
- require 'ostruct'
6
- require 'optparse'
7
- require 'fileutils'
8
- require 'ectoplasm'
9
-
10
- require_relative '../lib/spectre'
11
-
12
-
13
- DEFAULT_CONFIG = {
14
- 'config_file' => './spectre.yml',
15
- 'environment' => 'default',
16
- 'specs' => [],
17
- 'tags' => [],
18
- 'colored' => true,
19
- 'verbose' => false,
20
- 'reporters' => [
21
- 'Spectre::Reporter::Console',
22
- ],
23
- 'loggers' => [
24
- 'Spectre::Logger::Console',
25
- 'Spectre::Logger::File',
26
- ],
27
- 'log_file' => './logs/spectre_<date>.log',
28
- 'log_format' => {
29
- 'console' => {
30
- 'indent' => 2,
31
- 'width' => 80,
32
- 'end_context' => nil,
33
- 'separator' => '<indent><desc>',
34
- },
35
- 'file' => {
36
- 'separator' => '-- <desc>',
37
- 'start_group' => "-- Start '<desc>'",
38
- 'end_group' => "-- End '<desc>'",
39
- },
40
- },
41
- 'debug' => false,
42
- 'out_path' => './reports',
43
- 'secure_keys' => ['password', 'secret', 'token', 'secure', 'authorization'],
44
- 'spec_patterns' => ['./specs/**/*.spec.rb'],
45
- 'mixin_patterns' => ['../common/mixins/**/*.mixin.rb', './mixins/**/*.mixin.rb'],
46
- 'env_patterns' => ['./environments/**/*.env.yml'],
47
- 'env_partial_patterns' => ['./environments/**/*.env.secret.yml'],
48
- 'resource_paths' => ['../common/resources', './resources'],
49
- 'modules' => [
50
- 'spectre/helpers',
51
- 'spectre/reporter/console',
52
- 'spectre/reporter/junit',
53
- 'spectre/logger/console',
54
- 'spectre/logger/file',
55
- 'spectre/assertion',
56
- 'spectre/diagnostic',
57
- 'spectre/environment',
58
- 'spectre/mixin',
59
- 'spectre/bag',
60
- 'spectre/http',
61
- 'spectre/http/basic_auth',
62
- 'spectre/http/keystone',
63
- 'spectre/resources',
64
- ],
65
- 'include' => [
66
-
67
- ],
68
- 'exclude' => [
69
-
70
- ],
71
- }
72
-
73
-
74
- cmd_options = {}
75
-
76
- opt_parser = OptionParser.new do |opts|
77
- opts.banner = %{Spectre #{Spectre::VERSION}
78
-
79
- Usage: spectre [command] [options]
80
-
81
- Commands:
82
- list List specs
83
- run Run specs (default)
84
- show Print current environment settings
85
- dump Dumps the given environment in YAML format to console
86
- init Initializes a new spectre project
87
-
88
- Specific options:}
89
-
90
- opts.on('-s SPEC,SPEC', '--specs SPEC,SPEC', Array, 'The specs to run') do |specs|
91
- cmd_options['specs'] = specs
92
- end
93
-
94
- opts.on('-t TAG,TAG', '--tags TAG,TAG', Array, 'Run only specs with given tags') do |tags|
95
- cmd_options['tags'] = tags
96
- end
97
-
98
- opts.on('-e NAME', '--env NAME', 'Name of the environment to load') do |env_name|
99
- cmd_options['environment'] = env_name
100
- end
101
-
102
- opts.on('-c FILE', '--config FILE', 'Config file to load') do |file_path|
103
- cmd_options['config_file'] = file_path
104
- end
105
-
106
- opts.on('--spec-pattern PATTERN', Array, 'File pattern for spec files') do |spec_pattern|
107
- cmd_options['spec_patterns'] = spec_pattern
108
- end
109
-
110
- opts.on('--env-pattern PATTERN', Array, 'File pattern for environment files') do |env_patterns|
111
- cmd_options['env_patterns'] = env_patterns
112
- end
113
-
114
- opts.on('--no-color', 'Disable colored output') do
115
- cmd_options['colored'] = false
116
- end
117
-
118
- opts.on('--ignore-failure', 'Always exit with code 0') do
119
- cmd_options['ignore_failure'] = true
120
- end
121
-
122
- opts.on('-o PATH', '--out PATH', 'Output directory path') do |path|
123
- cmd_options['out_path'] = File.absolute_path(path)
124
- end
125
-
126
- opts.on('-r NAME', '--reporters NAME', Array, "A list of reporters to use") do |reporters|
127
- cmd_options['reporters'] = reporters
128
- end
129
-
130
- opts.on('-d', '--debug', "Run in debug mode") do
131
- cmd_options['debug'] = true
132
- end
133
-
134
- opts.on('-p KEY=VAL', '--property KEY=VAL', "Override config option. Use `spectre show` to get list of available options") do |option|
135
- key, val = option.split '='
136
- val = val.split ',' if DEFAULT_CONFIG[key].is_a? Array
137
- val = ['true', '1'].include? val if [true, false].include? DEFAULT_CONFIG[key]
138
- val = val.to_i if DEFAULT_CONFIG[key].is_a? Integer
139
- cmd_options[key] = val
140
-
141
- curr_opt = cmd_options
142
- (key.split '.').each do |k|
143
- curr_opt[k] = {} unless curr_opt.key? k
144
- end
145
- curr_opt = val
146
- end
147
-
148
- opts.separator "\nCommon options:"
149
-
150
- opts.on_tail('--version', 'Print current installed version') do
151
- puts Spectre::VERSION
152
- exit
153
- end
154
-
155
- opts.on_tail('-h', '--help', 'Print this help') do
156
- puts opts
157
- exit
158
- end
159
- end.parse!
160
-
161
-
162
- action = ARGV[0] || 'run'
163
-
164
-
165
- ###########################################
166
- # Load Config
167
- ###########################################
168
-
169
-
170
- cfg = {}
171
- cfg.deep_merge! DEFAULT_CONFIG
172
-
173
- global_config_file = File.join File.expand_path('~'), '.spectre'
174
-
175
- if File.exists? global_config_file
176
- global_options = YAML.load_file(global_config_file)
177
- cfg.deep_merge! global_options if global_options
178
- end
179
-
180
- config_file = cmd_options['config_file'] || cfg['config_file']
181
-
182
- if File.exists? config_file
183
- file_options = YAML.load_file(config_file)
184
- cfg.deep_merge! file_options
185
- Dir.chdir File.dirname(config_file)
186
- end
187
-
188
- cfg.deep_merge! cmd_options
189
-
190
-
191
- ###########################################
192
- # Load Environment
193
- ###########################################
194
-
195
-
196
- envs = {}
197
- read_env_files = {}
198
- cfg['env_patterns'].each do |pattern|
199
- Dir.glob(pattern).each do|f|
200
- spec_env = YAML.load_file(f) || {}
201
-
202
- name = spec_env['name'] || 'default'
203
-
204
- if envs.key? name
205
- existing_env_file = read_env_files[name]
206
- puts "Duplicate environment definition detected with name #{name} in '#{f}'. Previously defined in '#{existing_env_file}'"
207
- exit 1
208
- end
209
-
210
- read_env_files[name] = f
211
- envs[name] = spec_env
212
- end
213
- end
214
-
215
- # Merge partial environment configs with existing environments
216
- cfg['env_partial_patterns'].each do |pattern|
217
- Dir.glob(pattern).each do|f|
218
- partial_env = YAML.load_file(f)
219
- name = partial_env.delete('name') || 'default'
220
- next unless envs.key? name
221
-
222
- envs[name].deep_merge! partial_env
223
- end
224
- end
225
-
226
- env = envs[cfg['environment']]
227
- cfg.merge! env if env
228
-
229
-
230
- String.colored! if cfg['colored']
231
-
232
- # Load environment exlicitly before loading specs to make it available in spec definition
233
- require_relative '../lib/spectre/environment' unless cfg['exclude'].include? 'spectre/environment'
234
- Spectre.configure(cfg)
235
-
236
-
237
- ###########################################
238
- # Load Specs
239
- ###########################################
240
-
241
-
242
- cfg['spec_patterns'].each do |pattern|
243
- Dir.glob(pattern).each do|f|
244
- require_relative File.join(Dir.pwd, f)
245
- end
246
- end
247
-
248
-
249
- ###########################################
250
- # List specs
251
- ###########################################
252
-
253
-
254
- if 'list' == action
255
- colors = [:blue, :magenta, :yellow, :green]
256
- specs = Spectre.specs(cfg['specs'], cfg['tags'])
257
-
258
- exit 1 unless specs.any?
259
-
260
- counter = 0
261
-
262
- specs.group_by { |x| x.subject }.each do |subject, spec_group|
263
- spec_group.each do |spec|
264
- tags = spec.tags.map { |x| '#' + x.to_s }.join ' '
265
- desc = subject.desc
266
- desc += ' - ' + spec.context.__desc + ' -' if spec.context.__desc
267
- desc += ' ' + spec.desc
268
- puts "[#{spec.name}]".send(colors[counter % colors.length]) + " #{desc} #{tags.cyan}"
269
- end
270
-
271
- counter += 1
272
- end
273
-
274
- exit 0
275
- end
276
-
277
-
278
- ###########################################
279
- # Run
280
- ###########################################
281
-
282
-
283
- if 'run' == action
284
- # Initialize logger
285
- now = Time.now
286
-
287
- cfg['log_file'] = cfg['log_file'].frmt(
288
- {
289
- shortdate: now.strftime('%Y-%m-%d'),
290
- date: now.strftime('%Y-%m-%d_%H%M%S'),
291
- timestamp: now.strftime('%s'),
292
- subject: 'spectre',
293
- })
294
-
295
- log_dir = File.dirname cfg['log_file']
296
- FileUtils.makedirs log_dir unless Dir.exists? log_dir
297
-
298
- # Load Modules
299
-
300
- cfg['modules']
301
- .concat(cfg['include'])
302
- .select { |mod| !cfg['exclude'].include? mod }
303
- .each do |mod|
304
- begin
305
- mod_file = mod + '.rb'
306
- spectre_lib_mod = File.join(File.dirname(__dir__), 'lib', mod_file)
307
-
308
- if File.exists? mod_file
309
- require_relative mod_file
310
-
311
- elsif File.exists? spectre_lib_mod
312
- require_relative spectre_lib_mod
313
-
314
- else
315
- require mod
316
- end
317
- rescue LoadError => e
318
- puts "Unable to load module #{mod}. Check if the module exists or remove it from your spectre config:\n#{e.message}"
319
- exit 1
320
- end
321
- end
322
-
323
- # Load mixins
324
-
325
- cfg['mixin_patterns'].each do |pattern|
326
- Dir.glob(pattern).each do|f|
327
- require_relative File.join(Dir.pwd, f)
328
- end
329
- end
330
-
331
- Spectre.configure(cfg)
332
-
333
- Spectre::Logger.debug! if cfg['debug']
334
-
335
- cfg['loggers'].each do |logger_name|
336
- logger = Kernel.const_get(logger_name).new(cfg)
337
- Spectre::Logger.add(logger)
338
- end if cfg['loggers']
339
-
340
- specs = Spectre.specs(cfg['specs'], cfg['tags'])
341
-
342
- unless specs.any?
343
- puts "No specs found in #{Dir.pwd}"
344
- exit 1
345
- end
346
-
347
- run_infos = Spectre::Runner.new.run(specs)
348
-
349
- cfg['reporters'].each do |reporter|
350
- reporter = Kernel.const_get(reporter).new(cfg)
351
- reporter.report(run_infos)
352
- end
353
-
354
- errors = run_infos.select { |x| nil != x.error or nil != x.failure }
355
-
356
- exit 0 if cfg['ignore_failure'] or not errors.any?
357
-
358
- exit 1
359
- end
360
-
361
-
362
- ###########################################
363
- # Envs
364
- ###########################################
365
-
366
-
367
- if 'envs' == action
368
- exit 1 unless envs.any?
369
- puts envs.pretty
370
- exit 0
371
- end
372
-
373
-
374
- ###########################################
375
- # Show
376
- ###########################################
377
-
378
-
379
- if 'show' == action
380
- puts cfg.pretty
381
- exit 0
382
- end
383
-
384
-
385
- ###########################################
386
- # Dump
387
- ###########################################
388
-
389
-
390
- if 'dump' == action
391
- puts YAML.dump(cfg)
392
- end
393
-
394
-
395
- ###########################################
396
- # Init
397
- ###########################################
398
-
399
- DEFAULT_SPECTRE_CFG = %{log_file: ./logs/spectre_<date>.log
400
- env_patterns:
401
- - './environments/**/*.env.yml'
402
- env_partial_patterns:
403
- - './environments/**/*.env.secret.yml'
404
- spec_patterns:
405
- - './specs/**/*.spec.rb'
406
- mixin_patterns:
407
- - '../common/**/*.mixin.rb'
408
- - './mixins/**/*.mixin.rb'
409
- resource_paths:
410
- - '../common/resources'
411
- - './resources'
412
- }
413
-
414
-
415
- DEFAULT_ENV_CFG = %{cert: &cert ./resources/<root_cert>.cer
416
- http:
417
- <http_client_name>:
418
- base_url: http://localhost:5000/api/v1/
419
- # basic_auth:
420
- # username: <username>
421
- # password: <password>
422
- # keystone:
423
- # url: https://<keystone_url>/main/v3/
424
- # username: <username>
425
- # password: <password>
426
- # project: <project>
427
- # domain: <domain>
428
- # cert: *cert
429
- # ssh:
430
- # <ssh_client_name>:
431
- # host: <hostname>
432
- # username: <username>
433
- # password: <password>
434
- }
435
-
436
- DEFAULT_ENV_SECRET_CFG = %{http:
437
- <http_client_name>:
438
- # basic_auth:
439
- # username: <username>
440
- # password: <password>
441
- # keystone:
442
- # username: <username>
443
- # password: <password>
444
- # ssh:
445
- # <ssh_client_name>:
446
- # username: <username>
447
- # password: <password>
448
- }
449
-
450
- SAMPLE_SPEC = %[describe '<subject>' do
451
- it 'does some http requests', tags: [:sample] do
452
- log 'doing some http request'
453
-
454
- http '<http_client_name>' do
455
- auth 'basic'
456
- # auth 'keystone'
457
- method 'GET'
458
- path 'path/to/resource'
459
- param 'id', 4295118773
460
- param 'foo', 'bar'
461
- header 'X-Correlation-Id', '4c2367b1-bfee-4cc2-bdc5-ed17a6a9dd4b'
462
- header 'Range', 'bytes=500-999'
463
- json({
464
- "message": "Hello Spectre!"
465
- })
466
- end
467
-
468
- expect 'the response code to be 200' do
469
- response.code.should_be 200
470
- end
471
-
472
- expect 'a message to exist' do
473
- response.json.message.should_not_be nil
474
- end
475
- end
476
- end
477
- ]
478
-
479
- DEFAULT_GITIGNORE = %[*.code-workspace
480
- logs/
481
- reports/
482
- **/environments/*.env.secret.yml
483
- ]
484
-
485
- DEFAULT_GEMFILE = %[source 'https://rubygems.org'
486
-
487
- gem 'spectre-core', '>= #{Spectre::VERSION}'
488
- # gem 'spectre-mysql', '>= 1.0.0'
489
- # gem 'spectre-ssh', '>= 1.0.0'
490
- # gem 'spectre-ftp', '>= 1.0.0'
491
- # gem 'spectre-curl', '>= 1.0.0'
492
- # gem 'spectre-git', '>= 0.1.0'
493
- ]
494
-
495
- if 'init' == action
496
- DEFAULT_FILES = [
497
- ['./environments/default.env.yml', DEFAULT_ENV_CFG],
498
- ['./environments/default.env.secret.yml', DEFAULT_ENV_SECRET_CFG],
499
- ['./specs/sample.spec.rb', SAMPLE_SPEC],
500
- ['./spectre.yml', DEFAULT_SPECTRE_CFG],
501
- ['./.gitignore', DEFAULT_GITIGNORE],
502
- ['./Gemfile', DEFAULT_GEMFILE],
503
- ]
504
-
505
- %w(environments logs specs).each do |dir_name|
506
- Dir.mkdir(dir_name) unless File.directory? dir_name
507
- end
508
-
509
- DEFAULT_FILES.each do |file, content|
510
- unless File.exists? file
511
- File.write(file, content)
512
- end
513
- end
514
-
515
- exit 0
516
- end
1
+ #! /usr/bin/ruby
2
+ # frozen_string_literal: true
3
+
4
+ require 'yaml'
5
+ require 'ostruct'
6
+ require 'optparse'
7
+ require 'fileutils'
8
+ require 'ectoplasm'
9
+
10
+ require_relative '../lib/spectre'
11
+
12
+
13
+ DEFAULT_CONFIG = {
14
+ 'config_file' => './spectre.yml',
15
+ 'environment' => 'default',
16
+ 'specs' => [],
17
+ 'tags' => [],
18
+ 'colored' => true,
19
+ 'verbose' => false,
20
+ 'reporters' => [
21
+ 'Spectre::Reporter::Console',
22
+ ],
23
+ 'loggers' => [
24
+ 'Spectre::Logger::Console',
25
+ 'Spectre::Logger::File',
26
+ ],
27
+ 'log_file' => './logs/spectre_<date>.log',
28
+ 'log_format' => {
29
+ 'console' => {
30
+ 'indent' => 2,
31
+ 'width' => 80,
32
+ 'end_context' => nil,
33
+ 'separator' => '<indent><desc>',
34
+ },
35
+ 'file' => {
36
+ 'separator' => '-- <desc>',
37
+ 'start_group' => "-- Start '<desc>'",
38
+ 'end_group' => "-- End '<desc>'",
39
+ },
40
+ },
41
+ 'debug' => false,
42
+ 'out_path' => './reports',
43
+ 'secure_keys' => ['password', 'secret', 'token', 'secure', 'authorization'],
44
+ 'spec_patterns' => ['./specs/**/*.spec.rb'],
45
+ 'mixin_patterns' => ['../common/mixins/**/*.mixin.rb', './mixins/**/*.mixin.rb'],
46
+ 'env_patterns' => ['./environments/**/*.env.yml'],
47
+ 'env_partial_patterns' => ['./environments/**/*.env.secret.yml'],
48
+ 'resource_paths' => ['../common/resources', './resources'],
49
+ 'modules' => [
50
+ 'spectre/helpers',
51
+ 'spectre/reporter/console',
52
+ 'spectre/reporter/junit',
53
+ 'spectre/logger/console',
54
+ 'spectre/logger/file',
55
+ 'spectre/assertion',
56
+ 'spectre/diagnostic',
57
+ 'spectre/environment',
58
+ 'spectre/mixin',
59
+ 'spectre/bag',
60
+ 'spectre/http',
61
+ 'spectre/http/basic_auth',
62
+ 'spectre/http/keystone',
63
+ 'spectre/resources',
64
+ ],
65
+ 'include' => [
66
+
67
+ ],
68
+ 'exclude' => [
69
+
70
+ ],
71
+ }
72
+
73
+
74
+ cmd_options = {}
75
+ property_overrides = {}
76
+
77
+ opt_parser = OptionParser.new do |opts|
78
+ opts.banner = %{Spectre #{Spectre::VERSION}
79
+
80
+ Usage: spectre [command] [options]
81
+
82
+ Commands:
83
+ list List specs
84
+ run Run specs (default)
85
+ show Print current environment settings
86
+ dump Dumps the given environment in YAML format to console
87
+ cleanup Will remove all generated files (e.g. logs and reports)
88
+ init Initializes a new spectre project
89
+
90
+ Specific options:}
91
+
92
+ opts.on('-s SPEC,SPEC', '--specs SPEC,SPEC', Array, 'The specs to run') do |specs|
93
+ cmd_options['specs'] = specs
94
+ end
95
+
96
+ opts.on('-t TAG,TAG', '--tags TAG,TAG', Array, 'Run only specs with given tags') do |tags|
97
+ cmd_options['tags'] = tags
98
+ end
99
+
100
+ opts.on('-e NAME', '--env NAME', 'Name of the environment to load') do |env_name|
101
+ cmd_options['environment'] = env_name
102
+ end
103
+
104
+ opts.on('-c FILE', '--config FILE', 'Config file to load') do |file_path|
105
+ cmd_options['config_file'] = file_path
106
+ end
107
+
108
+ opts.on('--spec-pattern PATTERN', Array, 'File pattern for spec files') do |spec_pattern|
109
+ cmd_options['spec_patterns'] = spec_pattern
110
+ end
111
+
112
+ opts.on('--env-pattern PATTERN', Array, 'File pattern for environment files') do |env_patterns|
113
+ cmd_options['env_patterns'] = env_patterns
114
+ end
115
+
116
+ opts.on('--no-color', 'Disable colored output') do
117
+ cmd_options['colored'] = false
118
+ end
119
+
120
+ opts.on('--ignore-failure', 'Always exit with code 0') do
121
+ cmd_options['ignore_failure'] = true
122
+ end
123
+
124
+ opts.on('-o PATH', '--out PATH', 'Output directory path') do |path|
125
+ cmd_options['out_path'] = File.absolute_path(path)
126
+ end
127
+
128
+ opts.on('-r NAME', '--reporters NAME', Array, "A list of reporters to use") do |reporters|
129
+ cmd_options['reporters'] = reporters
130
+ end
131
+
132
+ opts.on('-d', '--debug', "Run in debug mode") do
133
+ cmd_options['debug'] = true
134
+ end
135
+
136
+ opts.on('-p KEY=VAL', '--property KEY=VAL', "Override config option. Use `spectre show` to get list of available options") do |option|
137
+ key, val = option.split('=')
138
+ val = val.split(',') if DEFAULT_CONFIG[key].is_a? Array
139
+ val = ['true', '1'].include? val if [true, false].include?(DEFAULT_CONFIG[key])
140
+ val = val.to_i if DEFAULT_CONFIG[key].is_a? Integer
141
+
142
+ opt_path = key.split('.')
143
+
144
+ curr_opt = property_overrides
145
+
146
+ opt_path.each_with_index do |part, i|
147
+ if i == opt_path.count-1
148
+ curr_opt[part] = val
149
+ break
150
+ end
151
+
152
+ curr_opt[part] = {} unless curr_opt.key?(part)
153
+ curr_opt = curr_opt[part]
154
+ end
155
+ end
156
+
157
+ opts.separator "\nCommon options:"
158
+
159
+ opts.on_tail('--version', 'Print current installed version') do
160
+ puts Spectre::VERSION
161
+ exit
162
+ end
163
+
164
+ opts.on_tail('-h', '--help', 'Print this help') do
165
+ puts opts
166
+ exit
167
+ end
168
+ end.parse!
169
+
170
+
171
+ action = ARGV[0] || 'run'
172
+
173
+
174
+ ###########################################
175
+ # Load Config
176
+ ###########################################
177
+
178
+
179
+ cfg = {}
180
+ cfg.deep_merge! DEFAULT_CONFIG
181
+
182
+ global_config_file = File.join File.expand_path('~'), '.spectre'
183
+
184
+ if File.exists? global_config_file
185
+ global_options = YAML.load_file(global_config_file)
186
+ cfg.deep_merge! global_options if global_options
187
+ end
188
+
189
+ config_file = cmd_options['config_file'] || cfg['config_file']
190
+
191
+ if File.exists? config_file
192
+ file_options = YAML.load_file(config_file)
193
+ cfg.deep_merge! file_options
194
+ Dir.chdir File.dirname(config_file)
195
+ end
196
+
197
+ cfg.deep_merge! cmd_options
198
+
199
+ ###########################################
200
+ # Load Environment
201
+ ###########################################
202
+
203
+
204
+ envs = {}
205
+ read_env_files = {}
206
+ cfg['env_patterns'].each do |pattern|
207
+ Dir.glob(pattern).each do|f|
208
+ spec_env = YAML.load_file(f) || {}
209
+
210
+ name = spec_env['name'] || 'default'
211
+
212
+ if envs.key? name
213
+ existing_env_file = read_env_files[name]
214
+ puts "Duplicate environment definition detected with name #{name} in '#{f}'. Previously defined in '#{existing_env_file}'"
215
+ exit 1
216
+ end
217
+
218
+ read_env_files[name] = f
219
+ envs[name] = spec_env
220
+ end
221
+ end
222
+
223
+ # Merge partial environment configs with existing environments
224
+ cfg['env_partial_patterns'].each do |pattern|
225
+ Dir.glob(pattern).each do|f|
226
+ partial_env = YAML.load_file(f)
227
+ name = partial_env.delete('name') || 'default'
228
+ next unless envs.key? name
229
+
230
+ envs[name].deep_merge! partial_env
231
+ end
232
+ end
233
+
234
+ env = envs[cfg['environment']]
235
+ cfg.deep_merge! env if env
236
+
237
+ # Merge property overrides after environment load to give it higher priority
238
+ cfg.deep_merge! property_overrides
239
+
240
+
241
+ String.colored! if cfg['colored']
242
+
243
+ # Load environment exlicitly before loading specs to make it available in spec definition
244
+ require_relative '../lib/spectre/environment' unless cfg['exclude'].include? 'spectre/environment'
245
+ Spectre.configure(cfg)
246
+
247
+
248
+ ###########################################
249
+ # Load Specs
250
+ ###########################################
251
+
252
+
253
+ cfg['spec_patterns'].each do |pattern|
254
+ Dir.glob(pattern).each do|f|
255
+ require_relative File.join(Dir.pwd, f)
256
+ end
257
+ end
258
+
259
+
260
+ ###########################################
261
+ # List specs
262
+ ###########################################
263
+
264
+
265
+ if 'list' == action
266
+ colors = [:blue, :magenta, :yellow, :green]
267
+ specs = Spectre.specs(cfg['specs'], cfg['tags'])
268
+
269
+ exit 1 unless specs.any?
270
+
271
+ counter = 0
272
+
273
+ specs.group_by { |x| x.subject }.each do |subject, spec_group|
274
+ spec_group.each do |spec|
275
+ tags = spec.tags.map { |x| '#' + x.to_s }.join ' '
276
+ desc = subject.desc
277
+ desc += ' - ' + spec.context.__desc + ' -' if spec.context.__desc
278
+ desc += ' ' + spec.desc
279
+ puts "[#{spec.name}]".send(colors[counter % colors.length]) + " #{desc} #{tags.cyan}"
280
+ end
281
+
282
+ counter += 1
283
+ end
284
+
285
+ exit 0
286
+ end
287
+
288
+
289
+ ###########################################
290
+ # Run
291
+ ###########################################
292
+
293
+
294
+ if 'run' == action
295
+ # Initialize logger
296
+ now = Time.now
297
+
298
+ cfg['log_file'] = cfg['log_file'].frmt(
299
+ {
300
+ shortdate: now.strftime('%Y-%m-%d'),
301
+ date: now.strftime('%Y-%m-%d_%H%M%S'),
302
+ timestamp: now.strftime('%s'),
303
+ subject: 'spectre',
304
+ })
305
+
306
+ log_dir = File.dirname cfg['log_file']
307
+ FileUtils.makedirs log_dir unless Dir.exists? log_dir
308
+
309
+ # Load Modules
310
+
311
+ cfg['modules']
312
+ .concat(cfg['include'])
313
+ .select { |mod| !cfg['exclude'].include? mod }
314
+ .each do |mod|
315
+ begin
316
+ mod_file = mod + '.rb'
317
+ spectre_lib_mod = File.join(File.dirname(__dir__), 'lib', mod_file)
318
+
319
+ if File.exists? mod_file
320
+ require_relative mod_file
321
+
322
+ elsif File.exists? spectre_lib_mod
323
+ require_relative spectre_lib_mod
324
+
325
+ else
326
+ require mod
327
+ end
328
+ rescue LoadError => e
329
+ puts "Unable to load module #{mod}. Check if the module exists or remove it from your spectre config:\n#{e.message}"
330
+ exit 1
331
+ end
332
+ end
333
+
334
+ # Load mixins
335
+
336
+ cfg['mixin_patterns'].each do |pattern|
337
+ Dir.glob(pattern).each do|f|
338
+ require_relative File.join(Dir.pwd, f)
339
+ end
340
+ end
341
+
342
+ Spectre.configure(cfg)
343
+
344
+ Spectre::Logger.debug! if cfg['debug']
345
+
346
+ cfg['loggers'].each do |logger_name|
347
+ logger = Kernel.const_get(logger_name).new(cfg)
348
+ Spectre::Logger.add(logger)
349
+ end if cfg['loggers']
350
+
351
+ specs = Spectre.specs(cfg['specs'], cfg['tags'])
352
+
353
+ unless specs.any?
354
+ puts "No specs found in #{Dir.pwd}"
355
+ exit 1
356
+ end
357
+
358
+ run_infos = Spectre::Runner.new.run(specs)
359
+
360
+ cfg['reporters'].each do |reporter|
361
+ reporter = Kernel.const_get(reporter).new(cfg)
362
+ reporter.report(run_infos)
363
+ end
364
+
365
+ errors = run_infos.select { |x| nil != x.error or nil != x.failure }
366
+
367
+ exit 0 if cfg['ignore_failure'] or not errors.any?
368
+
369
+ exit 1
370
+ end
371
+
372
+
373
+ ###########################################
374
+ # Envs
375
+ ###########################################
376
+
377
+
378
+ if 'envs' == action
379
+ exit 1 unless envs.any?
380
+ puts envs.pretty
381
+ exit 0
382
+ end
383
+
384
+
385
+ ###########################################
386
+ # Show
387
+ ###########################################
388
+
389
+
390
+ if 'show' == action
391
+ puts cfg.pretty
392
+ exit 0
393
+ end
394
+
395
+
396
+ ###########################################
397
+ # Dump
398
+ ###########################################
399
+
400
+
401
+ if 'dump' == action
402
+ puts YAML.dump(cfg)
403
+ end
404
+
405
+
406
+ ###########################################
407
+ # Cleanup
408
+ ###########################################
409
+
410
+
411
+ if 'cleanup' == action
412
+ log_file_pattern = cfg['log_file'].gsub('<date>', '*')
413
+
414
+ Dir.glob(log_file_pattern).each do |log_file|
415
+ File.delete(log_file)
416
+ end
417
+
418
+ Dir.glob(File.join cfg['out_path'], '/*').each do |out_file|
419
+ File.delete(out_file)
420
+ end
421
+ end
422
+
423
+
424
+ ###########################################
425
+ # Init
426
+ ###########################################
427
+
428
+ DEFAULT_SPECTRE_CFG = %{log_file: ./logs/spectre_<date>.log
429
+ env_patterns:
430
+ - './environments/**/*.env.yml'
431
+ env_partial_patterns:
432
+ - './environments/**/*.env.secret.yml'
433
+ spec_patterns:
434
+ - './specs/**/*.spec.rb'
435
+ mixin_patterns:
436
+ - '../common/**/*.mixin.rb'
437
+ - './mixins/**/*.mixin.rb'
438
+ resource_paths:
439
+ - '../common/resources'
440
+ - './resources'
441
+ }
442
+
443
+
444
+ DEFAULT_ENV_CFG = %{cert: &cert ./resources/<root_cert>.cer
445
+ http:
446
+ <http_client_name>:
447
+ base_url: http://localhost:5000/api/v1/
448
+ # basic_auth:
449
+ # username: <username>
450
+ # password: <password>
451
+ # keystone:
452
+ # url: https://<keystone_url>/main/v3/
453
+ # username: <username>
454
+ # password: <password>
455
+ # project: <project>
456
+ # domain: <domain>
457
+ # cert: *cert
458
+ # ssh:
459
+ # <ssh_client_name>:
460
+ # host: <hostname>
461
+ # username: <username>
462
+ # password: <password>
463
+ }
464
+
465
+ DEFAULT_ENV_SECRET_CFG = %{http:
466
+ <http_client_name>:
467
+ # basic_auth:
468
+ # username: <username>
469
+ # password: <password>
470
+ # keystone:
471
+ # username: <username>
472
+ # password: <password>
473
+ # ssh:
474
+ # <ssh_client_name>:
475
+ # username: <username>
476
+ # password: <password>
477
+ }
478
+
479
+ SAMPLE_SPEC = %[describe '<subject>' do
480
+ it 'does some http requests', tags: [:sample] do
481
+ log 'doing some http request'
482
+
483
+ http '<http_client_name>' do
484
+ auth 'basic'
485
+ # auth 'keystone'
486
+ method 'GET'
487
+ path 'path/to/resource'
488
+ param 'id', 4295118773
489
+ param 'foo', 'bar'
490
+ header 'X-Correlation-Id', '4c2367b1-bfee-4cc2-bdc5-ed17a6a9dd4b'
491
+ header 'Range', 'bytes=500-999'
492
+ json({
493
+ "message": "Hello Spectre!"
494
+ })
495
+ end
496
+
497
+ expect 'the response code to be 200' do
498
+ response.code.should_be 200
499
+ end
500
+
501
+ expect 'a message to exist' do
502
+ response.json.message.should_not_be nil
503
+ end
504
+ end
505
+ end
506
+ ]
507
+
508
+ DEFAULT_GITIGNORE = %[*.code-workspace
509
+ logs/
510
+ reports/
511
+ **/environments/*.env.secret.yml
512
+ ]
513
+
514
+ DEFAULT_GEMFILE = %[source 'https://rubygems.org'
515
+
516
+ gem 'spectre-core', '>= #{Spectre::VERSION}'
517
+ # gem 'spectre-mysql', '>= 1.0.0'
518
+ # gem 'spectre-ssh', '>= 1.0.0'
519
+ # gem 'spectre-ftp', '>= 1.0.0'
520
+ # gem 'spectre-curl', '>= 1.0.0'
521
+ # gem 'spectre-git', '>= 0.1.0'
522
+ ]
523
+
524
+ if 'init' == action
525
+ DEFAULT_FILES = [
526
+ ['./environments/default.env.yml', DEFAULT_ENV_CFG],
527
+ ['./environments/default.env.secret.yml', DEFAULT_ENV_SECRET_CFG],
528
+ ['./specs/sample.spec.rb', SAMPLE_SPEC],
529
+ ['./spectre.yml', DEFAULT_SPECTRE_CFG],
530
+ ['./.gitignore', DEFAULT_GITIGNORE],
531
+ ['./Gemfile', DEFAULT_GEMFILE],
532
+ ]
533
+
534
+ %w(environments logs specs).each do |dir_name|
535
+ Dir.mkdir(dir_name) unless File.directory? dir_name
536
+ end
537
+
538
+ DEFAULT_FILES.each do |file, content|
539
+ unless File.exists? file
540
+ File.write(file, content)
541
+ end
542
+ end
543
+
544
+ exit 0
545
+ end