fitting 1.5.0 → 1.6.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 7d1997d69b352bcdbba93f00c3e50cba25eb4d70
4
- data.tar.gz: c39ea9c0cc9ee2c81d506f83c4f85ab614accb4f
3
+ metadata.gz: 587fd41b2b12c20bbacc053f70f7e9f7daa1f866
4
+ data.tar.gz: db1f83a7fd72f7d556bb43b957d1de236b76f5ea
5
5
  SHA512:
6
- metadata.gz: 68b687226ff3b178bb8fb774cc011a4c5a285ee13b246362dab05e15a71b18bbfee1b406a7f9a88494c8bd7a786fad4b43373382fa93495d3d86dc9cffa37402
7
- data.tar.gz: 74928dc7e164fdad8fd68bc96fcf88753665cb49024fbb56db5bf68f706385f8a492254c4b1efec7960fbd6c0188a5264faca35af20e015bc2b78667fd272898
6
+ metadata.gz: c637ed7b9d401ee99b08b59cd2934f147fb64d55dc24f0f9c7572632695e9280af1aaf849876cbaf3c1d95dfcbd1252c64b5b0c5cd184c710d3d1e21d470b8ec
7
+ data.tar.gz: ba647de857ecf54c62b169b10d60884666a2847d71764131f96a7d78fcbbfc0bc84fa85f9c9775ea9d8ad364b1cedd2ec9e9ffcce377d5e0d4d21777b1d706f2
data/README.md CHANGED
@@ -2,9 +2,8 @@
2
2
 
3
3
  [![Build Status](https://travis-ci.org/funbox/fitting.svg?branch=master)](https://travis-ci.org/funbox/fitting)
4
4
 
5
- This gem will help to make your tests according to the documentation for the API.
6
-
7
- When writing tests, you can be sure that the implement API in accordance with documentation on API Blueprint.
5
+ This gem will help to realize your API in strict accordance with the documentation in the format API Bluprint.
6
+ To do this, when you run your RSpes tests on the controller, in the documentation automatically searches for the json-schema and then validate it with a response in the test.
8
7
 
9
8
  ## Installation
10
9
 
@@ -27,40 +26,81 @@ Or install it yourself as:
27
26
  In your `spec_helper.rb`:
28
27
 
29
28
  ```ruby
30
- config.include JSON::SchemaMatchers
29
+ Fitting.configure do |config|
30
+ config.apib_path = `doc.apib`
31
+ end
31
32
  ```
32
33
 
33
- This gem takes a simplified format json convert from API Blueprint which we have called API Tomogram.
34
-
35
- Use gem [tomograph](https://github.com/funbox/tomograph)
34
+ ## Example output
36
35
 
37
- ```ruby
38
- Fitting.configure do |config|
39
- config.tomogram = 'tomogram.json'
40
- end
36
+ After running tests you will get statistics in the console.
41
37
 
38
+ ```
39
+ Fully conforming requests:
40
+ DELETE /api/v1/book ✔ 200 ✔ 201 ✔ 404
41
+ DELETE /api/v1/book/{id} ✔ 200 ✔ 201 ✔ 404
42
+ GET /api/v1/book/{id}/seller ✔ 200 ✔ 201 ✔ 404
43
+
44
+ Partially conforming requests:
45
+ GET /api/v1/book ✖ 200 ✔ 404
46
+ POST /api/v1/book ✖ 200 ✔ 201 ✔ 404
47
+ GET /api/v1/book/{id} ✖ 200 ✔ 404 ✔ 200
48
+ PATCH /api/v1/book/{id} ✖ 200 ✔ 201 ✔ 404
49
+
50
+ Non-conforming requests:
51
+ GET /api/v1/seller ✖ 200 ✖ 201 ✖ 404
52
+ GET /api/v1/buyer ✖ 200 ✖ 404
53
+
54
+ API requests with fully implemented responses: 3 (33.33% of 9).
55
+ API requests with partially implemented responses: 4 (44.44% of 9).
56
+ API requests with no implemented responses: 2 (22.22% of 9).
57
+
58
+ API responses conforming to the blueprint: 16 (64.00% of 25).
59
+ API responses with validation errors or untested: 9 (36.00% of 25).
42
60
  ```
43
61
 
44
- You can then write tests such as:
62
+ ## Matchers
45
63
 
46
- ```ruby
47
- expect(response).to match_response
64
+ If you want know describe why you get crosses instead of checkmarks you can use matchers for RSpec.
65
+
66
+ ### match_response
67
+
68
+ Makes a simple validation JSON Schema.
69
+
70
+ ```
71
+ expect(response).to match_response
48
72
  ```
49
73
 
50
- If you want check all tests:
74
+ ### strict_match_response
51
75
 
52
- ```ruby
53
- config.after(:each, :type => :controller) do
54
- expect(response).to match_response
55
- end
76
+ Makes a strict validation JSON Schema. All properties are condisidered to have `"required": true` and all objects `"additionalProperties": false`.
77
+
78
+ ```
79
+ expect(response).to strict_match_response
56
80
  ```
57
81
 
58
82
  ## Config
59
83
 
84
+ ### apib_path
85
+
86
+ Path for API Blueprint documentation. There must be an installed library [drafter](https://github.com/apiaryio/drafter).
87
+
88
+ ### drafter_yaml_path
89
+
90
+ Path for API Blueprint documentation after use drafter and transformation in yaml.
91
+
60
92
  ### necessary_fully_implementation_of_responses
61
93
 
62
94
  Default `true`. It returns `exit 1` if not implemented all(with tests expect match response) the responses.
63
95
 
96
+ ### strict
97
+
98
+ Default `false`. If `true` than all properties are condisidered to have `"required": true` and all objects `"additionalProperties": false`.
99
+
100
+ ### prefix
101
+
102
+ Prefix for request.
103
+
64
104
  ### white_list
65
105
 
66
106
  Default all resources. This is an array of resources that are mandatory for implementation.
@@ -78,10 +118,16 @@ config.white_list = {
78
118
 
79
119
  Empty array `[]` means all methods.
80
120
 
121
+ Result be two statistic for two list.
122
+
81
123
  ### create_report_with_name
82
124
 
83
125
  Create report with name.
84
126
 
127
+ ### show_statistics_in_console
128
+
129
+ Default `true`.
130
+
85
131
  ## Report
86
132
 
87
133
  Autogenerate `report_request_by_response.yaml` and `report_response.yaml reports`.
data/fitting.gemspec CHANGED
@@ -23,6 +23,7 @@ Gem::Specification.new do |spec|
23
23
  spec.add_runtime_dependency 'tomogram_routing', '~> 0.1', '>= 0.1.0'
24
24
  spec.add_runtime_dependency 'multi_json'
25
25
  spec.add_runtime_dependency 'rspec-core', '~> 3.0', '>= 3.0.0'
26
+ spec.add_runtime_dependency 'tomograph', '~> 0.4', '>= 0.4.0'
26
27
  spec.add_development_dependency 'bundler', '~> 1.12'
27
28
  spec.add_development_dependency 'rake', '~> 10.0'
28
29
  spec.add_development_dependency 'byebug', '~> 8.2', '>= 8.2.1'
@@ -1,12 +1,20 @@
1
1
  module Fitting
2
2
  class Configuration
3
- attr_accessor :tomogram,
3
+ attr_accessor :apib_path,
4
+ :drafter_yaml_path,
4
5
  :necessary_fully_implementation_of_responses,
6
+ :strict,
7
+ :prefix,
5
8
  :white_list,
6
- :create_report_with_name
9
+ :create_report_with_name,
10
+ :tomogram,
11
+ :show_statistics_in_console
7
12
 
8
13
  def initialize
9
14
  @necessary_fully_implementation_of_responses = true
15
+ @strict = false
16
+ @prefix = ''
17
+ @show_statistics_in_console = true
10
18
  end
11
19
  end
12
20
  end
@@ -30,7 +30,7 @@ module Fitting
30
30
  end
31
31
 
32
32
  def all
33
- @all ||= MultiJson.load(@tomogram).inject([]) do |routes, request|
33
+ @all ||= @tomogram.inject([]) do |routes, request|
34
34
  request['responses'].inject({}) do |responses, response|
35
35
  responses[response['status']] ||= 0
36
36
  responses[response['status']] += 1
@@ -1,5 +1,4 @@
1
1
  require 'fitting/response'
2
- require 'fitting/storage/responses'
3
2
  require 'fitting/storage/documentation'
4
3
 
5
4
  module Fitting
@@ -10,8 +9,7 @@ module Fitting
10
9
  response,
11
10
  Fitting::Storage::Documentation.tomogram
12
11
  )
13
- Fitting::Storage::Responses.push(@response)
14
- @response.valid?
12
+ @response.fully_validates.valid?
15
13
  end
16
14
 
17
15
  def ===(other)
@@ -24,11 +22,39 @@ module Fitting
24
22
  "got: #{@response.real_request_with_status}"
25
23
  end
26
24
 
27
- unless @response.valid?
25
+ unless @response.fully_validates.valid?
28
26
  "response does not conform to json-schema\n"\
29
27
  "schemas: \n#{@response.expected}\n\n"\
30
28
  "got: #{@response.got}\n\n"\
31
- "errors: \n#{@response.diff}\n"
29
+ "errors: \n#{@response.fully_validates}\n"
30
+ end
31
+ end
32
+ end
33
+
34
+ class StrictResponse
35
+ def matches?(response)
36
+ @response = Fitting::Response.new(
37
+ response,
38
+ Fitting::Storage::Documentation.tomogram
39
+ )
40
+ @response.strict_fully_validates.valid?
41
+ end
42
+
43
+ def ===(other)
44
+ matches?(other)
45
+ end
46
+
47
+ def failure_message
48
+ unless @response.documented?
49
+ return "response not documented\n"\
50
+ "got: #{@response.real_request_with_status}"
51
+ end
52
+
53
+ unless @response.strict_fully_validates.valid?
54
+ "response does not conform to json-schema\n"\
55
+ "schemas: \n#{@response.expected}\n\n"\
56
+ "got: #{@response.got}\n\n"\
57
+ "errors: \n#{@response.strict_fully_validates}\n"
32
58
  end
33
59
  end
34
60
  end
@@ -36,5 +62,9 @@ module Fitting
36
62
  def match_response
37
63
  Response.new
38
64
  end
65
+
66
+ def strict_match_response
67
+ StrictResponse.new
68
+ end
39
69
  end
40
70
  end
@@ -0,0 +1,26 @@
1
+ require 'json-schema'
2
+
3
+ module Fitting
4
+ class Response
5
+ class FullyValidates < Array
6
+ def self.craft(schemas, body, strict)
7
+ if schemas
8
+ new(schemas.inject([]) do |res, schema|
9
+ res.push(JSON::Validator.fully_validate(schema, body, :strict => strict))
10
+ end)
11
+ else
12
+ @valid = false
13
+ new
14
+ end
15
+ end
16
+
17
+ def valid?
18
+ @valid ||= any? { |fully_validate| fully_validate == [] }
19
+ end
20
+
21
+ def to_s
22
+ @to_s ||= join("\n\n")
23
+ end
24
+ end
25
+ end
26
+ end
@@ -1,39 +1,35 @@
1
1
  require 'fitting/request'
2
- require 'json-schema'
2
+ require 'fitting/response/fully_validates'
3
3
 
4
4
  module Fitting
5
5
  class Response
6
6
  def initialize(env_response, tomogram)
7
- @request = Request.new(env_response.request, tomogram)
7
+ @request = Fitting::Request.new(env_response.request, tomogram)
8
8
  @status = env_response.status
9
9
  @body = env_response.body
10
10
  @schemas = @request.schemas_of_possible_responses(status: @status)
11
- @fully_validates = set_fully_validate if @schemas
12
11
  end
13
12
 
14
- def set_fully_validate
15
- @valid = false
16
- fully_validates = []
17
- @schemas.map do |old_schema|
18
- fully_validate = JSON::Validator.fully_validate(old_schema, @body)
19
- fully_validates.push(fully_validate)
20
- @valid = true if fully_validate == []
21
- end
22
- fully_validates
13
+ def fully_validates
14
+ @fully_validates ||= Fitting::Response::FullyValidates.craft(@schemas, @body, false)
23
15
  end
24
16
 
25
- def documented?
26
- @schemas && @schemas.present?
17
+ def strict_fully_validates
18
+ @strict_fully_validates ||= Fitting::Response::FullyValidates.craft(@schemas, @body, true)
27
19
  end
28
20
 
29
- def valid?
30
- @valid == true
21
+ def documented?
22
+ @schemas && @schemas.present?
31
23
  end
32
24
 
33
25
  def route
34
26
  "#{@request.route} #{@status} #{index}"
35
27
  end
36
28
 
29
+ def strict_route
30
+ "#{@request.route} #{@status} #{strict_index}"
31
+ end
32
+
37
33
  def real_request_with_status
38
34
  "#{@request.real_method_with_path} #{@status}"
39
35
  end
@@ -42,12 +38,6 @@ module Fitting
42
38
  @body
43
39
  end
44
40
 
45
- def diff
46
- @fully_validates.inject("") do |res, fully_validate|
47
- res + "#{fully_validate.join("\n")}\n\n"
48
- end
49
- end
50
-
51
41
  def expected
52
42
  @schemas.inject([]) do |res, schema|
53
43
  res.push("#{schema}")
@@ -58,7 +48,15 @@ module Fitting
58
48
 
59
49
  def index
60
50
  @schemas.size.times do |i|
61
- if @fully_validates[i] == []
51
+ if fully_validates[i] == []
52
+ return i
53
+ end
54
+ end
55
+ end
56
+
57
+ def strict_index
58
+ @schemas.size.times do |i|
59
+ if strict_fully_validates[i] == []
62
60
  return i
63
61
  end
64
62
  end
@@ -2,9 +2,10 @@ require 'multi_json'
2
2
  module Fitting
3
3
  class Route
4
4
  class Coverage
5
- def initialize(coverage_responses, responses_routes)
5
+ def initialize(coverage_responses, responses_routes, strict)
6
6
  @coverage_responses = coverage_responses
7
7
  @responses_routes = responses_routes
8
+ @strict = strict
8
9
  end
9
10
 
10
11
  def coverage
@@ -29,9 +30,15 @@ module Fitting
29
30
  private
30
31
 
31
32
  def full_coverage
32
- @coverage_responses.map do |response|
33
- response.route if response.documented? && response.valid?
34
- end.compact.uniq
33
+ if @strict
34
+ @coverage_responses.map do |response|
35
+ response.strict_route if response.documented? && response.strict_fully_validates.valid?
36
+ end.compact.uniq
37
+ else
38
+ @coverage_responses.map do |response|
39
+ response.route if response.documented? && response.fully_validates.valid?
40
+ end.compact.uniq
41
+ end
35
42
  end
36
43
  end
37
44
  end
data/lib/fitting/route.rb CHANGED
@@ -4,8 +4,8 @@ require 'fitting/route/responses'
4
4
 
5
5
  module Fitting
6
6
  class Route
7
- def initialize(all_responses, routes)
8
- coverage = Fitting::Route::Coverage.new(all_responses, routes)
7
+ def initialize(all_responses, routes, strict)
8
+ coverage = Fitting::Route::Coverage.new(all_responses, routes, strict)
9
9
  @requests = Fitting::Route::Requests.new(coverage)
10
10
  @responses = Fitting::Route::Responses.new(routes, coverage)
11
11
  end
@@ -2,10 +2,10 @@ require 'fitting/route'
2
2
 
3
3
  module Fitting
4
4
  class Statistics
5
- def initialize(documentation, all_responses)
5
+ def initialize(documentation, all_responses, strict)
6
6
  @documentation = documentation
7
- @black_route = Fitting::Route.new(all_responses, @documentation.black)
8
- @white_route = Fitting::Route.new(all_responses, @documentation.white)
7
+ @black_route = Fitting::Route.new(all_responses, @documentation.black, strict)
8
+ @white_route = Fitting::Route.new(all_responses, @documentation.white, strict)
9
9
  end
10
10
 
11
11
  def not_coverage?
@@ -19,7 +19,7 @@ module Fitting
19
19
  def to_s
20
20
  if @documentation.black.any?
21
21
  [
22
- ['[Black list]', @black_route.statistics].join("\n"),
22
+ ['[Black list]', @black_route.statistics_with_conformity_lists].join("\n"),
23
23
  ['[White list]', @white_route.statistics_with_conformity_lists].join("\n"),
24
24
  ""
25
25
  ].join("\n\n")
@@ -1,12 +1,29 @@
1
1
  require 'tomogram_routing'
2
2
  require 'fitting/configuration'
3
+ require 'tomograph'
3
4
 
4
5
  module Fitting
5
6
  module Storage
6
7
  module Documentation
7
8
  class << self
8
9
  def tomogram
9
- @tomogram ||= TomogramRouting::Tomogram.craft(Fitting.configuration.tomogram)
10
+ @tomogram ||= if Fitting.configuration.apib_path
11
+ @yaml ||= `drafter #{Fitting.configuration.apib_path}`
12
+ Tomograph.configure do |config|
13
+ config.drafter_yaml = @yaml
14
+ config.prefix = Fitting.configuration.prefix
15
+ end
16
+ TomogramRouting::Tomogram.craft(Tomograph::Tomogram.json)
17
+ elsif Fitting.configuration.tomogram
18
+ # legacy
19
+ TomogramRouting::Tomogram.craft(Fitting.configuration.tomogram)
20
+ else
21
+ Tomograph.configure do |config|
22
+ config.documentation = Fitting.configuration.drafter_yaml_path
23
+ config.prefix = Fitting.configuration.prefix
24
+ end
25
+ TomogramRouting::Tomogram.craft(Tomograph::Tomogram.json)
26
+ end
10
27
  end
11
28
  end
12
29
  end
@@ -1,3 +1,3 @@
1
1
  module Fitting
2
- VERSION = '1.5.0'.freeze
2
+ VERSION = '1.6.0'.freeze
3
3
  end
data/lib/fitting.rb CHANGED
@@ -6,6 +6,7 @@ require 'fitting/matchers/response_matcher'
6
6
  require 'rspec/core'
7
7
  require 'fitting/statistics'
8
8
  require 'fitting/documentation'
9
+ require 'fitting/storage/responses'
9
10
 
10
11
  ERROR_EXIT_CODE = 1
11
12
 
@@ -33,16 +34,18 @@ module RSpec
33
34
  return returned_exit_code if Fitting::Storage::Skip.get
34
35
 
35
36
  statistics = Fitting::Statistics.new(
36
- Fitting::Documentation.new(Fitting.configuration.tomogram, Fitting.configuration.white_list),
37
- Fitting::Storage::Responses.all)
38
- puts statistics
37
+ Fitting::Documentation.new(Fitting::Storage::Documentation.tomogram, Fitting.configuration.white_list),
38
+ Fitting::Storage::Responses.all,
39
+ Fitting.configuration.strict
40
+ )
41
+ puts statistics if Fitting.configuration.show_statistics_in_console
39
42
  if Fitting.configuration.create_report_with_name
40
43
  statistics.save(Fitting.configuration.create_report_with_name)
41
44
  end
42
45
 
43
46
  if Fitting.configuration.necessary_fully_implementation_of_responses &&
44
47
  returned_exit_code == 0 &&
45
- response.not_coverage?
48
+ statistics.not_coverage?
46
49
  return ERROR_EXIT_CODE
47
50
  end
48
51
  returned_exit_code
@@ -50,3 +53,12 @@ module RSpec
50
53
  end
51
54
  end
52
55
  end
56
+
57
+ RSpec.configure do |config|
58
+ config.after(:each, :type => :controller) do
59
+ Fitting::Storage::Responses.push(
60
+ Fitting::Response.new(
61
+ response,
62
+ Fitting::Storage::Documentation.tomogram))
63
+ end
64
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fitting
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.5.0
4
+ version: 1.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - d.efimov
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2017-03-16 00:00:00.000000000 Z
11
+ date: 2017-03-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: json-schema
@@ -84,6 +84,26 @@ dependencies:
84
84
  - - ">="
85
85
  - !ruby/object:Gem::Version
86
86
  version: 3.0.0
87
+ - !ruby/object:Gem::Dependency
88
+ name: tomograph
89
+ requirement: !ruby/object:Gem::Requirement
90
+ requirements:
91
+ - - "~>"
92
+ - !ruby/object:Gem::Version
93
+ version: '0.4'
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: 0.4.0
97
+ type: :runtime
98
+ prerelease: false
99
+ version_requirements: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '0.4'
104
+ - - ">="
105
+ - !ruby/object:Gem::Version
106
+ version: 0.4.0
87
107
  - !ruby/object:Gem::Dependency
88
108
  name: bundler
89
109
  requirement: !ruby/object:Gem::Requirement
@@ -216,6 +236,7 @@ files:
216
236
  - lib/fitting/matchers/response_matcher.rb
217
237
  - lib/fitting/request.rb
218
238
  - lib/fitting/response.rb
239
+ - lib/fitting/response/fully_validates.rb
219
240
  - lib/fitting/route.rb
220
241
  - lib/fitting/route/coverage.rb
221
242
  - lib/fitting/route/requests.rb