media_types 1.0.0 → 2.1.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.
- checksums.yaml +4 -4
- data/.github/workflows/debian.yml +43 -0
- data/.github/workflows/ruby.yml +3 -0
- data/.gitignore +10 -10
- data/CHANGELOG.md +80 -54
- data/Gemfile +6 -6
- data/Gemfile.lock +43 -114
- data/LICENSE +21 -0
- data/README.md +278 -85
- data/Rakefile +12 -12
- data/lib/media_types.rb +46 -3
- data/lib/media_types/constructable.rb +15 -9
- data/lib/media_types/dsl.rb +66 -31
- data/lib/media_types/dsl/errors.rb +18 -0
- data/lib/media_types/errors.rb +19 -0
- data/lib/media_types/scheme.rb +127 -13
- data/lib/media_types/scheme/errors.rb +66 -0
- data/lib/media_types/scheme/links.rb +15 -0
- data/lib/media_types/scheme/missing_validation.rb +12 -4
- data/lib/media_types/scheme/output_empty_guard.rb +5 -4
- data/lib/media_types/scheme/output_iterator_with_predicate.rb +13 -2
- data/lib/media_types/scheme/output_type_guard.rb +1 -1
- data/lib/media_types/scheme/rules.rb +53 -1
- data/lib/media_types/scheme/rules_exhausted_guard.rb +15 -4
- data/lib/media_types/scheme/validation_options.rb +17 -5
- data/lib/media_types/testing/assertions.rb +20 -0
- data/lib/media_types/validations.rb +29 -7
- data/lib/media_types/version.rb +1 -1
- data/media_types.gemspec +4 -7
- metadata +19 -63
- data/.travis.yml +0 -19
- data/lib/media_types/.dsl.rb.swp +0 -0
- data/lib/media_types/defaults.rb +0 -31
- data/lib/media_types/integrations.rb +0 -32
- data/lib/media_types/integrations/actionpack.rb +0 -21
- data/lib/media_types/integrations/http.rb +0 -47
- data/lib/media_types/minitest/assert_media_type_format.rb +0 -10
- data/lib/media_types/minitest/assert_media_types_registered.rb +0 -166
- data/lib/media_types/registrar.rb +0 -148
data/Rakefile
CHANGED
@@ -1,12 +1,12 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'bundler/gem_tasks'
|
4
|
-
require 'rake/testtask'
|
5
|
-
|
6
|
-
Rake::TestTask.new(:test) do |t|
|
7
|
-
t.libs << 'test'
|
8
|
-
t.libs << 'lib'
|
9
|
-
t.test_files = FileList['test/**/*_test.rb']
|
10
|
-
end
|
11
|
-
|
12
|
-
task default: :test
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'bundler/gem_tasks'
|
4
|
+
require 'rake/testtask'
|
5
|
+
|
6
|
+
Rake::TestTask.new(:test) do |t|
|
7
|
+
t.libs << 'test'
|
8
|
+
t.libs << 'lib'
|
9
|
+
t.test_files = FileList['test/**/*_test.rb']
|
10
|
+
end
|
11
|
+
|
12
|
+
task default: :test
|
data/lib/media_types.rb
CHANGED
@@ -7,9 +7,9 @@ require 'media_types/hash'
|
|
7
7
|
require 'media_types/object'
|
8
8
|
require 'media_types/scheme'
|
9
9
|
require 'media_types/dsl'
|
10
|
+
require 'media_types/errors'
|
10
11
|
|
11
12
|
require 'media_types/views'
|
12
|
-
require 'media_types/integrations'
|
13
13
|
|
14
14
|
module MediaTypes
|
15
15
|
def self.set_organisation(mod, organisation)
|
@@ -17,14 +17,57 @@ module MediaTypes
|
|
17
17
|
@organisation_prefixes[mod.name] = organisation
|
18
18
|
end
|
19
19
|
|
20
|
+
def self.expect_string_keys(mod)
|
21
|
+
set_key_expectation(mod, false)
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.expect_symbol_keys(mod)
|
25
|
+
set_key_expectation(mod, true)
|
26
|
+
end
|
27
|
+
|
28
|
+
# Keep track of modules setting their key expectations
|
29
|
+
def self.set_key_expectation(mod, expect_symbol_keys)
|
30
|
+
@key_expectations ||= {}
|
31
|
+
@key_expectations_used ||= {}
|
32
|
+
|
33
|
+
raise KeyExpectationSetError.new(mod: mod) unless @key_expectations[mod.name].nil?
|
34
|
+
raise KeyExpectationUsedError.new(mod: mod) if @key_expectations_used[mod.name]
|
35
|
+
|
36
|
+
@key_expectations[mod.name] = expect_symbol_keys
|
37
|
+
end
|
38
|
+
|
39
|
+
SYMBOL_KEYS_DEFAULT = true
|
40
|
+
|
41
|
+
def self.get_key_expectation(mod)
|
42
|
+
@key_expectations ||= {}
|
43
|
+
@key_expectations_used ||= {}
|
44
|
+
|
45
|
+
expect_symbol = find_key_expectation(mod)
|
46
|
+
|
47
|
+
expect_symbol.nil? ? SYMBOL_KEYS_DEFAULT : expect_symbol
|
48
|
+
end
|
49
|
+
|
50
|
+
def self.find_key_expectation(mod)
|
51
|
+
modules = mod.name.split('::')
|
52
|
+
expect_symbol = nil
|
53
|
+
|
54
|
+
while modules.any? && expect_symbol.nil?
|
55
|
+
current_module = modules.join('::')
|
56
|
+
expect_symbol = @key_expectations[current_module]
|
57
|
+
@key_expectations_used[current_module] = true
|
58
|
+
modules.pop
|
59
|
+
end
|
60
|
+
|
61
|
+
expect_symbol
|
62
|
+
end
|
63
|
+
|
20
64
|
def self.get_organisation(mod)
|
21
65
|
name = mod.name
|
22
66
|
prefixes = @organisation_prefixes.keys.select { |p| name.start_with? p }
|
23
67
|
return nil unless prefixes.any?
|
68
|
+
|
24
69
|
best = prefixes.max_by { |p| p.length }
|
25
70
|
|
26
71
|
@organisation_prefixes[best]
|
27
72
|
end
|
28
73
|
end
|
29
|
-
|
30
|
-
|
@@ -28,11 +28,6 @@ module MediaTypes
|
|
28
28
|
with(view: view)
|
29
29
|
end
|
30
30
|
|
31
|
-
def suffix(suffix = NO_ARG)
|
32
|
-
return opts[:suffix] if suffix == NO_ARG
|
33
|
-
with(suffix: suffix)
|
34
|
-
end
|
35
|
-
|
36
31
|
def collection
|
37
32
|
view(COLLECTION_VIEW)
|
38
33
|
end
|
@@ -74,13 +69,24 @@ module MediaTypes
|
|
74
69
|
end
|
75
70
|
|
76
71
|
def as_key
|
77
|
-
[type, view, version
|
72
|
+
[type, view&.to_s, version]
|
78
73
|
end
|
79
74
|
|
80
75
|
def hash
|
81
76
|
as_key.hash
|
82
77
|
end
|
83
78
|
|
79
|
+
def override_suffix(suffix)
|
80
|
+
with(suffix: suffix)
|
81
|
+
end
|
82
|
+
|
83
|
+
def suffix
|
84
|
+
return opts[:suffix] if opts.key?(:suffix)
|
85
|
+
|
86
|
+
schema = schema_for(self)
|
87
|
+
schema.type_attributes.fetch(:suffix, 'json')
|
88
|
+
end
|
89
|
+
|
84
90
|
def to_str(qualifier = nil)
|
85
91
|
qualified(
|
86
92
|
qualifier,
|
@@ -88,11 +94,11 @@ module MediaTypes
|
|
88
94
|
type: opts[:type],
|
89
95
|
view: opts[:view],
|
90
96
|
version: opts[:version],
|
91
|
-
suffix:
|
97
|
+
suffix: suffix
|
92
98
|
)
|
93
99
|
)
|
94
100
|
end
|
95
|
-
|
101
|
+
|
96
102
|
def available_validations
|
97
103
|
return [] if !validatable?
|
98
104
|
[self]
|
@@ -120,7 +126,7 @@ module MediaTypes
|
|
120
126
|
|
121
127
|
def validatable?
|
122
128
|
return false unless media_type_combinations.include? as_key
|
123
|
-
|
129
|
+
|
124
130
|
__getobj__.validatable?(self)
|
125
131
|
end
|
126
132
|
|
data/lib/media_types/dsl.rb
CHANGED
@@ -1,55 +1,74 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'set'
|
4
|
+
|
3
5
|
require 'media_types/constructable'
|
4
|
-
require 'media_types/defaults'
|
5
|
-
require 'media_types/registrar'
|
6
6
|
require 'media_types/validations'
|
7
7
|
|
8
|
+
require 'media_types/dsl/errors'
|
9
|
+
|
8
10
|
module MediaTypes
|
9
11
|
module Dsl
|
10
|
-
|
11
12
|
def self.included(base)
|
12
13
|
base.extend ClassMethods
|
13
14
|
base.class_eval do
|
14
15
|
class << self
|
15
|
-
attr_accessor :media_type_name_for, :media_type_combinations
|
16
|
+
attr_accessor :media_type_name_for, :media_type_combinations, :media_type_validations, :symbol_keys
|
16
17
|
|
17
18
|
private
|
18
19
|
|
19
|
-
attr_accessor :media_type_constructable, :symbol_base, :media_type_registrar
|
20
|
+
attr_accessor :media_type_constructable, :symbol_base, :media_type_registrar
|
20
21
|
end
|
21
22
|
base.media_type_combinations = Set.new
|
22
23
|
end
|
23
24
|
end
|
24
25
|
|
25
26
|
module ClassMethods
|
26
|
-
|
27
27
|
def to_constructable
|
28
|
+
raise UninitializedConstructable if media_type_constructable.nil?
|
29
|
+
|
28
30
|
media_type_constructable.dup.tap do |constructable|
|
29
31
|
constructable.__setobj__(self)
|
30
32
|
end
|
31
33
|
end
|
32
34
|
|
35
|
+
def symbol_keys?
|
36
|
+
if symbol_keys.nil?
|
37
|
+
MediaTypes.get_key_expectation(self)
|
38
|
+
else
|
39
|
+
symbol_keys
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def string_keys?
|
44
|
+
!symbol_keys?
|
45
|
+
end
|
46
|
+
|
33
47
|
def valid?(output, **opts)
|
34
48
|
to_constructable.valid?(output, **opts)
|
35
49
|
end
|
36
50
|
|
37
51
|
def valid_unsafe?(output, media_type = to_constructable, **opts)
|
52
|
+
opts[:expected_key_type] = string_keys? ? String : Symbol
|
38
53
|
validations.find(media_type).valid?(output, backtrace: ['.'], **opts)
|
39
54
|
end
|
40
|
-
|
55
|
+
|
41
56
|
def validate!(output, **opts)
|
57
|
+
assert_sane!
|
42
58
|
to_constructable.validate!(output, **opts)
|
43
59
|
end
|
44
60
|
|
45
61
|
def validate_unsafe!(output, media_type = to_constructable, **opts)
|
62
|
+
opts[:expected_key_type] = string_keys? ? String : Symbol
|
46
63
|
validations.find(media_type).validate(output, backtrace: ['.'], **opts)
|
47
64
|
end
|
48
65
|
|
49
66
|
def validatable?(media_type = to_constructable)
|
50
67
|
return false unless validations
|
51
68
|
|
52
|
-
validations.find(media_type, -> { nil })
|
69
|
+
resolved = validations.find(media_type, -> { nil })
|
70
|
+
|
71
|
+
!resolved.nil?
|
53
72
|
end
|
54
73
|
|
55
74
|
def register
|
@@ -58,19 +77,17 @@ module MediaTypes
|
|
58
77
|
registerable
|
59
78
|
end
|
60
79
|
end
|
61
|
-
|
80
|
+
|
62
81
|
def view(v)
|
63
82
|
to_constructable.view(v)
|
64
83
|
end
|
84
|
+
|
65
85
|
def version(v)
|
66
86
|
to_constructable.version(v)
|
67
87
|
end
|
68
|
-
def suffix(s)
|
69
|
-
to_constructable.suffix(s)
|
70
|
-
end
|
71
88
|
|
72
89
|
def identifier_format
|
73
|
-
self.media_type_name_for =
|
90
|
+
self.media_type_name_for = proc do |type:, view:, version:, suffix:|
|
74
91
|
yield(type: type, view: view, version: version, suffix: suffix)
|
75
92
|
end
|
76
93
|
end
|
@@ -80,24 +97,37 @@ module MediaTypes
|
|
80
97
|
end
|
81
98
|
|
82
99
|
def available_validations
|
83
|
-
|
84
|
-
_, view, version
|
85
|
-
view(view).version(version)
|
100
|
+
media_type_combinations.map do |a|
|
101
|
+
_, view, version = a
|
102
|
+
view(view).version(version)
|
86
103
|
end
|
87
104
|
end
|
88
105
|
|
106
|
+
def schema_for(constructable)
|
107
|
+
validations.find(constructable)
|
108
|
+
end
|
109
|
+
|
110
|
+
def assert_sane!
|
111
|
+
return if media_type_validations.scheme.asserted_sane?
|
112
|
+
|
113
|
+
media_type_validations.run_fixture_validations(symbol_keys?)
|
114
|
+
end
|
115
|
+
|
89
116
|
private
|
90
117
|
|
91
|
-
def use_name(name
|
92
|
-
if
|
93
|
-
self.media_type_name_for =
|
118
|
+
def use_name(name)
|
119
|
+
if media_type_name_for.nil?
|
120
|
+
self.media_type_name_for = proc do |type:, view:, version:, suffix:|
|
94
121
|
resolved_org = nil
|
95
122
|
if defined?(organisation)
|
96
123
|
resolved_org = organisation
|
97
124
|
else
|
98
|
-
resolved_org = MediaTypes
|
125
|
+
resolved_org = MediaTypes.get_organisation(self)
|
99
126
|
|
100
|
-
|
127
|
+
if resolved_org.nil?
|
128
|
+
raise OrganisationNotSetError,
|
129
|
+
format('Implement the class method "organisation" in %<klass>s or specify a global organisation using MediaTypes::set_organisation', klass: self)
|
130
|
+
end
|
101
131
|
end
|
102
132
|
raise ArgumentError, 'Unable to create a name for a schema with a nil name.' if type.nil?
|
103
133
|
raise ArgumentError, 'Unable to create a name for a schema with a nil organisation.' if resolved_org.nil?
|
@@ -109,29 +139,34 @@ module MediaTypes
|
|
109
139
|
result
|
110
140
|
end
|
111
141
|
end
|
112
|
-
self.media_type_constructable = Constructable.new(self, type: name)
|
142
|
+
self.media_type_constructable = Constructable.new(self, type: name)
|
113
143
|
end
|
114
144
|
|
115
|
-
def
|
116
|
-
|
117
|
-
self.media_type_constructable = Defaults.new(to_constructable, &block).to_constructable
|
145
|
+
def expect_string_keys
|
146
|
+
raise KeyTypeExpectationError, 'Key expectation already set' unless symbol_keys.nil?
|
118
147
|
|
119
|
-
self
|
148
|
+
self.symbol_keys = false
|
120
149
|
end
|
121
150
|
|
122
|
-
def
|
123
|
-
|
124
|
-
self.media_type_registrar = Registrar.new(self, symbol: symbol, &block)
|
151
|
+
def expect_symbol_keys
|
152
|
+
raise KeyTypeExpectationError, 'Key expectation already set' unless symbol_keys.nil?
|
125
153
|
|
126
|
-
self
|
154
|
+
self.symbol_keys = true
|
127
155
|
end
|
128
156
|
|
129
157
|
def validations(&block)
|
130
|
-
return
|
158
|
+
return lookup_validations unless block_given?
|
159
|
+
|
131
160
|
self.media_type_validations = Validations.new(to_constructable, &block)
|
132
161
|
|
133
162
|
self
|
134
163
|
end
|
164
|
+
|
165
|
+
def lookup_validations
|
166
|
+
raise MissingValidationError, "No validations defined for #{name}" if media_type_validations.nil?
|
167
|
+
|
168
|
+
media_type_validations
|
169
|
+
end
|
135
170
|
end
|
136
171
|
end
|
137
172
|
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module MediaTypes
|
4
|
+
module Dsl
|
5
|
+
class UninitializedConstructable < RuntimeError
|
6
|
+
def message
|
7
|
+
'Unable to generate constructable without a name, make sure to have called `use_name(name)` before.'
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
# Raised when an error occurs during setting expected key type
|
12
|
+
class KeyTypeExpectationError < StandardError; end
|
13
|
+
|
14
|
+
class MissingValidationError < StandardError; end
|
15
|
+
|
16
|
+
class OrganisationNotSetError < StandardError; end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module MediaTypes
|
4
|
+
module Errors
|
5
|
+
# Raised when trying to set a module key expectation twice
|
6
|
+
class KeyExpectationSetError < StandardError
|
7
|
+
def initialize(mod:)
|
8
|
+
super(format('%<mod>s already has a key expectation set', mod: mod.name))
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
# Raised when trying to set a module key expectation while default expectation already used
|
13
|
+
class KeyExpectationUsedError < StandardError
|
14
|
+
def initialize(mod:)
|
15
|
+
super(format('Unable to change key type expectation for %<mod>s since its current expectation is already used', mod: mod.name))
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
data/lib/media_types/scheme.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'json'
|
4
|
+
|
3
5
|
require 'media_types/scheme/validation_options'
|
4
6
|
require 'media_types/scheme/enumeration_context'
|
5
7
|
require 'media_types/scheme/errors'
|
@@ -17,10 +19,47 @@ require 'media_types/scheme/output_empty_guard'
|
|
17
19
|
require 'media_types/scheme/output_type_guard'
|
18
20
|
require 'media_types/scheme/rules_exhausted_guard'
|
19
21
|
|
20
|
-
require 'json'
|
21
|
-
|
22
22
|
module MediaTypes
|
23
23
|
class AssertionError < StandardError
|
24
|
+
def initialize(errors)
|
25
|
+
@fixture_errors = errors
|
26
|
+
end
|
27
|
+
|
28
|
+
def message
|
29
|
+
fixture_errors.map(&:message).join(', ')
|
30
|
+
end
|
31
|
+
|
32
|
+
attr_reader :fixture_errors
|
33
|
+
end
|
34
|
+
|
35
|
+
class UnexpectedValidationResultError < StandardError
|
36
|
+
def initialize(fixture_caller, error)
|
37
|
+
self.fixture_caller = fixture_caller
|
38
|
+
self.error = error
|
39
|
+
end
|
40
|
+
|
41
|
+
def message
|
42
|
+
format(
|
43
|
+
'%<caller_path>s:%<caller_line>s -> %<error>s',
|
44
|
+
caller_path: fixture_caller.path,
|
45
|
+
caller_line: fixture_caller.lineno,
|
46
|
+
error: error.is_a?(MediaTypes::Scheme::ValidationError) ? "#{error.class}:#{error.message}" : error
|
47
|
+
)
|
48
|
+
end
|
49
|
+
|
50
|
+
attr_accessor :fixture_caller, :error
|
51
|
+
end
|
52
|
+
|
53
|
+
class FixtureData
|
54
|
+
def initialize(caller:, fixture:, expect_to_pass:)
|
55
|
+
self.caller = caller
|
56
|
+
self.fixture = fixture
|
57
|
+
self.expect_to_pass = expect_to_pass
|
58
|
+
end
|
59
|
+
|
60
|
+
attr_accessor :caller, :fixture, :expect_to_pass
|
61
|
+
|
62
|
+
alias expect_to_pass? expect_to_pass
|
24
63
|
end
|
25
64
|
|
26
65
|
##
|
@@ -56,10 +95,18 @@ module MediaTypes
|
|
56
95
|
#
|
57
96
|
def initialize(allow_empty: false, expected_type: ::Object, &block)
|
58
97
|
self.rules = Rules.new(allow_empty: allow_empty, expected_type: expected_type)
|
98
|
+
self.type_attributes = {}
|
99
|
+
self.fixtures = []
|
100
|
+
self.asserted_sane = false
|
59
101
|
|
60
102
|
instance_exec(&block) if block_given?
|
61
103
|
end
|
62
104
|
|
105
|
+
attr_accessor :type_attributes, :fixtures
|
106
|
+
attr_reader :rules, :asserted_sane
|
107
|
+
|
108
|
+
alias asserted_sane? asserted_sane
|
109
|
+
|
63
110
|
##
|
64
111
|
# Checks if the +output+ is valid
|
65
112
|
#
|
@@ -98,6 +145,7 @@ module MediaTypes
|
|
98
145
|
#
|
99
146
|
def validate(output, options = nil, **opts)
|
100
147
|
options ||= ValidationOptions.new(**opts)
|
148
|
+
options.context = output
|
101
149
|
|
102
150
|
catch(:end) do
|
103
151
|
validate!(output, options, context: nil)
|
@@ -154,7 +202,11 @@ module MediaTypes
|
|
154
202
|
# MyMedia.valid?({ foo: { bar: 'my-string' }})
|
155
203
|
# # => true
|
156
204
|
#
|
157
|
-
def attribute(key, type =
|
205
|
+
def attribute(key, type = nil, optional: false, **opts, &block)
|
206
|
+
raise ConflictingTypeDefinitionError, 'You cannot apply a block to a non-hash typed attribute, either remove the type or the block' if type != ::Hash && block_given? && !type.nil?
|
207
|
+
|
208
|
+
type ||= ::Object
|
209
|
+
|
158
210
|
if block_given?
|
159
211
|
return collection(key, expected_type: ::Hash, optional: optional, **opts, &block)
|
160
212
|
end
|
@@ -207,6 +259,8 @@ module MediaTypes
|
|
207
259
|
# # => true
|
208
260
|
#
|
209
261
|
def any(scheme = nil, expected_type: ::Hash, allow_empty: false, &block)
|
262
|
+
raise ConflictingTypeDefinitionError, 'You cannot apply a block to a non-hash typed property, either remove the type or the block' if scheme != ::Hash && block_given? && !scheme.nil?
|
263
|
+
|
210
264
|
unless block_given?
|
211
265
|
if scheme.is_a?(Scheme)
|
212
266
|
return rules.default = scheme
|
@@ -294,6 +348,8 @@ module MediaTypes
|
|
294
348
|
# # => true
|
295
349
|
#
|
296
350
|
def collection(key, scheme = nil, allow_empty: false, expected_type: ::Array, optional: false, &block)
|
351
|
+
raise ConflictingTypeDefinitionError, 'You cannot apply a block to a non-hash typed collection, either remove the type or the block' if scheme != ::Hash && block_given? && !scheme.nil?
|
352
|
+
|
297
353
|
unless block_given?
|
298
354
|
return rules.add(
|
299
355
|
key,
|
@@ -376,24 +432,82 @@ module MediaTypes
|
|
376
432
|
end
|
377
433
|
|
378
434
|
def assert_pass(fixture)
|
379
|
-
|
380
|
-
|
381
|
-
validate(json)
|
435
|
+
reduced_stack = remove_current_dir_from_stack(caller_locations)
|
436
|
+
@fixtures << FixtureData.new(caller: reduced_stack.first, fixture: fixture, expect_to_pass: true)
|
382
437
|
end
|
383
|
-
|
438
|
+
|
384
439
|
def assert_fail(fixture)
|
385
|
-
|
440
|
+
reduced_stack = remove_current_dir_from_stack(caller_locations)
|
441
|
+
@fixtures << FixtureData.new(caller: reduced_stack.first, fixture: fixture, expect_to_pass: false)
|
442
|
+
end
|
443
|
+
|
444
|
+
# Removes all calls originating in current dir from given stack
|
445
|
+
# We need this so that we find out the caller of an assert_pass/fail in the caller_locations
|
446
|
+
# Which gets polluted by Scheme consecutively executing blocks within the validation blocks
|
447
|
+
def remove_current_dir_from_stack(stack)
|
448
|
+
stack.reject { |location| location.path.include?(__dir__) }
|
449
|
+
end
|
450
|
+
|
451
|
+
def validate_scheme_fixtures(expect_symbol_keys, backtrace)
|
452
|
+
@fixtures.map do |fixture_data|
|
453
|
+
begin
|
454
|
+
validate_fixture(fixture_data, expect_symbol_keys, backtrace)
|
455
|
+
nil
|
456
|
+
rescue UnexpectedValidationResultError => e
|
457
|
+
e
|
458
|
+
end
|
459
|
+
end.compact
|
460
|
+
end
|
461
|
+
|
462
|
+
def validate_nested_scheme_fixtures(expect_symbol_keys, backtrace)
|
463
|
+
@rules.flat_map do |key, rule|
|
464
|
+
next unless rule.is_a?(Scheme) || rule.is_a?(Links)
|
465
|
+
|
466
|
+
begin
|
467
|
+
rule.run_fixture_validations(expect_symbol_keys, backtrace.dup.append(key))
|
468
|
+
nil
|
469
|
+
rescue AssertionError => e
|
470
|
+
e.fixture_errors
|
471
|
+
end
|
472
|
+
end.compact
|
473
|
+
end
|
474
|
+
|
475
|
+
def validate_default_scheme_fixtures(expect_symbol_keys, backtrace)
|
476
|
+
return [] unless @rules.default.is_a?(Scheme)
|
477
|
+
|
478
|
+
@rules.default.run_fixture_validations(expect_symbol_keys, backtrace.dup.append('*'))
|
479
|
+
[]
|
480
|
+
rescue AssertionError => e
|
481
|
+
e.fixture_errors
|
482
|
+
end
|
483
|
+
|
484
|
+
def run_fixture_validations(expect_symbol_keys, backtrace = [])
|
485
|
+
fixture_errors = validate_scheme_fixtures(expect_symbol_keys, backtrace)
|
486
|
+
fixture_errors += validate_nested_scheme_fixtures(expect_symbol_keys, backtrace)
|
487
|
+
fixture_errors += validate_default_scheme_fixtures(expect_symbol_keys, backtrace)
|
488
|
+
|
489
|
+
raise AssertionError, fixture_errors unless fixture_errors.empty?
|
490
|
+
|
491
|
+
self.asserted_sane = true
|
492
|
+
end
|
493
|
+
|
494
|
+
def validate_fixture(fixture_data, expect_symbol_keys, backtrace = [])
|
495
|
+
json = JSON.parse(fixture_data.fixture, { symbolize_names: expect_symbol_keys })
|
496
|
+
expected_key_type = expect_symbol_keys ? Symbol : String
|
386
497
|
|
387
498
|
begin
|
388
|
-
validate(json)
|
389
|
-
|
390
|
-
|
499
|
+
validate(json, expected_key_type: expected_key_type, backtrace: backtrace)
|
500
|
+
unless fixture_data.expect_to_pass?
|
501
|
+
raise UnexpectedValidationResultError.new(fixture_data.caller, 'No error encounterd whilst expecting to')
|
502
|
+
end
|
503
|
+
rescue MediaTypes::Scheme::ValidationError => e
|
504
|
+
raise UnexpectedValidationResultError.new(fixture_data.caller, e) if fixture_data.expect_to_pass?
|
391
505
|
end
|
392
|
-
raise AssertionError
|
393
506
|
end
|
394
507
|
|
395
508
|
private
|
396
509
|
|
397
|
-
|
510
|
+
attr_writer :rules, :asserted_sane
|
511
|
+
|
398
512
|
end
|
399
513
|
end
|