fitting 3.0.2 → 4.0.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/.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
|