fitting 3.0.2 → 4.0.0
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 +9 -0
- data/README.md +272 -109
- 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 +116 -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 +64 -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,116 @@
|
|
1
|
+
require 'fitting/doc/step'
|
2
|
+
require 'fitting/doc/combination_one_of'
|
3
|
+
require 'fitting/cover/json_schema_one_of'
|
4
|
+
require 'fitting/doc/combination_enum'
|
5
|
+
require 'fitting/cover/json_schema_enum'
|
6
|
+
require 'fitting/cover/json_schema'
|
7
|
+
require 'fitting/doc/combination_optional'
|
8
|
+
require 'json'
|
9
|
+
require 'json-schema'
|
10
|
+
|
11
|
+
module Fitting
|
12
|
+
class Doc
|
13
|
+
class JsonSchema < Step
|
14
|
+
class NotFound < RuntimeError; end
|
15
|
+
|
16
|
+
def initialize(json_schema, super_schema)
|
17
|
+
@super_schema = super_schema
|
18
|
+
@logs = []
|
19
|
+
@step_cover_size = 0
|
20
|
+
@step_key = json_schema
|
21
|
+
@next_steps = []
|
22
|
+
@oneOf = false
|
23
|
+
Fitting::Cover::JSONSchemaOneOf.new(json_schema).combi.each do |combination|
|
24
|
+
@oneOf = true
|
25
|
+
@next_steps.push(CombinationOneOf.new(combination[0], combination[1][0], combination[1][1], json_schema))
|
26
|
+
end
|
27
|
+
combinations = Fitting::Cover::JSONSchemaEnum.new(json_schema).combi
|
28
|
+
if combinations.size > 1
|
29
|
+
combinations.each do |comb|
|
30
|
+
@next_steps.push(CombinationEnum.new(comb[0], comb[1][0], comb[1][1], json_schema))
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
if json_schema['type'] != 'array'
|
35
|
+
combinations = Fitting::Cover::JSONSchema.new(json_schema).combi
|
36
|
+
combinations.each do |comb|
|
37
|
+
@next_steps.push(CombinationOptional.new(comb[0], comb[1][0], comb[1][1], json_schema))
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def cover!(log)
|
43
|
+
if @super_schema
|
44
|
+
@step_cover_size += 1
|
45
|
+
@logs.push(log.body)
|
46
|
+
@next_steps.each { |combination| combination.cover!(log) }
|
47
|
+
elsif JSON::Validator.fully_validate(@step_key, log.body) == []
|
48
|
+
@step_cover_size += 1
|
49
|
+
@logs.push(log.body)
|
50
|
+
@next_steps.each { |combination| combination.cover!(log) }
|
51
|
+
else
|
52
|
+
raise Fitting::Doc::JsonSchema::NotFound.new "json-schema: #{::JSON.pretty_generate(@step_key)}\n\n"\
|
53
|
+
"body: #{::JSON.pretty_generate(log.body)}\n\n"\
|
54
|
+
"error #{::JSON.pretty_generate(JSON::Validator.fully_validate(@step_key, log.body))}"
|
55
|
+
end
|
56
|
+
rescue JSON::Schema::SchemaError => e
|
57
|
+
raise Fitting::Doc::JsonSchema::NotFound.new "json-schema: #{::JSON.pretty_generate(@step_key)}\n\n"\
|
58
|
+
"body: #{::JSON.pretty_generate(log.body)}\n\n"\
|
59
|
+
"error #{e.message}"
|
60
|
+
rescue Fitting::Doc::CombinationOneOf::NotFound => e
|
61
|
+
raise Fitting::Doc::JsonSchema::NotFound.new "#{e.message}\n\nsource json-schema: #{::JSON.pretty_generate(@step_key)}\n\n"
|
62
|
+
end
|
63
|
+
|
64
|
+
def logs
|
65
|
+
@logs
|
66
|
+
end
|
67
|
+
|
68
|
+
def mark_range(index, res)
|
69
|
+
start_index = index
|
70
|
+
end_index = start_index + 2
|
71
|
+
|
72
|
+
(start_index..end_index).each do |i|
|
73
|
+
res[i] = @step_cover_size
|
74
|
+
end
|
75
|
+
|
76
|
+
if @step_key["required"]
|
77
|
+
mark_required(end_index, res, @step_key)
|
78
|
+
end
|
79
|
+
end_index
|
80
|
+
end
|
81
|
+
|
82
|
+
def nocover!
|
83
|
+
@step_cover_size = nil
|
84
|
+
end
|
85
|
+
|
86
|
+
def to_hash
|
87
|
+
@step_key
|
88
|
+
end
|
89
|
+
|
90
|
+
def report(res, index)
|
91
|
+
@index_before = index
|
92
|
+
@res_before = [] + res
|
93
|
+
|
94
|
+
index = mark_range(index, res)
|
95
|
+
@index_medium = index
|
96
|
+
@res_medium = [] + res
|
97
|
+
|
98
|
+
if @next_steps != []
|
99
|
+
new_index = index
|
100
|
+
@next_steps.each do |next_step|
|
101
|
+
if @oneOf
|
102
|
+
res, new_index = next_step.report(res, new_index)
|
103
|
+
else
|
104
|
+
res, new_index = next_step.report(res, @index_before)
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
index += index_offset
|
110
|
+
@index_after = index
|
111
|
+
@res_after = [] + res
|
112
|
+
[res, index]
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
module Fitting
|
2
|
+
class Doc
|
3
|
+
class Step
|
4
|
+
attr_accessor :step_cover_size, :step_key, :next_steps, :index_before, :index_medium, :index_after, :res_before, :res_medium, :res_after
|
5
|
+
|
6
|
+
def to_hash
|
7
|
+
{
|
8
|
+
@step_key => @next_steps.inject({}) { |sum, value| sum.merge!(value) }
|
9
|
+
}
|
10
|
+
end
|
11
|
+
|
12
|
+
def nocover!
|
13
|
+
@step_cover_size = nil
|
14
|
+
@next_steps.each do |next_step|
|
15
|
+
next_step.nocover!
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def report(res, index)
|
20
|
+
@index_before = index
|
21
|
+
@res_before = [] + res
|
22
|
+
|
23
|
+
mark_range(index, res)
|
24
|
+
@index_medium = index
|
25
|
+
@res_medium = [] + res
|
26
|
+
|
27
|
+
if @next_steps != []
|
28
|
+
new_index = index + new_index_offset
|
29
|
+
@next_steps.each do |next_step|
|
30
|
+
if self.class == Fitting::Doc::CombinationOneOf
|
31
|
+
res, _new_index = next_step.report(res, new_index - 2)
|
32
|
+
else
|
33
|
+
res, new_index = next_step.report(res, new_index)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
index += index_offset
|
39
|
+
@index_after = index
|
40
|
+
@res_after = [] + res
|
41
|
+
[res, index]
|
42
|
+
end
|
43
|
+
|
44
|
+
def mark_range(index, res)
|
45
|
+
res[index] = @step_cover_size
|
46
|
+
if @json_schema && @json_schema["required"]
|
47
|
+
mark_required(index, res, @json_schema)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def new_index_offset
|
52
|
+
1
|
53
|
+
end
|
54
|
+
|
55
|
+
def index_offset
|
56
|
+
YAML.dump(@next_steps.inject({}) { |sum, value| sum.merge!(value) }).split("\n").size
|
57
|
+
end
|
58
|
+
|
59
|
+
def mark_required(index, res, schema)
|
60
|
+
start_index = index + YAML.dump(schema["properties"]).split("\n").size
|
61
|
+
end_index = start_index + YAML.dump(schema["required"]).split("\n").size - 1
|
62
|
+
(start_index..end_index).each do |i|
|
63
|
+
res[i] = @step_cover_size
|
64
|
+
end
|
65
|
+
|
66
|
+
return if schema["required"].nil?
|
67
|
+
|
68
|
+
schema["required"].each do |required|
|
69
|
+
required_index = YAML.dump(schema["properties"]).split("\n").index { |key| key == "#{required}:" }
|
70
|
+
break if required_index.nil?
|
71
|
+
res[index + required_index] = @step_cover_size
|
72
|
+
res[index + required_index + 1] = @step_cover_size
|
73
|
+
res[index + required_index + 2] = @step_cover_size if schema["properties"][required]["type"] == "string" && schema["properties"][required]["enum"]
|
74
|
+
if schema["properties"][required]["type"] == "object"
|
75
|
+
res[index + required_index + 2] = @step_cover_size
|
76
|
+
new_index = index + required_index + 2
|
77
|
+
mark_required(new_index, res, schema["properties"][required])
|
78
|
+
elsif schema["properties"][required]["type"] == "string" && schema["properties"][required]["enum"]
|
79
|
+
new_index = index + required_index + 2
|
80
|
+
mark_enum(new_index, res, schema["properties"][required])
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def mark_enum(index, res, schema)
|
86
|
+
if schema["enum"].size == 1
|
87
|
+
res[index] = @step_cover_size
|
88
|
+
res[index + 1] = @step_cover_size
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def valid?
|
93
|
+
end
|
94
|
+
|
95
|
+
def range
|
96
|
+
end
|
97
|
+
|
98
|
+
def next
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
data/lib/fitting/doc.rb
ADDED
@@ -0,0 +1,107 @@
|
|
1
|
+
require 'fitting/doc/action'
|
2
|
+
require 'tomograph'
|
3
|
+
|
4
|
+
module Fitting
|
5
|
+
class Doc
|
6
|
+
class NotFound < RuntimeError; end
|
7
|
+
|
8
|
+
def self.all
|
9
|
+
apis = YAML.safe_load(File.read('.fitting.yml'))['APIs']
|
10
|
+
return [] unless apis
|
11
|
+
apis.map do |api|
|
12
|
+
if api['type'] == 'openapi2'
|
13
|
+
Tomograph::Tomogram.new(prefix: api['prefix'] || '', openapi2_json_path: api['path']).to_a.map do |action|
|
14
|
+
Fitting::Doc::Action.new(
|
15
|
+
api['host'],
|
16
|
+
api['prefix'] || '',
|
17
|
+
action.to_hash['method'],
|
18
|
+
action.to_hash['path'].path,
|
19
|
+
action.responses
|
20
|
+
)
|
21
|
+
end
|
22
|
+
elsif api['type'] == 'openapi3'
|
23
|
+
Tomograph::Tomogram.new(prefix: api['prefix'] || '', openapi3_yaml_path: api['path']).to_a.map do |action|
|
24
|
+
Fitting::Doc::Action.new(
|
25
|
+
api['host'],
|
26
|
+
api['prefix'] || '',
|
27
|
+
action.to_hash['method'],
|
28
|
+
action.to_hash['path'].path,
|
29
|
+
action.responses
|
30
|
+
)
|
31
|
+
end
|
32
|
+
elsif api['type'] == 'drafter'
|
33
|
+
Tomograph::Tomogram.new(prefix: api['prefix'] || '', drafter_yaml_path: api['path']).to_a.map do |action|
|
34
|
+
Fitting::Doc::Action.new(
|
35
|
+
api['host'],
|
36
|
+
api['prefix'] || '',
|
37
|
+
action.to_hash['method'],
|
38
|
+
action.to_hash['path'].path,
|
39
|
+
action.responses
|
40
|
+
)
|
41
|
+
end
|
42
|
+
elsif api['type'] == 'crafter'
|
43
|
+
Tomograph::Tomogram.new(prefix: api['prefix'] || '', crafter_yaml_path: api['path']).to_a.map do |action|
|
44
|
+
Fitting::Doc::Action.new(
|
45
|
+
api['host'],
|
46
|
+
api['prefix'] || '',
|
47
|
+
action.to_hash['method'],
|
48
|
+
action.to_hash['path'].path,
|
49
|
+
action.responses
|
50
|
+
)
|
51
|
+
end
|
52
|
+
elsif api['type'] == 'tomogram'
|
53
|
+
Tomograph::Tomogram.new(prefix: api['prefix'] || '', tomogram_json_path: api['path']).to_a.map do |action|
|
54
|
+
Fitting::Doc::Action.new(
|
55
|
+
api['host'],
|
56
|
+
api['prefix'] || '',
|
57
|
+
action.to_hash['method'],
|
58
|
+
action.to_hash['path'].path,
|
59
|
+
action.responses
|
60
|
+
)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end.flatten
|
64
|
+
end
|
65
|
+
|
66
|
+
def self.cover!(docs, log)
|
67
|
+
docs.each do |doc|
|
68
|
+
return if doc.cover!(log)
|
69
|
+
end
|
70
|
+
raise NotFound.new "log: #{log.method} #{log.host} #{log.url} #{log.status}"
|
71
|
+
rescue Fitting::Doc::Action::NotFound => e
|
72
|
+
raise NotFound.new "log error: #{e.message}"
|
73
|
+
end
|
74
|
+
|
75
|
+
def self.debug(docs, debug)
|
76
|
+
docs.each do |doc|
|
77
|
+
res = doc.debug(debug)
|
78
|
+
return res if res
|
79
|
+
end
|
80
|
+
raise NotFound
|
81
|
+
end
|
82
|
+
|
83
|
+
def self.report(docs)
|
84
|
+
all = 0
|
85
|
+
cov = 0
|
86
|
+
docs.each do |provid|
|
87
|
+
provid.to_hash.values.first.each do |prov|
|
88
|
+
if prov == nil
|
89
|
+
break
|
90
|
+
elsif prov == 0
|
91
|
+
all += 1
|
92
|
+
elsif prov > 0
|
93
|
+
all += 1
|
94
|
+
cov += 1
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
res = (cov.to_f / all.to_f * 100).round(2)
|
99
|
+
puts "Coverage: #{res}%"
|
100
|
+
if res == 100.00
|
101
|
+
exit 0
|
102
|
+
else
|
103
|
+
exit 1
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
data/lib/fitting/host.rb
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
module Fitting
|
2
|
+
class Host
|
3
|
+
class Skip < RuntimeError; end
|
4
|
+
class NotFound < RuntimeError
|
5
|
+
attr_reader :log
|
6
|
+
def initialize(msg, log)
|
7
|
+
@log = log
|
8
|
+
super(msg)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def initialize(host, skip)
|
13
|
+
@host = host
|
14
|
+
@skip = skip
|
15
|
+
@cover = false
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.find!(log)
|
19
|
+
yaml = YAML.safe_load(File.read('.fitting.yml'))
|
20
|
+
yaml['hosts']&.map do |host|
|
21
|
+
if log.host == host.first.first
|
22
|
+
return new(host.first.first, host.first.last)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
raise NotFound.new(log.host, log)
|
26
|
+
end
|
27
|
+
|
28
|
+
def cover!
|
29
|
+
@cover = true
|
30
|
+
raise Skip if @skip == 'skip'
|
31
|
+
end
|
32
|
+
|
33
|
+
def to_s
|
34
|
+
@host
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
data/lib/fitting/log.rb
ADDED
@@ -0,0 +1,102 @@
|
|
1
|
+
module Fitting
|
2
|
+
class Log
|
3
|
+
def initialize(log, type)
|
4
|
+
@log = log
|
5
|
+
@type = type
|
6
|
+
@error = nil
|
7
|
+
@skip = false
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.all
|
11
|
+
logs = []
|
12
|
+
Dir["log/fitting*.log"].each do |file_path|
|
13
|
+
testlog = File.read(file_path)
|
14
|
+
testlog.split("\n").select { |f| f.include?('incoming request ') }.each do |test|
|
15
|
+
logs.push(new(JSON.load(test.split('incoming request ')[1]), 'incoming'))
|
16
|
+
end
|
17
|
+
testlog.split("\n").select { |f| f.include?('outgoing request ') }.each do |test|
|
18
|
+
logs.push(new(JSON.load(test.split('outgoing request ')[1]), 'outgoing'))
|
19
|
+
end
|
20
|
+
end
|
21
|
+
logs.sort { |a, b| b.path <=> a.path }
|
22
|
+
end
|
23
|
+
|
24
|
+
def url
|
25
|
+
"#{host}#{path}"
|
26
|
+
end
|
27
|
+
|
28
|
+
def path
|
29
|
+
@log['path']
|
30
|
+
end
|
31
|
+
|
32
|
+
def method
|
33
|
+
@log['method']
|
34
|
+
end
|
35
|
+
|
36
|
+
def status
|
37
|
+
@log['response']['status'].to_s
|
38
|
+
end
|
39
|
+
|
40
|
+
def body
|
41
|
+
@log['response']['body']
|
42
|
+
end
|
43
|
+
|
44
|
+
def content_type
|
45
|
+
@log['response']['content_type']
|
46
|
+
end
|
47
|
+
|
48
|
+
def host
|
49
|
+
@log['host'] || 'www.example.com'
|
50
|
+
end
|
51
|
+
|
52
|
+
def type
|
53
|
+
@type
|
54
|
+
end
|
55
|
+
|
56
|
+
def access!
|
57
|
+
print "\e[32m.\e[0m"
|
58
|
+
end
|
59
|
+
|
60
|
+
def pending!
|
61
|
+
@skip = true
|
62
|
+
print "\e[33m*\e[0m"
|
63
|
+
end
|
64
|
+
|
65
|
+
def failure!(error)
|
66
|
+
@error = error
|
67
|
+
print "\e[31mF\e[0m"
|
68
|
+
end
|
69
|
+
|
70
|
+
def error
|
71
|
+
@error
|
72
|
+
end
|
73
|
+
|
74
|
+
def failure?
|
75
|
+
@error.present?
|
76
|
+
end
|
77
|
+
|
78
|
+
def self.failure(logs)
|
79
|
+
logs.select do |log|
|
80
|
+
log.failure?
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def pending?
|
85
|
+
@skip
|
86
|
+
end
|
87
|
+
|
88
|
+
def self.pending(logs)
|
89
|
+
logs.select do |log|
|
90
|
+
log.pending?
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def self.report(logs)
|
95
|
+
puts "\n\n"
|
96
|
+
Fitting::Log.failure(logs).each_with_index do |log, index|
|
97
|
+
puts "\e[31m #{index + 1}) #{log.error.class} #{log.error.message}\n\n\e[0m"
|
98
|
+
end
|
99
|
+
print "\e[31m#{logs.size} examples, #{Fitting::Log.failure(logs).size} failure, #{Fitting::Log.pending(logs).size} pending\e[0m\n"
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
module Fitting
|
2
|
+
class NoCov
|
3
|
+
class NotFound < RuntimeError; end
|
4
|
+
|
5
|
+
def initialize(host, method, path, code, content_type, combination, combination_next)
|
6
|
+
@host = host
|
7
|
+
@method = method
|
8
|
+
@path = path
|
9
|
+
@code = code
|
10
|
+
@content_type = content_type
|
11
|
+
@combination = combination
|
12
|
+
@combination_next = combination_next
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.all(yaml)
|
16
|
+
return [] unless yaml['NoCov']
|
17
|
+
yaml['NoCov'].map do |action|
|
18
|
+
new(action['host'], action['method'], action['path'], action['code'], action['content-type'], action['combination'], action['combination_next'])
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def find(docs)
|
23
|
+
res = docs.find do |action|
|
24
|
+
action.host == @host && action.method == @method && action.path_match(@path)
|
25
|
+
end
|
26
|
+
|
27
|
+
if @code == nil
|
28
|
+
return res if res.present?
|
29
|
+
raise NotFound.new("host: #{@host}, method: #{@method}, path: #{@path}")
|
30
|
+
end
|
31
|
+
|
32
|
+
res_code = res.responses.find { |response| response.step_key == @code.to_s }
|
33
|
+
|
34
|
+
if @content_type == nil
|
35
|
+
return res_code if res_code.present?
|
36
|
+
raise NotFound.new("host: #{@host}, method: #{@method}, path: #{@path}, code: #{@code}")
|
37
|
+
end
|
38
|
+
|
39
|
+
res_content_type = res_code.next_steps.find { |content_type| content_type.step_key == @content_type.to_s }
|
40
|
+
|
41
|
+
if @combination == nil
|
42
|
+
return res_content_type if res_content_type.present?
|
43
|
+
raise NotFound.new("host: #{@host}, method: #{@method}, path: #{@path}, code: #{@code}, content-type: #{@content_type}")
|
44
|
+
end
|
45
|
+
|
46
|
+
res_json_schema = res_content_type.next_steps[0]
|
47
|
+
res_combination = res_json_schema.next_steps.find do |combination|
|
48
|
+
combination.step_key == @combination.to_s
|
49
|
+
end
|
50
|
+
|
51
|
+
if @combination_next == nil
|
52
|
+
return res_combination if res_combination
|
53
|
+
raise NotFound.new("host: #{@host}, method: #{@method}, path: #{@path}, code: #{@code}, content-type: #{@content_type}, combination: #{@combination}")
|
54
|
+
end
|
55
|
+
|
56
|
+
res_combination_next = res_combination.next_steps.find do |combination_next|
|
57
|
+
combination_next.step_key == @combination_next.to_s
|
58
|
+
end
|
59
|
+
|
60
|
+
return res_combination_next if res_combination_next
|
61
|
+
raise NotFound.new("host: #{@host}, method: #{@method}, path: #{@path}, code: #{@code}, content-type: #{@content_type}, combination: #{@combination}, combination_next: #{@combination_next}")
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
require 'fitting/report/actions'
|
2
|
+
|
3
|
+
module Fitting
|
4
|
+
class Prefix
|
5
|
+
KEYS = {
|
6
|
+
'openapi2' => :openapi2_json_path,
|
7
|
+
'openapi3' => :openapi3_yaml_path,
|
8
|
+
'drafter' => :drafter_yaml_path,
|
9
|
+
'crafter' => :crafter_yaml_path,
|
10
|
+
'tomogram' => :tomogram_json_path
|
11
|
+
}.freeze
|
12
|
+
|
13
|
+
attr_reader :name, :actions
|
14
|
+
|
15
|
+
def initialize(name, schema_paths, type, skip = false)
|
16
|
+
@prefix = name
|
17
|
+
@cover = false
|
18
|
+
|
19
|
+
@actions = Fitting::Report::Actions.new([])
|
20
|
+
raise Skip if skip
|
21
|
+
|
22
|
+
schema_paths.each do |path|
|
23
|
+
tomogram = Tomograph::Tomogram.new(prefix: name, KEYS[type] => path)
|
24
|
+
|
25
|
+
@actions.push(Fitting::Report::Actions.new(tomogram))
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
class Skip < RuntimeError; end
|
30
|
+
|
31
|
+
class NotFound < RuntimeError
|
32
|
+
attr_reader :log
|
33
|
+
|
34
|
+
def initialize(msg, log)
|
35
|
+
@log = log
|
36
|
+
super(msg)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.find(host:, log:)
|
41
|
+
yaml = YAML.safe_load(File.read('.fitting.yml'))
|
42
|
+
|
43
|
+
prefixes = yaml['hosts'].find{|h| h.first.first == host.to_s}.first.last['prefixes']
|
44
|
+
raise NotFound.new("host: #{log.host}, path: #{log.path}", log) unless prefixes
|
45
|
+
|
46
|
+
prefix = prefixes.find do |prefix|
|
47
|
+
prefix['name'].nil? || log.path[0..prefix['name'].size - 1] == prefix['name']
|
48
|
+
end
|
49
|
+
raise NotFound.new("host: #{log.host}, path: #{log.path}", log) unless prefix
|
50
|
+
|
51
|
+
new(prefix['name'], prefix['schema_paths'], prefix['type'], prefix['skip'])
|
52
|
+
end
|
53
|
+
|
54
|
+
def cover!
|
55
|
+
@cover = true
|
56
|
+
end
|
57
|
+
|
58
|
+
def cover?
|
59
|
+
@cover
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
data/lib/fitting/railtie.rb
CHANGED
@@ -5,15 +5,16 @@ module Fitting
|
|
5
5
|
class Records
|
6
6
|
class Spherical
|
7
7
|
class Request
|
8
|
-
attr_reader :method, :path, :body, :response, :title, :group
|
8
|
+
attr_reader :method, :path, :body, :response, :title, :group, :host
|
9
9
|
|
10
|
-
def initialize(method:, path:, body:, response:, title:, group:)
|
10
|
+
def initialize(method:, path:, body:, response:, title:, group:, host:)
|
11
11
|
@method = method
|
12
12
|
@path = path
|
13
13
|
@body = body
|
14
14
|
@response = response
|
15
15
|
@title = title
|
16
16
|
@group = group
|
17
|
+
@host = host
|
17
18
|
end
|
18
19
|
|
19
20
|
def to_hash
|
@@ -23,7 +24,8 @@ module Fitting
|
|
23
24
|
body: body,
|
24
25
|
response: response.to_hash,
|
25
26
|
title: title,
|
26
|
-
group: group
|
27
|
+
group: group,
|
28
|
+
host: host
|
27
29
|
}
|
28
30
|
end
|
29
31
|
|
@@ -39,7 +41,8 @@ module Fitting
|
|
39
41
|
body: hash['body'],
|
40
42
|
response: Fitting::Records::Spherical::Response.load(hash['response']),
|
41
43
|
title: hash['title'],
|
42
|
-
group: hash['group']
|
44
|
+
group: hash['group'],
|
45
|
+
host: hash['host']
|
43
46
|
)
|
44
47
|
end
|
45
48
|
end
|
@@ -6,32 +6,38 @@ module Fitting
|
|
6
6
|
class Response
|
7
7
|
attr_reader :status, :body
|
8
8
|
|
9
|
-
def initialize(status:, body:)
|
9
|
+
def initialize(status:, body:, content_type:)
|
10
10
|
@status = status
|
11
11
|
@body = body
|
12
|
+
@content_type = content_type
|
12
13
|
end
|
13
14
|
|
14
15
|
def to_hash
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
16
|
+
begin
|
17
|
+
parse_body = JSON.parse(body)
|
18
|
+
rescue JSON::ParserError
|
19
|
+
parse_body = {}
|
20
|
+
end
|
21
|
+
|
22
|
+
if @content_type == nil || @content_type == ''
|
23
|
+
{
|
24
|
+
status: status,
|
25
|
+
content_type: 'application/json',
|
26
|
+
body: parse_body
|
27
|
+
}
|
28
|
+
else
|
29
|
+
ct = @content_type.split("; ")
|
30
|
+
{
|
31
|
+
status: status,
|
32
|
+
content_type: ct.first,
|
33
|
+
body: parse_body
|
34
|
+
}
|
35
|
+
end
|
24
36
|
end
|
25
37
|
|
26
38
|
def to_json(*_args)
|
27
39
|
JSON.dump(to_hash)
|
28
40
|
end
|
29
|
-
|
30
|
-
class << self
|
31
|
-
def load(hash)
|
32
|
-
new(status: hash['status'], body: hash['body'])
|
33
|
-
end
|
34
|
-
end
|
35
41
|
end
|
36
42
|
end
|
37
43
|
end
|