fitting 2.2.0 → 2.3.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 +4 -4
- data/CHANGELOG.md +11 -0
- data/README.md +7 -0
- data/lib/fitting.rb +1 -3
- data/lib/fitting/configuration.rb +41 -11
- data/lib/fitting/configuration/legacy.rb +41 -0
- data/lib/fitting/configuration/yaml.rb +60 -0
- data/lib/fitting/matchers/response_matcher.rb +33 -14
- data/lib/fitting/records/documented/request.rb +53 -0
- data/lib/fitting/records/tested/request.rb +30 -0
- data/lib/fitting/records/tested/response.rb +19 -0
- data/lib/fitting/records/unit/json_schema.rb +25 -0
- data/lib/fitting/records/unit/request.rb +36 -0
- data/lib/fitting/records/unit/response.rb +31 -0
- data/lib/fitting/statistics.rb +14 -19
- data/lib/fitting/statistics/analysis.rb +24 -0
- data/lib/fitting/statistics/great.rb +13 -0
- data/lib/fitting/statistics/list.rb +45 -0
- data/lib/fitting/statistics/lists.rb +52 -0
- data/lib/fitting/statistics/measurement.rb +94 -0
- data/lib/fitting/statistics/not_covered_responses.rb +13 -0
- data/lib/fitting/statistics/percent.rb +19 -0
- data/lib/fitting/statistics/requests_stats.rb +40 -0
- data/lib/fitting/statistics/responses_stats.rb +32 -0
- data/lib/fitting/statistics/template.rb +90 -0
- data/lib/fitting/storage/responses.rb +6 -28
- data/lib/fitting/version.rb +1 -1
- metadata +20 -11
- data/lib/fitting/route.rb +0 -28
- data/lib/fitting/route/coverage.rb +0 -45
- data/lib/fitting/route/requests.rb +0 -25
- data/lib/fitting/route/requests/combine.rb +0 -113
- data/lib/fitting/route/requests/coverage.rb +0 -58
- data/lib/fitting/route/requests/lists.rb +0 -92
- data/lib/fitting/route/requests/statistics.rb +0 -40
- data/lib/fitting/route/responses.rb +0 -24
- data/lib/fitting/storage/documentation.rb +0 -22
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: bf841d9b8d6f50655b5d8f0a265980660be83232
|
4
|
+
data.tar.gz: 0413072e04a9eab17e619c0821d4bc05f0cd2592
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2f08e076fcfd2c8fae3ffa8a3173a35b727b47e240fb3cd7ad2fa65b9fd77da4cdb481cd07aabc60ec05b9bd56fa50fa6144f8071b2ea78026e51a362661b634
|
7
|
+
data.tar.gz: 610bcca9e1152c96af7fbfff997ff3146ffe7cfcdca20777b97e775c1384b7a0b0b34d2711795d717afd9ad1151560642a3d51095f617c8bb704235aeca5ce6f
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,16 @@
|
|
1
1
|
# Change log
|
2
2
|
|
3
|
+
### 2.3.0 - 2017-09-13
|
4
|
+
|
5
|
+
* features
|
6
|
+
* configuration via yaml file
|
7
|
+
* setting for multiple prefixes
|
8
|
+
|
9
|
+
### 2.2.0 - 2017-08-23
|
10
|
+
|
11
|
+
* features
|
12
|
+
* ignore requests which match with ignore list
|
13
|
+
|
3
14
|
### 2.1.3 - 2017-08-07
|
4
15
|
|
5
16
|
* bug fixes
|
data/README.md
CHANGED
@@ -1,5 +1,9 @@
|
|
1
1
|
# Fitting
|
2
2
|
|
3
|
+
<a href="https://funbox.ru">
|
4
|
+
<img src="https://funbox.ru/badges/sponsored_by_funbox.svg" alt="Sponsored by FunBox" width=250 />
|
5
|
+
</a>
|
6
|
+
|
3
7
|
[](https://travis-ci.org/funbox/fitting)
|
4
8
|
|
5
9
|
This gem will help you implement your API in strict accordance to the documentation in [API Blueprint](https://apiblueprint.org/) format.
|
@@ -113,6 +117,9 @@ expect(response).to strictly_match_schema
|
|
113
117
|
|
114
118
|
## Config
|
115
119
|
|
120
|
+
You can specify the settings either in a yaml file `.fitting.yml` or in config.
|
121
|
+
If your project uses several prefixes, for each one you need to create a separate yaml file in the folder `fitting` (`fitting/*.yml`).
|
122
|
+
|
116
123
|
### apib_path
|
117
124
|
|
118
125
|
Path to API Blueprint documentation. There must be an installed [drafter](https://github.com/apiaryio/drafter) to parse it.
|
data/lib/fitting.rb
CHANGED
@@ -1,8 +1,6 @@
|
|
1
1
|
require 'fitting/version'
|
2
2
|
require 'fitting/configuration'
|
3
|
-
require 'fitting/storage/documentation'
|
4
3
|
require 'fitting/matchers/response_matcher'
|
5
|
-
require 'fitting/statistics'
|
6
4
|
require 'fitting/documentation'
|
7
5
|
require 'fitting/storage/responses'
|
8
6
|
|
@@ -13,7 +11,7 @@ module Fitting
|
|
13
11
|
end
|
14
12
|
|
15
13
|
def configuration
|
16
|
-
@configuration ||= Configuration.
|
14
|
+
@configuration ||= Configuration.craft
|
17
15
|
end
|
18
16
|
|
19
17
|
def statistics
|
@@ -1,17 +1,47 @@
|
|
1
|
+
require 'fitting/configuration/yaml'
|
2
|
+
require 'fitting/configuration/legacy'
|
3
|
+
require 'yaml'
|
4
|
+
|
1
5
|
module Fitting
|
2
6
|
class Configuration
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
7
|
+
class << self
|
8
|
+
def craft
|
9
|
+
if one_yaml?
|
10
|
+
one_yaml
|
11
|
+
elsif more_than_one_yaml?
|
12
|
+
more_than_one_yaml
|
13
|
+
else
|
14
|
+
legacy
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def one_yaml?
|
19
|
+
File.file?('.fitting.yml')
|
20
|
+
end
|
21
|
+
|
22
|
+
def more_than_one_yaml?
|
23
|
+
!Dir['fitting/*.yml'].empty?
|
24
|
+
end
|
25
|
+
|
26
|
+
def one_yaml
|
27
|
+
yaml = YAML.safe_load(File.read('.fitting.yml'))
|
28
|
+
Fitting::Configuration::Yaml.new(yaml)
|
29
|
+
end
|
30
|
+
|
31
|
+
def more_than_one_yaml
|
32
|
+
files.map do |file|
|
33
|
+
yaml = YAML.safe_load(File.read(file))
|
34
|
+
Fitting::Configuration::Yaml.new(yaml, file[8..-5])
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def legacy
|
39
|
+
Fitting::Configuration::Legacy.new
|
40
|
+
end
|
10
41
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
@ignore_list = []
|
42
|
+
def files
|
43
|
+
Dir['fitting/*.yml']
|
44
|
+
end
|
15
45
|
end
|
16
46
|
end
|
17
47
|
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'tomograph'
|
2
|
+
|
3
|
+
module Fitting
|
4
|
+
class Configuration
|
5
|
+
class Legacy
|
6
|
+
attr_accessor :apib_path,
|
7
|
+
:drafter_yaml_path,
|
8
|
+
:strict,
|
9
|
+
:prefix,
|
10
|
+
:white_list,
|
11
|
+
:resource_white_list,
|
12
|
+
:ignore_list
|
13
|
+
|
14
|
+
def initialize
|
15
|
+
@strict = false
|
16
|
+
@prefix = ''
|
17
|
+
@ignore_list = []
|
18
|
+
end
|
19
|
+
|
20
|
+
def tomogram
|
21
|
+
@tomogram ||= Tomograph::Tomogram.new(
|
22
|
+
prefix: @prefix,
|
23
|
+
apib_path: @apib_path,
|
24
|
+
drafter_yaml_path: @drafter_yaml_path
|
25
|
+
)
|
26
|
+
end
|
27
|
+
|
28
|
+
def title
|
29
|
+
'fitting'
|
30
|
+
end
|
31
|
+
|
32
|
+
def stats_path
|
33
|
+
'fitting/stats'
|
34
|
+
end
|
35
|
+
|
36
|
+
def not_covered_path
|
37
|
+
'fitting/not_covered'
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require 'tomograph'
|
2
|
+
|
3
|
+
module Fitting
|
4
|
+
class Configuration
|
5
|
+
class Yaml
|
6
|
+
attr_reader :title
|
7
|
+
attr_accessor :apib_path,
|
8
|
+
:drafter_yaml_path,
|
9
|
+
:strict,
|
10
|
+
:prefix,
|
11
|
+
:white_list,
|
12
|
+
:resource_white_list,
|
13
|
+
:ignore_list
|
14
|
+
|
15
|
+
def initialize(yaml, title = 'fitting')
|
16
|
+
@apib_path = yaml['apib_path']
|
17
|
+
@drafter_yaml_path = yaml['drafter_yaml_path']
|
18
|
+
@strict = yaml['strict']
|
19
|
+
@prefix = yaml['prefix']
|
20
|
+
@white_list = yaml['white_list']
|
21
|
+
@resource_white_list = yaml['resource_white_list']
|
22
|
+
@ignore_list = yaml['ignore_list']
|
23
|
+
@title = title
|
24
|
+
default
|
25
|
+
end
|
26
|
+
|
27
|
+
def tomogram
|
28
|
+
@tomogram ||= Tomograph::Tomogram.new(
|
29
|
+
prefix: @prefix,
|
30
|
+
apib_path: @apib_path,
|
31
|
+
drafter_yaml_path: @drafter_yaml_path
|
32
|
+
)
|
33
|
+
end
|
34
|
+
|
35
|
+
def stats_path
|
36
|
+
if @title == 'fitting'
|
37
|
+
'fitting/stats'
|
38
|
+
else
|
39
|
+
"fitting/#{@title}/stats"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def not_covered_path
|
44
|
+
if @title == 'fitting'
|
45
|
+
'fitting/not_covered'
|
46
|
+
else
|
47
|
+
"fitting/#{@title}/not_covered"
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
def default
|
54
|
+
@strict ||= false if @strict.nil?
|
55
|
+
@prefix ||= ''
|
56
|
+
@ignore_list ||= []
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -1,20 +1,16 @@
|
|
1
1
|
require 'fitting/response'
|
2
|
-
require 'fitting/storage/documentation'
|
3
2
|
require 'fitting/configuration'
|
4
3
|
|
5
4
|
module Fitting
|
6
5
|
module Matchers
|
7
6
|
class Response
|
8
7
|
def matches?(response)
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
return true if @response.ignored?(Fitting.configuration.ignore_list)
|
14
|
-
if @response.within_prefix?(Fitting.configuration.prefix)
|
15
|
-
@response.fully_validates.valid?
|
8
|
+
if Fitting.configuration.is_a?(Array)
|
9
|
+
Fitting.configuration.all? do |config|
|
10
|
+
one_match(response, config)
|
11
|
+
end
|
16
12
|
else
|
17
|
-
|
13
|
+
one_match(response, Fitting.configuration)
|
18
14
|
end
|
19
15
|
end
|
20
16
|
|
@@ -35,15 +31,28 @@ module Fitting
|
|
35
31
|
"got: #{@response.got}\n\n"\
|
36
32
|
"errors: \n#{@response.fully_validates}\n"
|
37
33
|
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def one_match(response, config)
|
38
|
+
response = Fitting::Response.new(response, config.tomogram)
|
39
|
+
if response.within_prefix?(config.prefix)
|
40
|
+
@response = response
|
41
|
+
return true if @response.ignored?(config.ignore_list)
|
42
|
+
return @response.fully_validates.valid?
|
43
|
+
else
|
44
|
+
true
|
45
|
+
end
|
46
|
+
end
|
38
47
|
end
|
39
48
|
|
40
49
|
class StrictResponse
|
41
50
|
def matches?(response)
|
42
|
-
|
43
|
-
response,
|
44
|
-
|
45
|
-
|
46
|
-
|
51
|
+
if Fitting.configuration.is_a?(Array)
|
52
|
+
one_match(response, Fitting.configuration[0])
|
53
|
+
else
|
54
|
+
one_match(response, Fitting.configuration)
|
55
|
+
end
|
47
56
|
end
|
48
57
|
|
49
58
|
def ===(other)
|
@@ -63,6 +72,16 @@ module Fitting
|
|
63
72
|
"got: #{@response.got}\n\n"\
|
64
73
|
"errors: \n#{@response.strict_fully_validates}\n"
|
65
74
|
end
|
75
|
+
|
76
|
+
private
|
77
|
+
|
78
|
+
def one_match(response, config)
|
79
|
+
@response = Fitting::Response.new(
|
80
|
+
response,
|
81
|
+
config.tomogram
|
82
|
+
)
|
83
|
+
@response.strict_fully_validates.valid?
|
84
|
+
end
|
66
85
|
end
|
67
86
|
|
68
87
|
def match_schema
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module Fitting
|
2
|
+
class Records
|
3
|
+
class Documented
|
4
|
+
class Request
|
5
|
+
def initialize(tomogram_request, white_list)
|
6
|
+
@tomogram_request = tomogram_request
|
7
|
+
@white_list = white_list
|
8
|
+
end
|
9
|
+
|
10
|
+
def method
|
11
|
+
@method ||= @tomogram_request['method']
|
12
|
+
end
|
13
|
+
|
14
|
+
def path
|
15
|
+
@path ||= @tomogram_request['path']
|
16
|
+
end
|
17
|
+
|
18
|
+
def json_schema
|
19
|
+
@json_schema ||= @tomogram_request['json_schema']
|
20
|
+
end
|
21
|
+
|
22
|
+
def responses
|
23
|
+
@responses ||= groups.map do |group|
|
24
|
+
{
|
25
|
+
'status' => group[0],
|
26
|
+
'json_schemas' => group[1].map { |subgroup| subgroup['body'] }
|
27
|
+
}
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def white
|
32
|
+
@white ||= white?
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def white?
|
38
|
+
return true if @white_list == nil
|
39
|
+
return false if @white_list[path.to_s] == nil
|
40
|
+
return true if @white_list[path.to_s] == []
|
41
|
+
return true if @white_list[path.to_s].include?(method)
|
42
|
+
false
|
43
|
+
end
|
44
|
+
|
45
|
+
def groups
|
46
|
+
@groups ||= @tomogram_request['responses'].group_by do |tomogram_response|
|
47
|
+
tomogram_response['status']
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'tomograph/path'
|
2
|
+
require 'fitting/records/tested/response'
|
3
|
+
|
4
|
+
module Fitting
|
5
|
+
class Records
|
6
|
+
class Tested
|
7
|
+
class Request
|
8
|
+
def initialize(env_response)
|
9
|
+
@env_response = env_response
|
10
|
+
end
|
11
|
+
|
12
|
+
def method
|
13
|
+
@method ||= @env_response.request.request_method
|
14
|
+
end
|
15
|
+
|
16
|
+
def path
|
17
|
+
@path ||= Tomograph::Path.new(@env_response.request.env['PATH_INFO'] || @env_response.request.fullpath)
|
18
|
+
end
|
19
|
+
|
20
|
+
def body
|
21
|
+
@body ||= @env_response.request.env['action_dispatch.request.request_parameters']
|
22
|
+
end
|
23
|
+
|
24
|
+
def response
|
25
|
+
@response ||= Fitting::Records::Tested::Response.new(@env_response)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Fitting
|
2
|
+
class Records
|
3
|
+
class Tested
|
4
|
+
class Response
|
5
|
+
def initialize(env_response)
|
6
|
+
@env_response = env_response
|
7
|
+
end
|
8
|
+
|
9
|
+
def status
|
10
|
+
@status ||= @env_response.status
|
11
|
+
end
|
12
|
+
|
13
|
+
def body
|
14
|
+
@body ||= @env_response.body
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'json-schema'
|
2
|
+
|
3
|
+
module Fitting
|
4
|
+
class Records
|
5
|
+
class Unit
|
6
|
+
class JsonSchema
|
7
|
+
def initialize(json_schema, tested_bodies)
|
8
|
+
@json_schema = json_schema
|
9
|
+
@tested_bodies = tested_bodies
|
10
|
+
end
|
11
|
+
|
12
|
+
def bodies
|
13
|
+
@bodies ||= @tested_bodies.inject([]) do |res, tested_body|
|
14
|
+
begin
|
15
|
+
next res unless JSON::Validator.validate(@json_schema, tested_body)
|
16
|
+
res.push(tested_body)
|
17
|
+
rescue JSON::Schema::UriError
|
18
|
+
res.push(tested_body)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'fitting/records/unit/response'
|
2
|
+
|
3
|
+
module Fitting
|
4
|
+
class Records
|
5
|
+
class Unit
|
6
|
+
class Request
|
7
|
+
def initialize(documented_request, tested_requests)
|
8
|
+
@documented_request = documented_request
|
9
|
+
@tested_requests = tested_requests
|
10
|
+
end
|
11
|
+
|
12
|
+
def path
|
13
|
+
@path ||= @documented_request.path
|
14
|
+
end
|
15
|
+
|
16
|
+
def method
|
17
|
+
@method ||= @documented_request.method
|
18
|
+
end
|
19
|
+
|
20
|
+
def responses
|
21
|
+
@responses ||= @documented_request.responses.to_a.inject([]) do |res, documented_response|
|
22
|
+
res.push(Fitting::Records::Unit::Response.new(documented_response, tested_responses))
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def tested_responses
|
27
|
+
@tested_responses ||= @tested_requests.inject([]) do |res, tested_request|
|
28
|
+
next res unless @documented_request.method == tested_request.method &&
|
29
|
+
@documented_request.path.match(tested_request.path.to_s)
|
30
|
+
res.push(tested_request.response)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|