fitting 3.0.2 → 4.0.1

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.
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