bidu-mercy 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
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