spec_forge 0.5.0 → 0.6.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.
Files changed (71) hide show
  1. checksums.yaml +4 -4
  2. data/.standard.yml +3 -3
  3. data/CHANGELOG.md +106 -1
  4. data/README.md +34 -22
  5. data/flake.lock +3 -3
  6. data/flake.nix +8 -2
  7. data/lib/spec_forge/attribute/chainable.rb +208 -20
  8. data/lib/spec_forge/attribute/factory.rb +91 -14
  9. data/lib/spec_forge/attribute/faker.rb +62 -13
  10. data/lib/spec_forge/attribute/global.rb +96 -0
  11. data/lib/spec_forge/attribute/literal.rb +15 -2
  12. data/lib/spec_forge/attribute/matcher.rb +186 -11
  13. data/lib/spec_forge/attribute/parameterized.rb +45 -12
  14. data/lib/spec_forge/attribute/regex.rb +55 -5
  15. data/lib/spec_forge/attribute/resolvable.rb +48 -5
  16. data/lib/spec_forge/attribute/resolvable_array.rb +62 -4
  17. data/lib/spec_forge/attribute/resolvable_hash.rb +62 -4
  18. data/lib/spec_forge/attribute/store.rb +65 -0
  19. data/lib/spec_forge/attribute/transform.rb +33 -5
  20. data/lib/spec_forge/attribute/variable.rb +37 -6
  21. data/lib/spec_forge/attribute.rb +166 -66
  22. data/lib/spec_forge/backtrace_formatter.rb +26 -3
  23. data/lib/spec_forge/callbacks.rb +79 -0
  24. data/lib/spec_forge/cli/actions.rb +27 -0
  25. data/lib/spec_forge/cli/command.rb +78 -24
  26. data/lib/spec_forge/cli/init.rb +11 -1
  27. data/lib/spec_forge/cli/new.rb +54 -3
  28. data/lib/spec_forge/cli/run.rb +20 -0
  29. data/lib/spec_forge/cli.rb +16 -5
  30. data/lib/spec_forge/configuration.rb +94 -22
  31. data/lib/spec_forge/context/callbacks.rb +91 -0
  32. data/lib/spec_forge/context/global.rb +72 -0
  33. data/lib/spec_forge/context/store.rb +148 -0
  34. data/lib/spec_forge/context/variables.rb +91 -0
  35. data/lib/spec_forge/context.rb +36 -0
  36. data/lib/spec_forge/core_ext/rspec.rb +22 -4
  37. data/lib/spec_forge/error.rb +267 -113
  38. data/lib/spec_forge/factory.rb +33 -14
  39. data/lib/spec_forge/filter.rb +87 -0
  40. data/lib/spec_forge/forge.rb +170 -0
  41. data/lib/spec_forge/http/backend.rb +99 -29
  42. data/lib/spec_forge/http/client.rb +23 -13
  43. data/lib/spec_forge/http/request.rb +74 -62
  44. data/lib/spec_forge/http/verb.rb +79 -0
  45. data/lib/spec_forge/http.rb +105 -0
  46. data/lib/spec_forge/loader.rb +254 -0
  47. data/lib/spec_forge/matchers.rb +130 -0
  48. data/lib/spec_forge/normalizer/configuration.rb +24 -11
  49. data/lib/spec_forge/normalizer/constraint.rb +21 -8
  50. data/lib/spec_forge/normalizer/expectation.rb +31 -12
  51. data/lib/spec_forge/normalizer/factory.rb +24 -11
  52. data/lib/spec_forge/normalizer/factory_reference.rb +27 -13
  53. data/lib/spec_forge/normalizer/global_context.rb +88 -0
  54. data/lib/spec_forge/normalizer/spec.rb +39 -16
  55. data/lib/spec_forge/normalizer.rb +255 -41
  56. data/lib/spec_forge/runner/callbacks.rb +246 -0
  57. data/lib/spec_forge/runner/debug_proxy.rb +213 -0
  58. data/lib/spec_forge/runner/listener.rb +54 -0
  59. data/lib/spec_forge/runner/metadata.rb +58 -0
  60. data/lib/spec_forge/runner/state.rb +99 -0
  61. data/lib/spec_forge/runner.rb +132 -123
  62. data/lib/spec_forge/spec/expectation/constraint.rb +91 -20
  63. data/lib/spec_forge/spec/expectation.rb +43 -51
  64. data/lib/spec_forge/spec.rb +83 -96
  65. data/lib/spec_forge/type.rb +36 -4
  66. data/lib/spec_forge/version.rb +4 -1
  67. data/lib/spec_forge.rb +161 -76
  68. metadata +20 -5
  69. data/spec_forge/factories/user.yml +0 -4
  70. data/spec_forge/forge_helper.rb +0 -48
  71. data/spec_forge/specs/users.yml +0 -65
@@ -2,10 +2,26 @@
2
2
 
3
3
  module SpecForge
4
4
  class Normalizer
5
+ #
6
+ # Normalizes constraint hash structure for expectations
7
+ #
8
+ # Ensures that expectation constraints (status, json, etc.)
9
+ # have the correct structure and defaults.
10
+ #
5
11
  class Constraint < Normalizer
12
+ #
13
+ # Defines the normalized structure for configuration validation
14
+ #
15
+ # Specifies validation rules for configuration attributes:
16
+ # - Enforces specific data types
17
+ # - Provides default values
18
+ # - Supports alternative key names
19
+ #
20
+ # @return [Hash] Configuration attribute validation rules
21
+ #
6
22
  STRUCTURE = {
7
23
  status: {
8
- type: Integer
24
+ type: [Integer, String]
9
25
  },
10
26
  json: {
11
27
  type: [Hash, Array],
@@ -19,7 +35,7 @@ module SpecForge
19
35
  #
20
36
  # Generates an empty constraint hash
21
37
  #
22
- # @return [Hash]
38
+ # @return [Hash] Default constraint hash
23
39
  #
24
40
  def default_constraint
25
41
  Constraint.default
@@ -27,18 +43,15 @@ module SpecForge
27
43
 
28
44
  #
29
45
  # Normalize a constraint hash
30
- # Used internally by .normalize_spec, but is available for utility
31
46
  #
32
- # @param constraint [Hash] Constraint representation as a Hash
47
+ # @param constraint [Hash] Constraint hash
33
48
  #
34
- # @return [Array] Two item array
35
- # First - The normalized hash
36
- # Second - Array of errors, if any
49
+ # @return [Array] [normalized_hash, errors]
37
50
  #
38
51
  # @private
39
52
  #
40
53
  def normalize_constraint(constraint)
41
- raise InvalidTypeError.new(constraint, Hash, for: "expect") unless Type.hash?(constraint)
54
+ raise Error::InvalidTypeError.new(constraint, Hash, for: "expect") unless Type.hash?(constraint)
42
55
 
43
56
  Normalizer::Constraint.new("expect", constraint).normalize
44
57
  end
@@ -2,17 +2,39 @@
2
2
 
3
3
  module SpecForge
4
4
  class Normalizer
5
+ #
6
+ # Normalizes expectation hash structure
7
+ #
8
+ # Ensures that expectation definitions have the correct structure
9
+ # and default values for all required settings.
10
+ #
5
11
  class Expectation < Normalizer
12
+ #
13
+ # Defines the normalized structure for configuration validation
14
+ #
15
+ # Specifies validation rules for configuration attributes:
16
+ # - Enforces specific data types
17
+ # - Provides default values
18
+ # - Supports alternative key names
19
+ #
20
+ # @return [Hash] Configuration attribute validation rules
21
+ #
6
22
  STRUCTURE = {
7
- name: {type: String, default: ""},
23
+ # Internal
24
+ id: Normalizer::SHARED_ATTRIBUTES[:id],
25
+ line_number: Normalizer::SHARED_ATTRIBUTES[:line_number],
26
+
27
+ # User defined
28
+ name: Normalizer::SHARED_ATTRIBUTES[:name],
8
29
  base_url: Normalizer::SHARED_ATTRIBUTES[:base_url],
9
30
  url: Normalizer::SHARED_ATTRIBUTES[:url],
10
- http_method: Normalizer::SHARED_ATTRIBUTES[:http_method],
31
+ http_verb: Normalizer::SHARED_ATTRIBUTES[:http_verb],
11
32
  headers: Normalizer::SHARED_ATTRIBUTES[:headers],
12
33
  query: Normalizer::SHARED_ATTRIBUTES[:query],
13
34
  body: Normalizer::SHARED_ATTRIBUTES[:body],
14
35
  variables: Normalizer::SHARED_ATTRIBUTES[:variables],
15
36
  debug: Normalizer::SHARED_ATTRIBUTES[:debug],
37
+ store_as: {type: String, default: ""},
16
38
  expect: {type: Hash}
17
39
  }.freeze
18
40
  end
@@ -22,7 +44,7 @@ module SpecForge
22
44
  #
23
45
  # Generates an empty expectation hash
24
46
  #
25
- # @return [Hash]
47
+ # @return [Hash] Default expectation hash
26
48
  #
27
49
  def default_expectation
28
50
  Expectation.default
@@ -31,11 +53,11 @@ module SpecForge
31
53
  #
32
54
  # Normalize an array of expectation hashes
33
55
  #
34
- # @raises InvalidStructureError if anything is missing/invalid type
56
+ # @param input [Array<Hash>] The array to normalize
35
57
  #
36
- # @param input [Hash] The hash to normalize
58
+ # @return [Array<Hash>] Normalized array of expectation hashes
37
59
  #
38
- # @return [Hash] A normalized hash as a new instance
60
+ # @raise [Error::InvalidStructureError] If validation fails
39
61
  #
40
62
  def normalize_expectations!(input)
41
63
  raise_errors! do
@@ -45,19 +67,16 @@ module SpecForge
45
67
 
46
68
  #
47
69
  # Normalize an array of expectation hashes
48
- # Used internally by .normalize_spec, but is available for utility
49
70
  #
50
- # @param expectations [Array<Hash>] An array of expectation hashes
71
+ # @param expectations [Array<Hash>] Array of expectation hashes
51
72
  #
52
- # @return [Array] Two item array
53
- # First - The normalized Array<Hash>
54
- # Second - Array of errors, if any
73
+ # @return [Array] [normalized_array, errors]
55
74
  #
56
75
  # @private
57
76
  #
58
77
  def normalize_expectations(expectations)
59
78
  if !Type.array?(expectations)
60
- raise InvalidTypeError.new(expectations, Array, for: "\"expectations\" on spec")
79
+ raise Error::InvalidTypeError.new(expectations, Array, for: "\"expectations\" on spec")
61
80
  end
62
81
 
63
82
  final_errors = Set.new
@@ -2,7 +2,23 @@
2
2
 
3
3
  module SpecForge
4
4
  class Normalizer
5
+ #
6
+ # Normalizes factory hash structure
7
+ #
8
+ # Ensures that factory definitions have the correct structure
9
+ # and default values for all required settings.
10
+ #
5
11
  class Factory < Normalizer
12
+ #
13
+ # Defines the normalized structure for configuration validation
14
+ #
15
+ # Specifies validation rules for configuration attributes:
16
+ # - Enforces specific data types
17
+ # - Provides default values
18
+ # - Supports alternative key names
19
+ #
20
+ # @return [Hash] Configuration attribute validation rules
21
+ #
6
22
  STRUCTURE = {
7
23
  model_class: {
8
24
  type: String,
@@ -22,20 +38,20 @@ module SpecForge
22
38
  #
23
39
  # Generates an empty factory hash
24
40
  #
25
- # @return [Hash]
41
+ # @return [Hash] Default factory hash
26
42
  #
27
43
  def default_factory
28
44
  Factory.default
29
45
  end
30
46
 
31
47
  #
32
- # Normalizes a factory hash by standardizing its keys while ensuring the required data
33
- # is provided or defaulted.
34
- # Raises InvalidStructureError if anything is missing/invalid type
48
+ # Normalizes a factory hash with validation
35
49
  #
36
50
  # @param input [Hash] The hash to normalize
37
51
  #
38
- # @return [Hash] A normalized hash as a new instance
52
+ # @return [Hash] A normalized hash with defaults applied
53
+ #
54
+ # @raise [Error::InvalidStructureError] If validation fails
39
55
  #
40
56
  def normalize_factory!(input)
41
57
  raise_errors! do
@@ -45,18 +61,15 @@ module SpecForge
45
61
 
46
62
  #
47
63
  # Normalize a factory hash
48
- # Used internally by .normalize_factory, but is available for utility
49
64
  #
50
- # @param factory [Hash] Factory representation as a Hash
65
+ # @param factory [Hash] Factory hash
51
66
  #
52
- # @return [Array] Two item array
53
- # First - The normalized hash
54
- # Second - Array of errors, if any
67
+ # @return [Array] [normalized_hash, errors]
55
68
  #
56
69
  # @private
57
70
  #
58
71
  def normalize_factory(factory)
59
- raise InvalidTypeError.new(factory, Hash, for: "factory") unless Type.hash?(factory)
72
+ raise Error::InvalidTypeError.new(factory, Hash, for: "factory") unless Type.hash?(factory)
60
73
 
61
74
  Normalizer::Factory.new("factory", factory).normalize
62
75
  end
@@ -2,7 +2,23 @@
2
2
 
3
3
  module SpecForge
4
4
  class Normalizer
5
+ #
6
+ # Normalizes factory reference hash structure
7
+ #
8
+ # Ensures that factory references have the correct structure
9
+ # and default values for all required settings.
10
+ #
5
11
  class FactoryReference < Normalizer
12
+ #
13
+ # Defines the normalized structure for configuration validation
14
+ #
15
+ # Specifies validation rules for configuration attributes:
16
+ # - Enforces specific data types
17
+ # - Provides default values
18
+ # - Supports alternative key names
19
+ #
20
+ # @return [Hash] Configuration attribute validation rules
21
+ #
6
22
  STRUCTURE = {
7
23
  attributes: {
8
24
  type: Hash,
@@ -24,22 +40,22 @@ module SpecForge
24
40
  # On Normalizer
25
41
  class << self
26
42
  #
27
- # Generates an empty Attribute::Factory hash
43
+ # Generates an empty factory reference hash
28
44
  #
29
- # @return [Hash]
45
+ # @return [Hash] Default factory reference hash
30
46
  #
31
47
  def default_factory_reference
32
48
  FactoryReference.default
33
49
  end
34
50
 
35
51
  #
36
- # Normalizes a Attribute::Factory hash by standardizing
37
- # its keys while ensuring the required data is provided or defaulted.
38
- # Raises InvalidStructureError if anything is missing/invalid type
52
+ # Normalizes a factory reference hash with validation
39
53
  #
40
54
  # @param input [Hash] The hash to normalize
41
55
  #
42
- # @return [Hash] A normalized hash as a new instance
56
+ # @return [Hash] A normalized hash with defaults applied
57
+ #
58
+ # @raise [Error::InvalidStructureError] If validation fails
43
59
  #
44
60
  def normalize_factory_reference!(input, **)
45
61
  raise_errors! do
@@ -48,20 +64,18 @@ module SpecForge
48
64
  end
49
65
 
50
66
  #
51
- # Normalize a factory hash
52
- # Used internally by .normalize_factory_reference, but is available for utility
67
+ # Normalize a factory reference hash
53
68
  #
54
- # @param factory [Hash] Attribute::Factory representation as a Hash
69
+ # @param factory [Hash] Factory reference hash
70
+ # @param label [String] Label for error messages
55
71
  #
56
- # @return [Array] Two item array
57
- # First - The normalized hash
58
- # Second - Array of errors, if any
72
+ # @return [Array] [normalized_hash, errors]
59
73
  #
60
74
  # @private
61
75
  #
62
76
  def normalize_factory_reference(factory, label: "factory reference")
63
77
  if !Type.hash?(factory)
64
- raise InvalidTypeError.new(factory, Hash, for: "factory reference")
78
+ raise Error::InvalidTypeError.new(factory, Hash, for: "factory reference")
65
79
  end
66
80
 
67
81
  Normalizer::FactoryReference.new(label, factory).normalize
@@ -0,0 +1,88 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SpecForge
4
+ class Normalizer
5
+ #
6
+ # Normalizes global context hash structure
7
+ #
8
+ # Ensures that global context definitions have the correct structure
9
+ # and default values for all required settings.
10
+ #
11
+ class GlobalContext < Normalizer
12
+ #
13
+ # Defines the normalized structure for configuration validation
14
+ #
15
+ # Specifies validation rules for configuration attributes:
16
+ # - Enforces specific data types
17
+ # - Provides default values
18
+ # - Supports alternative key names
19
+ #
20
+ # @return [Hash] Configuration attribute validation rules
21
+ #
22
+ STRUCTURE = {
23
+ variables: Normalizer::SHARED_ATTRIBUTES[:variables],
24
+ callbacks: {
25
+ type: Array,
26
+ default: [],
27
+ structure: {
28
+ type: Hash,
29
+ default: {},
30
+ structure: {
31
+ before_file: Normalizer::SHARED_ATTRIBUTES[:callback],
32
+ before_spec: Normalizer::SHARED_ATTRIBUTES[:callback],
33
+ before_each: Normalizer::SHARED_ATTRIBUTES[:callback].merge(aliases: %i[before]),
34
+ around_each: Normalizer::SHARED_ATTRIBUTES[:callback].merge(aliases: %i[around]),
35
+ after_each: Normalizer::SHARED_ATTRIBUTES[:callback].merge(aliases: %i[after]),
36
+ after_spec: Normalizer::SHARED_ATTRIBUTES[:callback],
37
+ after_file: Normalizer::SHARED_ATTRIBUTES[:callback]
38
+ }
39
+ }
40
+ }
41
+ }.freeze
42
+ end
43
+
44
+ # On Normalizer
45
+ class << self
46
+ #
47
+ # Generates an empty global context hash
48
+ #
49
+ # @return [Hash] Default global context hash
50
+ #
51
+ def default_global_context
52
+ GlobalContext.default
53
+ end
54
+
55
+ #
56
+ # Normalizes a global context hash with validation
57
+ #
58
+ # @param input [Hash] The hash to normalize
59
+ #
60
+ # @return [Hash] A normalized hash with defaults applied
61
+ #
62
+ # @raise [Error::InvalidStructureError] If validation fails
63
+ #
64
+ def normalize_global_context!(input)
65
+ raise_errors! do
66
+ normalize_global_context(input)
67
+ end
68
+ end
69
+
70
+ #
71
+ # Normalize a global context hash
72
+ #
73
+ # @param global [Hash] Global context hash
74
+ #
75
+ # @return [Array] [normalized_hash, errors]
76
+ #
77
+ # @private
78
+ #
79
+ def normalize_global_context(global)
80
+ if !Type.hash?(global)
81
+ raise Error::InvalidTypeError.new(global, Hash, for: "global context")
82
+ end
83
+
84
+ Normalizer::GlobalContext.new("global context", global).normalize
85
+ end
86
+ end
87
+ end
88
+ end
@@ -2,11 +2,35 @@
2
2
 
3
3
  module SpecForge
4
4
  class Normalizer
5
+ #
6
+ # Normalizes spec hash structure
7
+ #
8
+ # Ensures that spec definitions have the correct structure
9
+ # and default values for all required settings.
10
+ #
5
11
  class Spec < Normalizer
12
+ #
13
+ # Defines the normalized structure for configuration validation
14
+ #
15
+ # Specifies validation rules for configuration attributes:
16
+ # - Enforces specific data types
17
+ # - Provides default values
18
+ # - Supports alternative key names
19
+ #
20
+ # @return [Hash] Configuration attribute validation rules
21
+ #
6
22
  STRUCTURE = {
23
+ # Internal
24
+ id: Normalizer::SHARED_ATTRIBUTES[:id],
25
+ name: Normalizer::SHARED_ATTRIBUTES[:name],
26
+ file_name: {type: String},
27
+ file_path: {type: String},
28
+ line_number: Normalizer::SHARED_ATTRIBUTES[:line_number],
29
+
30
+ # User defined
7
31
  base_url: Normalizer::SHARED_ATTRIBUTES[:base_url],
8
32
  url: Normalizer::SHARED_ATTRIBUTES[:url],
9
- http_method: Normalizer::SHARED_ATTRIBUTES[:http_method],
33
+ http_verb: Normalizer::SHARED_ATTRIBUTES[:http_verb],
10
34
  headers: Normalizer::SHARED_ATTRIBUTES[:headers],
11
35
  query: Normalizer::SHARED_ATTRIBUTES[:query],
12
36
  body: Normalizer::SHARED_ATTRIBUTES[:body],
@@ -21,24 +45,25 @@ module SpecForge
21
45
  #
22
46
  # Generates an empty spec hash
23
47
  #
24
- # @return [Hash]
48
+ # @return [Hash] Default spec hash
25
49
  #
26
50
  def default_spec
27
51
  Spec.default
28
52
  end
29
53
 
30
54
  #
31
- # Normalizes a complete spec hash by standardizing its keys while ensuring the required data
32
- # is provided or defaulted.
33
- # Raises InvalidStructureError if anything is missing/invalid type
55
+ # Normalizes a spec hash with validation and processes expectations
34
56
  #
35
57
  # @param input [Hash] The hash to normalize
58
+ # @param label [String] Label for error messages
59
+ #
60
+ # @return [Hash] A normalized hash with defaults applied
36
61
  #
37
- # @return [Hash] A normalized hash as a new instance
62
+ # @raise [Error::InvalidStructureError] If validation fails
38
63
  #
39
- def normalize_spec!(input)
64
+ def normalize_spec!(input, label: "spec")
40
65
  raise_errors! do
41
- output, errors = normalize_spec(input)
66
+ output, errors = normalize_spec(input, label:)
42
67
 
43
68
  # Process expectations
44
69
  if (expectations = input[:expectations]) && Type.array?(expectations)
@@ -54,20 +79,18 @@ module SpecForge
54
79
 
55
80
  #
56
81
  # Normalize a spec hash
57
- # Used internally by .normalize_spec, but is available for utility
58
82
  #
59
- # @param spec [Hash] Spec representation as a Hash
83
+ # @param spec [Hash] Spec hash
84
+ # @param label [String] Label for error messages
60
85
  #
61
- # @return [Array] Two item array
62
- # First - The normalized hash
63
- # Second - Array of errors, if any
86
+ # @return [Array] [normalized_hash, errors]
64
87
  #
65
88
  # @private
66
89
  #
67
- def normalize_spec(spec)
68
- raise InvalidTypeError.new(spec, Hash, for: "spec") unless Type.hash?(spec)
90
+ def normalize_spec(spec, label: "spec")
91
+ raise Error::InvalidTypeError.new(spec, Hash, for: label) unless Type.hash?(spec)
69
92
 
70
- Normalizer::Spec.new("spec", spec).normalize
93
+ Normalizer::Spec.new(label, spec).normalize
71
94
  end
72
95
  end
73
96
  end