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
@@ -3,6 +3,15 @@ require 'fitting/report/response'
3
3
  module Fitting
4
4
  module Report
5
5
  class Responses
6
+ class NotFound < RuntimeError
7
+ attr_reader :log
8
+
9
+ def initialize(msg, log)
10
+ @log = log
11
+ super(msg)
12
+ end
13
+ end
14
+
6
15
  def initialize(responses)
7
16
  @responses = responses
8
17
  @responses = []
@@ -11,38 +20,25 @@ module Fitting
11
20
  end
12
21
  end
13
22
 
14
- def to_a
15
- @responses
16
- end
17
-
18
- def join(tests)
19
- tests.to_a.map do |test|
20
- if there_a_suitable_response?(test)
21
- cram_into_the_appropriate_response(test)
22
- test.mark_response
23
- end
24
- end
25
- end
26
-
27
- def there_a_suitable_response?(test)
28
- return false if @responses.nil?
29
-
23
+ def find!(log)
24
+ message = ""
30
25
  @responses.map do |response|
31
- return true if response.status.to_s == test.status &&
32
- JSON::Validator.fully_validate(response.body, test.body) == []
26
+ if response.status.to_s == log.status
27
+ if JSON::Validator.fully_validate(response.body, log.body) == []
28
+ return response
29
+ else
30
+ message += "\nbody: #{response.body}\nvalidate: #{JSON::Validator.fully_validate(response.body, log.body)}\n"
31
+ end
32
+ else
33
+ message += "\nstatus: #{response.status}\n"
34
+ end
33
35
  end
34
36
 
35
- false
37
+ raise NotFound.new("method: #{log.method}, host: #{log.host}, path: #{log.path}, status: #{log.status}, body: #{log.body}\n\n#{message}", log)
36
38
  end
37
39
 
38
- def cram_into_the_appropriate_response(test)
39
- @responses.map do |response|
40
- next unless response.status.to_s == test.status &&
41
- JSON::Validator.fully_validate(response.body, test.body) == []
42
-
43
- response.add_test(test)
44
- break
45
- end
40
+ def to_a
41
+ @responses
46
42
  end
47
43
  end
48
44
  end
@@ -11,10 +11,8 @@ module Fitting
11
11
 
12
12
  def self.new_from_config
13
13
  tests = []
14
- Dir["#{Fitting.configuration.rspec_json_path}/*.json"].each do |file|
15
- JSON.parse(File.read(file)).map do |test|
16
- tests.push(Fitting::Report::Test.new(test))
17
- end
14
+ File.read('log/test.log').split("\n").select{|f|f.include?('incoming request ')}.each do |test|
15
+ tests.push(Fitting::Report::Test.new(JSON.load(test.split('incoming request ')[1])))
18
16
  end
19
17
  tests.sort { |a, b| b.path <=> a.path }
20
18
  new(tests)
@@ -22,10 +20,8 @@ module Fitting
22
20
 
23
21
  def self.new_from_outgoing_config
24
22
  tests = []
25
- Dir["#{Fitting.configuration.webmock_json_path}/*.json"].each do |file|
26
- JSON.parse(File.read(file)).map do |test|
27
- tests.push(Fitting::Report::Test.new(test))
28
- end
23
+ File.read('log/test.log').split("\n").select{|f|f.include?('outgoing request ')}.each do |test|
24
+ tests.push(Fitting::Report::Test.new(JSON.load(test.split('outgoing request ')[1])))
29
25
  end
30
26
  tests.sort { |a, b| b.path <=> a.path }
31
27
  new(tests)
@@ -0,0 +1,44 @@
1
+ module Fitting
2
+ class Skip
3
+ class Action
4
+ attr_accessor :host, :method, :path, :code
5
+
6
+ def initialize(host, method, path, code)
7
+ @host = host
8
+ @method = method
9
+ @path = path
10
+ @code = code
11
+ end
12
+
13
+ def self.all(actions)
14
+ return [] unless actions
15
+ actions.map do |action|
16
+ next if action['method'].nil? || action['path'].nil?
17
+ new(action['host'], action['method'], action['path'], action['code'])
18
+ end.compact
19
+ end
20
+
21
+ def self.find(actions, log)
22
+ actions.find do |action|
23
+ if action.host == log.host &&
24
+ action.method == log.method &&
25
+ action.path_match(log.path)
26
+ return action if action.code == nil
27
+ return action if action.code.to_s == log.status
28
+ end
29
+ end
30
+ end
31
+
32
+ def path_match(find_path)
33
+ regexp =~ find_path
34
+ end
35
+
36
+ def regexp
37
+ str = Regexp.escape(path)
38
+ str = str.gsub(/\\{\w+\\}/, '[^&=\/]+')
39
+ str = "\\A#{str}\\z"
40
+ Regexp.new(str)
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,29 @@
1
+ module Fitting
2
+ class Skip
3
+ class API
4
+ attr_accessor :type, :host, :prefix, :path
5
+
6
+ def initialize(type, host, prefix)
7
+ @type = type
8
+ @host = host
9
+ @prefix = prefix
10
+ end
11
+
12
+ def self.all(apis)
13
+ return [] unless apis
14
+ apis.map do |api|
15
+ next if api['method'] || api['path']
16
+ new('provided', api['host'], api['prefix'])
17
+ end.compact
18
+ end
19
+
20
+ def self.find(apis, log)
21
+ apis.find do |api|
22
+ if log.host == api.host
23
+ api.prefix.nil? || api.prefix == '' || log.path[0..api.prefix.size - 1] == api.prefix
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,21 @@
1
+ require 'fitting/skip/api'
2
+ require 'fitting/skip/action'
3
+
4
+ module Fitting
5
+ class Skip
6
+ def self.all
7
+ yaml = YAML.safe_load(File.read('.fitting.yml'))
8
+ {
9
+ apis: Fitting::Skip::API.all(yaml['SkipValidation']),
10
+ actions: Fitting::Skip::Action.all(yaml['SkipValidation'])
11
+ }
12
+ end
13
+
14
+ def self.find(skips, log)
15
+ api = Fitting::Skip::API.find(skips[:apis], log)
16
+ return api if api
17
+
18
+ Fitting::Skip::Action.find(skips[:actions], log)
19
+ end
20
+ end
21
+ end
@@ -1,3 +1,3 @@
1
1
  module Fitting
2
- VERSION = '3.0.2'.freeze
2
+ VERSION = '4.0.1'.freeze
3
3
  end
data/lib/fitting.rb CHANGED
@@ -1,35 +1,16 @@
1
1
  require 'json-schema'
2
2
  require 'fitting/version'
3
- require 'fitting/configuration'
4
- require 'fitting/storage/responses'
5
- require 'fitting/tests'
6
3
  require 'fitting/cover/json_schema_enum'
7
4
  require 'fitting/cover/json_schema_one_of'
8
5
  require 'fitting/records/documented/request'
9
6
  require 'fitting/railtie' if defined?(Rails)
7
+ require 'fitting/records/tested/request'
10
8
 
11
9
  module Fitting
12
10
  class << self
13
- def configuration
14
- yaml = YAML.safe_load(File.read('.fitting.yml'))
15
- @configuration ||= Configuration.new(yaml)
16
- end
17
-
18
- def configure
19
- yield(configuration)
20
- end
21
-
22
- def clear_tests_directory
23
- FileUtils.rm_r Dir.glob(configuration.rspec_json_path), force: true
24
- FileUtils.rm_r Dir.glob(configuration.webmock_json_path), force: true
25
- end
26
-
27
- def save_test_data
28
- clear_tests_directory
29
-
30
- outgoing_responses = Fitting::Storage::Responses.new
31
- responses = Fitting::Storage::Responses.new
32
-
11
+ def logger
12
+ # :nocov:
13
+ responses = ""
33
14
  RSpec.configure do |config|
34
15
  if defined?(WebMock)
35
16
  config.before(:each) do |example|
@@ -41,17 +22,20 @@ module Fitting
41
22
  response.body || {})
42
23
  mock_response.instance_variable_set(:@request, mock_request)
43
24
 
44
- outgoing_responses.add(mock_response, example)
25
+ request = Fitting::Records::Tested::Request.new(mock_response, example)
26
+ responses += "FITTING outgoing request #{request.to_spherical.to_json}\n"
45
27
  end
46
28
  end
47
29
  end
48
30
 
49
31
  config.after(:each, type: :request) do |example|
50
- responses.add(response, example)
32
+ request = Fitting::Records::Tested::Request.new(response, example)
33
+ responses += "FITTING incoming request #{request.to_spherical.to_json}\n"
51
34
  end
52
35
 
53
36
  config.after(:each, type: :controller) do |example|
54
- responses.add(response, example)
37
+ request = Fitting::Records::Tested::Request.new(response, example)
38
+ responses += "FITTING incoming request #{request.to_spherical.to_json}\n"
55
39
  end
56
40
 
57
41
  config.after(:each) do
@@ -59,10 +43,10 @@ module Fitting
59
43
  end
60
44
 
61
45
  config.after(:suite) do
62
- responses.tests.save
63
- outgoing_responses.tests.outgoing_save
46
+ File.open("log/fitting#{ENV['TEST_ENV_NUMBER']}.log", 'w') { |file| file.write(responses) }
64
47
  end
65
48
  end
49
+ # :nocov:
66
50
  end
67
51
  end
68
52
  end
@@ -1,91 +1,30 @@
1
- require 'fitting/records/spherical/requests'
2
- require 'fitting/cover/json_schema'
3
- require 'fitting/report/prefixes'
4
- require 'fitting/report/tests'
5
- require 'fitting/report/console'
1
+ require 'fitting/log'
2
+ require 'fitting/doc'
3
+ require 'fitting/skip'
4
+ require 'fitting/nocov'
5
+ require 'fitting/rep'
6
+ require 'fitting/debug'
6
7
 
7
8
  namespace :fitting do
8
- task :report do
9
- tests = Fitting::Report::Tests.new_from_config
10
- prefixes = Fitting::Report::Prefixes.new(Fitting.configuration.prefixes)
11
-
12
- prefixes.join(tests)
13
-
14
- prefixes.to_a.map do |prefix|
15
- prefix.actions.join(prefix.tests) unless prefix.skip?
9
+ task :validate do
10
+ logs = Fitting::Log.all
11
+ docs = Fitting::Doc.all
12
+ skips = Fitting::Skip.all
13
+
14
+ logs.each do |log|
15
+ Fitting::Doc.cover!(docs, log)
16
+ log.access!
17
+ rescue Fitting::Doc::NotFound => e
18
+ next log.pending! if Fitting::Skip.find(skips, log)
19
+ log.failure!(e)
16
20
  end
21
+ Fitting::Log.report(logs)
17
22
 
18
- prefixes.to_a.map do |prefix|
19
- next if prefix.skip?
20
-
21
- prefix.actions.to_a.map do |action|
22
- action.responses.join(action.tests)
23
- end
23
+ Fitting::NoCov.all(YAML.safe_load(File.read('.fitting.yml'))).each do |nocov|
24
+ nocov.find(docs).nocover!
24
25
  end
25
-
26
- prefixes.to_a.map do |prefix|
27
- next if prefix.skip?
28
-
29
- prefix.actions.to_a.map do |action|
30
- action.responses.to_a.map do |response|
31
- response.combinations.join(response.tests)
32
- end
33
- end
34
- end
35
-
36
- report = JSON.pretty_generate(
37
- {
38
- tests_without_prefixes: tests.without_prefixes,
39
- prefixes_details: prefixes.to_a.map(&:details)
40
- }
41
- )
42
-
43
- destination = 'fitting'
44
- FileUtils.mkdir_p(destination)
45
- FileUtils.rm_r Dir.glob("#{destination}/*"), force: true
46
- File.open('fitting/report.json', 'w') { |file| file.write(report) }
47
-
48
- gem_path = $LOAD_PATH.find { |i| i.include?('fitting') }
49
- source_path = "#{gem_path}/templates/bomboniere/dist"
50
- FileUtils.copy_entry source_path, destination
51
-
52
- json_schemas = {}
53
- combinations = {}
54
- prefixes.to_a.map do |prefix|
55
- next if prefix.skip?
56
-
57
- prefix.actions.to_a.map do |action|
58
- action.responses.to_a.map do |response|
59
- json_schemas.merge!(response.id => response.body)
60
- response.combinations.to_a.map do |combination|
61
- combinations.merge!(combination.id => combination.json_schema)
62
- end
63
- end
64
- end
65
- end
66
- File.open('fitting/json_schemas.json', 'w') { |file| file.write(JSON.pretty_generate(json_schemas)) }
67
- File.open('fitting/combinations.json', 'w') { |file| file.write(JSON.pretty_generate(combinations)) }
68
- File.open('fitting/tests.json', 'w') { |file| file.write(JSON.pretty_generate(tests.to_h)) }
69
-
70
- js_path = Dir["#{destination}/js/*"].find { |f| f[0..14] == 'fitting/js/app.' and f[-3..] == '.js' }
71
- js_file = File.read(js_path)
72
- new_js_file = js_file.gsub('{stub:"prefixes report"}', report)
73
- new_js_file = new_js_file.gsub('{stub:"for action page"}', report)
74
- new_js_file = new_js_file.gsub('{stub:"json-schemas"}', JSON.pretty_generate(json_schemas))
75
- new_js_file = new_js_file.gsub('{stub:"combinations"}', JSON.pretty_generate(combinations))
76
- new_js_file = new_js_file.gsub('{stub:"tests"}', JSON.pretty_generate(tests.to_h))
77
- File.open(js_path, 'w') { |file| file.write(new_js_file) }
78
-
79
- console = Fitting::Report::Console.new(
80
- tests.without_prefixes,
81
- prefixes.to_a.map(&:details)
82
- )
83
-
84
- puts console.output
85
- puts console.output_sum
86
-
87
- exit 1 unless console.good?
88
-
89
- exit 0
26
+ Fitting::Rep.new(docs).save!
27
+ Fitting::Debug.save!(docs, YAML.safe_load(File.read('.fitting.yml')))
28
+ Fitting::Doc.report(docs)
90
29
  end
91
30
  end