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 +7 -0
- data/exe/spectre +487 -0
- data/lib/spectre.rb +434 -0
- data/lib/spectre/assertion.rb +277 -0
- data/lib/spectre/bag.rb +19 -0
- data/lib/spectre/curl.rb +368 -0
- data/lib/spectre/database/postgres.rb +78 -0
- data/lib/spectre/diagnostic.rb +29 -0
- data/lib/spectre/environment.rb +26 -0
- data/lib/spectre/ftp.rb +195 -0
- data/lib/spectre/helpers.rb +64 -0
- data/lib/spectre/http.rb +343 -0
- data/lib/spectre/http/basic_auth.rb +22 -0
- data/lib/spectre/http/keystone.rb +98 -0
- data/lib/spectre/logger.rb +144 -0
- data/lib/spectre/logger/console.rb +142 -0
- data/lib/spectre/logger/file.rb +96 -0
- data/lib/spectre/mixin.rb +41 -0
- data/lib/spectre/mysql.rb +97 -0
- data/lib/spectre/reporter/console.rb +103 -0
- data/lib/spectre/reporter/junit.rb +98 -0
- data/lib/spectre/resources.rb +46 -0
- data/lib/spectre/ssh.rb +149 -0
- metadata +140 -0
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
|