fitting 3.0.2 → 4.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (85) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/.ruby-version +1 -1
  4. data/CHANGELOG.md +16 -0
  5. data/README.md +327 -108
  6. data/fitting.gemspec +2 -2
  7. data/images/b1.png +0 -0
  8. data/images/b2.png +0 -0
  9. data/images/w1.png +0 -0
  10. data/images/w2.png +0 -0
  11. data/lib/fitting/action.rb +105 -0
  12. data/lib/fitting/cover/json_schema.rb +2 -2
  13. data/lib/fitting/cover/json_schema_one_of.rb +4 -2
  14. data/lib/fitting/debug.rb +47 -0
  15. data/lib/fitting/doc/action.rb +141 -0
  16. data/lib/fitting/doc/code.rb +53 -0
  17. data/lib/fitting/doc/combination_enum.rb +110 -0
  18. data/lib/fitting/doc/combination_one_of.rb +61 -0
  19. data/lib/fitting/doc/combination_optional.rb +54 -0
  20. data/lib/fitting/doc/combination_step.rb +48 -0
  21. data/lib/fitting/doc/content_type.rb +152 -0
  22. data/lib/fitting/doc/json_schema.rb +112 -0
  23. data/lib/fitting/doc/step.rb +102 -0
  24. data/lib/fitting/doc.rb +107 -0
  25. data/lib/fitting/host.rb +37 -0
  26. data/lib/fitting/log.rb +102 -0
  27. data/lib/fitting/nocov.rb +68 -0
  28. data/lib/fitting/prefix.rb +62 -0
  29. data/lib/fitting/railtie.rb +0 -1
  30. data/lib/fitting/records/spherical/request.rb +7 -4
  31. data/lib/fitting/records/spherical/response.rb +22 -16
  32. data/lib/fitting/records/tested/request.rb +6 -1
  33. data/lib/fitting/records/tested/response.rb +6 -1
  34. data/lib/fitting/rep/html.rb +32 -0
  35. data/lib/fitting/rep.rb +24 -0
  36. data/lib/fitting/report/action.rb +9 -15
  37. data/lib/fitting/report/actions.rb +22 -33
  38. data/lib/fitting/report/combination.rb +10 -6
  39. data/lib/fitting/report/combinations.rb +9 -29
  40. data/lib/fitting/report/prefix.rb +7 -24
  41. data/lib/fitting/report/prefixes.rb +11 -25
  42. data/lib/fitting/report/response.rb +12 -22
  43. data/lib/fitting/report/responses.rb +23 -27
  44. data/lib/fitting/report/tests.rb +4 -8
  45. data/lib/fitting/skip/action.rb +44 -0
  46. data/lib/fitting/skip/api.rb +29 -0
  47. data/lib/fitting/skip.rb +21 -0
  48. data/lib/fitting/version.rb +1 -1
  49. data/lib/fitting.rb +12 -28
  50. data/lib/tasks/fitting.rake +23 -84
  51. data/lib/templates/htmlcss/bootstrap-nightshade.min.css +12 -0
  52. data/lib/templates/htmlcss/bootstrap.min.js +7 -0
  53. data/lib/templates/htmlcss/darkmode.min.js +6 -0
  54. data/lib/templates/htmlcss/fitting.html +196 -0
  55. data/lib/templates/htmlcss/jquery-3.6.0.min.js +2 -0
  56. metadata +40 -39
  57. data/lib/fitting/configuration.rb +0 -17
  58. data/lib/fitting/records/spherical/requests.rb +0 -25
  59. data/lib/fitting/storage/responses.rb +0 -21
  60. data/lib/fitting/tests.rb +0 -31
  61. data/lib/tasks/fitting_outgoing.rake +0 -91
  62. data/lib/templates/bomboniere/.gitignore +0 -21
  63. data/lib/templates/bomboniere/.tool-versions +0 -1
  64. data/lib/templates/bomboniere/README.md +0 -19
  65. data/lib/templates/bomboniere/dist/css/app.aa2bcd8a.css +0 -1
  66. data/lib/templates/bomboniere/dist/css/chunk-vendors.ec5f6c3f.css +0 -1
  67. data/lib/templates/bomboniere/dist/favicon.ico +0 -0
  68. data/lib/templates/bomboniere/dist/index.html +0 -1
  69. data/lib/templates/bomboniere/dist/js/app.e5f1a5ec.js +0 -2
  70. data/lib/templates/bomboniere/dist/js/app.e5f1a5ec.js.map +0 -1
  71. data/lib/templates/bomboniere/dist/js/chunk-vendors.0f99b670.js +0 -13
  72. data/lib/templates/bomboniere/dist/js/chunk-vendors.0f99b670.js.map +0 -1
  73. data/lib/templates/bomboniere/package-lock.json +0 -9292
  74. data/lib/templates/bomboniere/package.json +0 -27
  75. data/lib/templates/bomboniere/public/favicon.ico +0 -0
  76. data/lib/templates/bomboniere/public/index.html +0 -17
  77. data/lib/templates/bomboniere/src/App.vue +0 -102
  78. data/lib/templates/bomboniere/src/assets/logo.png +0 -0
  79. data/lib/templates/bomboniere/src/components/HelloWorld.vue +0 -204
  80. data/lib/templates/bomboniere/src/main.js +0 -10
  81. data/lib/templates/bomboniere/src/router/index.js +0 -31
  82. data/lib/templates/bomboniere/src/views/About.vue +0 -5
  83. data/lib/templates/bomboniere/src/views/Action.vue +0 -173
  84. data/lib/templates/bomboniere/src/views/Home.vue +0 -17
  85. data/lib/templates/bomboniere/vue.config.js +0 -3
@@ -0,0 +1,152 @@
1
+ require 'fitting/doc/step'
2
+ require 'fitting/doc/json_schema'
3
+
4
+ module Fitting
5
+ class Doc
6
+ class ContentType < Step
7
+ class NotFound < RuntimeError; end
8
+
9
+ def initialize(content_type, subvalue)
10
+ @logs = []
11
+ @step_key = content_type
12
+ @next_steps = []
13
+ @step_cover_size = 0
14
+ return self if content_type != 'application/json'
15
+
16
+ definitions = subvalue.inject({}) do |sum, sv|
17
+ if sv['body']["definitions"] != nil
18
+ sum.merge!(sv['body'].delete('definitions'))
19
+ end
20
+ sum
21
+ end
22
+
23
+ if subvalue.size == 1
24
+ if definitions && definitions != {}
25
+ res = merge_definitions(subvalue[0], definitions)
26
+ if res && res['properties']['type'] == 'object'
27
+ @next_steps.push(JsonSchema.new(
28
+ {
29
+ "$schema" => "http://json-schema.org/draft-04/schema#",
30
+ }.merge(res['properties']), false
31
+ ))
32
+ elsif res
33
+ @next_steps.push(JsonSchema.new(
34
+ {
35
+ "$schema" => "http://json-schema.org/draft-04/schema#",
36
+ "type" => "object"
37
+ }.merge(res), false
38
+ ))
39
+ else
40
+ @next_steps.push(JsonSchema.new({}, false))
41
+ end
42
+ else
43
+ @next_steps.push(JsonSchema.new(subvalue[0]['body'], false))
44
+ end
45
+ else
46
+ if definitions && definitions != {}
47
+ @next_steps.push(JsonSchema.new(
48
+ {
49
+ "$schema" => "http://json-schema.org/draft-04/schema#",
50
+ "type" => "object",
51
+ "oneOf" => subvalue.inject([]) do |sum, sv|
52
+ res = merge_definitions(sv, definitions)
53
+ sum.push(res)
54
+ end
55
+ }, false
56
+ ))
57
+ else
58
+ super_schema = false
59
+ subvalue.each do |sv|
60
+ super_schema = true if sv['body']["properties"] == nil
61
+ end
62
+
63
+ if super_schema
64
+ @next_steps.push(JsonSchema.new(
65
+ {
66
+ "$schema" => "http://json-schema.org/draft-04/schema#",
67
+ "type" => "object",
68
+ "oneOf" => subvalue.inject([]) do |sum, sv|
69
+ if sv['body']["properties"]
70
+ sum.push(check_body(sv['body']["properties"], sv))
71
+ end
72
+ sum
73
+ end
74
+ }, super_schema
75
+ ))
76
+ else
77
+ @next_steps.push(JsonSchema.new(
78
+ {
79
+ "$schema" => "http://json-schema.org/draft-04/schema#",
80
+ "type" => "object",
81
+ "oneOf" => subvalue.inject([]) do |sum, sv|
82
+ sum.push(check_body(sv['body']["properties"], sv))
83
+ end
84
+ }, super_schema
85
+ ))
86
+ super_schema
87
+ end
88
+ end
89
+ end
90
+ end
91
+
92
+ def cover!(log)
93
+ if @step_key == log.content_type && log.content_type == 'application/json'
94
+ @step_cover_size += 1
95
+ @logs.push(log.body)
96
+ @next_steps.each { |json_schema| json_schema.cover!(log) }
97
+ elsif @step_key != 'application/json' && log.content_type != 'application/json'
98
+ @step_cover_size += 1
99
+ end
100
+ rescue Fitting::Doc::JsonSchema::NotFound => e
101
+ raise NotFound.new "content-type: #{@step_key}\n\n"\
102
+ "#{e.message}"
103
+ end
104
+
105
+ def debug(debug)
106
+ return self if @step_key == debug.content_type && debug.content_type == 'application/json'
107
+ nil
108
+ end
109
+
110
+ def logs
111
+ @logs
112
+ end
113
+
114
+ def merge_definitions(sv, definitions)
115
+ if sv['body']["properties"]
116
+ res = sv['body']["properties"]
117
+ elsif sv['body']['type'] != 'array'
118
+ res = sv['body']
119
+ end
120
+ definitions.each_pair do |key, value|
121
+ while JSON.dump(res).include?("\"$ref\":\"#/definitions/#{key}\"") do
122
+ new_res_array = JSON.dump(res).split('{')
123
+ index = new_res_array.index { |js| js.include?("\"$ref\":\"#/definitions/#{key}\"") }
124
+ if index != nil
125
+ def_size = "\"$ref\":\"#/definitions/#{key}\"".size
126
+ new_res_array[index] = JSON.dump(value)[1..-2] + new_res_array[index][def_size..-1]
127
+ res = JSON.load(new_res_array.join("{"))
128
+ end
129
+ end
130
+ end
131
+ if res == nil
132
+ nil
133
+ else
134
+ check_body(res, sv)
135
+ end
136
+ end
137
+
138
+ def check_body(res, sv)
139
+ if sv['body']["required"] == nil
140
+ {
141
+ "properties" => res
142
+ }
143
+ else
144
+ {
145
+ "properties" => res,
146
+ "required" => sv['body']["required"]
147
+ }
148
+ end
149
+ end
150
+ end
151
+ end
152
+ end
@@ -0,0 +1,112 @@
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 to_hash
83
+ @step_key
84
+ end
85
+
86
+ def report(res, index)
87
+ @index_before = index
88
+ @res_before = [] + res
89
+
90
+ index = mark_range(index, res)
91
+ @index_medium = index
92
+ @res_medium = [] + res
93
+
94
+ if @next_steps != []
95
+ new_index = index
96
+ @next_steps.each do |next_step|
97
+ if @oneOf
98
+ res, new_index = next_step.report(res, new_index)
99
+ else
100
+ res, new_index = next_step.report(res, @index_before)
101
+ end
102
+ end
103
+ end
104
+
105
+ index += index_offset
106
+ @index_after = index
107
+ @res_after = [] + res
108
+ [res, index]
109
+ end
110
+ end
111
+ end
112
+ 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
@@ -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(actions)
84
+ all = 0
85
+ cov = 0
86
+ actions.each do |action|
87
+ action.to_hash.values.first.each do |cover_line|
88
+ if cover_line == nil
89
+ next
90
+ elsif cover_line == 0
91
+ all += 1
92
+ elsif cover_line > 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
@@ -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
@@ -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,68 @@
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
+ if res == nil
33
+ raise NotFound.new("host: #{@host}, method: #{@method}, path: #{@path}")
34
+ end
35
+
36
+ res_code = res.responses.find { |response| response.step_key == @code.to_s }
37
+
38
+ if @content_type == nil
39
+ return res_code if res_code.present?
40
+ raise NotFound.new("host: #{@host}, method: #{@method}, path: #{@path}, code: #{@code}")
41
+ end
42
+
43
+ res_content_type = res_code.next_steps.find { |content_type| content_type.step_key == @content_type.to_s }
44
+
45
+ if @combination == nil
46
+ return res_content_type if res_content_type.present?
47
+ raise NotFound.new("host: #{@host}, method: #{@method}, path: #{@path}, code: #{@code}, content-type: #{@content_type}")
48
+ end
49
+
50
+ res_json_schema = res_content_type.next_steps[0]
51
+ res_combination = res_json_schema.next_steps.find do |combination|
52
+ combination.step_key == @combination.to_s
53
+ end
54
+
55
+ if @combination_next == nil
56
+ return res_combination if res_combination
57
+ raise NotFound.new("host: #{@host}, method: #{@method}, path: #{@path}, code: #{@code}, content-type: #{@content_type}, combination: #{@combination}")
58
+ end
59
+
60
+ res_combination_next = res_combination.next_steps.find do |combination_next|
61
+ combination_next.step_key == @combination_next.to_s
62
+ end
63
+
64
+ return res_combination_next if res_combination_next
65
+ raise NotFound.new("host: #{@host}, method: #{@method}, path: #{@path}, code: #{@code}, content-type: #{@content_type}, combination: #{@combination}, combination_next: #{@combination_next}")
66
+ end
67
+ end
68
+ end