spectre-core 1.8.0

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