mtk_framework 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. checksums.yaml +7 -0
  2. data/.document +5 -0
  3. data/.github/workflows/test.yml +21 -0
  4. data/.rspec +1 -0
  5. data/Gemfile +22 -0
  6. data/LICENSE.txt +20 -0
  7. data/README.rdoc +18 -0
  8. data/Rakefile +50 -0
  9. data/VERSION +1 -0
  10. data/lib/mtk_framework.rb +50 -0
  11. data/lib/mtk_framework/active_interaction_concerns/i18nable.rb +18 -0
  12. data/lib/mtk_framework/active_interaction_concerns/interruptable.rb +32 -0
  13. data/lib/mtk_framework/active_interaction_concerns/loggable.rb +84 -0
  14. data/lib/mtk_framework/active_interaction_concerns/rescuable.rb +27 -0
  15. data/lib/mtk_framework/active_interaction_concerns/updatable_object.rb +32 -0
  16. data/lib/mtk_framework/active_interaction_mocks/interaction_group_mocks_helper.rb +32 -0
  17. data/lib/mtk_framework/active_interaction_mocks/interaction_mocks_helper.rb +61 -0
  18. data/lib/mtk_framework/active_interaction_mocks/mocks_loader.rb +53 -0
  19. data/lib/mtk_framework/active_interaction_params.rb +16 -0
  20. data/lib/mtk_framework/active_interaction_params/parametrize_filter.rb +108 -0
  21. data/lib/mtk_framework/core_extensions/array/except.rb +7 -0
  22. data/lib/mtk_framework/core_extensions/class/virtual_method.rb +11 -0
  23. data/lib/mtk_framework/core_extensions/hash/rename_keys.rb +11 -0
  24. data/lib/mtk_framework/gem_extensions/active_interaction/base.rb +20 -0
  25. data/lib/mtk_framework/gem_extensions/active_interaction/filters/abstract_tz_filter.rb +28 -0
  26. data/lib/mtk_framework/gem_extensions/active_interaction/filters/hash_filter.rb +38 -0
  27. data/lib/mtk_framework/gem_extensions/active_interaction/filters/tz_account_filter.rb +23 -0
  28. data/lib/mtk_framework/gem_extensions/active_interaction/filters/tz_contract_address_filter.rb +23 -0
  29. data/lib/mtk_framework/gem_extensions/active_interaction/filters/tz_operation_filter.rb +23 -0
  30. data/lib/mtk_framework/gem_extensions/active_interaction/filters/tz_public_key_filter.rb +23 -0
  31. data/lib/mtk_framework/gem_extensions/active_interaction/filters/tz_secret_key_filter.rb +23 -0
  32. data/lib/mtk_framework/gem_extensions/active_interaction/filters/tz_signature_filter.rb +23 -0
  33. data/lib/mtk_framework/locale/active_interaction.fr.yml +8 -0
  34. data/lib/mtk_framework/locale/fr.yml +219 -0
  35. data/lib/mtk_framework/railtie.rb +13 -0
  36. data/mtk_framework.gemspec +114 -0
  37. data/spec/active_interaction_concerns/interruptable_spec.rb +127 -0
  38. data/spec/active_interaction_concerns/loggable_spec.rb +195 -0
  39. data/spec/application_interaction_spec.rb +17 -0
  40. data/spec/fake_app.rb +14 -0
  41. data/spec/gem_extensions/active_interaction/filters/tz_account_filter_spec.rb +7 -0
  42. data/spec/gem_extensions/active_interaction/filters/tz_contract_address_filter_spec.rb +7 -0
  43. data/spec/gem_extensions/active_interaction/filters/tz_operation_filter_spec.rb +7 -0
  44. data/spec/gem_extensions/active_interaction/filters/tz_public_key_filter_spec.rb +7 -0
  45. data/spec/gem_extensions/active_interaction/filters/tz_secret_key_filter_spec.rb +7 -0
  46. data/spec/gem_extensions/active_interaction/filters/tz_signature_filter_spec.rb +7 -0
  47. data/spec/gem_extensions/active_interaction/filters_shared.rb +253 -0
  48. data/spec/gem_extensions/active_interaction/tz_filters_shared.rb +60 -0
  49. data/spec/spec_helper.rb +24 -0
  50. metadata +232 -0
@@ -0,0 +1,195 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe MtkFramework::ActiveInteractionConcerns::Loggable do
4
+ let(:logger_output) { StringIO.new }
5
+ let(:logger) { Logger.new(logger_output) }
6
+
7
+ before do
8
+ allow(Rails).to receive(:logger).and_return(logger)
9
+ end
10
+
11
+ describe 'run' do
12
+ before do
13
+ application_interaction = Class.new(ActiveInteraction::Base) do
14
+ include MtkFramework::ActiveInteractionConcerns::Loggable
15
+ include MtkFramework::ActiveInteractionConcerns::Interruptable
16
+ end
17
+
18
+ stub_const('ApplicationInteraction', application_interaction)
19
+ end
20
+ context 'simple interaction' do
21
+ subject { DummyInteraction.run(params) }
22
+
23
+ before do
24
+ dummy_interaction = Class.new(ApplicationInteraction) do
25
+ integer :integer_param
26
+
27
+ validates :integer_param, numericality: { geater_than: 0 }
28
+
29
+ def execute
30
+ errors.add(:integer_param, :invalid) unless integer_param > 10
31
+ end
32
+ end
33
+
34
+ stub_const('DummyInteraction', dummy_interaction)
35
+ end
36
+
37
+ context 'type_check fails' do
38
+ let(:params) { {} }
39
+
40
+ it 'logs errors' do
41
+ expect(subject).not_to be_valid
42
+ expect(logger_output.string)
43
+ .to match %r{Interaction DummyInteraction failed: 'Integer param est obligatoire' called from: spec/active_interaction_concerns/loggable_spec\.rb} # rubocop:disable Layout/LineLength
44
+ end
45
+ end
46
+
47
+ context 'type_check succeeds' do
48
+ context 'validation fails' do
49
+ let(:params) { { integer_param: 0 } }
50
+
51
+ it 'logs errors' do
52
+ expect(subject).not_to be_valid
53
+ expect(logger_output.string)
54
+ end
55
+ end
56
+
57
+ context 'validation succeeds' do
58
+ context 'execution fails' do
59
+ let(:params) { { integer_param: 1 } }
60
+
61
+ it 'logs errors' do
62
+ expect(subject).not_to be_valid
63
+ expect(logger_output.string)
64
+ expect(logger_output.string)
65
+ end
66
+ end
67
+
68
+ context 'execution succeeds' do
69
+ let(:params) { { integer_param: 11 } }
70
+
71
+ it 'logs call' do
72
+ expect(subject).to be_valid
73
+ expect(logger_output.string)
74
+ end
75
+ end
76
+ end
77
+ end
78
+
79
+ context 'with compose' do
80
+ subject { DummyWithcompose.run(params) }
81
+
82
+ before do
83
+ dummy_with_compose = Class.new(ApplicationInteraction) do
84
+ integer :integer_param
85
+
86
+ def execute
87
+ compose(DummyInteraction, integer_param: integer_param)
88
+ end
89
+ end
90
+
91
+ stub_const('DummyWithcompose', dummy_with_compose)
92
+ end
93
+
94
+ context 'succeeds' do
95
+ let(:params) { { integer_param: 11 } }
96
+
97
+ it 'works' do
98
+ expect(subject).to be_valid
99
+ expect(logger_output.string)
100
+ expect(logger_output.string)
101
+ .to include 'Interaction DummyInteraction [{:integer_param=>11}] called from spec/active_interaction_concerns/loggable_spec.rb' # rubocop:disable Layout/LineLength
102
+ end
103
+ end
104
+
105
+ context 'compose execution fails' do
106
+ let(:params) { { integer_param: 0 } }
107
+
108
+ it 'logs failures for each interaction' do
109
+ expect(subject).not_to be_valid
110
+ expect(logger_output.string).to include 'Interaction DummyInteraction failed'
111
+ end
112
+ end
113
+ end
114
+ end
115
+
116
+ context 'interrupted' do
117
+ subject { DummyInterrupted.run }
118
+
119
+ before do
120
+ dummy_interaction = Class.new(ApplicationInteraction) do
121
+ def execute
122
+ interrupt_execute! :base, :invalid
123
+ end
124
+ end
125
+
126
+ stub_const('DummyInterrupted', dummy_interaction)
127
+ end
128
+
129
+ it 'works' do
130
+ expect(subject).not_to be_valid
131
+ expect(logger_output.string)
132
+ .to include 'Interaction DummyInterrupted [] called from spec/active_interaction_concerns/loggable_spec.rb' # rubocop:disable Layout/LineLength
133
+ expect(logger_output.string)
134
+ .to include "Interaction DummyInterrupted failed: 'est invalide' called from: spec/active_interaction_concerns/loggable_spec.rb" # rubocop:disable Layout/LineLength
135
+ end
136
+ end
137
+
138
+ context 'run! interrupted' do
139
+ subject { DummyInterrupted.run! }
140
+
141
+ before do
142
+ dummy_interaction = Class.new(ApplicationInteraction) do
143
+ def execute
144
+ interrupt_execute! :base, :invalid
145
+ end
146
+ end
147
+
148
+ stub_const('DummyInterrupted', dummy_interaction)
149
+ end
150
+
151
+ it 'works' do
152
+ expect { subject }.to raise_error ActiveInteraction::InvalidInteractionError, 'est invalide'
153
+ expect(logger_output.string)
154
+ .to include 'Interaction DummyInterrupted [] called from spec/active_interaction_concerns/loggable_spec.rb' # rubocop:disable Layout/LineLength
155
+ expect(logger_output.string)
156
+ .to include "Interaction DummyInterrupted failed: 'est invalide' called from: spec/active_interaction_concerns/loggable_spec.rb" # rubocop:disable Layout/LineLength
157
+ end
158
+ end
159
+
160
+ context 'filtering logs' do
161
+ subject { DummyInteraction.run(params) }
162
+
163
+ let(:params) do
164
+ {
165
+ password: 'super secret',
166
+ bank_account: {
167
+ name: 'name_value'
168
+ },
169
+ not_filtered: 'value'
170
+ }
171
+ end
172
+
173
+ before do
174
+ dummy_interaction = Class.new(ApplicationInteraction) do
175
+ string :password
176
+ hash :bank_account do
177
+ string :name
178
+ end
179
+ string :not_filtered
180
+
181
+ def execute; end
182
+ end
183
+
184
+ stub_const('DummyInteraction', dummy_interaction)
185
+ end
186
+
187
+ it 'logs call filtering params' do
188
+ expect(subject).to be_valid
189
+ x = logger_output.string
190
+ expect(logger_output.string)
191
+ .to include 'Interaction DummyInteraction [{:password=>"[FILTERED]", :bank_account=>{:name=>"[FILTERED]"}, :not_filtered=>"value"}] called from spec/active_interaction_concerns/loggable_spec.rb' # rubocop:disable Layout/LineLength
192
+ end
193
+ end
194
+ end
195
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ # require_relative 'spec_helper'
4
+ # require 'active_interaction'
5
+ # require 'mtk_framework'
6
+ # require 'pry'
7
+
8
+ class ApplicationInteraction < ActiveInteraction::Base
9
+ include MtkFramework::ActiveInteractionConcerns::I18nable
10
+ include MtkFramework::ActiveInteractionConcerns::Interruptable
11
+ include MtkFramework::ActiveInteractionConcerns::Loggable
12
+ include MtkFramework::ActiveInteractionConcerns::Rescuable
13
+ include MtkFramework::ActiveInteractionConcerns::UpdatableObject
14
+ end
15
+
16
+ RSpec.describe ApplicationInteraction do
17
+ end
data/spec/fake_app.rb ADDED
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Dummy
4
+ class Application < Rails::Application
5
+ config.eager_load = false
6
+ config.root = ''
7
+ config.filter_parameters = ['password', 'bank_account.name']
8
+ config.i18n.default_locale = :fr
9
+ end
10
+ end
11
+
12
+ Rails.logger = Logger.new('/dev/null')
13
+
14
+ Rails.application.initialize!
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe ActiveInteraction::TzAccountFilter, :filter do # rubocop: disable RSpec/FilePath
4
+ it_behaves_like "a tz filter",
5
+ valid: [Faker::Blockchain::Tezos.account],
6
+ invalid: ["invalid", Faker::Blockchain::Tezos.secret_key]
7
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe ActiveInteraction::TzContractAddressFilter, :filter do # rubocop: disable RSpec/FilePath
4
+ it_behaves_like "a tz filter",
5
+ valid: [Faker::Blockchain::Tezos.contract],
6
+ invalid: ["invalid", Faker::Blockchain::Tezos.secret_key]
7
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe ActiveInteraction::TzOperationFilter, :filter do # rubocop: disable RSpec/FilePath
4
+ it_behaves_like "a tz filter",
5
+ valid: [Faker::Blockchain::Tezos.operation],
6
+ invalid: ["invalid", Faker::Blockchain::Tezos.secret_key]
7
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe ActiveInteraction::TzPublicKeyFilter, :filter do # rubocop: disable RSpec/FilePath
4
+ it_behaves_like "a tz filter",
5
+ valid: [Faker::Blockchain::Tezos.public_key],
6
+ invalid: ["invalid", Faker::Blockchain::Tezos.secret_key]
7
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe ActiveInteraction::TzSecretKeyFilter, :filter do # rubocop: disable RSpec/FilePath
4
+ it_behaves_like "a tz filter",
5
+ valid: [Faker::Blockchain::Tezos.secret_key],
6
+ invalid: ["invalid", 10, Faker::Blockchain::Tezos.public_key]
7
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe ActiveInteraction::TzSignatureFilter, :filter do # rubocop: disable RSpec/FilePath
4
+ it_behaves_like "a tz filter",
5
+ valid: [Faker::Blockchain::Tezos.signature],
6
+ invalid: ["invalid", 10, Faker::Blockchain::Tezos.public_key]
7
+ end
@@ -0,0 +1,253 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.shared_context "filters" do
4
+ subject(:filter) { described_class.new(name, options, &block) }
5
+
6
+ let(:block) { nil }
7
+ let(:name) { SecureRandom.hex.to_sym }
8
+ let(:options) { {} }
9
+
10
+ shared_context "optional" do
11
+ before do
12
+ options[:default] = nil
13
+ end
14
+ end
15
+
16
+ shared_context "required" do
17
+ before do
18
+ options.delete(:default)
19
+ end
20
+ end
21
+ end
22
+
23
+ RSpec.shared_examples_for "a filter" do
24
+ include_context "filters"
25
+
26
+ describe ".factory" do
27
+ context "with an invalid slug" do
28
+ it "raises an error" do
29
+ expect do
30
+ described_class.factory(:invalid)
31
+ end.to raise_error ActiveInteraction::MissingFilterError
32
+ end
33
+ end
34
+
35
+ context "with a valid slug" do
36
+ it "returns a Filter" do
37
+ expect(
38
+ described_class.factory(described_class.slug)
39
+ ).to eql described_class
40
+ end
41
+ end
42
+ end
43
+
44
+ describe ".slug" do
45
+ it "returns a symbol" do
46
+ expect(described_class.slug).to be_a Symbol
47
+ end
48
+ end
49
+
50
+ describe "#cast" do
51
+ let(:value) { nil }
52
+ let(:result) { filter.send(:cast, value, nil) }
53
+
54
+ context "optional" do
55
+ include_context "optional"
56
+
57
+ it "returns nil" do
58
+ expect(result).to be_nil
59
+ end
60
+ end
61
+
62
+ context "required" do
63
+ include_context "required"
64
+
65
+ it "raises an error" do
66
+ expect { result }.to raise_error ActiveInteraction::MissingValueError
67
+ end
68
+
69
+ context "with an invalid default" do
70
+ let(:value) { Object.new }
71
+
72
+ it "raises an error" do
73
+ expect { result }.to raise_error ActiveInteraction::InvalidValueError
74
+ end
75
+ end
76
+ end
77
+
78
+ # BasicObject is missing a lot of methods
79
+ context "with a BasicObject" do
80
+ let(:value) { BasicObject.new }
81
+
82
+ it "raises an error" do
83
+ expect { result }.to raise_error ActiveInteraction::InvalidValueError
84
+ end
85
+ end
86
+ end
87
+
88
+ describe "#clean" do
89
+ let(:value) { nil }
90
+
91
+ context "optional" do
92
+ include_context "optional"
93
+
94
+ it "returns the default" do
95
+ expect(filter.clean(value, nil)).to eql options[:default]
96
+ end
97
+ end
98
+
99
+ context "required" do
100
+ include_context "required"
101
+
102
+ it "raises an error" do
103
+ expect do
104
+ filter.clean(value, nil)
105
+ end.to raise_error ActiveInteraction::MissingValueError
106
+ end
107
+
108
+ context "with an invalid value" do
109
+ let(:value) { Object.new }
110
+
111
+ it "raises an error" do
112
+ expect do
113
+ filter.clean(value, nil)
114
+ end.to raise_error ActiveInteraction::InvalidValueError
115
+ end
116
+ end
117
+ end
118
+
119
+ context "with an invalid default" do
120
+ before do
121
+ options[:default] = Object.new
122
+ end
123
+
124
+ it "raises an error" do
125
+ expect do
126
+ filter.clean(value, nil)
127
+ end.to raise_error ActiveInteraction::InvalidDefaultError
128
+ end
129
+ end
130
+ end
131
+
132
+ describe "#default" do
133
+ context "optional" do
134
+ include_context "optional"
135
+
136
+ it "returns the default" do
137
+ expect(filter.default(nil)).to eql options[:default]
138
+ end
139
+ end
140
+
141
+ context "required" do
142
+ include_context "required"
143
+
144
+ it "raises an error" do
145
+ expect do
146
+ filter.default(nil)
147
+ end.to raise_error ActiveInteraction::NoDefaultError
148
+ end
149
+ end
150
+
151
+ context "with an invalid default" do
152
+ before do
153
+ options[:default] = Object.new
154
+ end
155
+
156
+ it "raises an error" do
157
+ expect do
158
+ filter.default(nil)
159
+ end.to raise_error ActiveInteraction::InvalidDefaultError
160
+ end
161
+ end
162
+
163
+ context "with a callable default" do
164
+ include_context "optional"
165
+
166
+ before do
167
+ default = options[:default]
168
+ options[:default] = -> { default }
169
+ end
170
+
171
+ it "returns the default" do
172
+ expect(filter.default(nil)).to eql options[:default].call
173
+ end
174
+ end
175
+
176
+ context "with a callable default that takes an argument" do
177
+ include_context "optional"
178
+
179
+ it "returns the default" do
180
+ default = options[:default]
181
+
182
+ spec = self
183
+ filter # Necessary to bring into scope for lambda.
184
+ options[:default] = ->(this) do
185
+ spec.expect(this).to be filter
186
+ default
187
+ end
188
+
189
+ expect(filter.default(nil)).to be default
190
+ end
191
+ end
192
+ end
193
+
194
+ describe "#desc" do
195
+ it "returns nil" do
196
+ expect(filter.desc).to be_nil
197
+ end
198
+
199
+ context "with a description" do
200
+ let(:desc) { SecureRandom.hex }
201
+
202
+ before do
203
+ options[:desc] = desc
204
+ end
205
+
206
+ it "returns the description" do
207
+ expect(filter.desc).to eql desc
208
+ end
209
+ end
210
+ end
211
+
212
+ describe "#filters" do
213
+ it "returns Hash" do
214
+ expect(filter.filters).to be_a Hash
215
+ end
216
+ end
217
+
218
+ describe "#default?" do
219
+ context "optional" do
220
+ include_context "optional"
221
+
222
+ it "returns true" do
223
+ expect(filter).to be_default
224
+ end
225
+ end
226
+
227
+ context "required" do
228
+ include_context "required"
229
+
230
+ it "returns false" do
231
+ expect(filter).not_to be_default
232
+ end
233
+ end
234
+ end
235
+
236
+ describe "#name" do
237
+ it "returns the name" do
238
+ expect(filter.name).to eql name
239
+ end
240
+ end
241
+
242
+ describe "#options" do
243
+ it "returns the options" do
244
+ expect(filter.options).to eql options
245
+ end
246
+ end
247
+
248
+ describe "#database_column_type" do
249
+ it "returns a symbol" do
250
+ expect(filter.database_column_type).to be_a Symbol
251
+ end
252
+ end
253
+ end