vigia 0.0.9 → 0.1.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.
@@ -3,8 +3,12 @@ module Vigia
3
3
  class RestClient
4
4
  attr_accessor :code, :headers, :body
5
5
 
6
- def initialize(http_options)
7
- @http_options = http_options
6
+ def initialize(http_client_options)
7
+ @http_client_options = http_client_options
8
+ end
9
+
10
+ def run
11
+ cache_or_perform_request
8
12
  end
9
13
 
10
14
  def run!
@@ -13,32 +17,50 @@ module Vigia
13
17
 
14
18
  private
15
19
 
16
- # :nocov:
20
+ # FIXME move cache to HttpClient::Base
21
+ def cache_or_perform_request
22
+ if cache.has_key?(request_key)
23
+ cache[request_key]
24
+ else
25
+ @@cache[request_key] = perform_request
26
+ end
27
+ end
28
+
29
+ def cache
30
+ @@cache ||= {}
31
+ end
32
+
33
+ def request_key
34
+ hash = @http_client_options.to_h.select do |k,v|
35
+ [ :headers, :method, :url, :payload ].include?(k)
36
+ end
37
+ Digest::MD5.hexdigest(hash.to_s)
38
+ end
39
+
17
40
  def parse_request(rest_client_result)
18
- {
19
- code: rest_client_result.code,
41
+ Vigia::HttpClient::ClientRequest.new(
42
+ code: rest_client_result.code,
20
43
  headers: rest_client_result.headers,
21
- body: rest_client_result.body
22
- }
44
+ body: rest_client_result.body
45
+ )
23
46
  end
24
47
 
25
48
  def error_request(exception)
26
- {
27
- code: exception.http_code, # Todo. Parse each exception
49
+ Vigia::HttpClient::ClientRequest.new(
50
+ code: exception.http_code, # Todo. Parse each exception
28
51
  headers: exception.response.headers,
29
- body: exception.response
30
- }
52
+ body: exception.http_body
53
+ )
31
54
  end
32
55
 
33
56
  def perform_request
34
57
  begin
35
- request = ::RestClient::Request.execute(@http_options)
58
+ request = ::RestClient::Request.execute(@http_client_options.to_h)
36
59
  parse_request(request)
37
60
  rescue ::RestClient::Exception => exception
38
61
  error_request(exception)
39
62
  end
40
63
  end
41
- # :nocov:
42
64
  end
43
65
  end
44
66
  end
@@ -1,22 +1,33 @@
1
1
  module Vigia
2
2
  class Parameters
3
3
 
4
- attr_reader :resource, :action
4
+ attr_reader :parameters
5
5
 
6
- def initialize(resource, action)
7
- @resource = resource
8
- @action = action
6
+ def initialize(parameters)
7
+ @parameters = parameters
8
+ validate
9
9
  end
10
10
 
11
11
  def to_hash
12
- all.each_with_object({}) do |parameter, collection|
13
- collection[parameter.name] = parameter.example_value
14
- collection
12
+ @parameters.each_with_object({}) do |parameter, hash|
13
+ hash.merge!(parameter[:name] => parameter[:value])
15
14
  end
16
15
  end
17
16
 
18
- def all
19
- resource.parameters.collection + action.parameters.collection
17
+ def validate
18
+ return if required_parameters.empty?
19
+
20
+ raise("Missing parameters #{ missing_parameters.join(',') }") unless missing_parameters.empty?
21
+ end
22
+
23
+ private
24
+
25
+ def required_parameters
26
+ parameters.select{ |k| k[:required] == true }
27
+ end
28
+
29
+ def missing_parameters
30
+ required_parameters.select{ |k| k[:value].nil? || k[:value].empty? }.map{ |k| k[:name] }
20
31
  end
21
32
  end
22
33
  end
data/lib/vigia/rspec.rb CHANGED
@@ -1,15 +1,13 @@
1
1
  module Vigia
2
2
  class Rspec
3
3
  class << self
4
- def apib
5
- Vigia.config.blueprint.apib
4
+ def adapter
5
+ @@adapter
6
6
  end
7
7
 
8
8
  def include_shared_folders
9
9
  require_custom_examples
10
10
  require "#{ __dir__ }/spec/support/utils"
11
- require "#{ __dir__ }/spec/support/shared_examples/skip_example"
12
- require "#{ __dir__ }/spec/support/shared_examples/apib_example"
13
11
  end
14
12
 
15
13
  private
@@ -22,26 +20,45 @@ module Vigia
22
20
  end
23
21
  end
24
22
 
25
- # Run `Vigia.spec_folder` spec file
26
23
  def run!
27
- with_options do
24
+ with_rspec_options do
28
25
  RSpec::Core::Runner::run(
29
- [ Vigia.spec_folder ], $stderr, $stdout)
26
+ [ Vigia.spec_folder ], Vigia.config.stderr, Vigia.config.stdout)
30
27
  end
31
28
  end
32
29
 
33
- private
30
+ def start_tests(rspec)
31
+ with_rspec_options do
32
+ set_global_memoizers(rspec)
33
+
34
+ Vigia::Sail::Group.setup_and_run_primary(rspec)
35
+ end
36
+ end
34
37
 
35
- # ToDo: Do we need rspec config?
38
+ def set_global_memoizers(rspec)
39
+ instance = adapter
40
+ rspec.let(:client) { Vigia.config.http_client_class.new(http_client_options) }
41
+ rspec.let(:result) { client.run }
42
+ rspec.let(:result!) { client.run! }
43
+ rspec.let(:adapter) { instance }
44
+ end
45
+
46
+ def adapter
47
+ @@adapter ||= Vigia.config.adapter.instance
48
+ end
36
49
 
37
- def with_options &block
50
+ private
51
+
52
+ def with_rspec_options
38
53
  RSpec.configure do |config|
39
- config.before(:each) do |example|
40
- end
41
- config.before(:suite) do
42
- end
54
+ configure_vigia_rspec(config)
43
55
  end
44
56
  yield
45
57
  end
58
+
59
+ def configure_vigia_rspec(rspec_config)
60
+ return unless Vigia.config.rspec_config_block.respond_to?(:call)
61
+ Vigia.config.rspec_config_block.call(rspec_config)
62
+ end
46
63
  end
47
64
  end
@@ -0,0 +1,67 @@
1
+ module Vigia
2
+ module Sail
3
+ class Context < RSpecObject
4
+
5
+ def run
6
+ instance = self
7
+ rspec.context instance.to_s do
8
+ instance.with_hooks(self) do
9
+ let(:http_client_options) { instance.set_http_client_options(self) }
10
+ let(:expectations) { instance.set_expectations(self) }
11
+
12
+ instance.run_examples(self)
13
+ instance.run_shared_examples(self)
14
+ end
15
+ end
16
+ end
17
+
18
+ def set_http_client_options(in_let_context)
19
+ Vigia::HttpClient::Options.build(self, in_let_context)
20
+ end
21
+
22
+ def set_expectations(in_let_context)
23
+ Vigia::HttpClient::ExpectedRequest.new.tap do |instance|
24
+ expectations.each do |name|
25
+ option_object = options[:expectations][name]
26
+ instance[name] = contextual_object(object: option_object, context: in_let_context)
27
+ end
28
+ instance
29
+ end
30
+ end
31
+
32
+ def run_examples(in_context)
33
+ Vigia::Sail::Example.run_in_context(self, in_context)
34
+ end
35
+
36
+ def run_shared_examples(in_context)
37
+ if custom_examples.any?
38
+ custom_examples.each do |example_name|
39
+ in_context.include_examples example_name
40
+ end
41
+ end
42
+ end
43
+
44
+ def to_s
45
+ "context #{ name }"
46
+ end
47
+
48
+ private
49
+
50
+ def custom_examples
51
+ Vigia.config.custom_examples.each_with_object([]) do |custom_example, collection|
52
+ collection << custom_example[:name] if custom_example[:filter] == :all
53
+ collection
54
+ end
55
+ end
56
+
57
+ def expectations
58
+ (options[:expectations] || {}).keys & default_expectations
59
+ end
60
+
61
+ def default_expectations
62
+ [ :code, :headers, :body ]
63
+ end
64
+ end
65
+ end
66
+ end
67
+
@@ -0,0 +1,65 @@
1
+ module Vigia
2
+ module Sail
3
+ class Example < Vigia::Sail::RSpecObject
4
+ class << self
5
+ def register(name, options)
6
+ @collection = {} if collection.nil?
7
+ @collection.merge!(name => options)
8
+ end
9
+
10
+ def run_in_context(context, rspec_context)
11
+ @collection.each do |name, options|
12
+ setup_and_run(name, rspec_context) if example_contexts_include?(context, options[:contexts])
13
+ end
14
+ end
15
+
16
+ def example_contexts_include?(context, enabled_contexts)
17
+ [ *(enabled_contexts || :default) ].include?(context.name)
18
+ end
19
+ end
20
+
21
+ def run
22
+ instance = self
23
+ rspec.it instance do
24
+ skip if instance.skip?(self) || (respond_to?(:skip?) and send(:skip?))
25
+ skip('__vigia__') if instance.disabled?(self) || (respond_to?(:disabled?) and send(:disabled?))
26
+
27
+ instance_exec(&instance.expectation)
28
+ end
29
+ end
30
+
31
+ def expectation
32
+ must_be_a_block(options[:expectation], "Example `#{ name }` expectation must be a block")
33
+ end
34
+
35
+ def to_s
36
+ return contextual_object(option_name: :description) if options[:description]
37
+
38
+ name.to_s
39
+ end
40
+
41
+ def skip?(in_context)
42
+ return false unless options[:skip_if]
43
+ contextual_object(option_name: :skip_if, context: in_context)
44
+ end
45
+
46
+ def disabled?(in_context)
47
+ return false unless options[:disable_if]
48
+ contextual_object(option_name: :disable_if, context: in_context)
49
+ end
50
+ end
51
+ end
52
+ end
53
+
54
+
55
+ Vigia::Sail::Example.register(
56
+ :code_match,
57
+ description: 'has the expected HTTP code',
58
+ expectation: -> { expect(result.code).to be(expectations.code) }
59
+ )
60
+
61
+ Vigia::Sail::Example.register(
62
+ :include_headers,
63
+ description: 'includes the expected headers',
64
+ expectation: -> { expect(result[:headers]).to include(expectations[:headers]) }
65
+ )
@@ -0,0 +1,47 @@
1
+ module Vigia
2
+ module Sail
3
+ class Group < Vigia::Sail::RSpecObject
4
+ class << self
5
+ def setup_and_run_primary(rspec)
6
+ name, options = collection.select{ |k,v| v[:primary] == true }.first
7
+ instance = new(name, options, rspec)
8
+ instance.run
9
+ end
10
+ end
11
+
12
+ def run
13
+ described_objects.each_with_index do |object, index|
14
+ group_instance = create_group_instance(object, index)
15
+ rspec.describe group_instance do
16
+ let(group_instance.group.name) { group_instance.described_object }
17
+
18
+ group_instance.run(self)
19
+ end
20
+ end
21
+ end
22
+
23
+ def children
24
+ [ *options[:children] ] || []
25
+ end
26
+
27
+ def contexts
28
+ [ *options[:contexts] ] || []
29
+ end
30
+
31
+ def described_objects
32
+ contextual_object(option_name: :describes) || []
33
+ end
34
+
35
+ def create_group_instance(object, index)
36
+ instance_name = "#{ name }#{ index }"
37
+ Vigia::Sail::GroupInstance.new(instance_name, options, rspec).tap do |instance|
38
+ instance.define_singleton_method("#{ name }", -> { object })
39
+ instance.parent_object = rspec.described_class.described_object unless options[:primary]
40
+ instance.described_object = object
41
+ instance.description = (options[:description] || object.to_s)
42
+ instance.group = self
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,24 @@
1
+ module Vigia
2
+ module Sail
3
+ class GroupInstance < Vigia::Sail::RSpecObject
4
+
5
+ attr_accessor :parent_object, :described_object, :group, :description
6
+
7
+ def run(rspec_context)
8
+ with_hooks(rspec_context) do
9
+ group.children.each do |group_name|
10
+ Vigia::Sail::Group.setup_and_run(group_name, rspec_context)
11
+ end
12
+
13
+ group.contexts.each do |context_name|
14
+ Vigia::Sail::Context.setup_and_run(context_name, rspec_context)
15
+ end
16
+ end
17
+ end
18
+
19
+ def to_s
20
+ description.respond_to?(:call) ? instance_exec(&description) : description
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,73 @@
1
+ module Vigia
2
+ module Sail
3
+ class RSpecObject
4
+ class << self
5
+ attr_accessor :collection
6
+
7
+ def setup_and_run(name, rspec)
8
+ name, options = collection.select{ |k,v| k == name }.first
9
+ instance = new(name, options, rspec)
10
+ instance.run
11
+ end
12
+ end
13
+
14
+ attr_reader :name, :options, :rspec
15
+
16
+ def initialize(name, options, rspec)
17
+ @name = name
18
+ @options = options
19
+ @rspec = rspec
20
+
21
+ end
22
+
23
+ def execute_hook(filter_name, rspec_context)
24
+ hooks_for_object(filter_name).each do |hook|
25
+ rspec_context.instance_exec(&hook)
26
+ end
27
+ end
28
+
29
+ def hooks_for_object(filter_name)
30
+ config_hooks(filter_name) + object_hooks(filter_name)
31
+ end
32
+
33
+ def with_hooks(rspec_context)
34
+ execute_hook(:before, rspec_context)
35
+ yield
36
+ execute_hook(:after, rspec_context)
37
+ end
38
+
39
+ def contextual_object(option_name: nil, object: nil, context: nil)
40
+ context ||= rspec.described_class
41
+ object ||= options[option_name]
42
+ case object
43
+ when Symbol
44
+ context.adapter.send(object)
45
+ when Proc
46
+ context.instance_exec(&object)
47
+ else
48
+ object
49
+ end
50
+ end
51
+
52
+ private
53
+
54
+ def must_be_a_block(block, error_message)
55
+ return block if block.respond_to?(:call)
56
+ raise error_message
57
+ end
58
+
59
+ def object_hooks(filter_name)
60
+ option_name = "#{ filter_name }_#{ self.class.name.split('::').last.downcase }".to_sym
61
+ [ *options[option_name] ].compact
62
+
63
+ end
64
+
65
+ def config_hooks(filter_name)
66
+ Vigia.config.hooks.each_with_object([]) do |hook, collection|
67
+ next unless self.is_a?(hook[:rspec_class]) and filter_name == hook[:filter]
68
+ collection << hook[:block]
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end
@@ -3,29 +3,5 @@
3
3
  describe Vigia::Rspec do
4
4
 
5
5
  described_class.include_shared_folders
6
-
7
- described_class.apib.resource_groups.each do |resource_group|
8
- describe description_for(resource_group) do
9
- resource_group.resources.each do |resource|
10
- describe description_for(resource) do
11
- resource.actions.each do |action|
12
- describe description_for(action) do
13
- action.examples.each do |apib_example|
14
- runner_example = Vigia::Example.new(
15
- resource: resource,
16
- action: action,
17
- apib_example: apib_example)
18
-
19
- if runner_example.skip?
20
- include_examples 'skip example', runner_example
21
- else
22
- include_examples 'apib example', runner_example
23
- end
24
- end
25
- end
26
- end
27
- end
28
- end
29
- end
30
- end
6
+ described_class.new.start_tests(self)
31
7
  end
@@ -1,20 +1,7 @@
1
1
  # encoding: utf-8
2
2
 
3
- def description_for object
4
- case object
5
- when RedSnow::ResourceGroup
6
- "Resource Group: #{ object.name }"
7
- when RedSnow::Resource
8
- "Resource: #{ object.name } (#{ object.uri_template })"
9
- when RedSnow::Action
10
- "Action: #{ object.name } (#{ object.method })"
11
- when RedSnow::Payload
12
- "Response: #{ object.name }"
13
- end
14
- end
15
-
16
3
  def format_error(result, expectation)
17
- <<-FORMATTED_ERROR
4
+ <<-FORMATTED_ERROR
18
5
 
19
6
  Expected:
20
7
 
data/lib/vigia/url.rb CHANGED
@@ -8,41 +8,25 @@ module Vigia
8
8
  # ApiBluprint uses RFC6570 in all its resource's uri_templates
9
9
  # https://github.com/apiaryio/api-blueprint/blob/master/API%20Blueprint%20Specification.md#1-uri-templates
10
10
  def initialize(apib_uri_template)
11
- @uri_template = ::URITemplate.new(:rfc6570, apib_uri_template)
11
+ @uri_template = Addressable::Template.new(apib_uri_template)
12
12
  end
13
13
 
14
14
  def expand(parameters)
15
- validate(parameters) # if Vigia.config.validate_url_parameters?
16
-
17
- absolute_url uri_template.expand(parameters.to_hash)
15
+ absolute_url uri_template.expand(valid_parameters(parameters).to_hash)
18
16
  end
19
17
 
20
18
  def absolute_url path
21
19
  URI.join(host, path).to_s
22
20
  end
23
21
 
24
- def validate(parameters)
25
- return if required_template_parameters.empty?
26
-
27
- missing_parameters = required_template_parameters - valid_paremeters(parameters)
28
-
29
- raise("Uri template #{ @uri_template } needs parameter/s #{ missing_parameters.join(',') }") unless missing_parameters.empty?
30
- end
31
-
32
22
  def host
33
23
  Vigia.config.host
34
24
  end
35
25
 
36
26
  private
37
27
 
38
- def valid_paremeters(parameters)
39
- parameters.to_hash.delete_if{|_,v| v.nil? || v.empty? }.keys
40
- end
41
-
42
- def required_template_parameters
43
- uri_template.tokens.select{
44
- |token| token.is_a?(URITemplate::RFC6570::Expression::Basic)
45
- }.map(&:variables).flatten
28
+ def valid_parameters(parameters)
29
+ Vigia::Parameters.new(parameters)
46
30
  end
47
31
  end
48
32
  end
data/lib/vigia/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # encoding: utf-8
2
2
 
3
3
  module Vigia
4
- VERSION = "0.0.9"
4
+ VERSION = "0.1.0"
5
5
  end
data/lib/vigia.rb CHANGED
@@ -3,15 +3,21 @@
3
3
  require 'redsnow'
4
4
  require 'rspec'
5
5
  require 'rest_client'
6
- require 'uri_template'
6
+ require 'addressable/template'
7
7
 
8
- require_relative 'vigia/blueprint'
8
+ require_relative 'vigia/adapter'
9
+ require_relative 'vigia/adapters/blueprint'
9
10
  require_relative 'vigia/config'
10
- require_relative 'vigia/example'
11
- require_relative 'vigia/headers'
11
+ require_relative 'vigia/http_client/options'
12
12
  require_relative 'vigia/http_client/rest_client'
13
+ require_relative 'vigia/http_client/requests'
13
14
  require_relative 'vigia/parameters'
14
15
  require_relative 'vigia/rspec'
16
+ require_relative 'vigia/sail/rspec_object'
17
+ require_relative 'vigia/sail/example'
18
+ require_relative 'vigia/sail/context'
19
+ require_relative 'vigia/sail/group'
20
+ require_relative 'vigia/sail/group_instance'
15
21
  require_relative 'vigia/url'
16
22
  require_relative 'vigia/version'
17
23
 
@@ -23,7 +29,7 @@ module Vigia
23
29
  def configure
24
30
  @config = Vigia::Config.new
25
31
  yield @config
26
- @config.validate!
32
+ @config.apply
27
33
  end
28
34
  def spec_folder
29
35
  File.join(__dir__, 'vigia', 'spec')