spectre-core 1.8.3 → 1.11.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4917b9c51ecccb083b1032af33b98c2efd2a0905890d8c33d2a739f2f85e7391
4
- data.tar.gz: a8800ce11d35f41dd78c4f63e3fb9f0192bd64c9ab02e436a99ac2641e13ba62
3
+ metadata.gz: 8333942c6050c7adf1bb9c238538b5364b5648514264612aefc161f715c48e58
4
+ data.tar.gz: 34ca46ef693108549a69c754bc3b22d5be33e642d9a6dfde040b664af47cfe58
5
5
  SHA512:
6
- metadata.gz: d60156f1003d5cfd86d6ecb570f785979a24a192f4ca3800916eebb5ceaecdff78125ab893c61aef41a67992fedca580a6d2ff95026504358569a1abede4bdba
7
- data.tar.gz: df1057c2157e65a5bcc59c84594a1ef2cc5dfc9a9db0c16a96738f952adc68d747c25801573d16cdaa0a57fe005102436ed26b6ec3b4dbce75fdbf23a0fea516
6
+ metadata.gz: a840c8d9afd7010e0873cf273400a8a5d82b28ad05941d1c4562c03a8aed60fabc7ed704dc2538c5960a940440204df0228d0ce77b6ee11ec68f820c3deb6954
7
+ data.tar.gz: f6913fa578a938341fbd15455c1e122a4c425bacb68d1353ce7c5f0c8ed96b206eba2625d28d1d8df91fec4161343a28b15db5535c3785000c88cbee914c0f2b
data/exe/spectre CHANGED
@@ -48,6 +48,7 @@ DEFAULT_CONFIG = {
48
48
  },
49
49
  'debug' => false,
50
50
  'out_path' => './reports',
51
+ 'secure_keys' => ['password', 'secret', 'token', 'secure', 'authorization'],
51
52
  'spec_patterns' => ['./specs/**/*.spec.rb'],
52
53
  'mixin_patterns' => ['../common/mixins/**/*.mixin.rb', './mixins/**/*.mixin.rb'],
53
54
  'env_patterns' => ['./environments/**/*.env.yml'],
@@ -68,10 +69,6 @@ DEFAULT_CONFIG = {
68
69
  'spectre/http/basic_auth',
69
70
  'spectre/http/keystone',
70
71
  'spectre/resources',
71
- 'spectre/ssh',
72
- 'spectre/ftp',
73
- 'spectre/mysql',
74
- # 'spectre/postgres',
75
72
  ],
76
73
  'include' => [
77
74
 
@@ -126,11 +123,15 @@ Specific options:}
126
123
  cmd_options['colored'] = false
127
124
  end
128
125
 
126
+ opts.on('--ignore-failure', 'Always exit with code 0') do
127
+ cmd_options['ignore_failure'] = true
128
+ end
129
+
129
130
  opts.on('-o PATH', '--out PATH', 'Output directory path') do |path|
130
- cmd_options['out_path'] = path
131
+ cmd_options['out_path'] = File.absolute_path(path)
131
132
  end
132
133
 
133
- opts.on('-r NAME', '--reporter NAME', Array, "A list of reporters to use") do |reporters|
134
+ opts.on('-r NAME', '--reporters NAME', Array, "A list of reporters to use") do |reporters|
134
135
  cmd_options['reporters'] = reporters
135
136
  end
136
137
 
@@ -205,8 +206,9 @@ envs = {}
205
206
  read_env_files = {}
206
207
  cfg['env_patterns'].each do |pattern|
207
208
  Dir.glob(pattern).each do|f|
208
- spec_env = YAML.load_file(f)
209
- name = spec_env.delete('name') || 'default'
209
+ spec_env = YAML.load_file(f) || {}
210
+
211
+ name = spec_env['name'] || 'default'
210
212
 
211
213
  if envs.key? name
212
214
  existing_env_file = read_env_files[name]
@@ -236,6 +238,10 @@ cfg.merge! env if env
236
238
 
237
239
  String.colored! if cfg['colored']
238
240
 
241
+ # Load environment exlicitly before loading specs to make it available in spec definition
242
+ require_relative '../lib/spectre/environment' unless cfg['exclude'].include? 'spectre/environment'
243
+ Spectre.configure(cfg)
244
+
239
245
 
240
246
  ###########################################
241
247
  # Load Specs
@@ -258,7 +264,7 @@ if action == 'list'
258
264
  colors = [:blue, :magenta, :yellow, :green]
259
265
  specs = Spectre.specs(cfg['specs'], cfg['tags'])
260
266
 
261
- exit 1 if specs.length == 0
267
+ exit 1 unless specs.any?
262
268
 
263
269
  counter = 0
264
270
 
@@ -298,22 +304,38 @@ if action == 'run'
298
304
  FileUtils.makedirs log_dir if !Dir.exists? log_dir
299
305
 
300
306
  # Load Modules
307
+
301
308
  cfg['modules']
302
309
  .concat(cfg['include'])
303
310
  .select { |mod| !cfg['exclude'].include? mod }
304
311
  .each do |mod|
305
312
  begin
306
- if !File.exists? mod
307
- require_relative File.join('../lib', mod)
308
- else
313
+ spectre_lib_mod = File.join('../lib', mod)
314
+
315
+ if File.exists? mod
309
316
  require_relative mod
317
+
318
+ elsif File.exists? spectre_lib_mod
319
+ require_relative spectre_lib_mod
320
+
321
+ else
322
+ require mod
310
323
  end
311
- rescue => e
324
+
325
+ rescue LoadError => e
312
326
  puts "Unable to load module #{mod}. Check if the module exists or remove it from your spectre config:\n#{e.message}"
313
327
  exit 1
314
328
  end
315
329
  end
316
330
 
331
+ # Load mixins
332
+
333
+ cfg['mixin_patterns'].each do |pattern|
334
+ Dir.glob(pattern).each do|f|
335
+ require_relative File.join(Dir.pwd, f)
336
+ end
337
+ end
338
+
317
339
  Spectre.configure(cfg)
318
340
 
319
341
  Spectre::Logger.debug! if cfg['debug']
@@ -325,7 +347,7 @@ if action == 'run'
325
347
 
326
348
  specs = Spectre.specs(cfg['specs'], cfg['tags'])
327
349
 
328
- if specs.length == 0
350
+ if not specs.any?
329
351
  puts "No specs found in #{Dir.pwd}"
330
352
  exit 1
331
353
  end
@@ -337,7 +359,11 @@ if action == 'run'
337
359
  reporter.report(run_infos)
338
360
  end
339
361
 
340
- exit 0
362
+ errors = run_infos.select { |x| x.error != nil or x.failure != nil }
363
+
364
+ exit 0 if cfg['ignore_failure'] or not errors.any?
365
+
366
+ exit 1
341
367
  end
342
368
 
343
369
 
@@ -347,7 +373,7 @@ end
347
373
 
348
374
 
349
375
  if action == 'envs'
350
- exit 1 if envs.length == 0
376
+ exit 1 unless envs.any?
351
377
  puts envs.pretty
352
378
  exit 0
353
379
  end
@@ -464,6 +490,16 @@ reports/
464
490
  **/environments/*.env.secret.yml
465
491
  ]
466
492
 
493
+ DEFAULT_GEMFILE = %[source 'https://rubygems.org'
494
+
495
+ gem 'spectre-core', '>= #{Spectre::VERSION}'
496
+ # gem 'spectre-mysql', '>= 1.0.0'
497
+ # gem 'spectre-ssh', '>= 1.0.0'
498
+ # gem 'spectre-ftp', '>= 1.0.0'
499
+ # gem 'spectre-curl', '>= 1.0.0'
500
+ # gem 'spectre-git', '>= 0.1.0'
501
+ ]
502
+
467
503
  if action == 'init'
468
504
  DEFAULT_FILES = [
469
505
  ['./environments/default.env.yml', DEFAULT_ENV_CFG],
@@ -471,6 +507,7 @@ if action == 'init'
471
507
  ['./specs/sample.spec.rb', SAMPLE_SPEC],
472
508
  ['./spectre.yml', DEFAULT_SPECTRE_CFG],
473
509
  ['./.gitignore', DEFAULT_GITIGNORE],
510
+ ['./Gemfile', DEFAULT_GEMFILE],
474
511
  ]
475
512
 
476
513
  %w(environments logs specs).each do |dir_name|
@@ -6,19 +6,19 @@ module Spectre
6
6
  module Assertion
7
7
  class ::Object
8
8
  def should_be(val)
9
- raise AssertionFailure.new("The value '#{self.to_s.trim}' should be '#{val.to_s.trim}'", val, self) unless self.to_s == val.to_s
9
+ raise AssertionFailure.new("The value '#{self.to_s.trim}' should be '#{val.to_s.trim}'", val, self) unless self.to_s == val.to_s
10
10
  end
11
11
 
12
12
  def should_be_empty
13
- raise AssertionFailure.new("The value '#{self.to_s.trim}' should be empty", nil, self) unless self == nil
13
+ raise AssertionFailure.new("The value '#{self.to_s.trim}' should be empty", nil, self) unless self.nil?
14
14
  end
15
15
 
16
16
  def should_not_be(val)
17
- raise AssertionFailure.new("The value '#{self.to_s.trim}' should not be '#{val.to_s.trim}'", val, self) unless self.to_s != val.to_s
17
+ raise AssertionFailure.new("The value '#{self.to_s.trim}' should not be '#{val.to_s.trim}'", val, self) unless self.to_s != val.to_s
18
18
  end
19
19
 
20
20
  def should_not_exist
21
- raise AssertionFailure.new("The value '#{self.to_s.trim}' should not exist, but it does", val, self) unless self.to_s != nil
21
+ raise AssertionFailure.new("The value '#{self.to_s.trim}' should not exist, but it does", val, self) unless self.to_s != nil
22
22
  end
23
23
 
24
24
  def should_not_be_empty
@@ -76,7 +76,7 @@ module Spectre
76
76
  val = OpenStruct.new(val)
77
77
  end
78
78
 
79
- raise AssertionFailure.new("The list [#{list.join(', ').trim}] should contain '#{val.trim}'", val, list) unless list.include? val
79
+ raise AssertionFailure.new("The list [#{list.join(', ').trim}] should contain '#{val.to_s.trim}'", val, list) unless list.include? val
80
80
  end
81
81
 
82
82
  def should_not_contain(val)
@@ -87,15 +87,15 @@ module Spectre
87
87
  val = OpenStruct.new(val)
88
88
  end
89
89
 
90
- raise AssertionFailure.new("The list [#{list.join(', ').trim}] should not contain '#{val.trim}'", val, list) if list.include? val
90
+ raise AssertionFailure.new("The list [#{list.join(', ').trim}] should not contain '#{val.to_s.trim}'", val, list) if list.include? val
91
91
  end
92
92
 
93
93
  def should_be_empty
94
- raise AssertionFailure.new('empty list', self) unless self.length == 0
94
+ raise AssertionFailure.new('empty list', self) unless self.empty?
95
95
  end
96
96
 
97
97
  def should_not_be_empty
98
- raise AssertionFailure.new('no empty list', self) unless self.length > 0
98
+ raise AssertionFailure.new('no empty list', self) if self.empty?
99
99
  end
100
100
  end
101
101
 
@@ -110,7 +110,7 @@ module Spectre
110
110
  end
111
111
 
112
112
  def should_not_be(val)
113
- raise AssertionFailure.new("The text '#{self.trim}' should not be '#{val.to_s.trim}'", val, self) unless self != val
113
+ raise AssertionFailure.new("The text '#{self.trim}' should not be '#{val.to_s.trim}'", val, self) unless self != val
114
114
  end
115
115
 
116
116
  def should_not_be_empty
@@ -118,8 +118,10 @@ module Spectre
118
118
  end
119
119
 
120
120
  def should_contain(value)
121
+ raise AssertionFailure.new("`value' must not be nil") if value.nil?
122
+
121
123
  predicate = proc { |x| self.include? x.to_s }
122
- evaluation = SingleEvaluation.new value
124
+ evaluation = SingleEvaluation.new(value)
123
125
  success = evaluation.call(predicate)
124
126
 
125
127
  return if success
@@ -165,7 +167,7 @@ module Spectre
165
167
 
166
168
  class SingleEvaluation < Evaluation
167
169
  def initialize value
168
- super value, nil
170
+ super(value, nil)
169
171
  end
170
172
 
171
173
  def call predicate
@@ -180,7 +182,7 @@ module Spectre
180
182
 
181
183
  class OrEvaluation < Evaluation
182
184
  def initialize value, other
183
- super value, other
185
+ super(value, other)
184
186
  end
185
187
 
186
188
  def call predicate
@@ -195,7 +197,7 @@ module Spectre
195
197
 
196
198
  class AndEvaluation < Evaluation
197
199
  def initialize value, other
198
- super value, other
200
+ super(value, other)
199
201
  end
200
202
 
201
203
  def call predicate
@@ -222,14 +224,6 @@ module Spectre
222
224
  class << self
223
225
  @@success = nil
224
226
 
225
- def eval_assertion predicate, val
226
- if val.is_a? Proc
227
- val.call(predicate)
228
- else
229
- predicate.call(val)
230
- end
231
- end
232
-
233
227
  def expect desc
234
228
  begin
235
229
  Logger.log_process("expect #{desc}")
data/lib/spectre/curl.rb CHANGED
@@ -192,7 +192,8 @@ module Spectre::Curl
192
192
  return str unless str or str.empty?
193
193
 
194
194
  begin
195
- json = JSON.parse str
195
+ json = JSON.parse(str)
196
+ json.obfuscate!(@@secure_keys) if not @@debug
196
197
 
197
198
  if pretty
198
199
  str = JSON.pretty_generate(json)
@@ -206,6 +207,25 @@ module Spectre::Curl
206
207
  str
207
208
  end
208
209
 
210
+ def is_secure? key
211
+ @@secure_keys.any? { |x| key.to_s.downcase.include? x.downcase }
212
+ end
213
+
214
+ def header_to_s headers
215
+ s = ''
216
+
217
+ return s unless headers
218
+
219
+ headers.each do |header|
220
+ key = header[0].to_s
221
+ value = header[1].to_s
222
+ value = '*****' if is_secure?(key) and not @@debug
223
+ s += "#{key.ljust(30, '.')}: #{value}\n"
224
+ end
225
+
226
+ s
227
+ end
228
+
209
229
  def invoke req
210
230
  cmd = [@@curl_path]
211
231
 
@@ -230,11 +250,11 @@ module Spectre::Curl
230
250
  uri += '?'
231
251
  uri += req['query']
232
252
  .map { |x| x.join '='}
233
- .join '&'
253
+ .join('&')
234
254
  end
235
255
 
236
- cmd.append '"' + uri + '"'
237
- cmd.append '-X', req['method'] unless req['method'] == 'GET' or (req['body'] and req['method'] == 'POST')
256
+ cmd.append('"' + uri + '"')
257
+ cmd.append('-X', req['method']) unless req['method'] == 'GET' or (req['body'] and req['method'] == 'POST')
238
258
 
239
259
  # Call all registered modules
240
260
  @@modules.each do |mod|
@@ -243,43 +263,41 @@ module Spectre::Curl
243
263
 
244
264
  # Add headers to curl command
245
265
  req['headers'].each do |header|
246
- cmd.append '-H', '"' + header.join(':') + '"'
266
+ cmd.append('-H', '"' + header.join(':') + '"')
247
267
  end if req['headers']
248
268
 
249
269
  # Add request body
250
270
  if req['body'] != nil and not req['body'].empty?
251
271
  req_body = try_format_json(req['body']).gsub(/"/, '\\"')
252
- cmd.append '-d', '"' + req_body + '"'
272
+ cmd.append('-d', '"' + req_body + '"')
253
273
  elsif ['POST', 'PUT', 'PATCH'].include? req['method'].upcase
254
- cmd.append '-d', '"\n"'
274
+ cmd.append('-d', '"\n"')
255
275
  end
256
276
 
257
277
  # Add certificate path if one if given
258
278
  if req['cert']
259
279
  raise "Certificate '#{req['cert']}' does not exist" unless File.exists? req['cert']
260
- cmd.append '--cacert', req['cert']
280
+ cmd.append('--cacert', req['cert'])
261
281
  elsif req['use_ssl'] or uri.start_with? 'https'
262
- cmd.append '-k'
282
+ cmd.append('-k')
263
283
  end
264
284
 
265
- cmd.append '-i'
266
- cmd.append '-v'
285
+ cmd.append('-i')
286
+ cmd.append('-v')
267
287
 
268
- @@request = OpenStruct.new req
288
+ @@request = OpenStruct.new(req)
269
289
 
270
- sys_cmd = cmd.join ' '
290
+ sys_cmd = cmd.join(' ')
271
291
 
272
- @@logger.debug sys_cmd
292
+ @@logger.debug(sys_cmd)
273
293
 
274
294
  req_id = SecureRandom.uuid()[0..5]
275
295
 
276
296
  req_log = "[>] #{req_id} #{req['method']} #{uri}\n"
277
- req['headers'].each do |header|
278
- req_log += "#{header[0].to_s.ljust(30, '.')}: #{header[1].to_s}\n"
279
- end if req['headers']
280
- req_log += req['body'] if req['body'] != nil and not req['body'].empty?
297
+ req_log += header_to_s(req['headers'])
298
+ req_log += try_format_json(req['body'], pretty: true)
281
299
 
282
- @@logger.info req_log
300
+ @@logger.info(req_log)
283
301
 
284
302
  start_time = Time.now
285
303
 
@@ -297,7 +315,7 @@ module Spectre::Curl
297
315
 
298
316
  raise "Unable to request #{uri}. Please check if this service is reachable." unless output
299
317
 
300
- @@logger.debug "[<] #{req_id} stdout:\n#{output}"
318
+ @@logger.debug("[<] #{req_id} stdout:\n#{output}")
301
319
 
302
320
  header, body = output.split /\r?\n\r?\n/
303
321
 
@@ -342,7 +360,7 @@ module Spectre::Curl
342
360
 
343
361
  @@logger.info res_log
344
362
 
345
- @@response = SpectreHttpResponse.new res
363
+ @@response = SpectreHttpResponse.new(res)
346
364
 
347
365
  raise "Response did not indicate success: #{@@response.code} #{@@response.message}" if req['ensure_success'] and not @@response.success?
348
366
 
@@ -351,7 +369,12 @@ module Spectre::Curl
351
369
  end
352
370
 
353
371
  Spectre.register do |config|
354
- @@logger = ::Logger.new config['log_file'], progname: 'spectre/curl'
372
+ @@debug = config['debug']
373
+
374
+ @@logger = ::Logger.new(config['log_file'], progname: 'spectre/curl')
375
+ @@logger.level = @@debug ? Logger::DEBUG : Logger::INFO
376
+
377
+ @@secure_keys = config['secure_keys'] || []
355
378
 
356
379
  @@curl_path = config['curl_path'] || 'curl'
357
380
 
@@ -2,6 +2,7 @@ require 'securerandom'
2
2
  require 'json'
3
3
  require 'date'
4
4
  require 'ostruct'
5
+ require 'jsonpath'
5
6
 
6
7
  class ::String
7
8
  def as_json
@@ -12,17 +13,57 @@ class ::String
12
13
  DateTime.parse(self)
13
14
  end
14
15
 
16
+ def as_timestamp
17
+ DateTime.parse(self).to_time.to_i
18
+ end
19
+
20
+ def with mapping
21
+ return self unless mapping and mapping.is_a? Hash
22
+
23
+ new_string = self
24
+
25
+ mapping.each do |key, value|
26
+ new_string = new_string.gsub('#{' + key.to_s + '}', value.to_s)
27
+ end
28
+
29
+ new_string
30
+ end
31
+
32
+ def trim size = 50
33
+ if (self.length + 3) > size
34
+ return self[0..size-4] + '...'
35
+ end
36
+
37
+ self
38
+ end
39
+
40
+ def pick path
41
+ raise ArgumentError.new("`path' must not be nil or empty") if path.nil? or path.empty?
42
+
43
+ begin
44
+ JsonPath.on(self, path)
45
+
46
+ rescue MultiJson::ParseError
47
+ # do nothing and return nil
48
+ end
49
+ end
50
+
51
+ # File helpers
52
+
15
53
  def content with: nil
16
54
  fail "'#{self}' is not a file path, or the file does not exist." if !File.exists? self
17
55
  file_content = File.read(self)
18
56
 
19
57
  if with
20
- with.each do |key, value|
21
- file_content = file_content.gsub '#{' + key.to_s + '}', value.to_s
22
- end
58
+ file_content.with(with)
59
+ else
60
+ file_content
23
61
  end
62
+ end
24
63
 
25
- file_content
64
+ def file_size
65
+ fail "'#{self}' is not a file path, or the file does not exist." if !File.exists? self
66
+ File.size(self)
26
67
  end
27
68
 
28
69
  def exists?
@@ -31,17 +72,8 @@ class ::String
31
72
 
32
73
  def remove!
33
74
  fail "'#{self}' is not a file path, or the file does not exist." if !File.exists? self
34
-
35
75
  File.delete self
36
76
  end
37
-
38
- def trim count=50
39
- if (self.length + 3) > count
40
- return self[0..count] + '...'
41
- end
42
-
43
- self
44
- end
45
77
  end
46
78
 
47
79
 
@@ -49,6 +81,22 @@ class ::OpenStruct
49
81
  def to_json *args, **kwargs
50
82
  self.to_h.inject({}) { |memo, (k,v)| memo[k] = v.is_a?(OpenStruct) ? v.to_h : v; memo }.to_json(*args, **kwargs)
51
83
  end
84
+
85
+ def pick path
86
+ raise ArgumentError.new("`path' must not be nil or empty") if path.nil? or path.empty?
87
+
88
+ JsonPath.on(self, path)
89
+ end
90
+
91
+ def default_to! defaults
92
+ defaults.each_key do |key|
93
+ if not self[key] != nil
94
+ self[key] = defaults[key]
95
+ end
96
+ end
97
+ end
98
+
99
+ alias :defaults_to! :default_to!
52
100
  end
53
101
 
54
102
 
@@ -56,9 +104,31 @@ class ::Hash
56
104
  def symbolize_keys
57
105
  self.inject({}) { |memo, (k,v)| memo[k.to_sym] = v; memo }
58
106
  end
107
+
108
+ def default_to! defaults
109
+ defaults.each_key do |key|
110
+ if not self[key] != nil
111
+ self[key] = defaults[key]
112
+ end
113
+ end
114
+ end
115
+
116
+ alias :defaults_to! :default_to!
117
+ end
118
+
119
+
120
+ class ::Array
121
+ def last
122
+ self[-1]
123
+ end
59
124
  end
60
125
 
61
126
 
62
127
  def uuid length = 5
63
- SecureRandom.uuid().gsub('-', '')[0..length]
128
+ SecureRandom.uuid().gsub('-', '')[0..length-1]
129
+ end
130
+
131
+
132
+ def now
133
+ Time.now
64
134
  end