mercy 1.3.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 (41) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +2 -0
  3. data/.rspec +1 -0
  4. data/Gemfile +4 -0
  5. data/Gemfile.lock +88 -0
  6. data/LICENSE +22 -0
  7. data/README.md +100 -0
  8. data/Rakefile +7 -0
  9. data/circle.yml +16 -0
  10. data/lib/bidu.rb +3 -0
  11. data/lib/bidu/mercy.rb +18 -0
  12. data/lib/bidu/mercy/class_methods.rb +19 -0
  13. data/lib/bidu/mercy/concern.rb +10 -0
  14. data/lib/bidu/mercy/report.rb +44 -0
  15. data/lib/bidu/mercy/report/active_record.rb +28 -0
  16. data/lib/bidu/mercy/report/error.rb +62 -0
  17. data/lib/bidu/mercy/report/multiple.rb +29 -0
  18. data/lib/bidu/mercy/report/range.rb +54 -0
  19. data/lib/bidu/mercy/report_builder.rb +24 -0
  20. data/lib/bidu/mercy/report_config.rb +37 -0
  21. data/lib/bidu/mercy/status.rb +27 -0
  22. data/lib/bidu/mercy/status_builder.rb +35 -0
  23. data/lib/bidu/mercy/version.rb +5 -0
  24. data/lib/bidu/period_parser.rb +32 -0
  25. data/lib/json_parser/type_cast_ext.rb +7 -0
  26. data/mercy.gemspec +33 -0
  27. data/spec/lib/bidu/mercy/report/error_spec.rb +385 -0
  28. data/spec/lib/bidu/mercy/report/multiple_spec.rb +122 -0
  29. data/spec/lib/bidu/mercy/report/range_spec.rb +302 -0
  30. data/spec/lib/bidu/mercy/report/report_config_spec.rb +39 -0
  31. data/spec/lib/bidu/mercy/report_builder_spec.rb +72 -0
  32. data/spec/lib/bidu/mercy/report_spec.rb +44 -0
  33. data/spec/lib/bidu/mercy/status_builder_spec.rb +84 -0
  34. data/spec/lib/bidu/mercy/status_spec.rb +135 -0
  35. data/spec/lib/bidu/period_parser_spec.rb +27 -0
  36. data/spec/spec_helper.rb +32 -0
  37. data/spec/support/fixture_helpers.rb +19 -0
  38. data/spec/support/models/document.rb +6 -0
  39. data/spec/support/report/dummy.rb +17 -0
  40. data/spec/support/schema.rb +11 -0
  41. metadata +236 -0
@@ -0,0 +1,122 @@
1
+ require 'spec_helper'
2
+
3
+ describe Bidu::Mercy::Report::Multiple do
4
+ class Bidu::Mercy::Report::DocTypeError < Bidu::Mercy::Report::Error
5
+ ALLOWED_PARAMETERS=[:period, :threshold]
6
+ DEFAULT_OPTION = {
7
+ threshold: 0.25,
8
+ clazz: Document,
9
+ external_key: :external_id
10
+ }
11
+
12
+ json_parse :doc_type, case: :snake
13
+
14
+ def base
15
+ super.where(doc_type: doc_type)
16
+ end
17
+ end
18
+
19
+ class Bidu::Mercy::Report::Multiple::Dummy < Bidu::Mercy::Report
20
+ include Bidu::Mercy::Report::Multiple
21
+ DEFAULT_OPTION = {
22
+ doc_type: [:a, :b]
23
+ }
24
+ json_parse :doc_type, case: :snake
25
+
26
+ def reports_ids
27
+ [ doc_type ].flatten
28
+ end
29
+
30
+ def sub_report_class
31
+ Bidu::Mercy::Report::DocTypeError
32
+ end
33
+
34
+ def key
35
+ :doc_type
36
+ end
37
+ end
38
+
39
+ let(:subject) { described_class::Dummy.new }
40
+ let(:a_errors) { 1 }
41
+ let(:a_successes) { 1 }
42
+ let(:b_errors) { 1 }
43
+ let(:b_successes) { 1 }
44
+ let(:setup) do
45
+ {
46
+ success: { a: a_successes, b: b_successes },
47
+ error: { a: a_errors, b: b_errors }
48
+ }
49
+ end
50
+
51
+ before do
52
+ Document.delete_all
53
+ setup.each do |status, map|
54
+ map.each do |doc_type, quantity|
55
+ quantity.times do
56
+ Document.create(status: status, doc_type: doc_type, external_id: Document.count)
57
+ end
58
+ end
59
+ end
60
+ end
61
+
62
+ describe '#error?' do
63
+ context 'when all subreports are with error' do
64
+ it { expect(subject.error?).to be_truthy }
65
+ end
66
+
67
+ context 'when one of the reports is not an error' do
68
+ let(:a_successes) { 4 }
69
+
70
+ it { expect(subject.error?).to be_truthy }
71
+ end
72
+
73
+ context 'when none of the reports is an error' do
74
+ let(:a_successes) { 4 }
75
+ let(:b_successes) { 4 }
76
+
77
+ it { expect(subject.error?).to be_falsey }
78
+ end
79
+ end
80
+
81
+ describe '#as_json' do
82
+ let(:expected) do
83
+ {
84
+ 'a' => { ids: [2], percentage: 0.5, status: :error },
85
+ 'b' => { ids: [3], percentage: 0.5, status: :error },
86
+ status: :error
87
+ }
88
+ end
89
+
90
+ context 'when all subreports are with error' do
91
+ it { expect(subject.as_json).to eq(expected) }
92
+ end
93
+
94
+ context 'when one of the reports is not an error' do
95
+ let(:a_successes) { 4 }
96
+ let(:expected) do
97
+ {
98
+ 'a' => { ids: [5], percentage: 0.2, status: :ok },
99
+ 'b' => { ids: [6], percentage: 0.5, status: :error },
100
+ status: :error
101
+ }
102
+ end
103
+
104
+ it { expect(subject.as_json).to eq(expected) }
105
+ end
106
+
107
+ context 'when none of the reports is an error' do
108
+ let(:a_successes) { 4 }
109
+ let(:b_successes) { 4 }
110
+ let(:expected) do
111
+ {
112
+ 'a' => { ids: [8], percentage: 0.2, status: :ok },
113
+ 'b' => { ids: [9], percentage: 0.2, status: :ok },
114
+ status: :ok
115
+ }
116
+ end
117
+
118
+ it { expect(subject.as_json).to eq(expected) }
119
+ end
120
+ end
121
+ end
122
+
@@ -0,0 +1,302 @@
1
+ require 'spec_helper'
2
+
3
+ describe Bidu::Mercy::Report::Range do
4
+ let(:errors) { 1 }
5
+ let(:successes) { 1 }
6
+ let(:old_errors) { 2 }
7
+ let(:old_sucesses) { 2 }
8
+ let(:period) { 1.day }
9
+ let(:scope) { :with_error }
10
+ let(:maximum) { nil }
11
+ let(:minimum) { nil }
12
+ let(:options) do
13
+ {
14
+ period: period,
15
+ scope: scope,
16
+ clazz: Document,
17
+ minimum: minimum,
18
+ maximum: maximum
19
+ }
20
+ end
21
+ let(:subject) { described_class.new(options) }
22
+ let(:types) { [:a] }
23
+ before do
24
+ Document.all.each(&:destroy)
25
+ types.each do |type|
26
+ successes.times { Document.create status: :success, doc_type: type }
27
+ errors.times do |i|
28
+ Document.create status: :error, external_id: 10 * successes + i, outter_external_id: i, doc_type: type
29
+ end
30
+ old_errors.times do
31
+ Document.create status: :error, created_at: 2.days.ago, updated_at: 2.days.ago, doc_type: type
32
+ end
33
+ old_sucesses.times do
34
+ Document.create status: :success, created_at: 2.days.ago, updated_at: 2.days.ago, doc_type: type
35
+ end
36
+ end
37
+ end
38
+
39
+ describe '#status' do
40
+ context 'when looking for maximum counts' do
41
+ context 'when there are more errors than the allowed by the maximum' do
42
+ let(:errors) { 2 }
43
+ let(:maximum) { 1 }
44
+ it { expect(subject.status).to eq(:error) }
45
+ end
46
+
47
+ context 'when the maximum is 0 and there are no errors' do
48
+ let(:errors) { 0 }
49
+ let(:maximum) { 0 }
50
+ it { expect(subject.status).to eq(:ok) }
51
+ end
52
+
53
+ context 'when the maximum is nil and there are no errors' do
54
+ let(:errors) { 0 }
55
+ let(:maximum) { nil }
56
+ it { expect(subject.status).to eq(:ok) }
57
+ end
58
+
59
+ context 'when the count is the same as the maximum' do
60
+ let(:errors) { 1 }
61
+ let(:maximum) { 1 }
62
+ it { expect(subject.status).to eq(:ok) }
63
+ end
64
+
65
+ context 'when the count is less than the maximum' do
66
+ let(:errors) { 1 }
67
+ let(:maximum) { 2 }
68
+ it { expect(subject.status).to eq(:ok) }
69
+ end
70
+
71
+ context 'when there are older errors out of the period' do
72
+ let(:maximum) { 1 }
73
+
74
+ it 'ignores the older errros' do
75
+ expect(subject.status).to eq(:ok)
76
+ end
77
+
78
+ context 'when passing a bigger period' do
79
+ let(:period) { 3.days }
80
+
81
+ it 'consider the older errros' do
82
+ expect(subject.status).to eq(:error)
83
+ end
84
+ end
85
+ end
86
+ end
87
+
88
+ context 'when looking for minimum' do
89
+ let(:scope) { :with_success }
90
+ context 'when there are less successes than the allowed by the minimum' do
91
+ let(:successes) { 1 }
92
+ let(:minimum) { 2 }
93
+ it { expect(subject.status).to eq(:error) }
94
+ end
95
+
96
+ context 'when the minimum is 0 and there are no sucesses' do
97
+ let(:successes) { 0 }
98
+ let(:minimum) { 0 }
99
+ it { expect(subject.status).to eq(:ok) }
100
+ end
101
+
102
+ context 'when the minimum is nil and there are no sucesses' do
103
+ let(:successes) { 0 }
104
+ let(:minimum) { nil }
105
+ it { expect(subject.status).to eq(:ok) }
106
+ end
107
+
108
+ context 'when the count is the same as the minimum' do
109
+ let(:successes) { 1 }
110
+ let(:minimum) { 1 }
111
+ it { expect(subject.status).to eq(:ok) }
112
+ end
113
+
114
+ context 'when the count is greater than the minimum' do
115
+ let(:successes) { 2 }
116
+ let(:minimum) { 1 }
117
+ it { expect(subject.status).to eq(:ok) }
118
+ end
119
+
120
+ context 'when there are older sucesses out of the period' do
121
+ let(:successes) { 0 }
122
+ let(:minimum) { 1 }
123
+
124
+ it 'ignores the older sucesses' do
125
+ expect(subject.status).to eq(:error)
126
+ end
127
+
128
+ context 'when passing a bigger period' do
129
+ let(:period) { 3.days }
130
+
131
+ it 'consider the older sucesses' do
132
+ expect(subject.status).to eq(:ok)
133
+ end
134
+ end
135
+ end
136
+ end
137
+
138
+ context 'when looking for a range' do
139
+ let(:scope) { :all }
140
+ let(:minimum) { 2 }
141
+ let(:maximum) { 4 }
142
+
143
+ context 'when there are less documents than the allowed by the minimum' do
144
+ let(:successes) { 1 }
145
+ let(:errors) { 0 }
146
+ it { expect(subject.status).to eq(:error) }
147
+ end
148
+
149
+ context 'when the count is the same as the minimum' do
150
+ let(:successes) { 1 }
151
+ let(:errors) { 2 }
152
+ it { expect(subject.status).to eq(:ok) }
153
+ end
154
+
155
+ context 'when the count is inside the range' do
156
+ let(:successes) { 1 }
157
+ let(:errors) { 2 }
158
+ it { expect(subject.status).to eq(:ok) }
159
+ end
160
+
161
+ context 'when the count is the same as the maximum' do
162
+ let(:successes) { 2 }
163
+ let(:errors) { 2 }
164
+ it { expect(subject.status).to eq(:ok) }
165
+ end
166
+
167
+ context 'when the count is greater than the maximum' do
168
+ let(:successes) { 3 }
169
+ let(:errors) { 2 }
170
+ it { expect(subject.status).to eq(:error) }
171
+ end
172
+
173
+ context 'when the minimum is 0 and the count is 0' do
174
+ let(:successes) { 0 }
175
+ let(:errors) { 0 }
176
+ let(:minimum) { 0 }
177
+ it { expect(subject.status).to eq(:ok) }
178
+ end
179
+
180
+ context 'when there are older sucesses out of the period' do
181
+ let(:old_errors) { 1 }
182
+ let(:old_sucesses) { 2 }
183
+
184
+ context 'and the regular documents are not enough' do
185
+ let(:successes) { 1 }
186
+ let(:errors) { 0 }
187
+
188
+ it 'ignores the older sucesses' do
189
+ expect(subject.status).to eq(:error)
190
+ end
191
+
192
+ context 'when passing a bigger period' do
193
+ let(:period) { 3.days }
194
+
195
+ it 'consider the older sucesses' do
196
+ expect(subject.status).to eq(:ok)
197
+ end
198
+ end
199
+ end
200
+
201
+ context 'and the regular documents are almost in the limit' do
202
+ let(:successes) { 2 }
203
+ let(:errors) { 1 }
204
+
205
+ it 'ignores the older sucesses' do
206
+ expect(subject.status).to eq(:ok)
207
+ end
208
+
209
+ context 'when passing a bigger period' do
210
+ let(:period) { 3.days }
211
+
212
+ it 'consider the older documents' do
213
+ expect(subject.status).to eq(:error)
214
+ end
215
+ end
216
+ end
217
+ end
218
+ end
219
+ end
220
+
221
+ describe '#count' do
222
+ let(:types) { [:a, :b] }
223
+ let(:errors) { 1 }
224
+ let(:scope) { :with_error }
225
+
226
+ it 'returns all the documents found' do
227
+ expect(subject.count).to eq(2)
228
+ end
229
+
230
+ context 'when configuring with a complex scope' do
231
+ let(:old_errors) { 0 }
232
+ let(:scope) { :'with_error.type_b' }
233
+ let(:errors) { 1 }
234
+
235
+ context 'as symbol' do
236
+ let(:scope) { :'with_error.type_b' }
237
+
238
+ it 'fetches from each scope in order' do
239
+ expect(subject.count).to eq(1)
240
+ end
241
+ end
242
+
243
+ context 'as string where scope' do
244
+ let(:scope) { "status = 'error' and doc_type = 'b'" }
245
+
246
+ it 'fetches from each scope in order' do
247
+ expect(subject.count).to eq(1)
248
+ end
249
+ end
250
+
251
+ context 'as hash where scope' do
252
+ let(:scope) { { status: :error, doc_type: :b } }
253
+
254
+ it 'fetches from each scope in order' do
255
+ expect(subject.count).to eq(1)
256
+ end
257
+ end
258
+ end
259
+ end
260
+
261
+ describe '#error?' do
262
+ let(:errors) { 2 }
263
+ let(:maximum) { 1 }
264
+
265
+ context 'when errors count overcome maximum' do
266
+ it { expect(subject.error?).to be_truthy }
267
+ end
268
+
269
+ context 'when errors count do not overcome maximum' do
270
+ let(:errors) { 0 }
271
+ it { expect(subject.error?).to be_falsey }
272
+ end
273
+ end
274
+
275
+ describe '#as_json' do
276
+ let(:expected) do
277
+ { count: count_expected, status: status_expected }
278
+ end
279
+
280
+ context 'when everything is ok' do
281
+ let(:errors) { 1 }
282
+ let(:status_expected) { :ok }
283
+ let(:count_expected) { errors }
284
+ let(:maximum) { 2 }
285
+
286
+ it 'returns the count and status' do
287
+ expect(subject.as_json).to eq(expected)
288
+ end
289
+ end
290
+
291
+ context 'when there is an error' do
292
+ let(:errors) { 2 }
293
+ let(:status_expected) { :error }
294
+ let(:count_expected) { errors }
295
+ let(:maximum) { 1 }
296
+
297
+ it 'returns the count and status' do
298
+ expect(subject.as_json).to eq(expected)
299
+ end
300
+ end
301
+ end
302
+ end
@@ -0,0 +1,39 @@
1
+ require 'spec_helper'
2
+
3
+ describe Bidu::Mercy::ReportConfig do
4
+ let(:config) { {} }
5
+ let(:parameters) { {} }
6
+ let(:subject) { described_class.new(config) }
7
+
8
+ describe '#build' do
9
+ context 'when no config is given' do
10
+ it do
11
+ expect(subject.build(parameters)).to be_a(Bidu::Mercy::Report::Error)
12
+ end
13
+ end
14
+
15
+ context 'when a dummy type is given' do
16
+ let(:config) { { type: :dummy } }
17
+
18
+ it do
19
+ expect(subject.build(parameters)).to be_a(Bidu::Mercy::Report::Dummy)
20
+ end
21
+ end
22
+
23
+ context 'when a class is given as type' do
24
+ let(:config) { { type: Bidu::Mercy::Report::Dummy } }
25
+
26
+ it do
27
+ expect(subject.build(parameters)).to be_a(Bidu::Mercy::Report::Dummy)
28
+ end
29
+ end
30
+
31
+ context 'when a global class is given as type' do
32
+ let(:config) { { type: Dummy } }
33
+
34
+ it do
35
+ expect(subject.build(parameters)).to be_a(Dummy)
36
+ end
37
+ end
38
+ end
39
+ end