spectre-core 1.15.2 → 2.0.1
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 +4 -4
- data/exe/spectre +271 -463
- data/lib/spectre/assertion.rb +118 -250
- data/lib/spectre/expectation.rb +89 -0
- data/lib/spectre/helpers.rb +56 -59
- data/lib/spectre/version.rb +3 -0
- data/lib/spectre.rb +1510 -334
- metadata +57 -32
- data/lib/spectre/async.rb +0 -31
- data/lib/spectre/bag.rb +0 -17
- data/lib/spectre/curl.rb +0 -398
- data/lib/spectre/diagnostic.rb +0 -39
- data/lib/spectre/environment.rb +0 -30
- data/lib/spectre/http/basic_auth.rb +0 -25
- data/lib/spectre/http/keystone.rb +0 -99
- data/lib/spectre/http.rb +0 -394
- data/lib/spectre/logging/console.rb +0 -156
- data/lib/spectre/logging/file.rb +0 -106
- data/lib/spectre/logging.rb +0 -183
- data/lib/spectre/mixin.rb +0 -61
- data/lib/spectre/reporter/console.rb +0 -104
- data/lib/spectre/reporter.rb +0 -17
- data/lib/spectre/resources.rb +0 -53
    
        data/exe/spectre
    CHANGED
    
    | @@ -1,536 +1,344 @@ | |
| 1 | 
            -
             | 
| 2 | 
            -
            # frozen_string_literal: true
         | 
| 3 | 
            -
             | 
| 4 | 
            -
            require 'yaml'
         | 
| 5 | 
            -
            require 'ostruct'
         | 
| 6 | 
            -
            require 'optparse'
         | 
| 7 | 
            -
            require 'fileutils'
         | 
| 8 | 
            -
            require 'ectoplasm'
         | 
| 1 | 
            +
            #!/usr/bin/ruby
         | 
| 9 2 |  | 
| 10 3 | 
             
            require_relative '../lib/spectre'
         | 
| 11 | 
            -
            require_relative '../lib/spectre/ | 
| 4 | 
            +
            require_relative '../lib/spectre/expectation'
         | 
| 12 5 |  | 
| 13 | 
            -
             | 
| 14 | 
            -
             | 
| 15 | 
            -
             | 
| 16 | 
            -
            end
         | 
| 6 | 
            +
            FAILED_FILENAME = '.failed'
         | 
| 7 | 
            +
             | 
| 8 | 
            +
            config_overrides = {}
         | 
| 17 9 |  | 
| 10 | 
            +
            cleanup = false
         | 
| 11 | 
            +
            run_failed = false
         | 
| 18 12 |  | 
| 19 | 
            -
             | 
| 20 | 
            -
              'project' => nil,
         | 
| 21 | 
            -
              'config_file' => './spectre.yml',
         | 
| 22 | 
            -
              'environment' => 'default',
         | 
| 23 | 
            -
              'specs' => [],
         | 
| 24 | 
            -
              'tags' => [],
         | 
| 25 | 
            -
              'verbose' => false,
         | 
| 26 | 
            -
              'log_file' => './logs/spectre_<date>.log',
         | 
| 27 | 
            -
              'log_format' => {
         | 
| 28 | 
            -
                'console' => {
         | 
| 29 | 
            -
                  'indent' => 2,
         | 
| 30 | 
            -
                  'width' => 80,
         | 
| 31 | 
            -
                  'end_context' => nil,
         | 
| 32 | 
            -
                  'separator' => '<indent><desc>',
         | 
| 33 | 
            -
                },
         | 
| 34 | 
            -
                'file' => {
         | 
| 35 | 
            -
                  'separator' => '-- <desc>',
         | 
| 36 | 
            -
                  'start_group' => "-- Start '<desc>'",
         | 
| 37 | 
            -
                  'end_group' => "-- End '<desc>'",
         | 
| 38 | 
            -
                },
         | 
| 39 | 
            -
              },
         | 
| 40 | 
            -
              'debug' => false,
         | 
| 41 | 
            -
              'out_path' => './reports',
         | 
| 42 | 
            -
              'secure_keys' => ['password', 'secret', 'token', 'secure', 'authorization'],
         | 
| 43 | 
            -
              'spec_patterns' => ['./specs/**/*.spec.rb'],
         | 
| 44 | 
            -
              'mixin_patterns' => ['../common/mixins/**/*.mixin.rb', './mixins/**/*.mixin.rb'],
         | 
| 45 | 
            -
              'env_patterns' => ['./environments/**/*.env.yml'],
         | 
| 46 | 
            -
              'env_partial_patterns' => ['./environments/**/*.env.secret.yml'],
         | 
| 47 | 
            -
              'resource_paths' => ['../common/resources', './resources'],
         | 
| 48 | 
            -
              'default_modules' => [
         | 
| 49 | 
            -
                'spectre/helpers',
         | 
| 50 | 
            -
                'spectre/reporter/console',
         | 
| 51 | 
            -
                'spectre/logging/console',
         | 
| 52 | 
            -
                'spectre/logging/file',
         | 
| 53 | 
            -
                'spectre/assertion',
         | 
| 54 | 
            -
                'spectre/diagnostic',
         | 
| 55 | 
            -
                'spectre/environment',
         | 
| 56 | 
            -
                'spectre/mixin',
         | 
| 57 | 
            -
                'spectre/bag',
         | 
| 58 | 
            -
                'spectre/http',
         | 
| 59 | 
            -
                'spectre/http/basic_auth',
         | 
| 60 | 
            -
                'spectre/http/keystone',
         | 
| 61 | 
            -
                'spectre/resources',
         | 
| 62 | 
            -
                'spectre/async',
         | 
| 63 | 
            -
              ],
         | 
| 64 | 
            -
              'include' => [
         | 
| 65 | 
            -
             | 
| 66 | 
            -
              ],
         | 
| 67 | 
            -
              'exclude' => [
         | 
| 68 | 
            -
             | 
| 69 | 
            -
              ],
         | 
| 70 | 
            -
            }
         | 
| 71 | 
            -
             | 
| 72 | 
            -
             | 
| 73 | 
            -
            cmd_options = {}
         | 
| 74 | 
            -
            property_overrides = {}
         | 
| 75 | 
            -
             | 
| 76 | 
            -
            $COMMAND = ['spectre'].concat(ARGV.clone).join(' ')
         | 
| 77 | 
            -
             | 
| 78 | 
            -
            OptionParser.new do |opts|
         | 
| 13 | 
            +
            options = OptionParser.new do |opts|
         | 
| 79 14 | 
             
              opts.banner = <<~BANNER
         | 
| 80 15 | 
             
                Spectre #{Spectre::VERSION}
         | 
| 81 16 |  | 
| 82 17 | 
             
                Usage: spectre [command] [options]
         | 
| 83 18 |  | 
| 84 19 | 
             
                  Commands:
         | 
| 85 | 
            -
                     | 
| 86 | 
            -
                     | 
| 87 | 
            -
                     | 
| 88 | 
            -
                     | 
| 89 | 
            -
                     | 
| 90 | 
            -
                     | 
| 20 | 
            +
                    run, ru           Run specs (default)
         | 
| 21 | 
            +
                    list, li          List specs
         | 
| 22 | 
            +
                    describe, de      Print all subject descriptions
         | 
| 23 | 
            +
                    specs, sp         Print detailed information about specs
         | 
| 24 | 
            +
                    mixins, mi        List all mixin names
         | 
| 25 | 
            +
                    collections, co   List the loaded collections
         | 
| 26 | 
            +
                    env, en           Print current environment settings
         | 
| 27 | 
            +
                    cleanup, cl       Will remove all generated files (e.g. logs and reports)
         | 
| 28 | 
            +
                    init, in          Initialize a new spectre project
         | 
| 91 29 |  | 
| 92 30 | 
             
                  Specific options:
         | 
| 93 31 | 
             
              BANNER
         | 
| 94 32 |  | 
| 95 33 | 
             
              opts.on('-s SPEC,SPEC', '--specs SPEC,SPEC', Array, 'The specs to run') do |specs|
         | 
| 96 | 
            -
                 | 
| 34 | 
            +
                config_overrides['specs'] = specs
         | 
| 97 35 | 
             
              end
         | 
| 98 36 |  | 
| 99 37 | 
             
              opts.on('-t TAG,TAG', '--tags TAG,TAG', Array, 'Run only specs with given tags') do |tags|
         | 
| 100 | 
            -
                 | 
| 38 | 
            +
                config_overrides['tags'] = tags
         | 
| 101 39 | 
             
              end
         | 
| 102 40 |  | 
| 103 41 | 
             
              opts.on('-e NAME', '--env NAME', 'Name of the environment to load') do |env_name|
         | 
| 104 | 
            -
                 | 
| 42 | 
            +
                config_overrides['selected_env'] = env_name
         | 
| 105 43 | 
             
              end
         | 
| 106 44 |  | 
| 107 45 | 
             
              opts.on('-c FILE', '--config FILE', 'Config file to load') do |file_path|
         | 
| 108 | 
            -
                 | 
| 46 | 
            +
                config_overrides['config_file'] = file_path
         | 
| 109 47 | 
             
              end
         | 
| 110 48 |  | 
| 111 | 
            -
              opts.on(' | 
| 112 | 
            -
                 | 
| 49 | 
            +
              opts.on('-C', '--collection NAME', 'Run or list specs of a specific collection') do |name|
         | 
| 50 | 
            +
                config_overrides['collection'] = name
         | 
| 113 51 | 
             
              end
         | 
| 114 52 |  | 
| 115 | 
            -
              opts.on('-- | 
| 116 | 
            -
                 | 
| 53 | 
            +
              opts.on('--work-dir PATH', 'Path of the working directory') do |path|
         | 
| 54 | 
            +
                config_overrides['work_dir'] = path
         | 
| 117 55 | 
             
              end
         | 
| 118 56 |  | 
| 119 | 
            -
              opts.on('-- | 
| 120 | 
            -
                 | 
| 57 | 
            +
              opts.on('--spec-patterns PATTERN', Array, 'File pattern for spec files') do |spec_pattern|
         | 
| 58 | 
            +
                config_overrides['spec_patterns'] = spec_pattern
         | 
| 121 59 | 
             
              end
         | 
| 122 60 |  | 
| 123 | 
            -
              opts.on('- | 
| 124 | 
            -
                 | 
| 61 | 
            +
              opts.on('--env-patterns PATTERN', Array, 'File pattern for environment files') do |env_patterns|
         | 
| 62 | 
            +
                config_overrides['env_patterns'] = env_patterns
         | 
| 125 63 | 
             
              end
         | 
| 126 64 |  | 
| 127 | 
            -
              opts.on('- | 
| 128 | 
            -
                 | 
| 129 | 
            -
                  require mod
         | 
| 130 | 
            -
                end
         | 
| 65 | 
            +
              opts.on('--ignore-failure', 'Always exit with code 0') do
         | 
| 66 | 
            +
                config_overrides['ignore_failure'] = true
         | 
| 131 67 | 
             
              end
         | 
| 132 68 |  | 
| 133 | 
            -
              opts.on(' | 
| 134 | 
            -
                 | 
| 69 | 
            +
              opts.on('--formatter NAME', 'Use specified formatter') do |class_name|
         | 
| 70 | 
            +
                config_overrides['formatter'] = class_name
         | 
| 135 71 | 
             
              end
         | 
| 136 72 |  | 
| 137 | 
            -
              opts.on(' | 
| 138 | 
            -
                 | 
| 139 | 
            -
                val = val.split(',') if DEFAULT_CONFIG[key].is_a? Array
         | 
| 140 | 
            -
                val = ['true', '1'].include? val if [true, false].include?(DEFAULT_CONFIG[key])
         | 
| 141 | 
            -
                val = val.to_i if DEFAULT_CONFIG[key].is_a? Integer
         | 
| 142 | 
            -
             | 
| 143 | 
            -
                opt_path = key.split('.')
         | 
| 144 | 
            -
             | 
| 145 | 
            -
                curr_opt = property_overrides
         | 
| 146 | 
            -
             | 
| 147 | 
            -
                opt_path.each_with_index do |part, i|
         | 
| 148 | 
            -
                  if i == opt_path.count-1
         | 
| 149 | 
            -
                    curr_opt[part] = val
         | 
| 150 | 
            -
                    break
         | 
| 151 | 
            -
                  end
         | 
| 152 | 
            -
             | 
| 153 | 
            -
                  curr_opt[part] = {} unless curr_opt.key?(part)
         | 
| 154 | 
            -
                  curr_opt = curr_opt[part]
         | 
| 155 | 
            -
                end
         | 
| 156 | 
            -
              end
         | 
| 157 | 
            -
             | 
| 158 | 
            -
              opts.separator "\n  Common options:"
         | 
| 159 | 
            -
             | 
| 160 | 
            -
              opts.on_tail('-v', '--version', 'Print current installed version') do
         | 
| 161 | 
            -
                puts Spectre::VERSION
         | 
| 162 | 
            -
                exit
         | 
| 73 | 
            +
              opts.on('--reporter NAME', 'Use specified reporter') do |class_name|
         | 
| 74 | 
            +
                config_overrides['reporter'] = class_name
         | 
| 163 75 | 
             
              end
         | 
| 164 76 |  | 
| 165 | 
            -
              opts. | 
| 166 | 
            -
                 | 
| 167 | 
            -
                 | 
| 77 | 
            +
              opts.on('--json', 'Use JSON formatter') do
         | 
| 78 | 
            +
                Spectre::CONFIG['formatter'] = 'Spectre::JsonFormatter'
         | 
| 79 | 
            +
                Spectre::CONFIG['reporters'] = ['Spectre::JsonReporter']
         | 
| 168 80 | 
             
              end
         | 
| 169 | 
            -
            end.parse!
         | 
| 170 | 
            -
             | 
| 171 | 
            -
             | 
| 172 | 
            -
            action = ARGV[0] || 'run'
         | 
| 173 | 
            -
             | 
| 174 | 
            -
             | 
| 175 | 
            -
            ###########################################
         | 
| 176 | 
            -
            # Load Config
         | 
| 177 | 
            -
            ###########################################
         | 
| 178 81 |  | 
| 82 | 
            +
              opts.on('--failed', 'Run previously failed specs') do
         | 
| 83 | 
            +
                run_failed = true
         | 
| 179 84 |  | 
| 180 | 
            -
             | 
| 181 | 
            -
             | 
| 85 | 
            +
                next if config_overrides.key? 'specs' or
         | 
| 86 | 
            +
                        config_overrides.key? 'tags' or
         | 
| 87 | 
            +
                        config_overrides.key? 'collection'
         | 
| 182 88 |  | 
| 183 | 
            -
             | 
| 184 | 
            -
             | 
| 89 | 
            +
                if File.exist?(FAILED_FILENAME)
         | 
| 90 | 
            +
                  config_overrides['specs'] = File
         | 
| 91 | 
            +
                    .readlines(FAILED_FILENAME)
         | 
| 92 | 
            +
                    .map(&:strip)
         | 
| 185 93 |  | 
| 186 | 
            -
             | 
| 187 | 
            -
              global_options = load_yaml(global_config_file)
         | 
| 188 | 
            -
              cfg.deep_merge! global_options if global_options
         | 
| 189 | 
            -
            end
         | 
| 190 | 
            -
             | 
| 191 | 
            -
            # Then load local config file
         | 
| 192 | 
            -
            config_file = cmd_options['config_file'] || cfg['config_file']
         | 
| 193 | 
            -
             | 
| 194 | 
            -
            if File.exist? config_file
         | 
| 195 | 
            -
              file_options = load_yaml(config_file)
         | 
| 196 | 
            -
              cfg.deep_merge! file_options
         | 
| 197 | 
            -
              Dir.chdir File.dirname(config_file)
         | 
| 198 | 
            -
            end
         | 
| 199 | 
            -
             | 
| 200 | 
            -
            # Set config options, which are only allowed to be overriden by command options
         | 
| 201 | 
            -
            cfg['debug'] = DEFAULT_CONFIG['debug']
         | 
| 202 | 
            -
             | 
| 203 | 
            -
            # Set project name
         | 
| 204 | 
            -
            cfg['project'] = File.basename(Dir.pwd) unless cfg['project']
         | 
| 205 | 
            -
             | 
| 206 | 
            -
            # And merge the command line arguments last
         | 
| 207 | 
            -
            cfg.deep_merge! cmd_options
         | 
| 208 | 
            -
             | 
| 209 | 
            -
             | 
| 210 | 
            -
            ###########################################
         | 
| 211 | 
            -
            # Load Environment
         | 
| 212 | 
            -
            ###########################################
         | 
| 213 | 
            -
             | 
| 214 | 
            -
            envs = {}
         | 
| 215 | 
            -
            read_env_files = {}
         | 
| 216 | 
            -
            cfg['env_patterns'].each do |pattern|
         | 
| 217 | 
            -
              Dir.glob(pattern).each do|f|
         | 
| 218 | 
            -
                spec_env = load_yaml(f) || {}
         | 
| 219 | 
            -
             | 
| 220 | 
            -
                name = spec_env['name'] || 'default'
         | 
| 221 | 
            -
             | 
| 222 | 
            -
                if envs.key? name
         | 
| 223 | 
            -
                  existing_env_file = read_env_files[name]
         | 
| 224 | 
            -
                  puts "Duplicate environment definition detected with name #{name} in '#{f}'. Previously defined in '#{existing_env_file}'"
         | 
| 225 | 
            -
                  exit 1
         | 
| 94 | 
            +
                  File.delete(FAILED_FILENAME)
         | 
| 226 95 | 
             
                end
         | 
| 227 | 
            -
             | 
| 228 | 
            -
                read_env_files[name] = f
         | 
| 229 | 
            -
                envs[name] = spec_env
         | 
| 230 96 | 
             
              end
         | 
| 231 | 
            -
            end
         | 
| 232 | 
            -
             | 
| 233 | 
            -
            # Merge partial environment configs with existing environments
         | 
| 234 | 
            -
            cfg['env_partial_patterns'].each do |pattern|
         | 
| 235 | 
            -
              Dir.glob(pattern).each do|f|
         | 
| 236 | 
            -
                partial_env = load_yaml(f)
         | 
| 237 | 
            -
                name = partial_env.delete('name') || 'default'
         | 
| 238 | 
            -
                next unless envs.key? name
         | 
| 239 97 |  | 
| 240 | 
            -
             | 
| 98 | 
            +
              opts.on('--no-log', 'Disable file logging') do
         | 
| 99 | 
            +
                config_overrides['log_file'] = StringIO.new
         | 
| 241 100 | 
             
              end
         | 
| 242 | 
            -
            end
         | 
| 243 | 
            -
             | 
| 244 | 
            -
            env = envs[cfg['environment']]
         | 
| 245 | 
            -
            cfg.deep_merge! env if env
         | 
| 246 101 |  | 
| 247 | 
            -
             | 
| 248 | 
            -
             | 
| 249 | 
            -
             | 
| 250 | 
            -
             | 
| 251 | 
            -
            # Load environment exlicitly before loading specs to make it available in spec definition
         | 
| 252 | 
            -
            require_relative '../lib/spectre/environment' unless cfg['exclude'].include? 'spectre/environment'
         | 
| 253 | 
            -
            Spectre.configure(cfg)
         | 
| 254 | 
            -
             | 
| 255 | 
            -
             | 
| 256 | 
            -
            # Load specs only, when listing or running specs
         | 
| 257 | 
            -
            if ['list', 'run'].include? action
         | 
| 258 | 
            -
             | 
| 259 | 
            -
              ###########################################
         | 
| 260 | 
            -
              # Load Specs
         | 
| 261 | 
            -
              ###########################################
         | 
| 262 | 
            -
             | 
| 263 | 
            -
             | 
| 264 | 
            -
              cfg['spec_patterns'].each do |pattern|
         | 
| 265 | 
            -
                Dir.glob(pattern).each do|f|
         | 
| 266 | 
            -
                  require_relative File.join(Dir.pwd, f)
         | 
| 267 | 
            -
                end
         | 
| 102 | 
            +
              opts.on('--cleanup', 'Remove generated file before running specs') do
         | 
| 103 | 
            +
                cleanup = true
         | 
| 268 104 | 
             
              end
         | 
| 269 105 |  | 
| 270 | 
            -
             | 
| 271 | 
            -
             | 
| 272 | 
            -
              # List specs
         | 
| 273 | 
            -
              ###########################################
         | 
| 274 | 
            -
             | 
| 275 | 
            -
             | 
| 276 | 
            -
              if 'list' == action
         | 
| 277 | 
            -
                colors = [:blue, :magenta, :yellow, :green]
         | 
| 278 | 
            -
                specs = Spectre.specs(cfg['specs'], cfg['tags'])
         | 
| 279 | 
            -
             | 
| 280 | 
            -
                exit 1 unless specs.any?
         | 
| 281 | 
            -
             | 
| 282 | 
            -
                counter = 0
         | 
| 283 | 
            -
             | 
| 284 | 
            -
                specs.group_by { |x| x.subject }.each do |subject, spec_group|
         | 
| 285 | 
            -
                  spec_group.each do |spec|
         | 
| 286 | 
            -
                    tags = spec.tags.map { |x| '#' + x.to_s }.join ' '
         | 
| 287 | 
            -
                    desc = subject.desc
         | 
| 288 | 
            -
                    desc += ' - ' + spec.context.__desc + ' -' if spec.context.__desc
         | 
| 289 | 
            -
                    desc += ' ' + spec.desc
         | 
| 290 | 
            -
                    puts "[#{spec.name}]".send(colors[counter % colors.length]) + " #{desc} #{tags.cyan}"
         | 
| 291 | 
            -
                  end
         | 
| 292 | 
            -
             | 
| 293 | 
            -
                  counter += 1
         | 
| 294 | 
            -
                end
         | 
| 295 | 
            -
             | 
| 296 | 
            -
                exit 0
         | 
| 106 | 
            +
              opts.on('-o PATH', '--out PATH', 'Output directory path') do |path|
         | 
| 107 | 
            +
                config_overrides['out_path'] = File.absolute_path(path)
         | 
| 297 108 | 
             
              end
         | 
| 298 109 |  | 
| 110 | 
            +
              opts.on('-m MODULE,MODULE', '--modules MODULE,MODULE', Array, 'Load the given modules') do |modules|
         | 
| 111 | 
            +
                config_overrides['modules'] += modules
         | 
| 112 | 
            +
              end
         | 
| 299 113 |  | 
| 300 | 
            -
               | 
| 301 | 
            -
             | 
| 302 | 
            -
             | 
| 303 | 
            -
             | 
| 304 | 
            -
             | 
| 305 | 
            -
              if 'run' == action
         | 
| 306 | 
            -
                # Initialize logger
         | 
| 307 | 
            -
                now = Time.now
         | 
| 308 | 
            -
             | 
| 309 | 
            -
                cfg['log_file'] = cfg['log_file'].frmt({
         | 
| 310 | 
            -
                  shortdate: now.strftime('%Y-%m-%d'),
         | 
| 311 | 
            -
                  date: now.strftime('%Y-%m-%d_%H%M%S'),
         | 
| 312 | 
            -
                  timestamp: now.strftime('%s'),
         | 
| 313 | 
            -
                  subject: 'spectre',
         | 
| 314 | 
            -
                })
         | 
| 315 | 
            -
             | 
| 316 | 
            -
                log_dir = File.dirname(cfg['log_file'])
         | 
| 317 | 
            -
                FileUtils.makedirs(log_dir)
         | 
| 114 | 
            +
              opts.on('-d', '--debug', 'Run in debug mode. Do not use in production!') do
         | 
| 115 | 
            +
                require 'debug'
         | 
| 116 | 
            +
                config_overrides['debug'] = true
         | 
| 117 | 
            +
              end
         | 
| 318 118 |  | 
| 319 | 
            -
             | 
| 119 | 
            +
              opts.on('-p KEY=VAL',
         | 
| 120 | 
            +
                      '--property KEY=VAL',
         | 
| 121 | 
            +
                      'Override config option. Use `spectre show` to get list of available options') do |option|
         | 
| 122 | 
            +
                index = option.index('=')
         | 
| 123 | 
            +
                key = option[0...index]
         | 
| 124 | 
            +
                val = option[index + 1..]
         | 
| 320 125 |  | 
| 321 | 
            -
                 | 
| 322 | 
            -
             | 
| 323 | 
            -
             | 
| 324 | 
            -
                  .select { |mod| !cfg['exclude'].include? mod }
         | 
| 325 | 
            -
                  .each do |mod|
         | 
| 326 | 
            -
                    begin
         | 
| 327 | 
            -
                      mod_file = mod + '.rb'
         | 
| 328 | 
            -
                      spectre_lib_mod = File.join(File.dirname(__dir__), 'lib', mod_file)
         | 
| 126 | 
            +
                val = val.split(',') if Spectre::CONFIG[key].is_a? Array
         | 
| 127 | 
            +
                val = ['true', '1'].include? val if [true, false].include?(Spectre::CONFIG[key])
         | 
| 128 | 
            +
                val = val.to_i if Spectre::CONFIG[key].is_a? Integer
         | 
| 329 129 |  | 
| 330 | 
            -
             | 
| 331 | 
            -
                        require_relative mod_file
         | 
| 130 | 
            +
                opt_path = key.split('.')
         | 
| 332 131 |  | 
| 333 | 
            -
             | 
| 334 | 
            -
                        require_relative spectre_lib_mod
         | 
| 132 | 
            +
                curr_opt = config_overrides
         | 
| 335 133 |  | 
| 336 | 
            -
             | 
| 337 | 
            -
             | 
| 338 | 
            -
             | 
| 339 | 
            -
                     | 
| 340 | 
            -
                      puts "Unable to load module #{mod}. Check if the module exists or remove it from your spectre config:\n#{e.message}"
         | 
| 341 | 
            -
                      exit 1
         | 
| 342 | 
            -
                    end
         | 
| 134 | 
            +
                opt_path.each_with_index do |part, i|
         | 
| 135 | 
            +
                  if i == opt_path.count - 1
         | 
| 136 | 
            +
                    curr_opt[part] = val
         | 
| 137 | 
            +
                    break
         | 
| 343 138 | 
             
                  end
         | 
| 344 139 |  | 
| 345 | 
            -
             | 
| 346 | 
            -
             | 
| 347 | 
            -
                cfg['mixin_patterns'].each do |pattern|
         | 
| 348 | 
            -
                  Dir.glob(pattern).each do|f|
         | 
| 349 | 
            -
                    require_relative File.join(Dir.pwd, f)
         | 
| 350 | 
            -
                  end
         | 
| 351 | 
            -
                end
         | 
| 352 | 
            -
             | 
| 353 | 
            -
                Spectre.configure(cfg)
         | 
| 354 | 
            -
             | 
| 355 | 
            -
                Spectre::Logging.debug! if cfg['debug']
         | 
| 356 | 
            -
             | 
| 357 | 
            -
                specs = Spectre.specs(cfg['specs'], cfg['tags'])
         | 
| 358 | 
            -
             | 
| 359 | 
            -
                unless specs.any?
         | 
| 360 | 
            -
                  puts "No specs found in #{Dir.pwd}"
         | 
| 361 | 
            -
                  exit 1
         | 
| 140 | 
            +
                  curr_opt[part] = {} unless curr_opt.key?(part)
         | 
| 141 | 
            +
                  curr_opt = curr_opt[part]
         | 
| 362 142 | 
             
                end
         | 
| 363 | 
            -
             | 
| 364 | 
            -
                run_infos = Spectre::Runner.new.run(specs)
         | 
| 365 | 
            -
             | 
| 366 | 
            -
                Spectre::Reporter.report(run_infos)
         | 
| 367 | 
            -
             | 
| 368 | 
            -
                errors = run_infos.select { |x| x.error? or x.failed? }
         | 
| 369 | 
            -
             | 
| 370 | 
            -
                exit 0 if cfg['ignore_failure'] or not errors.any?
         | 
| 371 | 
            -
             | 
| 372 | 
            -
                exit 1
         | 
| 373 143 | 
             
              end
         | 
| 374 | 
            -
            end
         | 
| 375 144 |  | 
| 145 | 
            +
              opts.separator "\n  Common options:"
         | 
| 376 146 |  | 
| 377 | 
            -
             | 
| 378 | 
            -
             | 
| 379 | 
            -
             | 
| 380 | 
            -
             | 
| 381 | 
            -
             | 
| 382 | 
            -
            if 'envs' == action
         | 
| 383 | 
            -
              exit 1 unless envs.any?
         | 
| 384 | 
            -
              puts envs.pretty
         | 
| 385 | 
            -
              exit 0
         | 
| 386 | 
            -
            end
         | 
| 387 | 
            -
             | 
| 388 | 
            -
             | 
| 389 | 
            -
            ###########################################
         | 
| 390 | 
            -
            # Show
         | 
| 391 | 
            -
            ###########################################
         | 
| 392 | 
            -
             | 
| 393 | 
            -
             | 
| 394 | 
            -
            if 'show' == action
         | 
| 395 | 
            -
              puts cfg.pretty
         | 
| 396 | 
            -
              exit 0
         | 
| 397 | 
            -
            end
         | 
| 398 | 
            -
             | 
| 399 | 
            -
             | 
| 400 | 
            -
            ###########################################
         | 
| 401 | 
            -
            # Dump
         | 
| 402 | 
            -
            ###########################################
         | 
| 403 | 
            -
             | 
| 404 | 
            -
             | 
| 405 | 
            -
            if 'dump' == action
         | 
| 406 | 
            -
              puts YAML.dump(cfg)
         | 
| 407 | 
            -
            end
         | 
| 408 | 
            -
             | 
| 409 | 
            -
             | 
| 410 | 
            -
            ###########################################
         | 
| 411 | 
            -
            # Cleanup
         | 
| 412 | 
            -
            ###########################################
         | 
| 413 | 
            -
             | 
| 414 | 
            -
             | 
| 415 | 
            -
            if 'cleanup' == action
         | 
| 416 | 
            -
              log_file_pattern = cfg['log_file'].gsub('<date>', '*')
         | 
| 417 | 
            -
             | 
| 418 | 
            -
              Dir.glob(log_file_pattern).each do |log_file|
         | 
| 419 | 
            -
                File.delete(log_file)
         | 
| 420 | 
            -
                puts "#{log_file} deleted"
         | 
| 147 | 
            +
              opts.on_tail('-v', '--version', 'Print current installed version') do
         | 
| 148 | 
            +
                puts Spectre::VERSION
         | 
| 149 | 
            +
                exit
         | 
| 421 150 | 
             
              end
         | 
| 422 151 |  | 
| 423 | 
            -
               | 
| 424 | 
            -
                 | 
| 425 | 
            -
                 | 
| 152 | 
            +
              opts.on_tail('-h', '--help', 'Print this help') do
         | 
| 153 | 
            +
                puts opts
         | 
| 154 | 
            +
                exit
         | 
| 426 155 | 
             
              end
         | 
| 427 156 | 
             
            end
         | 
| 428 157 |  | 
| 158 | 
            +
            # Parse options and get action
         | 
| 159 | 
            +
            action = options.parse!.shift || 'run'
         | 
| 160 | 
            +
             | 
| 161 | 
            +
            # Setup spectre
         | 
| 162 | 
            +
            engine = Spectre::Engine.new(config_overrides)
         | 
| 163 | 
            +
             | 
| 164 | 
            +
            case action
         | 
| 165 | 
            +
            when 'list', 'li', 'ls'
         | 
| 166 | 
            +
              engine.formatter.list(engine.list)
         | 
| 167 | 
            +
            when 'specs', 'sp'
         | 
| 168 | 
            +
              engine.formatter.details(engine.list)
         | 
| 169 | 
            +
            when 'describe', 'de'
         | 
| 170 | 
            +
              engine.formatter.describe(engine.contexts)
         | 
| 171 | 
            +
            when 'run', 'ru'
         | 
| 172 | 
            +
              engine.cleanup if cleanup
         | 
| 173 | 
            +
              runs = engine.run
         | 
| 174 | 
            +
              exit if runs.nil?
         | 
| 175 | 
            +
             | 
| 176 | 
            +
              if run_failed and runs.any? { |x| x.status != :success }
         | 
| 177 | 
            +
                File.write('.failed', runs
         | 
| 178 | 
            +
                  .select do |x|
         | 
| 179 | 
            +
                    x.type == :spec and
         | 
| 180 | 
            +
                      x.status != :success
         | 
| 181 | 
            +
                  end
         | 
| 182 | 
            +
                  .map(&:name)
         | 
| 183 | 
            +
                  .join("\n"))
         | 
| 184 | 
            +
              end
         | 
| 429 185 |  | 
| 430 | 
            -
             | 
| 431 | 
            -
             | 
| 432 | 
            -
             | 
| 433 | 
            -
             | 
| 434 | 
            -
             | 
| 435 | 
            -
             | 
| 436 | 
            -
               | 
| 437 | 
            -
             | 
| 438 | 
            -
             | 
| 439 | 
            -
             | 
| 440 | 
            -
             | 
| 441 | 
            -
             | 
| 442 | 
            -
             | 
| 443 | 
            -
               | 
| 444 | 
            -
             | 
| 445 | 
            -
             | 
| 446 | 
            -
             | 
| 447 | 
            -
             | 
| 448 | 
            -
             | 
| 449 | 
            -
             | 
| 450 | 
            -
             | 
| 451 | 
            -
             | 
| 452 | 
            -
             | 
| 453 | 
            -
             | 
| 454 | 
            -
                 | 
| 455 | 
            -
                   | 
| 456 | 
            -
                   | 
| 457 | 
            -
             | 
| 458 | 
            -
             | 
| 459 | 
            -
             | 
| 460 | 
            -
             | 
| 461 | 
            -
             | 
| 462 | 
            -
             | 
| 463 | 
            -
             | 
| 464 | 
            -
            #  | 
| 465 | 
            -
             | 
| 466 | 
            -
                 | 
| 467 | 
            -
             | 
| 468 | 
            -
                #  | 
| 469 | 
            -
             | 
| 470 | 
            -
             | 
| 471 | 
            -
             | 
| 472 | 
            -
             | 
| 473 | 
            -
             | 
| 474 | 
            -
             | 
| 475 | 
            -
             | 
| 476 | 
            -
             | 
| 477 | 
            -
             | 
| 478 | 
            -
             | 
| 479 | 
            -
            #  | 
| 480 | 
            -
             | 
| 481 | 
            -
             | 
| 482 | 
            -
             | 
| 483 | 
            -
             | 
| 484 | 
            -
             | 
| 485 | 
            -
             | 
| 486 | 
            -
             | 
| 487 | 
            -
             | 
| 488 | 
            -
             | 
| 489 | 
            -
             | 
| 490 | 
            -
             | 
| 491 | 
            -
             | 
| 492 | 
            -
             | 
| 493 | 
            -
             | 
| 494 | 
            -
             | 
| 495 | 
            -
             | 
| 496 | 
            -
                   | 
| 497 | 
            -
             | 
| 498 | 
            -
             | 
| 499 | 
            -
                     | 
| 500 | 
            -
             | 
| 501 | 
            -
             | 
| 186 | 
            +
              puts
         | 
| 187 | 
            +
              engine.report(runs)
         | 
| 188 | 
            +
            when 'env', 'en'
         | 
| 189 | 
            +
              engine.formatter.environment(engine.env)
         | 
| 190 | 
            +
            when 'mixins', 'mi', 'mx'
         | 
| 191 | 
            +
              search = ARGV.first
         | 
| 192 | 
            +
              mixins = engine.mixins.select { |x| search.nil? or x.include? search }
         | 
| 193 | 
            +
              engine.formatter.mixins(mixins)
         | 
| 194 | 
            +
            when 'collections', 'co'
         | 
| 195 | 
            +
              engine.formatter.collections(engine)
         | 
| 196 | 
            +
            when 'cleanup', 'cl'
         | 
| 197 | 
            +
              engine.cleanup
         | 
| 198 | 
            +
            when 'init', 'in'
         | 
| 199 | 
            +
              DEFAULT_SPECTRE_CFG = <<~CONTENT
         | 
| 200 | 
            +
                log_file: ./logs/spectre_<date>.log
         | 
| 201 | 
            +
                env_patterns:
         | 
| 202 | 
            +
                  - './environments/**/*.env.yml'
         | 
| 203 | 
            +
                env_partial_patterns:
         | 
| 204 | 
            +
                  - './environments/**/*.env.secret.yml'
         | 
| 205 | 
            +
                spec_patterns:
         | 
| 206 | 
            +
                  - './specs/**/*.spec.rb'
         | 
| 207 | 
            +
                mixin_patterns:
         | 
| 208 | 
            +
                  - '../common/**/*.mixin.rb'
         | 
| 209 | 
            +
                  - './mixins/**/*.mixin.rb'
         | 
| 210 | 
            +
                resource_paths:
         | 
| 211 | 
            +
                  - '../common/resources'
         | 
| 212 | 
            +
                  - './resources'
         | 
| 213 | 
            +
              CONTENT
         | 
| 214 | 
            +
             | 
| 215 | 
            +
              DEFAULT_ENV_CFG = <<~CONTENT
         | 
| 216 | 
            +
                # Set the environment name.
         | 
| 217 | 
            +
                # This property is optional. If not set, the environment name is "default"
         | 
| 218 | 
            +
                name: default
         | 
| 219 | 
            +
             | 
| 220 | 
            +
                # Define reusable variables
         | 
| 221 | 
            +
                # See https://yaml.org/spec/1.2.2/#anchors-and-aliases
         | 
| 222 | 
            +
                cert: &cert ./resources/<root_cert>.cer
         | 
| 223 | 
            +
             | 
| 224 | 
            +
                # Define HTTP client configs used by the `http` module
         | 
| 225 | 
            +
                http:
         | 
| 226 | 
            +
                  <http_client_name>:
         | 
| 227 | 
            +
                    # Set the base URL of the API for the client to use
         | 
| 228 | 
            +
                    base_url: http://localhost:5000/api/v1/
         | 
| 229 | 
            +
             | 
| 230 | 
            +
                    # Set a custom CA certificate for server cert validation
         | 
| 231 | 
            +
                    cert: *cert
         | 
| 232 | 
            +
             | 
| 233 | 
            +
                    # You can set a path to an OpenAPI spec.
         | 
| 234 | 
            +
                    # This can be a file or a URL.
         | 
| 235 | 
            +
                    # Request options can then be set by the `endpoint` method
         | 
| 236 | 
            +
                    # which refers to the operation ID of the OpenAPI endpoint
         | 
| 237 | 
            +
                    # openapi: [https://localhost:5000]/doc/openapi.json
         | 
| 238 | 
            +
             | 
| 239 | 
            +
                    # Define basicauth authentication here
         | 
| 240 | 
            +
                    # Do NOT put credentials in this file.
         | 
| 241 | 
            +
                    # Use the `*.env.secret.yml` files instead
         | 
| 242 | 
            +
                    # basic_auth:
         | 
| 243 | 
            +
                    #   username: <username>
         | 
| 244 | 
            +
                    #   password:
         | 
| 245 | 
            +
              CONTENT
         | 
| 246 | 
            +
             | 
| 247 | 
            +
              DEFAULT_ENV_SECRET_CFG = <<~CONTENT
         | 
| 248 | 
            +
                # Define sensitive data here and do NOT commit this file in version control
         | 
| 249 | 
            +
                some_secret: thisissensitivedata
         | 
| 250 | 
            +
             | 
| 251 | 
            +
                http:
         | 
| 252 | 
            +
                  <http_client_name>:
         | 
| 253 | 
            +
                    # basic_auth:
         | 
| 254 | 
            +
                    #   username: <username>
         | 
| 255 | 
            +
                    #   password: <password>
         | 
| 256 | 
            +
              CONTENT
         | 
| 257 | 
            +
             | 
| 258 | 
            +
              SAMPLE_SPEC = <<~CONTENT
         | 
| 259 | 
            +
                # Define a test subject. This can be the name of the component
         | 
| 260 | 
            +
                # or the name of a specific feature or requirement.
         | 
| 261 | 
            +
                describe '<subject>' do
         | 
| 262 | 
            +
                  it 'does some http requests', tags: [:sample] do
         | 
| 263 | 
            +
                    # Use log messages to describe processes
         | 
| 264 | 
            +
                    log 'doing some http request'
         | 
| 265 | 
            +
             | 
| 266 | 
            +
                    # Use external or custom module
         | 
| 267 | 
            +
                    http '<http_client_name>' do
         | 
| 268 | 
            +
                      # To request a specific endpoint, predefined endpoints
         | 
| 269 | 
            +
                      # or openapi operations can be used...
         | 
| 270 | 
            +
                      # endpoint 'getSomeResource'
         | 
| 271 | 
            +
             | 
| 272 | 
            +
                      # ... or define a request in short form...
         | 
| 273 | 
            +
                      get 'api/v1/some-resource/{resourceId}'
         | 
| 274 | 
            +
             | 
| 275 | 
            +
                      # ... or use explicit method and path settings
         | 
| 276 | 
            +
                      # method 'GET'
         | 
| 277 | 
            +
                      # path 'api/v1/some-resource/{resourceId}'
         | 
| 278 | 
            +
             | 
| 279 | 
            +
                      # Set route parameter
         | 
| 280 | 
            +
                      with resourceId: 42
         | 
| 281 | 
            +
             | 
| 282 | 
            +
                      # Set query parameters. This method can
         | 
| 283 | 
            +
                      # be called multiple times.
         | 
| 284 | 
            +
                      query id: 4295118773,
         | 
| 285 | 
            +
                            foo: 'bar'
         | 
| 286 | 
            +
             | 
| 287 | 
            +
                      # Set request headers
         | 
| 288 | 
            +
                      header 'X-Correlation-Id', '4c2367b1-bfee-4cc2-bdc5-ed17a6a9dd4b'
         | 
| 289 | 
            +
                      header 'Range', 'bytes=500-999'
         | 
| 290 | 
            +
             | 
| 291 | 
            +
                      # Set request body as JSON.
         | 
| 292 | 
            +
                      # This call also sets `application/json` content type.
         | 
| 293 | 
            +
                      json({
         | 
| 294 | 
            +
                        "message": "Hello Spectre!"
         | 
| 295 | 
            +
                      })
         | 
| 296 | 
            +
                    end
         | 
| 502 297 |  | 
| 503 | 
            -
             | 
| 504 | 
            -
             | 
| 298 | 
            +
                    # Expect conditions in a single line
         | 
| 299 | 
            +
                    # If the check fails, the run will continue
         | 
| 300 | 
            +
                    expect(response.headers['X-Correlation-Id']).not to be empty
         | 
| 301 | 
            +
             | 
| 302 | 
            +
                    # Assert conditions in a single line...
         | 
| 303 | 
            +
                    # If an assertion fails, the run will be aborted
         | 
| 304 | 
            +
                    assert response.code.to be 200
         | 
| 305 | 
            +
             | 
| 306 | 
            +
                    # ...or assert multiple conditions in a block
         | 
| 307 | 
            +
                    # All conditions within this block will be evaluated.
         | 
| 308 | 
            +
                    # After the block finishes and a failure was reported,
         | 
| 309 | 
            +
                    # the run will be aborted.
         | 
| 310 | 
            +
                    # When using `expect` instead of `assert` the run will continue.
         | 
| 311 | 
            +
                    assert 'the correct data to be returned' do
         | 
| 312 | 
            +
                      report failure 'message not correct' unless respone.json.message == 'Hello World!'
         | 
| 313 | 
            +
                      report failure 'incorrect number' unless respone.json.number == 42
         | 
| 314 | 
            +
                    end
         | 
| 315 | 
            +
                  end
         | 
| 505 316 | 
             
                end
         | 
| 317 | 
            +
              CONTENT
         | 
| 318 | 
            +
             | 
| 319 | 
            +
              DEFAULT_GITIGNORE = <<~CONTENT
         | 
| 320 | 
            +
                *.code-workspace
         | 
| 321 | 
            +
                logs/
         | 
| 322 | 
            +
                reports/
         | 
| 323 | 
            +
                **/environments/*.env.secret.yml
         | 
| 324 | 
            +
              CONTENT
         | 
| 325 | 
            +
             | 
| 326 | 
            +
              DEFAULT_GEMFILE = <<~CONTENT
         | 
| 327 | 
            +
                source 'https://rubygems.org'
         | 
| 328 | 
            +
             | 
| 329 | 
            +
                gem 'spectre-core'
         | 
| 330 | 
            +
                gem 'spectre-http'
         | 
| 331 | 
            +
                # gem 'spectre-mysql'
         | 
| 332 | 
            +
                # gem 'spectre-ssh'
         | 
| 333 | 
            +
                # gem 'spectre-ftp'
         | 
| 334 | 
            +
                # gem 'spectre-curl'
         | 
| 335 | 
            +
                # gem 'spectre-git'
         | 
| 336 | 
            +
                # gem 'spectre-rabbitmq'
         | 
| 337 | 
            +
                # gem 'spectre-reporter-junit'
         | 
| 338 | 
            +
                # gem 'spectre-reporter-vstest'
         | 
| 339 | 
            +
                # gem 'spectre-reporter-html'
         | 
| 340 | 
            +
              CONTENT
         | 
| 506 341 |  | 
| 507 | 
            -
                expect 'a message to exist' do
         | 
| 508 | 
            -
                  response.json.message.should_not_be_empty
         | 
| 509 | 
            -
                end
         | 
| 510 | 
            -
              end
         | 
| 511 | 
            -
            end
         | 
| 512 | 
            -
            ]
         | 
| 513 | 
            -
             | 
| 514 | 
            -
            DEFAULT_GITIGNORE = %[*.code-workspace
         | 
| 515 | 
            -
            logs/
         | 
| 516 | 
            -
            reports/
         | 
| 517 | 
            -
            **/environments/*.env.secret.yml
         | 
| 518 | 
            -
            ]
         | 
| 519 | 
            -
             | 
| 520 | 
            -
            DEFAULT_GEMFILE = %[source 'https://rubygems.org'
         | 
| 521 | 
            -
             | 
| 522 | 
            -
            gem 'spectre-core', '>= #{Spectre::VERSION}'
         | 
| 523 | 
            -
            # gem 'spectre-mysql', '>= 1.0.0'
         | 
| 524 | 
            -
            # gem 'spectre-ssh', '>= 1.0.2'
         | 
| 525 | 
            -
            # gem 'spectre-ftp', '>= 1.0.0'
         | 
| 526 | 
            -
            # gem 'spectre-curl', '>= 1.0.0'
         | 
| 527 | 
            -
            # gem 'spectre-git', '>= 0.2.1'
         | 
| 528 | 
            -
            # gem 'spectre-reporter-junit', '>= 1.0.0'
         | 
| 529 | 
            -
            # gem 'spectre-reporter-vstest', '>= 1.0.1'
         | 
| 530 | 
            -
            # gem 'spectre-reporter-html', '>= 1.0.0'
         | 
| 531 | 
            -
            ]
         | 
| 532 | 
            -
             | 
| 533 | 
            -
            if 'init' == action
         | 
| 534 342 | 
             
              DEFAULT_FILES = [
         | 
| 535 343 | 
             
                ['./environments/default.env.yml', DEFAULT_ENV_CFG],
         | 
| 536 344 | 
             
                ['./environments/default.env.secret.yml', DEFAULT_ENV_SECRET_CFG],
         | 
| @@ -540,15 +348,15 @@ if 'init' == action | |
| 540 348 | 
             
                ['./Gemfile', DEFAULT_GEMFILE],
         | 
| 541 349 | 
             
              ]
         | 
| 542 350 |  | 
| 543 | 
            -
              %w | 
| 351 | 
            +
              %w[environments logs modules reports resources specs].each do |dir_name|
         | 
| 544 352 | 
             
                Dir.mkdir(dir_name) unless File.directory? dir_name
         | 
| 545 353 | 
             
              end
         | 
| 546 354 |  | 
| 547 355 | 
             
              DEFAULT_FILES.each do |file, content|
         | 
| 548 | 
            -
                unless File.exist? file
         | 
| 549 | 
            -
                  File.write(file, content)
         | 
| 550 | 
            -
                end
         | 
| 356 | 
            +
                File.write(file, content) unless File.exist? file
         | 
| 551 357 | 
             
              end
         | 
| 552 | 
            -
             | 
| 553 | 
            -
               | 
| 358 | 
            +
            else
         | 
| 359 | 
            +
              puts "unknown action #{action.inspect}\n".red
         | 
| 360 | 
            +
              puts options
         | 
| 361 | 
            +
              exit 1
         | 
| 554 362 | 
             
            end
         |