fitting 1.5.0 → 1.6.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
  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