fixturama 0.0.7 → 0.4.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 (50) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +100 -0
  3. data/README.md +71 -3
  4. data/lib/fixturama.rb +49 -20
  5. data/lib/fixturama/changes.rb +72 -0
  6. data/lib/fixturama/changes/base.rb +28 -0
  7. data/lib/fixturama/changes/chain.rb +64 -0
  8. data/lib/fixturama/changes/chain/actions.rb +31 -0
  9. data/lib/fixturama/changes/chain/arguments.rb +59 -0
  10. data/lib/fixturama/changes/chain/raise_action.rb +43 -0
  11. data/lib/fixturama/changes/chain/return_action.rb +33 -0
  12. data/lib/fixturama/changes/const.rb +30 -0
  13. data/lib/fixturama/changes/env.rb +35 -0
  14. data/lib/fixturama/changes/request.rb +91 -0
  15. data/lib/fixturama/changes/request/response.rb +62 -0
  16. data/lib/fixturama/changes/request/responses.rb +22 -0
  17. data/lib/fixturama/changes/seed.rb +39 -0
  18. data/lib/fixturama/config.rb +5 -0
  19. data/lib/fixturama/fixture_error.rb +31 -0
  20. data/lib/fixturama/loader.rb +1 -0
  21. data/lib/fixturama/loader/context.rb +1 -0
  22. data/lib/fixturama/loader/value.rb +1 -0
  23. data/lib/fixturama/version.rb +4 -0
  24. metadata +80 -50
  25. data/.gitignore +0 -12
  26. data/.rspec +0 -2
  27. data/.rubocop.yml +0 -22
  28. data/.travis.yml +0 -23
  29. data/Gemfile +0 -9
  30. data/LICENSE.txt +0 -21
  31. data/Rakefile +0 -6
  32. data/fixturama.gemspec +0 -23
  33. data/lib/fixturama/seed.rb +0 -15
  34. data/lib/fixturama/stubs.rb +0 -57
  35. data/lib/fixturama/stubs/chain.rb +0 -89
  36. data/lib/fixturama/stubs/chain/actions.rb +0 -39
  37. data/lib/fixturama/stubs/chain/actions/raise.rb +0 -21
  38. data/lib/fixturama/stubs/chain/actions/return.rb +0 -23
  39. data/lib/fixturama/stubs/chain/arguments.rb +0 -86
  40. data/lib/fixturama/stubs/const.rb +0 -42
  41. data/lib/fixturama/utils.rb +0 -39
  42. data/spec/fixturama/load_fixture/_spec.rb +0 -53
  43. data/spec/fixturama/load_fixture/data.json +0 -5
  44. data/spec/fixturama/load_fixture/data.yaml +0 -3
  45. data/spec/fixturama/load_fixture/data.yml +0 -3
  46. data/spec/fixturama/seed_fixture/_spec.rb +0 -33
  47. data/spec/fixturama/seed_fixture/seed.yml +0 -16
  48. data/spec/fixturama/stub_fixture/_spec.rb +0 -96
  49. data/spec/fixturama/stub_fixture/stub.yml +0 -60
  50. data/spec/spec_helper.rb +0 -26
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 64d9722c641abfd4aa64fbf45b8f1177f768d1b8d2b99a89aa824e1f64db9417
4
- data.tar.gz: e1186c8944916943bbab46584747553b07b9d5cfc4e9d875d1a401fc8bd7017a
3
+ metadata.gz: 779a1dc8057604e6a5524eff0ef07520ecdbaf62895711411a312fb5c81a7558
4
+ data.tar.gz: 7976718e10f2fe8036d9e934d6e720cdcb91890f4263221720bfe76815eca071
5
5
  SHA512:
6
- metadata.gz: 29d44cd1b06c482ba40df2d4397c064888a64ae1f9a0a4151aa581bf81a83e9ca1a5e84c052340d515032d0f580f11418f9838a5efe00fd722401117248a0311
7
- data.tar.gz: 2b8a86f7fffdb20bae92839790254e052d2dbbbb39172003c9cfb4439620c782e4bc3f2ccdd985540dcba4d19e1fc228a3f7d940d58a865ad289f2f265427589
6
+ metadata.gz: 25047655a38dedca47af366563ad14c7f0bea54ddd0230097f6a940b127593a70a6144cf556670308dc1ad1048cce74180cecfe7c89871682602e5e5bbca0988
7
+ data.tar.gz: c23c3fe8662badf370ffc5cb02c756d510ca2be2f75639bc77e8d54f4243b49be74f17f54561329861865778a06b7e08efcf1c24d6fbb873dfacbb912f53618b
data/CHANGELOG.md CHANGED
@@ -5,6 +5,101 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](http://keepachangelog.com/)
6
6
  and this project adheres to [Semantic Versioning](http://semver.org/).
7
7
 
8
+ ## [0.4.1] - [2021-03-31]
9
+
10
+ ### Fixed
11
+
12
+ - Dependency from hashie updated to allow v4+ (nepalez)
13
+
14
+ ## [0.4.0] - [2020-03-15]
15
+
16
+ ### Added
17
+
18
+ - Support for stubbing ENV variables (nepalez)
19
+
20
+ ```yaml
21
+ ---
22
+ - env: GOOGLE_CLOUD_KEY
23
+ value: foo
24
+
25
+ - env: GOOGLE_CLOUD_PASSWORD
26
+ value: bar
27
+ ```
28
+
29
+ This would stub selected variables only, not touching the others
30
+
31
+ ## [0.3.0] - [2020-03-08]
32
+
33
+ ### Added
34
+
35
+ - Support for exception arguments (nepalez)
36
+
37
+ ```yaml
38
+ ---
39
+ - class: API
40
+ chain: get_product
41
+ arguments:
42
+ - 1
43
+ actions:
44
+ - raise: API::NotFoundError
45
+ arguments: # <--- that's that
46
+ - "Cannot find a product by id: 1"
47
+ ```
48
+
49
+ which would raise `API::NotFoundError.new("Cannot find a product by id: 1")`
50
+
51
+ ## [0.2.0] - [2020-02-17]
52
+
53
+ ### Added
54
+
55
+ - Stubbing and seeding from the same source file via the `call_fixture` method (nepalez)
56
+
57
+ ```yaml
58
+ # ./changes.yml
59
+ ---
60
+ - type: user
61
+ params:
62
+ id: 1
63
+
64
+ - const: DEFAULT_USER_ID
65
+ value: 1
66
+
67
+ - url: https://example.com/users/default
68
+ method: get
69
+ responses:
70
+ - body:
71
+ id: 1
72
+ name: Andrew
73
+ ```
74
+
75
+ ```ruby
76
+ before { call_fixture "#{__dir__}/changes.yml" }
77
+ ```
78
+
79
+ ## [0.1.0] - [2020-02-09]
80
+
81
+ ### Added
82
+
83
+ - Stubbing of the HTTP requests using webmock (nepalez)
84
+
85
+ ```yaml
86
+ ---
87
+ - url: example.com/foo
88
+ method: get
89
+ body: foobar
90
+ query:
91
+ foo: bar
92
+ basic_auth:
93
+ user: foo
94
+ password: bar
95
+ headers:
96
+ Accept: utf-8
97
+ responses:
98
+ - status: 200
99
+ body: foobar
100
+ - status: 404
101
+ ```
102
+
8
103
  ## [0.0.7] - [2019-07-01]
9
104
 
10
105
  ### Added
@@ -175,3 +270,8 @@ This is a first public release with features extracted from production app.
175
270
  [0.0.5]: https://github.com/nepalez/fixturama/compare/v0.0.4...v0.0.5
176
271
  [0.0.6]: https://github.com/nepalez/fixturama/compare/v0.0.5...v0.0.6
177
272
  [0.0.7]: https://github.com/nepalez/fixturama/compare/v0.0.6...v0.0.7
273
+ [0.1.0]: https://github.com/nepalez/fixturama/compare/v0.0.7...v0.1.0
274
+ [0.2.0]: https://github.com/nepalez/fixturama/compare/v0.1.0...v0.2.0
275
+ [0.3.0]: https://github.com/nepalez/fixturama/compare/v0.2.0...v0.3.0
276
+ [0.4.0]: https://github.com/nepalez/fixturama/compare/v0.3.0...v0.4.0
277
+ [0.4.1]: https://github.com/nepalez/fixturama/compare/v0.4.0...v0.4.1
data/README.md CHANGED
@@ -124,21 +124,41 @@ Use the `count: 2` key to create more objects at once.
124
124
 
125
125
  ### Stubbing
126
126
 
127
- Another opinionated format we use for stubs (`stub_fixture`). The gem supports stubbing both message chains and constants.
127
+ The gem supports stubbing message chains, constants and http requests with the following keys.
128
128
 
129
129
  For message chains:
130
130
 
131
131
  - `class` for stubbed class
132
132
  - `chain` for messages chain
133
133
  - `arguments` (optional) for specific arguments
134
- - `actions` for an array of actions for consecutive invocations of the chain
134
+ - `actions` for an array of actions for consecutive invocations of the chain with keys
135
+ - `return` for a value to be returned
136
+ - `raise` for an exception to be risen
137
+ - `repeate` for a number of invocations with this action
135
138
 
136
139
  For constants:
137
140
 
138
141
  - `const` for stubbed constant
139
142
  - `value` for a value of the constant
140
143
 
141
- Every action either `return` some value, or `raise` some exception
144
+ For environment variables:
145
+
146
+ - `env` for the name of a variable
147
+ `value` for a value of the variable
148
+
149
+ For http requests:
150
+
151
+ - `url` or `uri` for the URI of the request (treats values like `/.../` as regular expressions)
152
+ - `method` for the specific http-method (like `get` or `post`)
153
+ - `body` for the request body (treats values like `/.../` as regular expressions)
154
+ - `headers` for the request headers
155
+ - `query` for the request query
156
+ - `basic_auth` for the `user` and `password` of basic authentication
157
+ - `response` or `responses` for consecutively envoked responses with keys:
158
+ - `status`
159
+ - `body`
160
+ - `headers`
161
+ - `repeate` for the number of times this response should be returned before switching to the next one
142
162
 
143
163
  ```yaml
144
164
  # ./stubs.yml
@@ -166,10 +186,34 @@ Every action either `return` some value, or `raise` some exception
166
186
  - <%= profile_id %>
167
187
  actions:
168
188
  - return: true
189
+ repeate: 1 # this is the default value
169
190
  - raise: ActiveRecord::RecordNotFound
191
+ arguments:
192
+ - "Profile with id: 1 not found" # for error message
170
193
 
194
+ # Here we stubbing a constant
171
195
  - const: NOTIFIER_TIMEOUT_SEC
172
196
  value: 10
197
+
198
+ # This is a stub for ENV['DEFAULT_EMAIL']
199
+ - env: DEFAULT_EMAIL
200
+ value: foo@example.com
201
+
202
+ # Examples for stubbing HTTP
203
+ - uri: /example.com/foo/ # regexp!
204
+ method: delete
205
+ basic_auth:
206
+ user: foo
207
+ password: bar
208
+ responses:
209
+ - status: 200 # for the first call
210
+ repeate: 1 # this is the default value, but you can set another one
211
+ - status: 404 # for any other call
212
+
213
+ - uri: htpps://example.com/foo # exact string!
214
+ method: delete
215
+ responses:
216
+ - status: 401
173
217
  ```
174
218
 
175
219
  ```graphql
@@ -203,6 +247,30 @@ I find it especially helpful when I need to check different edge cases. Instead
203
247
 
204
248
  Looking at the spec I can easily figure out the "structure" of expectation, while looking at fixtures I can check the concrete corner cases.
205
249
 
250
+ ## Single Source of Changes
251
+
252
+ If you will, you can list all stubs and seeds at the one single file like
253
+
254
+ ```yaml
255
+ # ./changes.yml
256
+ ---
257
+ - type: user
258
+ params:
259
+ id: 1
260
+ name: Andrew
261
+
262
+ - const: DEFAULT_USER_ID
263
+ value: 1
264
+ ```
265
+
266
+ This fixture can be applied via `call_fixture` method just like we did above with `seed_fixture` and `stub_fixture`:
267
+
268
+ ```ruby
269
+ before { call_fixture "#{__dir__}/changes.yml" }
270
+ ```
271
+
272
+ In fact, since the `v0.2.0` all those methods are just the aliases of the `call_fixture`.
273
+
206
274
  ## License
207
275
 
208
276
  The gem is available as open source under the terms of the [MIT License][license].
data/lib/fixturama.rb CHANGED
@@ -3,46 +3,75 @@ require "factory_bot"
3
3
  require "hashie/mash"
4
4
  require "json"
5
5
  require "rspec"
6
+ require "webmock/rspec"
6
7
  require "yaml"
7
8
 
9
+ #
10
+ # A set of helpers to prettify specs with fixtures
11
+ #
8
12
  module Fixturama
13
+ require_relative "fixturama/fixture_error"
9
14
  require_relative "fixturama/config"
10
- require_relative "fixturama/utils"
11
15
  require_relative "fixturama/loader"
12
- require_relative "fixturama/stubs"
13
- require_relative "fixturama/seed"
16
+ require_relative "fixturama/changes"
14
17
 
18
+ # Set the initial value for database-generated IDs
19
+ # @param [#to_i] value
20
+ # @return [Fixturama]
15
21
  def self.start_ids_from(value)
16
22
  Config.start_ids_from(value)
23
+ self
17
24
  end
18
25
 
19
- def stub_fixture(path, **opts)
20
- items = load_fixture(path, **opts)
21
- raise "The fixture should contain an array" unless items.is_a?(Array)
26
+ # @!method read_fixture(path, options)
27
+ # Read the text content of the fixture
28
+ # @param [#to_s] path The path to the fixture file
29
+ # @param [Hash<Symbol, _>] options
30
+ # The list of options to be accessible in the fixture
31
+ # @return [String]
32
+ def read_fixture(path, **options)
33
+ content = File.read(path)
34
+ hashie = Hashie::Mash.new(options)
35
+ bindings = hashie.instance_eval { binding }
22
36
 
23
- items.each { |item| fixturama_stubs.add(item) }
24
- fixturama_stubs.apply(self)
37
+ ERB.new(content).result(bindings)
25
38
  end
26
39
 
27
- def seed_fixture(path, **opts)
28
- Array(load_fixture(path, **opts)).each { |item| Seed.call(item) }
40
+ # @!method load_fixture(path, options)
41
+ # Load data from a fixture
42
+ # @param (see #read_fixture)
43
+ # @return [Object]
44
+ def load_fixture(path, **options)
45
+ Loader.new(path, options).call
29
46
  end
30
47
 
31
- def load_fixture(path, **opts)
32
- Loader.new(path, opts).call
48
+ # @!method call_fixture(path, options)
49
+ # Stub different objects and seed the database from a fixture
50
+ # @param (see #read_fixture)
51
+ # @return [RSpec::Core::Example] the current example
52
+ def call_fixture(path, **options)
53
+ items = Array load_fixture(path, **options)
54
+ items.each { |item| changes.add(item) }
55
+ tap { changes.call(self) }
56
+ rescue FixtureError => err
57
+ raise err.with_file(path)
33
58
  end
34
59
 
35
- def read_fixture(path, **opts)
36
- content = File.read(path)
37
- hashie = Hashie::Mash.new(opts)
38
- bindings = hashie.instance_eval { binding }
60
+ # @!method seed_fixture(path, options)
61
+ # The alias for the +call_fixture+
62
+ # @param (see #call_fixture)
63
+ # @return (see #call_fixture)
64
+ alias seed_fixture call_fixture
39
65
 
40
- ERB.new(content).result(bindings)
41
- end
66
+ # @!method stub_fixture(path, options)
67
+ # The alias for the +call_fixture+
68
+ # @param (see #call_fixture)
69
+ # @return (see #call_fixture)
70
+ alias stub_fixture call_fixture
42
71
 
43
72
  private
44
73
 
45
- def fixturama_stubs
46
- @fixturama_stubs ||= Stubs.new
74
+ def changes
75
+ @changes ||= Changes.new
47
76
  end
48
77
  end
@@ -0,0 +1,72 @@
1
+ module Fixturama
2
+ #
3
+ # @private
4
+ # Registry of changes (stubs and seeds)
5
+ #
6
+ class Changes
7
+ require_relative "changes/base"
8
+ require_relative "changes/chain"
9
+ require_relative "changes/const"
10
+ require_relative "changes/env"
11
+ require_relative "changes/request"
12
+ require_relative "changes/seed"
13
+
14
+ # Match option keys to the type of an item
15
+ TYPES = {
16
+ actions: Chain,
17
+ arguments: Chain,
18
+ basic_auth: Request,
19
+ body: Request,
20
+ chain: Chain,
21
+ class: Chain,
22
+ const: Const,
23
+ count: Seed,
24
+ env: Env,
25
+ headers: Request,
26
+ http_method: Request,
27
+ object: Chain,
28
+ params: Seed,
29
+ query: Request,
30
+ response: Request,
31
+ responses: Request,
32
+ traits: Seed,
33
+ type: Seed,
34
+ uri: Request,
35
+ url: Request,
36
+ }.freeze
37
+
38
+ # Adds new change to the registry
39
+ # @param [Hash] options
40
+ # @return [Fixturama::Changes]
41
+ # @raise [Fixturama::FixtureError] if the options cannot be processed
42
+ def add(options)
43
+ options = Hash(options).transform_keys(&:to_sym)
44
+ types = options.keys.map { |key| TYPES[key] }.compact.uniq
45
+ raise "Wrong count" unless types.count == 1
46
+
47
+ @changes << types.first.new(options)
48
+ self
49
+ rescue FixtureError => err
50
+ raise err
51
+ rescue StandardError => err
52
+ raise FixtureError.new("an operation", options, err)
53
+ end
54
+
55
+ # Apply all registered changes to the RSpec example
56
+ # @param [RSpec::Core::Example] example
57
+ # @return [self]
58
+ def call(example)
59
+ @changes
60
+ .group_by(&:key)
61
+ .values
62
+ .map { |changes| changes.reduce :merge }
63
+ .each { |change| change.call(example) }
64
+ end
65
+
66
+ private
67
+
68
+ def initialize
69
+ @changes = []
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,28 @@
1
+ class Fixturama::Changes
2
+ #
3
+ # @private
4
+ # @abstract
5
+ # Base class for changes downloaded from a fixture
6
+ #
7
+ class Base
8
+ # @!attribute [r] key The key identifier of the change
9
+ # @return [String]
10
+ alias key hash
11
+
12
+ # Merge the other change into the current one
13
+ # @param [Fixturama::Changes::Base] other
14
+ # @return [Fixturama::Changes::Base]
15
+ def merge(other)
16
+ # By default just take the other change if applicable
17
+ other.class == self.class && other.key == key ? other : self
18
+ end
19
+
20
+ # @abstract
21
+ # Call the corresponding change (either a stub or a seed)
22
+ # @param [RSpec::Core::Example] _example The RSpec example
23
+ # @return [self]
24
+ def call(_example)
25
+ self
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,64 @@
1
+ class Fixturama::Changes
2
+ #
3
+ # @private
4
+ # Stub a chain of messages
5
+ #
6
+ class Chain < Base
7
+ require_relative "chain/raise_action"
8
+ require_relative "chain/return_action"
9
+ require_relative "chain/actions"
10
+ require_relative "chain/arguments"
11
+
12
+ def key
13
+ @key ||= ["chain", @receiver.name, *@messages].join(".")
14
+ end
15
+
16
+ def merge(other)
17
+ return self unless other.class == self.class && other.key == key
18
+
19
+ tap { @arguments = (other.arguments | arguments).sort_by(&:order) }
20
+ end
21
+
22
+ def call(example)
23
+ call_action = example.send(:receive_message_chain, *@messages) do |*real|
24
+ action = arguments.find { |expected| expected.match?(*real) }
25
+ action ? action.call : raise("Unexpected arguments: #{real}")
26
+ end
27
+
28
+ example.send(:allow, @receiver).to call_action
29
+ end
30
+
31
+ protected
32
+
33
+ attr_reader :arguments
34
+
35
+ private
36
+
37
+ def initialize(**options)
38
+ @receiver = receiver_from(options)
39
+ @messages = messages_from(options)
40
+ @arguments = [Arguments.new(options)]
41
+ end
42
+
43
+ def receiver_from(options)
44
+ case options.slice(:class, :object).keys
45
+ when %i[class] then Kernel.const_get(options[:class])
46
+ when %i[object] then Object.send(:eval, options[:object])
47
+ else raise
48
+ end
49
+ rescue StandardError => err
50
+ raise Fixturama::FixtureError.new("a stabbed object", options, err)
51
+ end
52
+
53
+ def messages_from(options)
54
+ case value = options[:chain]
55
+ when Array then value.map(&:to_sym)
56
+ when String then [value.to_sym]
57
+ when Symbol then value
58
+ else raise
59
+ end
60
+ rescue StandardError => err
61
+ raise Fixturama::FixtureError.new("a messages chain", options, err)
62
+ end
63
+ end
64
+ end