fitting 3.0.2 → 4.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/.ruby-version +1 -1
- data/CHANGELOG.md +16 -0
- data/README.md +327 -108
- data/fitting.gemspec +2 -2
- data/images/b1.png +0 -0
- data/images/b2.png +0 -0
- data/images/w1.png +0 -0
- data/images/w2.png +0 -0
- data/lib/fitting/action.rb +105 -0
- data/lib/fitting/cover/json_schema.rb +2 -2
- data/lib/fitting/cover/json_schema_one_of.rb +4 -2
- data/lib/fitting/debug.rb +47 -0
- data/lib/fitting/doc/action.rb +141 -0
- data/lib/fitting/doc/code.rb +53 -0
- data/lib/fitting/doc/combination_enum.rb +110 -0
- data/lib/fitting/doc/combination_one_of.rb +61 -0
- data/lib/fitting/doc/combination_optional.rb +54 -0
- data/lib/fitting/doc/combination_step.rb +48 -0
- data/lib/fitting/doc/content_type.rb +152 -0
- data/lib/fitting/doc/json_schema.rb +112 -0
- data/lib/fitting/doc/step.rb +102 -0
- data/lib/fitting/doc.rb +107 -0
- data/lib/fitting/host.rb +37 -0
- data/lib/fitting/log.rb +102 -0
- data/lib/fitting/nocov.rb +68 -0
- data/lib/fitting/prefix.rb +62 -0
- data/lib/fitting/railtie.rb +0 -1
- data/lib/fitting/records/spherical/request.rb +7 -4
- data/lib/fitting/records/spherical/response.rb +22 -16
- data/lib/fitting/records/tested/request.rb +6 -1
- data/lib/fitting/records/tested/response.rb +6 -1
- data/lib/fitting/rep/html.rb +32 -0
- data/lib/fitting/rep.rb +24 -0
- data/lib/fitting/report/action.rb +9 -15
- data/lib/fitting/report/actions.rb +22 -33
- data/lib/fitting/report/combination.rb +10 -6
- data/lib/fitting/report/combinations.rb +9 -29
- data/lib/fitting/report/prefix.rb +7 -24
- data/lib/fitting/report/prefixes.rb +11 -25
- data/lib/fitting/report/response.rb +12 -22
- data/lib/fitting/report/responses.rb +23 -27
- data/lib/fitting/report/tests.rb +4 -8
- data/lib/fitting/skip/action.rb +44 -0
- data/lib/fitting/skip/api.rb +29 -0
- data/lib/fitting/skip.rb +21 -0
- data/lib/fitting/version.rb +1 -1
- data/lib/fitting.rb +12 -28
- data/lib/tasks/fitting.rake +23 -84
- data/lib/templates/htmlcss/bootstrap-nightshade.min.css +12 -0
- data/lib/templates/htmlcss/bootstrap.min.js +7 -0
- data/lib/templates/htmlcss/darkmode.min.js +6 -0
- data/lib/templates/htmlcss/fitting.html +196 -0
- data/lib/templates/htmlcss/jquery-3.6.0.min.js +2 -0
- metadata +40 -39
- data/lib/fitting/configuration.rb +0 -17
- data/lib/fitting/records/spherical/requests.rb +0 -25
- data/lib/fitting/storage/responses.rb +0 -21
- data/lib/fitting/tests.rb +0 -31
- data/lib/tasks/fitting_outgoing.rake +0 -91
- data/lib/templates/bomboniere/.gitignore +0 -21
- data/lib/templates/bomboniere/.tool-versions +0 -1
- data/lib/templates/bomboniere/README.md +0 -19
- data/lib/templates/bomboniere/dist/css/app.aa2bcd8a.css +0 -1
- data/lib/templates/bomboniere/dist/css/chunk-vendors.ec5f6c3f.css +0 -1
- data/lib/templates/bomboniere/dist/favicon.ico +0 -0
- data/lib/templates/bomboniere/dist/index.html +0 -1
- data/lib/templates/bomboniere/dist/js/app.e5f1a5ec.js +0 -2
- data/lib/templates/bomboniere/dist/js/app.e5f1a5ec.js.map +0 -1
- data/lib/templates/bomboniere/dist/js/chunk-vendors.0f99b670.js +0 -13
- data/lib/templates/bomboniere/dist/js/chunk-vendors.0f99b670.js.map +0 -1
- data/lib/templates/bomboniere/package-lock.json +0 -9292
- data/lib/templates/bomboniere/package.json +0 -27
- data/lib/templates/bomboniere/public/favicon.ico +0 -0
- data/lib/templates/bomboniere/public/index.html +0 -17
- data/lib/templates/bomboniere/src/App.vue +0 -102
- data/lib/templates/bomboniere/src/assets/logo.png +0 -0
- data/lib/templates/bomboniere/src/components/HelloWorld.vue +0 -204
- data/lib/templates/bomboniere/src/main.js +0 -10
- data/lib/templates/bomboniere/src/router/index.js +0 -31
- data/lib/templates/bomboniere/src/views/About.vue +0 -5
- data/lib/templates/bomboniere/src/views/Action.vue +0 -173
- data/lib/templates/bomboniere/src/views/Home.vue +0 -17
- data/lib/templates/bomboniere/vue.config.js +0 -3
@@ -0,0 +1,105 @@
|
|
1
|
+
require 'fitting/host'
|
2
|
+
require 'fitting/prefix'
|
3
|
+
require 'fitting/report/prefixes'
|
4
|
+
|
5
|
+
module Fitting
|
6
|
+
class Action
|
7
|
+
class Skip < RuntimeError; end
|
8
|
+
class Empty < RuntimeError; end
|
9
|
+
class NotFound < RuntimeError
|
10
|
+
attr_reader :log
|
11
|
+
|
12
|
+
def initialize(msg, log)
|
13
|
+
@log = log
|
14
|
+
super(msg)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def initialize(action)
|
19
|
+
@action = action
|
20
|
+
end
|
21
|
+
|
22
|
+
def action
|
23
|
+
@action
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.all
|
27
|
+
[]
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.find!(actions, log)
|
31
|
+
host = Fitting::Host.find!(log)
|
32
|
+
host.cover!
|
33
|
+
|
34
|
+
prefix = Fitting::Prefix.find(host: host, log: log)
|
35
|
+
prefix.cover!
|
36
|
+
|
37
|
+
new(prefix.actions.find!(log))
|
38
|
+
rescue Fitting::Report::Actions::Empty,
|
39
|
+
Fitting::Host::Skip,
|
40
|
+
Fitting::Prefix::Skip
|
41
|
+
raise Skip
|
42
|
+
rescue Fitting::Report::Combinations::Empty,
|
43
|
+
Fitting::Report::Combinations::NotFound
|
44
|
+
raise Skip
|
45
|
+
rescue Fitting::Host::NotFound,
|
46
|
+
Fitting::Prefix::NotFound,
|
47
|
+
Fitting::Report::Actions::NotFound,
|
48
|
+
Fitting::Report::Responses::NotFound => e
|
49
|
+
raise NotFound.new(e.message, log)
|
50
|
+
end
|
51
|
+
|
52
|
+
def cover!(log)
|
53
|
+
action.cover!
|
54
|
+
|
55
|
+
response = action.responses.find!(log)
|
56
|
+
response.cover!
|
57
|
+
|
58
|
+
combination = response.combinations.find!(log)
|
59
|
+
combination.cover!
|
60
|
+
|
61
|
+
print "\e[32m.\e[0m"
|
62
|
+
rescue Fitting::Report::Actions::Empty,
|
63
|
+
Fitting::Host::Skip,
|
64
|
+
Fitting::Prefix::Skip
|
65
|
+
rescue Fitting::Report::Combinations::Empty,
|
66
|
+
Fitting::Report::Combinations::NotFound
|
67
|
+
rescue Fitting::Host::NotFound,
|
68
|
+
Fitting::Prefix::NotFound,
|
69
|
+
Fitting::Report::Actions::NotFound,
|
70
|
+
Fitting::Report::Responses::NotFound => e
|
71
|
+
end
|
72
|
+
|
73
|
+
def self.report(actions)
|
74
|
+
all = 0
|
75
|
+
cover = 0
|
76
|
+
|
77
|
+
# prefixes.to_a.map do |prefix|
|
78
|
+
# all += 1
|
79
|
+
# break unless prefix.cover?
|
80
|
+
# cover += 1
|
81
|
+
# prefix.actions.to_a.map do |action|
|
82
|
+
# all += 1
|
83
|
+
# break unless action.cover?
|
84
|
+
# cover += 1
|
85
|
+
# action.responses.to_a.map do |response|
|
86
|
+
# all += 1
|
87
|
+
# break unless action.cover?
|
88
|
+
# cover += 1
|
89
|
+
# response.combinations.to_a.map do |combination|
|
90
|
+
# all += 1
|
91
|
+
# break unless combination.cover?
|
92
|
+
# cover += 1
|
93
|
+
# end
|
94
|
+
# end
|
95
|
+
# end
|
96
|
+
# end
|
97
|
+
|
98
|
+
puts
|
99
|
+
puts "Coverage #{(cover.to_f / all.to_f * 100).round(2)}%"
|
100
|
+
exit 1 unless false
|
101
|
+
|
102
|
+
exit 0
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
@@ -17,12 +17,12 @@ module Fitting
|
|
17
17
|
|
18
18
|
def inception(json_schema, combinations)
|
19
19
|
json_schema.each do |key, value|
|
20
|
-
if (key == 'properties') && (json_schema['required'] != value.keys)
|
20
|
+
if (key == 'properties') && value && (json_schema['required'] != value.keys)
|
21
21
|
schema = json_schema.dup
|
22
22
|
one_of = schema.delete('required') || []
|
23
23
|
schema['properties'].each_key do |property|
|
24
24
|
next if one_of.include?(property)
|
25
|
-
|
25
|
+
next if property == 'type' || property == 'properties'
|
26
26
|
combinations.push([schema.merge('required' => one_of + [property]), "required.#{property}"])
|
27
27
|
end
|
28
28
|
elsif value.is_a?(Hash)
|
@@ -10,7 +10,7 @@ module Fitting
|
|
10
10
|
|
11
11
|
def combi
|
12
12
|
inception(json_schema, combinations).each do |combination|
|
13
|
-
combination[0] =
|
13
|
+
combination[0] = combination[0]
|
14
14
|
combination[1] = ['one_of', combination[1]]
|
15
15
|
end
|
16
16
|
end
|
@@ -21,7 +21,9 @@ module Fitting
|
|
21
21
|
schema = json_schema.dup
|
22
22
|
one_of = schema.delete('oneOf')
|
23
23
|
one_of.each_index do |index|
|
24
|
-
|
24
|
+
schema_new = json_schema.dup
|
25
|
+
schema_new.delete('oneOf')
|
26
|
+
combinations.push([schema_new.merge(one_of[index]), "oneOf.#{index}"])
|
25
27
|
end
|
26
28
|
elsif value.is_a?(Hash)
|
27
29
|
com = inception(value, [])
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module Fitting
|
2
|
+
class Debug
|
3
|
+
attr_accessor :host, :method, :path, :status, :content_type
|
4
|
+
|
5
|
+
def initialize(host, method, path, code, content_type)
|
6
|
+
@host = host
|
7
|
+
@method = method
|
8
|
+
@path = path
|
9
|
+
@status = code.to_s
|
10
|
+
@content_type = content_type
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.save!(docs, yaml)
|
14
|
+
return unless yaml['Debug']
|
15
|
+
yaml['Debug'].map do |action|
|
16
|
+
debug = new(action['host'], action['method'], action['path'], action['code'], action['content-type'])
|
17
|
+
res = Fitting::Doc.debug(docs, debug)
|
18
|
+
index = res.next_steps.first.index_before
|
19
|
+
next_steps_first = res.next_steps.first
|
20
|
+
res_debug = report(index, next_steps_first, res.logs)
|
21
|
+
File.open('coverage/.fitting.debug.yml', 'w') { |file| file.write(YAML.dump(res_debug)) }
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.report(index, next_steps_first, res_logs)
|
26
|
+
return {} if index.nil?
|
27
|
+
combinations = []
|
28
|
+
next_steps_first.next_steps.each do |next_step|
|
29
|
+
combinations.push(
|
30
|
+
next_step.debug_report(index)
|
31
|
+
)
|
32
|
+
end
|
33
|
+
res_debug = {
|
34
|
+
"json_schema" => next_steps_first.to_hash,
|
35
|
+
"jsons" => res_logs,
|
36
|
+
"valid_jsons" => next_steps_first.logs,
|
37
|
+
"index_before" => next_steps_first.index_before - index,
|
38
|
+
"index_medium" => next_steps_first.index_medium - index,
|
39
|
+
"index_after" => next_steps_first.index_after - index,
|
40
|
+
"res_before" => next_steps_first.res_before.map{|r| r ? r : "null"}[index..-1],
|
41
|
+
"res_medium" => next_steps_first.res_medium.map{|r| r ? r : "null"}[index..-1],
|
42
|
+
"res_after" => next_steps_first.res_after.map{|r| r ? r : "null"}[index..-1],
|
43
|
+
"combinations" => combinations
|
44
|
+
}
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,141 @@
|
|
1
|
+
require 'fitting/doc/code'
|
2
|
+
require 'fitting/debug'
|
3
|
+
|
4
|
+
module Fitting
|
5
|
+
class Doc
|
6
|
+
class Action
|
7
|
+
attr_accessor :host, :prefix, :path, :method, :responses
|
8
|
+
|
9
|
+
class NotFound < RuntimeError; end
|
10
|
+
|
11
|
+
def initialize(host, prefix, method, path, responses)
|
12
|
+
@host = host
|
13
|
+
@prefix = prefix
|
14
|
+
@method = method
|
15
|
+
@path = path
|
16
|
+
@path.slice!(prefix)
|
17
|
+
@responses = []
|
18
|
+
responses.group_by { |response| response['status'] }.each do |code, value|
|
19
|
+
@responses.push(Code.new(code, value))
|
20
|
+
end
|
21
|
+
@key = "#{method} #{host}#{prefix}#{path}"
|
22
|
+
|
23
|
+
@host_cover = 0
|
24
|
+
@prefix_cover = 0
|
25
|
+
@path_cover = 0
|
26
|
+
@method_cover = 0
|
27
|
+
end
|
28
|
+
|
29
|
+
def to_hash_lock
|
30
|
+
res = YAML.dump(
|
31
|
+
{
|
32
|
+
host => {
|
33
|
+
prefix => {
|
34
|
+
path => {
|
35
|
+
method => @responses.inject({}) { |sum, response| sum.merge!(response) }
|
36
|
+
}
|
37
|
+
}
|
38
|
+
}
|
39
|
+
}
|
40
|
+
).split("\n")
|
41
|
+
{ @key => res[1..-1] }
|
42
|
+
end
|
43
|
+
|
44
|
+
def to_hash
|
45
|
+
res = [
|
46
|
+
@host_cover,
|
47
|
+
@prefix_cover,
|
48
|
+
@path_cover,
|
49
|
+
@method_cover
|
50
|
+
]
|
51
|
+
(to_hash_lock[@key].size - 4).times { res.push(nil) }
|
52
|
+
|
53
|
+
if @method_cover != nil && @method_cover != 0
|
54
|
+
code_index = 4
|
55
|
+
@responses.each do |code|
|
56
|
+
res, code_index = code.report(res, code_index)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
{ @key => res }
|
61
|
+
end
|
62
|
+
|
63
|
+
def cover!(log)
|
64
|
+
unless log.host == host
|
65
|
+
return
|
66
|
+
end
|
67
|
+
@host_cover += 1
|
68
|
+
|
69
|
+
unless prefix.size == 0 || log.path[0..prefix.size - 1] == prefix
|
70
|
+
return
|
71
|
+
end
|
72
|
+
@prefix_cover += 1
|
73
|
+
|
74
|
+
unless path_match(log.path)
|
75
|
+
return
|
76
|
+
end
|
77
|
+
@path_cover += 1
|
78
|
+
|
79
|
+
unless log.method == method
|
80
|
+
return
|
81
|
+
end
|
82
|
+
@method_cover += 1
|
83
|
+
|
84
|
+
@responses.each { |response| response.cover!(log) }
|
85
|
+
|
86
|
+
true
|
87
|
+
rescue Fitting::Doc::Code::NotFound => e
|
88
|
+
raise NotFound.new "\n\ntype: #{@type}\n"\
|
89
|
+
"host: #{@host}\n" \
|
90
|
+
"prefix: #{@prefix}\n" \
|
91
|
+
"method: #{@method}\n" \
|
92
|
+
"path: #{@path}\n" \
|
93
|
+
"#{e.message}"
|
94
|
+
end
|
95
|
+
|
96
|
+
def debug(debug)
|
97
|
+
unless debug.host == host
|
98
|
+
return nil
|
99
|
+
end
|
100
|
+
|
101
|
+
unless prefix.size == 0 || debug.path[0..prefix.size - 1] == prefix
|
102
|
+
return nil
|
103
|
+
end
|
104
|
+
|
105
|
+
unless path_match(debug.path)
|
106
|
+
return nil
|
107
|
+
end
|
108
|
+
|
109
|
+
unless debug.method == method
|
110
|
+
return nil
|
111
|
+
end
|
112
|
+
|
113
|
+
@responses.each do |response|
|
114
|
+
res = response.debug(debug)
|
115
|
+
return res if res
|
116
|
+
end
|
117
|
+
nil
|
118
|
+
end
|
119
|
+
|
120
|
+
def nocover!
|
121
|
+
@host_cover = nil
|
122
|
+
@prefix_cover = nil
|
123
|
+
@path_cover = nil
|
124
|
+
@method_cover = nil
|
125
|
+
end
|
126
|
+
|
127
|
+
def path_match(find_path)
|
128
|
+
regexp =~ find_path
|
129
|
+
end
|
130
|
+
|
131
|
+
def regexp
|
132
|
+
return @regexp if @regexp
|
133
|
+
|
134
|
+
str = Regexp.escape(@prefix + path)
|
135
|
+
str = str.gsub(/\\{\w+\\}/, '[^&=\/]+')
|
136
|
+
str = "\\A#{str}\\z"
|
137
|
+
@regexp = Regexp.new(str)
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'fitting/doc/step'
|
2
|
+
require 'fitting/doc/content_type'
|
3
|
+
|
4
|
+
module Fitting
|
5
|
+
class Doc
|
6
|
+
class Code < Step
|
7
|
+
class NotFound < RuntimeError; end
|
8
|
+
|
9
|
+
def initialize(code, value)
|
10
|
+
@step_cover_size = 0
|
11
|
+
@step_key = code
|
12
|
+
@next_steps = []
|
13
|
+
value.group_by { |val| val['content-type'] }.each do |content_type, subvalue|
|
14
|
+
break if content_type == nil
|
15
|
+
@next_steps.push(ContentType.new(content_type, subvalue))
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def to_hash
|
20
|
+
if @next_steps.size == 1 && @next_steps[0].step_key == nil
|
21
|
+
{
|
22
|
+
@step_key => {}
|
23
|
+
}
|
24
|
+
else
|
25
|
+
{
|
26
|
+
@step_key => @next_steps.inject({}) { |sum, value| sum.merge!(value) }
|
27
|
+
}
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def cover!(log)
|
32
|
+
if @step_key == log.status
|
33
|
+
@step_cover_size += 1
|
34
|
+
@next_steps.each { |content_type| content_type.cover!(log) }
|
35
|
+
end
|
36
|
+
rescue Fitting::Doc::ContentType::NotFound => e
|
37
|
+
raise NotFound.new "code: #{@step_key}\n\n"\
|
38
|
+
"#{e.message}"
|
39
|
+
end
|
40
|
+
|
41
|
+
def debug(debug)
|
42
|
+
if @step_key == debug.status
|
43
|
+
@next_steps.each do |content_type|
|
44
|
+
res = content_type.debug(debug)
|
45
|
+
return res if res
|
46
|
+
end
|
47
|
+
nil
|
48
|
+
end
|
49
|
+
nil
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,110 @@
|
|
1
|
+
require 'fitting/doc/combination_step'
|
2
|
+
require 'fitting/cover/json_schema_enum'
|
3
|
+
|
4
|
+
module Fitting
|
5
|
+
class Doc
|
6
|
+
class CombinationEnum < CombinationStep
|
7
|
+
def cover!(log)
|
8
|
+
error = JSON::Validator.fully_validate(@json_schema, log.body)
|
9
|
+
if error == []
|
10
|
+
@step_cover_size += 1
|
11
|
+
@logs.push(log.body)
|
12
|
+
nil
|
13
|
+
else
|
14
|
+
bodies = custom_body(log.body)
|
15
|
+
errors = []
|
16
|
+
bodies.each do |body|
|
17
|
+
error = JSON::Validator.fully_validate(@json_schema, body)
|
18
|
+
if error == []
|
19
|
+
@step_cover_size += 1
|
20
|
+
@logs.push(body)
|
21
|
+
nil
|
22
|
+
else
|
23
|
+
errors.push(
|
24
|
+
{
|
25
|
+
combination: @step_key,
|
26
|
+
type: @type,
|
27
|
+
json_schema: @json_schema,
|
28
|
+
error: error,
|
29
|
+
body: body
|
30
|
+
})
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
if errors.size == bodies.size
|
35
|
+
errors
|
36
|
+
else
|
37
|
+
nil
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def find_keys
|
43
|
+
new_keys = []
|
44
|
+
if YAML.dump(@json_schema).include?("array")
|
45
|
+
res = @json_schema
|
46
|
+
@step_key.split('.').each do |key|
|
47
|
+
if res[key].class == Hash && res[key] != nil
|
48
|
+
if res[key].class == Hash && res[key]['type'] == 'array'
|
49
|
+
new_keys.push(key)
|
50
|
+
return new_keys
|
51
|
+
else
|
52
|
+
if key != 'properties'
|
53
|
+
new_keys.push(key)
|
54
|
+
end
|
55
|
+
res = res[key]
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
[]
|
61
|
+
end
|
62
|
+
|
63
|
+
def custom_body(body)
|
64
|
+
bodies = []
|
65
|
+
keys = find_keys
|
66
|
+
if keys != []
|
67
|
+
if keys.size == 1
|
68
|
+
if body[keys[0]].size == 1
|
69
|
+
return body
|
70
|
+
else
|
71
|
+
body[keys[0]].each do |bb|
|
72
|
+
new_body = body.dup
|
73
|
+
new_body.delete(keys[0])
|
74
|
+
bodies.push(new_body.merge(keys[0] => bb))
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
return bodies
|
79
|
+
end
|
80
|
+
[body]
|
81
|
+
end
|
82
|
+
|
83
|
+
def report(res, index)
|
84
|
+
@index_before = index
|
85
|
+
@res_before = [] + res
|
86
|
+
@index_medium = index
|
87
|
+
@res_medium = [] + res
|
88
|
+
|
89
|
+
combinations = @step_key.split('.')
|
90
|
+
elements = YAML.dump(@source_json_schema).split("\n")[1..-1]
|
91
|
+
|
92
|
+
res_index = 0
|
93
|
+
element_index = 0
|
94
|
+
elements.each_with_index do |element, i|
|
95
|
+
if element.include?(combinations[element_index])
|
96
|
+
element_index += 1
|
97
|
+
end
|
98
|
+
if combinations.size == element_index
|
99
|
+
res_index = i
|
100
|
+
break
|
101
|
+
end
|
102
|
+
end
|
103
|
+
res[res_index + index] = @step_cover_size
|
104
|
+
@index_after = res_index + index
|
105
|
+
@res_after = [] + res
|
106
|
+
[res, index]
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require 'fitting/doc/combination_step'
|
2
|
+
require 'fitting/cover/json_schema_enum'
|
3
|
+
require 'fitting/doc/combination_enum'
|
4
|
+
require 'fitting/cover/json_schema'
|
5
|
+
require 'fitting/doc/combination_optional'
|
6
|
+
|
7
|
+
module Fitting
|
8
|
+
class Doc
|
9
|
+
class CombinationOneOf < CombinationStep
|
10
|
+
def initialize_combinations(combination, json_schema)
|
11
|
+
combinations = Fitting::Cover::JSONSchemaEnum.new(@json_schema).combi
|
12
|
+
if combinations.size > 1
|
13
|
+
combinations.each do |comb|
|
14
|
+
@next_steps.push(CombinationEnum.new(comb[0], "#{comb[1][0]}", "#{comb[1][1]}", json_schema))
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
combinations = Fitting::Cover::JSONSchema.new(@json_schema).combi
|
19
|
+
combinations.each do |comb|
|
20
|
+
@next_steps.push(CombinationOptional.new(comb[0], "#{type}.#{comb[1][0]}", "#{combination}.#{comb[1][1]}", json_schema))
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def cover!(log)
|
25
|
+
if JSON::Validator.fully_validate(@json_schema, log.body) == []
|
26
|
+
@step_cover_size += 1
|
27
|
+
@logs.push(log.body)
|
28
|
+
errors = @next_steps.inject([]) do |sum, combination|
|
29
|
+
sum.push(combination.cover!(log))
|
30
|
+
end.flatten.compact
|
31
|
+
|
32
|
+
return if @next_steps.size == 0
|
33
|
+
return unless @next_steps.size == errors.size
|
34
|
+
|
35
|
+
error_message = ""
|
36
|
+
errors.each do |error|
|
37
|
+
error_message += "combination: #{error[:combination]}\n"\
|
38
|
+
"combination type: #{error[:type]}\n"\
|
39
|
+
"combination json-schema: #{::JSON.pretty_generate(error[:json_schema])}\n"\
|
40
|
+
"combination error #{::JSON.pretty_generate(error[:error])}\n"\
|
41
|
+
"combination body #{::JSON.pretty_generate(error[:body])}\n"
|
42
|
+
end
|
43
|
+
|
44
|
+
# raise NotFound.new "#{error_message}\n"\
|
45
|
+
# "source body: #{::JSON.pretty_generate(log.body)}"
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def mark_range(index, res)
|
50
|
+
res[index + 1] = @step_cover_size
|
51
|
+
if @json_schema && @json_schema["required"]
|
52
|
+
mark_required(index + 1, res, @json_schema)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def index_offset
|
57
|
+
YAML.dump(@json_schema['properties']).split("\n").size + YAML.dump(@json_schema['required']).split("\n").size
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'fitting/doc/combination_step'
|
2
|
+
require 'fitting/cover/json_schema_enum'
|
3
|
+
|
4
|
+
module Fitting
|
5
|
+
class Doc
|
6
|
+
class CombinationOptional < CombinationStep
|
7
|
+
def cover!(log)
|
8
|
+
error = JSON::Validator.fully_validate(@json_schema, log.body)
|
9
|
+
if error == []
|
10
|
+
@step_cover_size += 1
|
11
|
+
@logs.push(log.body)
|
12
|
+
nil
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def report(res, index)
|
17
|
+
@index_before = index
|
18
|
+
@res_before = [] + res
|
19
|
+
@index_medium = index
|
20
|
+
@res_medium = [] + res
|
21
|
+
|
22
|
+
combinations = @step_key.split('.')
|
23
|
+
elements = YAML.dump(@source_json_schema).split("\n")[1..-1]
|
24
|
+
res_index = 0
|
25
|
+
elements.each_with_index do |element, i|
|
26
|
+
if element.include?(combinations[-1])
|
27
|
+
res_index = i
|
28
|
+
break
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
res[res_index + index] = @step_cover_size
|
33
|
+
res[res_index + index + 1] = @step_cover_size
|
34
|
+
|
35
|
+
if @source_json_schema["properties"][combinations[-1]] && @source_json_schema["properties"][combinations[-1]].class == Hash && @source_json_schema["properties"][combinations[-1]]["type"] == "array"
|
36
|
+
res[res_index + index + 2] = @step_cover_size
|
37
|
+
res[res_index + index + 3] = @step_cover_size
|
38
|
+
res[res_index + index + 4] = @step_cover_size
|
39
|
+
|
40
|
+
schema = @source_json_schema["properties"][combinations[-1]]['items']
|
41
|
+
mark_required(res_index + index + 4, res, schema)
|
42
|
+
elsif @source_json_schema["properties"][combinations[-1]] && @source_json_schema["properties"][combinations[-1]].class == Hash && @source_json_schema["properties"][combinations[-1]]["type"] == "object"
|
43
|
+
schema = @source_json_schema["properties"][combinations[-1]]
|
44
|
+
res[res_index + index + 2] = @step_cover_size
|
45
|
+
mark_required(res_index + index + 2, res, schema)
|
46
|
+
end
|
47
|
+
|
48
|
+
@index_after = res_index + index + 4
|
49
|
+
@res_after = [] + res
|
50
|
+
[res, index]
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'fitting/doc/step'
|
2
|
+
|
3
|
+
module Fitting
|
4
|
+
class Doc
|
5
|
+
class CombinationStep < Step
|
6
|
+
class NotFound < RuntimeError; end
|
7
|
+
|
8
|
+
attr_accessor :json_schema, :type, :logs
|
9
|
+
|
10
|
+
def initialize(json_schema, type, combination, source_json_schema)
|
11
|
+
@logs = []
|
12
|
+
@step_cover_size = 0
|
13
|
+
@json_schema = json_schema
|
14
|
+
@next_steps = []
|
15
|
+
@type = type
|
16
|
+
@step_key = combination
|
17
|
+
@source_json_schema = source_json_schema
|
18
|
+
initialize_combinations(combination, json_schema)
|
19
|
+
end
|
20
|
+
|
21
|
+
def initialize_combinations(combination, json_schema)
|
22
|
+
end
|
23
|
+
|
24
|
+
def debug_report(index)
|
25
|
+
combinations = []
|
26
|
+
@next_steps.each do |next_step|
|
27
|
+
combinations.push(
|
28
|
+
next_step.debug_report(index)
|
29
|
+
)
|
30
|
+
end
|
31
|
+
return {} if index.nil? || index_before.nil?
|
32
|
+
{
|
33
|
+
"combination type" => @type,
|
34
|
+
"combination" => @step_key,
|
35
|
+
"json_schema" => @json_schema,
|
36
|
+
"valid_jsons" => @logs,
|
37
|
+
"index_before" => @index_before - index,
|
38
|
+
"index_medium" => @index_medium - index,
|
39
|
+
"index_after" => @index_after - index,
|
40
|
+
"res_before" => @res_before.map { |r| r ? r : "null" }[index..-1],
|
41
|
+
"res_medium" => @res_medium.map { |r| r ? r : "null" }[index..-1],
|
42
|
+
"res_after" => @res_after.map { |r| r ? r : "null" }[index..-1],
|
43
|
+
"combinations" => combinations
|
44
|
+
}
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|