fitting 2.0.3 → 2.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.
- checksums.yaml +4 -4
- data/.rubocop.yml +9 -0
- data/CHANGELOG.md +7 -2
- data/README.md +17 -1
- data/fitting.gemspec +1 -1
- data/lib/fitting/configuration.rb +2 -1
- data/lib/fitting/documentation.rb +21 -11
- data/lib/fitting/matchers/response_matcher.rb +6 -6
- data/lib/fitting/request.rb +4 -4
- data/lib/fitting/response.rb +3 -7
- data/lib/fitting/response/fully_validates.rb +6 -6
- data/lib/fitting/route.rb +7 -6
- data/lib/fitting/route/requests.rb +10 -133
- data/lib/fitting/route/requests/combine.rb +113 -0
- data/lib/fitting/route/requests/coverage.rb +58 -0
- data/lib/fitting/route/requests/lists.rb +92 -0
- data/lib/fitting/route/requests/statistics.rb +40 -0
- data/lib/fitting/route/responses.rb +2 -1
- data/lib/fitting/statistics.rb +2 -2
- data/lib/fitting/storage/documentation.rb +1 -1
- data/lib/fitting/storage/responses.rb +18 -2
- data/lib/fitting/storage/white_list.rb +51 -0
- data/lib/fitting/version.rb +1 -1
- metadata +11 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a085daed12d7e3b16bdeb141928c50db861ff4ba
|
4
|
+
data.tar.gz: 73cf6002cb3b3b55a83c6c723a329f90c7a4f164
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 66b61927387c25a314c800dc5d385f34ed0a89c5bf754532674e64f092a3100e9476d539be357883b1e71b1094715fda3245bf4b6a4d6a0828380f5ba73e0b5a
|
7
|
+
data.tar.gz: d5ad8dd2673d588d9f0d2b32b0a2cac492a62787d6ddc600f14731d8fc24096ca8fa42376ce23e1ff4a0f4777af9b8f0d7d929bbc9927b4fb1dcdae929bced97
|
data/.rubocop.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -3,9 +3,14 @@
|
|
3
3
|
### 2.0.2 - 2017-05-02
|
4
4
|
|
5
5
|
* bug fixes
|
6
|
-
*
|
6
|
+
* validate html response
|
7
7
|
|
8
8
|
### 2.0.3 - 2017-05-02
|
9
9
|
|
10
10
|
* bug fixes
|
11
|
-
*
|
11
|
+
* skip expect if prefix not match
|
12
|
+
|
13
|
+
### 2.1.0 - 2017-06-19
|
14
|
+
|
15
|
+
* features
|
16
|
+
* add resource_white_list config
|
data/README.md
CHANGED
@@ -131,7 +131,7 @@ Prefix of API requests. Example: `'/api'`. Validation will not be performed if t
|
|
131
131
|
|
132
132
|
### white_list
|
133
133
|
|
134
|
-
Default: all
|
134
|
+
Default: all paths. This is an array of paths that are mandatory for implementation.
|
135
135
|
This list does not affect the work of the matcher.
|
136
136
|
This list is only for the report in the console.
|
137
137
|
|
@@ -146,6 +146,22 @@ config.white_list = {
|
|
146
146
|
|
147
147
|
Empty array `[]` means all methods.
|
148
148
|
|
149
|
+
### resource_white_list
|
150
|
+
|
151
|
+
Default: all resources. This is an array of resources that are mandatory for implementation.
|
152
|
+
This list does not affect the work of the matcher.
|
153
|
+
This list is only for the report in the console.
|
154
|
+
|
155
|
+
```ruby
|
156
|
+
config.resource_white_list = {
|
157
|
+
'/users' => ['DELETE /users/{id}', 'POST /users', 'GET /users/{id}', 'PATCH /users/{id}'],
|
158
|
+
'/users/{id}/employees' => ['GET /users/{id}/employees'],
|
159
|
+
'/sessions' => []
|
160
|
+
}
|
161
|
+
```
|
162
|
+
|
163
|
+
Empty array `[]` means all methods.
|
164
|
+
|
149
165
|
## Contributing
|
150
166
|
|
151
167
|
Bug reports and pull requests are welcome on GitHub at https://github.com/funbox/fitting. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
|
data/fitting.gemspec
CHANGED
@@ -21,7 +21,7 @@ Gem::Specification.new do |spec|
|
|
21
21
|
|
22
22
|
spec.add_runtime_dependency 'json-schema', '~> 2.6', '>= 2.6.2'
|
23
23
|
spec.add_runtime_dependency 'multi_json'
|
24
|
-
spec.add_runtime_dependency 'tomograph', '~> 1.
|
24
|
+
spec.add_runtime_dependency 'tomograph', '~> 1.1', '>= 1.1.0'
|
25
25
|
spec.add_development_dependency 'bundler', '~> 1.12'
|
26
26
|
spec.add_development_dependency 'rake', '~> 10.0'
|
27
27
|
spec.add_development_dependency 'byebug', '~> 8.2', '>= 8.2.1'
|
@@ -10,8 +10,7 @@ module Fitting
|
|
10
10
|
def black
|
11
11
|
if @white_list
|
12
12
|
all.select do |response|
|
13
|
-
|
14
|
-
data[1] && !@white_list[data[1]] || (@white_list[data[1]] != [] && !@white_list[data[1]].include?(data[0]))
|
13
|
+
black?(response)
|
15
14
|
end
|
16
15
|
else
|
17
16
|
[]
|
@@ -21,8 +20,7 @@ module Fitting
|
|
21
20
|
def white
|
22
21
|
if @white_list
|
23
22
|
all.select do |response|
|
24
|
-
|
25
|
-
data[1] && @white_list[data[1]] && (@white_list[data[1]] == [] || @white_list[data[1]].include?(data[0]))
|
23
|
+
white?(response)
|
26
24
|
end
|
27
25
|
else
|
28
26
|
all
|
@@ -30,19 +28,31 @@ module Fitting
|
|
30
28
|
end
|
31
29
|
|
32
30
|
def all
|
33
|
-
@all ||= @tomogram.to_hash.
|
34
|
-
|
35
|
-
responses[response['status']] ||= 0
|
36
|
-
responses[response['status']] += 1
|
37
|
-
responses
|
38
|
-
end.map do |status, indexes|
|
31
|
+
@all ||= @tomogram.to_hash.each_with_object([]) do |request, routes|
|
32
|
+
responses(request).map do |status, indexes|
|
39
33
|
indexes.times do |index|
|
40
34
|
route = "#{request['method']}\t#{request['path']} #{status} #{index}"
|
41
35
|
routes.push(route)
|
42
36
|
end
|
43
37
|
end
|
44
|
-
routes
|
45
38
|
end.uniq
|
46
39
|
end
|
40
|
+
|
41
|
+
def responses(request)
|
42
|
+
request['responses'].each_with_object({}) do |response, responses|
|
43
|
+
responses[response['status']] ||= 0
|
44
|
+
responses[response['status']] += 1
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def black?(response)
|
49
|
+
data = response.split(' ')
|
50
|
+
data[1] && !@white_list[data[1]] || (@white_list[data[1]] != [] && !@white_list[data[1]].include?(data[0]))
|
51
|
+
end
|
52
|
+
|
53
|
+
def white?(response)
|
54
|
+
data = response.split(' ')
|
55
|
+
data[1] && @white_list[data[1]] && (@white_list[data[1]] == [] || @white_list[data[1]].include?(data[0]))
|
56
|
+
end
|
47
57
|
end
|
48
58
|
end
|
@@ -27,12 +27,12 @@ module Fitting
|
|
27
27
|
"got: #{@response.real_request_with_status}"
|
28
28
|
end
|
29
29
|
|
30
|
-
|
31
|
-
|
30
|
+
return nil if @response.fully_validates.valid?
|
31
|
+
|
32
|
+
"response does not conform to json-schema\n"\
|
32
33
|
"schemas: \n#{@response.expected}\n\n"\
|
33
34
|
"got: #{@response.got}\n\n"\
|
34
35
|
"errors: \n#{@response.fully_validates}\n"
|
35
|
-
end
|
36
36
|
end
|
37
37
|
end
|
38
38
|
|
@@ -55,12 +55,12 @@ module Fitting
|
|
55
55
|
"got: #{@response.real_request_with_status}"
|
56
56
|
end
|
57
57
|
|
58
|
-
|
59
|
-
|
58
|
+
return nil if @response.strict_fully_validates.valid?
|
59
|
+
|
60
|
+
"response does not conform to json-schema\n"\
|
60
61
|
"schemas: \n#{@response.expected}\n\n"\
|
61
62
|
"got: #{@response.got}\n\n"\
|
62
63
|
"errors: \n#{@response.strict_fully_validates}\n"
|
63
|
-
end
|
64
64
|
end
|
65
65
|
end
|
66
66
|
|
data/lib/fitting/request.rb
CHANGED
@@ -19,10 +19,10 @@ module Fitting
|
|
19
19
|
end
|
20
20
|
|
21
21
|
def schemas_of_possible_responses(status:)
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
22
|
+
return nil unless @schema
|
23
|
+
|
24
|
+
@schema.find_responses(status: status).map do |response|
|
25
|
+
response['body']
|
26
26
|
end
|
27
27
|
end
|
28
28
|
|
data/lib/fitting/response.rb
CHANGED
@@ -46,7 +46,7 @@ module Fitting
|
|
46
46
|
|
47
47
|
def expected
|
48
48
|
@expected ||= @schemas.inject([]) do |res, schema|
|
49
|
-
res.push(
|
49
|
+
res.push(JSON.pretty_generate(schema).to_s)
|
50
50
|
end.join("\n\n")
|
51
51
|
end
|
52
52
|
|
@@ -54,17 +54,13 @@ module Fitting
|
|
54
54
|
|
55
55
|
def index
|
56
56
|
@schemas.size.times do |i|
|
57
|
-
if fully_validates[i] == []
|
58
|
-
return i
|
59
|
-
end
|
57
|
+
return i if fully_validates[i] == []
|
60
58
|
end
|
61
59
|
end
|
62
60
|
|
63
61
|
def strict_index
|
64
62
|
@schemas.size.times do |i|
|
65
|
-
if strict_fully_validates[i] == []
|
66
|
-
return i
|
67
|
-
end
|
63
|
+
return i if strict_fully_validates[i] == []
|
68
64
|
end
|
69
65
|
end
|
70
66
|
end
|
@@ -22,12 +22,12 @@ module Fitting
|
|
22
22
|
@to_s ||= join("\n\n")
|
23
23
|
end
|
24
24
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
JSON::
|
29
|
-
|
30
|
-
|
25
|
+
class << self
|
26
|
+
def fully_validate(schema, body, strict)
|
27
|
+
JSON::Validator.fully_validate(schema, body, strict: strict)
|
28
|
+
rescue JSON::Schema::UriError
|
29
|
+
[]
|
30
|
+
end
|
31
31
|
end
|
32
32
|
end
|
33
33
|
end
|
data/lib/fitting/route.rb
CHANGED
@@ -10,14 +10,15 @@ module Fitting
|
|
10
10
|
@responses = Fitting::Route::Responses.new(routes, @coverage)
|
11
11
|
end
|
12
12
|
|
13
|
-
def statistics
|
14
|
-
[@requests.statistics, @responses.statistics].join("\n\n")
|
15
|
-
end
|
16
|
-
|
17
13
|
def statistics_with_conformity_lists
|
18
|
-
|
14
|
+
congratulation = 'All responses are 100% valid! Great job!' if @coverage.not_coverage.empty?
|
19
15
|
|
20
|
-
[
|
16
|
+
[
|
17
|
+
@requests.conformity_lists,
|
18
|
+
@requests.statistics,
|
19
|
+
@responses.statistics,
|
20
|
+
congratulation
|
21
|
+
].compact.join("\n\n")
|
21
22
|
end
|
22
23
|
|
23
24
|
def errors
|
@@ -1,147 +1,24 @@
|
|
1
1
|
require 'multi_json'
|
2
|
+
require 'fitting/route/requests/statistics'
|
3
|
+
require 'fitting/route/requests/lists'
|
4
|
+
require 'fitting/route/requests/coverage'
|
5
|
+
require 'fitting/route/requests/combine'
|
2
6
|
|
3
7
|
module Fitting
|
4
8
|
class Route
|
5
9
|
class Requests
|
6
10
|
def initialize(coverage)
|
7
|
-
@
|
8
|
-
|
9
|
-
|
10
|
-
def coverage_statistic
|
11
|
-
stat = {}
|
12
|
-
@coverage.coverage.map do |route|
|
13
|
-
macro_key = route.split(' ')[0..1].join(' ')
|
14
|
-
micro_key = route.split(' ')[2..3].join(' ')
|
15
|
-
stat[macro_key] ||= {}
|
16
|
-
stat[macro_key]['cover'] ||= []
|
17
|
-
stat[macro_key]['not_cover'] ||= []
|
18
|
-
stat[macro_key]['cover'].push(micro_key)
|
19
|
-
stat[macro_key]['all'] ||= []
|
20
|
-
stat[macro_key]['all'].push("✔ #{route.split(' ')[2..3].join(' ')}")
|
21
|
-
end
|
22
|
-
@coverage.not_coverage.map do |route|
|
23
|
-
macro_key = route.split(' ')[0..1].join(' ')
|
24
|
-
micro_key = route.split(' ')[2..3].join(' ')
|
25
|
-
stat[macro_key] ||= {}
|
26
|
-
stat[macro_key]['cover'] ||= []
|
27
|
-
stat[macro_key]['not_cover'] ||= []
|
28
|
-
stat[macro_key]['not_cover'].push(micro_key)
|
29
|
-
stat[macro_key]['all'] ||= []
|
30
|
-
stat[macro_key]['all'].push("✖ #{route.split(' ')[2..3].join(' ')}")
|
31
|
-
end
|
32
|
-
@stat = stat.inject(
|
33
|
-
{
|
34
|
-
'full cover' => [],
|
35
|
-
'partial cover' => [],
|
36
|
-
'no cover' => []
|
37
|
-
}
|
38
|
-
) do |res, date|
|
39
|
-
ratio = date.last['cover_ratio'] =
|
40
|
-
(date.last['cover'].size.to_f /
|
41
|
-
(date.last['cover'].size + date.last['not_cover'].size).to_f * 100.0).round(2)
|
42
|
-
info = {date.first => {
|
43
|
-
'cover' => date.last['cover'],
|
44
|
-
'not_cover' => date.last['not_cover'],
|
45
|
-
'all' => "#{beautiful_output(date.last)}"
|
46
|
-
}}
|
47
|
-
if ratio == 100.0
|
48
|
-
res['full cover'].push(info)
|
49
|
-
elsif ratio == 0.0
|
50
|
-
res['no cover'].push(info)
|
51
|
-
else
|
52
|
-
res['partial cover'].push(info)
|
53
|
-
end
|
54
|
-
path = date.first.split(' ')[1].size / 8
|
55
|
-
@max ||= 1
|
56
|
-
if path.size > @max
|
57
|
-
@max = path.size
|
58
|
-
end
|
59
|
-
res
|
60
|
-
end
|
61
|
-
end
|
62
|
-
|
63
|
-
def to_hash
|
64
|
-
@stat ||= coverage_statistic
|
65
|
-
end
|
66
|
-
|
67
|
-
def fully_implemented
|
68
|
-
@stat ||= coverage_statistic
|
69
|
-
@fully_implemented ||= @stat['full cover'].map do |response|
|
70
|
-
"#{response.first.to_a.first.split(' ').join("\t")}#{"\t"*(@max-response.first.to_a.first.split(' ')[1].size/8)}#{response.first.to_a.last['all']}"
|
71
|
-
end.sort do |first, second|
|
72
|
-
first.split("\t")[1] <=> second.split("\t")[1]
|
73
|
-
end
|
74
|
-
end
|
75
|
-
|
76
|
-
def partially_implemented
|
77
|
-
@stat ||= coverage_statistic
|
78
|
-
@partially_implemented ||= @stat['partial cover'].map do |response|
|
79
|
-
"#{response.first.to_a.first.split(' ').join("\t")}#{"\t"*(@max-response.first.to_a.first.split(' ')[1].size/8)}#{response.first.to_a.last['all']}"
|
80
|
-
end.sort do |first, second|
|
81
|
-
first.split("\t")[1] <=> second.split("\t")[1]
|
82
|
-
end
|
83
|
-
end
|
84
|
-
|
85
|
-
def no_implemented
|
86
|
-
@stat ||= coverage_statistic
|
87
|
-
@no_implemented ||= @stat['no cover'].map do |response|
|
88
|
-
"#{response.first.to_a.first.split(' ').join("\t")}#{"\t"*(@max-response.first.to_a.first.split(' ')[1].size/8)}#{response.first.to_a.last['all']}"
|
89
|
-
end.sort do |first, second|
|
90
|
-
first.split("\t")[1] <=> second.split("\t")[1]
|
91
|
-
end
|
92
|
-
end
|
93
|
-
|
94
|
-
def statistics
|
95
|
-
@stat ||= coverage_statistic
|
96
|
-
full_count = @stat.to_hash['full cover'].size
|
97
|
-
part_count = @stat.to_hash['partial cover'].size
|
98
|
-
no_count = @stat.to_hash['no cover'].size
|
99
|
-
total_count = full_count + part_count + no_count
|
100
|
-
full_percentage = (full_count.to_f / total_count.to_f * 100.0).round(2)
|
101
|
-
part_percentage = (part_count.to_f / total_count.to_f * 100.0).round(2)
|
102
|
-
no_percentage = (no_count.to_f / total_count.to_f * 100.0).round(2)
|
103
|
-
|
104
|
-
[
|
105
|
-
"API requests with fully implemented responses: #{full_count} (#{full_percentage}% of #{total_count}).",
|
106
|
-
"API requests with partially implemented responses: #{part_count} (#{part_percentage}% of #{total_count}).",
|
107
|
-
"API requests with no implemented responses: #{no_count} (#{no_percentage}% of #{total_count})."
|
108
|
-
].join("\n")
|
11
|
+
@combine = Fitting::Route::Requests::Combine.new(Fitting::Route::Requests::Coverage.new(coverage))
|
12
|
+
@lists = Fitting::Route::Requests::Lists.new(@combine)
|
13
|
+
@statistics = Fitting::Route::Requests::Statistics.new(@combine)
|
109
14
|
end
|
110
15
|
|
111
16
|
def conformity_lists
|
112
|
-
@
|
113
|
-
fully_implemented ||= self.fully_implemented.join("\n")
|
114
|
-
partially_implemented ||= self.partially_implemented.join("\n")
|
115
|
-
no_implemented ||= self.no_implemented.join("\n")
|
116
|
-
|
117
|
-
[
|
118
|
-
['Fully conforming requests:', fully_implemented].join("\n"),
|
119
|
-
['Partially conforming requests:', partially_implemented].join("\n"),
|
120
|
-
['Non-conforming requests:', no_implemented].join("\n")
|
121
|
-
].join("\n\n")
|
17
|
+
@lists.to_s
|
122
18
|
end
|
123
19
|
|
124
|
-
|
125
|
-
|
126
|
-
def beautiful_output(hash)
|
127
|
-
methods = {}
|
128
|
-
res = []
|
129
|
-
hash['cover'].map do |response|
|
130
|
-
method, index = response.split(' ')
|
131
|
-
methods[method] ||= []
|
132
|
-
methods[method][index.to_i] = {'method' => method, 'cover' => true}
|
133
|
-
end
|
134
|
-
hash['not_cover'].map do |response|
|
135
|
-
method, index = response.split(' ')
|
136
|
-
methods[method] ||= []
|
137
|
-
methods[method][index.to_i] = {'method' => method, 'cover' => false}
|
138
|
-
end
|
139
|
-
methods.map do |method|
|
140
|
-
method.last.size.times do |index|
|
141
|
-
res.push("#{method.last[index]['cover'] ? '✔' : '✖'} #{method.first}")
|
142
|
-
end
|
143
|
-
end
|
144
|
-
res.join(' ')
|
20
|
+
def statistics
|
21
|
+
@statistics.to_s
|
145
22
|
end
|
146
23
|
end
|
147
24
|
end
|
@@ -0,0 +1,113 @@
|
|
1
|
+
module Fitting
|
2
|
+
class Route
|
3
|
+
class Requests
|
4
|
+
class Combine
|
5
|
+
def initialize(stat)
|
6
|
+
@stat = stat
|
7
|
+
@full_cover = []
|
8
|
+
@partial_cover = []
|
9
|
+
@no_cover = []
|
10
|
+
end
|
11
|
+
|
12
|
+
def to_hash
|
13
|
+
stat_each
|
14
|
+
@to_hash ||= {
|
15
|
+
'full cover' => @full_cover,
|
16
|
+
'partial cover' => @partial_cover,
|
17
|
+
'no cover' => @no_cover
|
18
|
+
}
|
19
|
+
end
|
20
|
+
|
21
|
+
def max
|
22
|
+
stat_each
|
23
|
+
@max
|
24
|
+
end
|
25
|
+
|
26
|
+
def full_cover
|
27
|
+
stat_each
|
28
|
+
@full_cover
|
29
|
+
end
|
30
|
+
|
31
|
+
def partial_cover
|
32
|
+
stat_each
|
33
|
+
@partial_cover
|
34
|
+
end
|
35
|
+
|
36
|
+
def no_cover
|
37
|
+
stat_each
|
38
|
+
@no_cover
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
def stat_each
|
44
|
+
@stat_each ||= @stat.to_hash.map do |date|
|
45
|
+
date.last['cover_ratio'] = ratio(date)
|
46
|
+
res_cover(ratio(date), info(date))
|
47
|
+
path = date.first.split(' ')[1].size / 8
|
48
|
+
find_max(path)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def find_max(path)
|
53
|
+
@max ||= 1
|
54
|
+
@max = path.size if path.size > @max
|
55
|
+
end
|
56
|
+
|
57
|
+
def res_cover(ratio, info)
|
58
|
+
if ratio == 100.0
|
59
|
+
@full_cover.push(info)
|
60
|
+
elsif ratio == 0.0
|
61
|
+
@no_cover.push(info)
|
62
|
+
else
|
63
|
+
@partial_cover.push(info)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def ratio(date)
|
68
|
+
(date.last['cover'].size.to_f /
|
69
|
+
(date.last['cover'].size + date.last['not_cover'].size).to_f * 100.0).round(2)
|
70
|
+
end
|
71
|
+
|
72
|
+
def info(date)
|
73
|
+
{ date.first => {
|
74
|
+
'cover' => date.last['cover'],
|
75
|
+
'not_cover' => date.last['not_cover'],
|
76
|
+
'all' => beautiful_output(date.last).to_s
|
77
|
+
} }
|
78
|
+
end
|
79
|
+
|
80
|
+
def beautiful_output(hash)
|
81
|
+
methods = {}
|
82
|
+
res = []
|
83
|
+
methods = beautiful_cover(hash, methods)
|
84
|
+
methods = beautiful_not_cover(hash, methods)
|
85
|
+
methods.map do |method|
|
86
|
+
method.last.size.times do |index|
|
87
|
+
res.push("#{method.last[index]['cover'] ? '✔' : '✖'} #{method.first}")
|
88
|
+
end
|
89
|
+
end
|
90
|
+
res.join(' ')
|
91
|
+
end
|
92
|
+
|
93
|
+
def beautiful_cover(hash, methods)
|
94
|
+
hash['cover'].map do |response|
|
95
|
+
method, index = response.split(' ')
|
96
|
+
methods[method] ||= []
|
97
|
+
methods[method][index.to_i] = { 'method' => method, 'cover' => true }
|
98
|
+
end
|
99
|
+
methods
|
100
|
+
end
|
101
|
+
|
102
|
+
def beautiful_not_cover(hash, methods)
|
103
|
+
hash['not_cover'].map do |response|
|
104
|
+
method, index = response.split(' ')
|
105
|
+
methods[method] ||= []
|
106
|
+
methods[method][index.to_i] = { 'method' => method, 'cover' => false }
|
107
|
+
end
|
108
|
+
methods
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module Fitting
|
2
|
+
class Route
|
3
|
+
class Requests
|
4
|
+
class Coverage
|
5
|
+
def initialize(coverage)
|
6
|
+
@coverage = coverage
|
7
|
+
end
|
8
|
+
|
9
|
+
def to_hash
|
10
|
+
stat = {}
|
11
|
+
@coverage.coverage.map do |route|
|
12
|
+
stat = coverage_stat(stat, route, '✔')
|
13
|
+
end
|
14
|
+
@coverage.not_coverage.map do |route|
|
15
|
+
stat = not_coverage_stat(stat, route, '✖')
|
16
|
+
end
|
17
|
+
stat
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def coverage_stat(stat, route, symbol)
|
23
|
+
macro_key = macro_key(route)
|
24
|
+
micro_key = micro_key(route)
|
25
|
+
stat = default_stat(stat, macro_key)
|
26
|
+
stat[macro_key]['cover'].push(micro_key)
|
27
|
+
stat[macro_key]['all'].push("#{symbol} #{route.split(' ')[2..3].join(' ')}")
|
28
|
+
stat
|
29
|
+
end
|
30
|
+
|
31
|
+
def not_coverage_stat(stat, route, symbol)
|
32
|
+
macro_key = macro_key(route)
|
33
|
+
micro_key = micro_key(route)
|
34
|
+
stat = default_stat(stat, macro_key)
|
35
|
+
stat[macro_key]['not_cover'].push(micro_key)
|
36
|
+
stat[macro_key]['all'].push("#{symbol} #{route.split(' ')[2..3].join(' ')}")
|
37
|
+
stat
|
38
|
+
end
|
39
|
+
|
40
|
+
def default_stat(stat, macro_key)
|
41
|
+
stat[macro_key] ||= {}
|
42
|
+
stat[macro_key]['cover'] ||= []
|
43
|
+
stat[macro_key]['not_cover'] ||= []
|
44
|
+
stat[macro_key]['all'] ||= []
|
45
|
+
stat
|
46
|
+
end
|
47
|
+
|
48
|
+
def macro_key(route)
|
49
|
+
route.split(' ')[0..1].join(' ')
|
50
|
+
end
|
51
|
+
|
52
|
+
def micro_key(route)
|
53
|
+
route.split(' ')[2..3].join(' ')
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
module Fitting
|
2
|
+
class Route
|
3
|
+
class Requests
|
4
|
+
class Lists
|
5
|
+
def initialize(combine)
|
6
|
+
@full_cover = combine.full_cover
|
7
|
+
@partial_cover = combine.partial_cover
|
8
|
+
@no_cover = combine.no_cover
|
9
|
+
@max = combine.max
|
10
|
+
end
|
11
|
+
|
12
|
+
def to_s
|
13
|
+
@to_s ||=
|
14
|
+
[
|
15
|
+
list_fully_implemented,
|
16
|
+
list_partially_implemented,
|
17
|
+
list_no_implemented
|
18
|
+
].compact.join("\n\n")
|
19
|
+
end
|
20
|
+
|
21
|
+
def list_fully_implemented
|
22
|
+
return nil if fully_implemented == []
|
23
|
+
['Fully conforming requests:', fully_implemented].join("\n")
|
24
|
+
end
|
25
|
+
|
26
|
+
def list_partially_implemented
|
27
|
+
return nil if partially_implemented == []
|
28
|
+
['Partially conforming requests:', partially_implemented].join("\n")
|
29
|
+
end
|
30
|
+
|
31
|
+
def list_no_implemented
|
32
|
+
return nil if no_implemented == []
|
33
|
+
['Non-conforming requests:', no_implemented].join("\n")
|
34
|
+
end
|
35
|
+
|
36
|
+
def fully_implemented
|
37
|
+
@fully_implemented ||= stat_full_cover.sort do |first, second|
|
38
|
+
first.split("\t")[1] <=> second.split("\t")[1]
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def partially_implemented
|
43
|
+
@partially_implemented ||= stat_partial_cover.sort do |first, second|
|
44
|
+
first.split("\t")[1] <=> second.split("\t")[1]
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def no_implemented
|
49
|
+
@no_implemented ||= stat_no_cover.sort do |first, second|
|
50
|
+
first.split("\t")[1] <=> second.split("\t")[1]
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
def stat_full_cover
|
57
|
+
@full_cover.map do |response|
|
58
|
+
cover_request(response)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def stat_partial_cover
|
63
|
+
@partial_cover.map do |response|
|
64
|
+
cover_request(response)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def stat_no_cover
|
69
|
+
@no_cover.map do |response|
|
70
|
+
cover_request(response)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def cover_request(response)
|
75
|
+
"#{cover_method(response)}#{tabulation(response)}#{cover_status(response)}"
|
76
|
+
end
|
77
|
+
|
78
|
+
def cover_method(response)
|
79
|
+
response.first.to_a.first.split(' ').join("\t")
|
80
|
+
end
|
81
|
+
|
82
|
+
def tabulation(response)
|
83
|
+
"\t" * (@max - response.first.to_a.first.split(' ')[1].size / 8)
|
84
|
+
end
|
85
|
+
|
86
|
+
def cover_status(response)
|
87
|
+
response.first.to_a.last['all']
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module Fitting
|
2
|
+
class Route
|
3
|
+
class Requests
|
4
|
+
class Statistics
|
5
|
+
def initialize(combine)
|
6
|
+
@full_count = combine.full_cover.size
|
7
|
+
@part_count = combine.partial_cover.size
|
8
|
+
@no_count = combine.no_cover.size
|
9
|
+
end
|
10
|
+
|
11
|
+
def to_s
|
12
|
+
@to_s ||= [
|
13
|
+
"API requests with fully implemented responses: #{@full_count} (#{full_percent}% of #{total_count}).",
|
14
|
+
"API requests with partially implemented responses: #{@part_count} (#{part_percent}% of #{total_count}).",
|
15
|
+
"API requests with no implemented responses: #{@no_count} (#{no_percent}% of #{total_count})."
|
16
|
+
].join("\n")
|
17
|
+
end
|
18
|
+
|
19
|
+
def total_count
|
20
|
+
@total_count ||= @full_count + @part_count + @no_count
|
21
|
+
end
|
22
|
+
|
23
|
+
def full_percent
|
24
|
+
@full_percentage ||= 0.0 if total_count.zero?
|
25
|
+
@full_percentage ||= (@full_count.to_f / total_count.to_f * 100.0).round(2)
|
26
|
+
end
|
27
|
+
|
28
|
+
def part_percent
|
29
|
+
@part_percentage ||= 0.0 if total_count.zero?
|
30
|
+
@part_percentage ||= (@part_count.to_f / total_count.to_f * 100.0).round(2)
|
31
|
+
end
|
32
|
+
|
33
|
+
def no_percent
|
34
|
+
@no_percentage ||= 0.0 if total_count.zero?
|
35
|
+
@no_percentage ||= (@no_count.to_f / total_count.to_f * 100.0).round(2)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -15,7 +15,8 @@ module Fitting
|
|
15
15
|
|
16
16
|
[
|
17
17
|
"API responses conforming to the blueprint: #{valid_count} (#{valid_percentage}% of #{total_count}).",
|
18
|
-
|
18
|
+
'API responses with validation errors or untested: '\
|
19
|
+
"#{invalid_count} (#{invalid_percentage}% of #{total_count})."
|
19
20
|
].join("\n")
|
20
21
|
end
|
21
22
|
end
|
data/lib/fitting/statistics.rb
CHANGED
@@ -10,7 +10,7 @@ module Fitting
|
|
10
10
|
end
|
11
11
|
|
12
12
|
def save
|
13
|
-
FileUtils
|
13
|
+
FileUtils.mkdir_p 'fitting'
|
14
14
|
File.open('fitting/stats', 'w') { |file| file.write(to_s) }
|
15
15
|
File.open('fitting/not_covered', 'w') { |file| file.write(@white_route.errors) }
|
16
16
|
end
|
@@ -20,7 +20,7 @@ module Fitting
|
|
20
20
|
[
|
21
21
|
['[Black list]', @black_route.statistics_with_conformity_lists].join("\n"),
|
22
22
|
['[White list]', @white_route.statistics_with_conformity_lists].join("\n"),
|
23
|
-
|
23
|
+
''
|
24
24
|
].join("\n\n")
|
25
25
|
else
|
26
26
|
[@white_route.statistics_with_conformity_lists, "\n\n"].join
|
@@ -13,7 +13,7 @@ module Fitting
|
|
13
13
|
Tomograph::Tomogram.new(
|
14
14
|
prefix: Fitting.configuration.prefix,
|
15
15
|
apib_path: Fitting.configuration.apib_path,
|
16
|
-
drafter_yaml_path: Fitting.configuration.drafter_yaml_path
|
16
|
+
drafter_yaml_path: Fitting.configuration.drafter_yaml_path
|
17
17
|
)
|
18
18
|
end
|
19
19
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'fitting/storage/white_list'
|
2
|
+
|
1
3
|
module Fitting
|
2
4
|
module Storage
|
3
5
|
class Responses
|
@@ -9,18 +11,32 @@ module Fitting
|
|
9
11
|
@responses.push(
|
10
12
|
Fitting::Response.new(
|
11
13
|
response,
|
12
|
-
Fitting::Storage::Documentation.tomogram
|
14
|
+
Fitting::Storage::Documentation.tomogram
|
15
|
+
)
|
16
|
+
)
|
13
17
|
end
|
14
18
|
|
15
19
|
def statistics
|
20
|
+
@white_list = white_list
|
16
21
|
Fitting::Statistics.new(
|
17
22
|
Fitting::Documentation.new(
|
18
23
|
Fitting::Storage::Documentation.tomogram,
|
19
|
-
|
24
|
+
@white_list
|
25
|
+
),
|
20
26
|
@responses.uniq,
|
21
27
|
Fitting.configuration.strict
|
22
28
|
)
|
23
29
|
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def white_list
|
34
|
+
Fitting::Storage::WhiteList.new(
|
35
|
+
Fitting.configuration.white_list,
|
36
|
+
Fitting.configuration.resource_white_list,
|
37
|
+
Fitting::Storage::Documentation.tomogram.to_resources
|
38
|
+
).to_a
|
39
|
+
end
|
24
40
|
end
|
25
41
|
end
|
26
42
|
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'tomograph'
|
2
|
+
|
3
|
+
module Fitting
|
4
|
+
module Storage
|
5
|
+
class WhiteList
|
6
|
+
def initialize(white_list, resource_white_list, resources)
|
7
|
+
@white_list = white_list
|
8
|
+
@resource_white_list = resource_white_list
|
9
|
+
@resources = resources
|
10
|
+
end
|
11
|
+
|
12
|
+
def to_a
|
13
|
+
return @white_list if @white_list
|
14
|
+
@white_list = transformation
|
15
|
+
end
|
16
|
+
|
17
|
+
def without_group
|
18
|
+
@resource_white_list.inject([]) do |all_requests, asd|
|
19
|
+
if asd[1] == []
|
20
|
+
requests(@resources[asd[0]], all_requests)
|
21
|
+
else
|
22
|
+
requests(asd[1], all_requests)
|
23
|
+
end
|
24
|
+
end.flatten.uniq
|
25
|
+
end
|
26
|
+
|
27
|
+
def requests(resource, all_requests)
|
28
|
+
resource.map do |request|
|
29
|
+
all_requests.push(request_hash(request))
|
30
|
+
end
|
31
|
+
all_requests
|
32
|
+
end
|
33
|
+
|
34
|
+
def transformation
|
35
|
+
result = without_group.group_by { |action| action[:path] }
|
36
|
+
result.inject({}) do |res, group|
|
37
|
+
methods = group.last.map { |gr| gr[:method] }
|
38
|
+
res.merge(group.first => methods)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def request_hash(request)
|
43
|
+
array = request.split(' ')
|
44
|
+
{
|
45
|
+
method: array[0],
|
46
|
+
path: Tomograph::Path.new(array[1]).to_s
|
47
|
+
}
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
data/lib/fitting/version.rb
CHANGED
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: 2.0
|
4
|
+
version: 2.1.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-
|
11
|
+
date: 2017-06-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: json-schema
|
@@ -50,20 +50,20 @@ dependencies:
|
|
50
50
|
requirements:
|
51
51
|
- - "~>"
|
52
52
|
- !ruby/object:Gem::Version
|
53
|
-
version: '1.
|
53
|
+
version: '1.1'
|
54
54
|
- - ">="
|
55
55
|
- !ruby/object:Gem::Version
|
56
|
-
version: 1.
|
56
|
+
version: 1.1.0
|
57
57
|
type: :runtime
|
58
58
|
prerelease: false
|
59
59
|
version_requirements: !ruby/object:Gem::Requirement
|
60
60
|
requirements:
|
61
61
|
- - "~>"
|
62
62
|
- !ruby/object:Gem::Version
|
63
|
-
version: '1.
|
63
|
+
version: '1.1'
|
64
64
|
- - ">="
|
65
65
|
- !ruby/object:Gem::Version
|
66
|
-
version: 1.
|
66
|
+
version: 1.1.0
|
67
67
|
- !ruby/object:Gem::Dependency
|
68
68
|
name: bundler
|
69
69
|
requirement: !ruby/object:Gem::Requirement
|
@@ -201,10 +201,15 @@ files:
|
|
201
201
|
- lib/fitting/route.rb
|
202
202
|
- lib/fitting/route/coverage.rb
|
203
203
|
- lib/fitting/route/requests.rb
|
204
|
+
- lib/fitting/route/requests/combine.rb
|
205
|
+
- lib/fitting/route/requests/coverage.rb
|
206
|
+
- lib/fitting/route/requests/lists.rb
|
207
|
+
- lib/fitting/route/requests/statistics.rb
|
204
208
|
- lib/fitting/route/responses.rb
|
205
209
|
- lib/fitting/statistics.rb
|
206
210
|
- lib/fitting/storage/documentation.rb
|
207
211
|
- lib/fitting/storage/responses.rb
|
212
|
+
- lib/fitting/storage/white_list.rb
|
208
213
|
- lib/fitting/version.rb
|
209
214
|
homepage: https://github.com/funbox/fitting
|
210
215
|
licenses:
|