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.
- checksums.yaml +7 -0
- data/.gitignore +2 -0
- data/.rspec +1 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +88 -0
- data/LICENSE +22 -0
- data/README.md +100 -0
- data/Rakefile +7 -0
- data/circle.yml +16 -0
- data/lib/bidu.rb +3 -0
- data/lib/bidu/mercy.rb +18 -0
- data/lib/bidu/mercy/class_methods.rb +19 -0
- data/lib/bidu/mercy/concern.rb +10 -0
- data/lib/bidu/mercy/report.rb +44 -0
- data/lib/bidu/mercy/report/active_record.rb +28 -0
- data/lib/bidu/mercy/report/error.rb +62 -0
- data/lib/bidu/mercy/report/multiple.rb +29 -0
- data/lib/bidu/mercy/report/range.rb +54 -0
- data/lib/bidu/mercy/report_builder.rb +24 -0
- data/lib/bidu/mercy/report_config.rb +37 -0
- data/lib/bidu/mercy/status.rb +27 -0
- data/lib/bidu/mercy/status_builder.rb +35 -0
- data/lib/bidu/mercy/version.rb +5 -0
- data/lib/bidu/period_parser.rb +32 -0
- data/lib/json_parser/type_cast_ext.rb +7 -0
- data/mercy.gemspec +33 -0
- data/spec/lib/bidu/mercy/report/error_spec.rb +385 -0
- data/spec/lib/bidu/mercy/report/multiple_spec.rb +122 -0
- data/spec/lib/bidu/mercy/report/range_spec.rb +302 -0
- data/spec/lib/bidu/mercy/report/report_config_spec.rb +39 -0
- data/spec/lib/bidu/mercy/report_builder_spec.rb +72 -0
- data/spec/lib/bidu/mercy/report_spec.rb +44 -0
- data/spec/lib/bidu/mercy/status_builder_spec.rb +84 -0
- data/spec/lib/bidu/mercy/status_spec.rb +135 -0
- data/spec/lib/bidu/period_parser_spec.rb +27 -0
- data/spec/spec_helper.rb +32 -0
- data/spec/support/fixture_helpers.rb +19 -0
- data/spec/support/models/document.rb +6 -0
- data/spec/support/report/dummy.rb +17 -0
- data/spec/support/schema.rb +11 -0
- metadata +236 -0
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
module Bidu::Mercy::Report::Multiple
|
|
2
|
+
def as_json
|
|
3
|
+
{
|
|
4
|
+
status: status
|
|
5
|
+
}.merge(sub_reports_hash)
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def error?
|
|
9
|
+
sub_reports.any?(&:error?)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
private
|
|
13
|
+
|
|
14
|
+
def sub_reports_hash
|
|
15
|
+
sub_reports.map(&:as_json).as_hash(reports_ids.map(&:to_s))
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def sub_reports
|
|
19
|
+
@sub_reports ||= reports_ids.map do |id|
|
|
20
|
+
build_sub_report(id)
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def build_sub_report(id)
|
|
25
|
+
sub_report_class.new(json.merge(key => id))
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
module Bidu
|
|
2
|
+
module Mercy
|
|
3
|
+
class Report
|
|
4
|
+
class Range < Report::ActiveRecord
|
|
5
|
+
ALLOWED_PARAMETERS=[:period, :maximum, :minimum]
|
|
6
|
+
DEFAULT_OPTION = {
|
|
7
|
+
period: 1.day,
|
|
8
|
+
scope: :all,
|
|
9
|
+
minimum: nil,
|
|
10
|
+
maximum: nil
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
json_parse :scope
|
|
14
|
+
json_parse :minimum, :maximum, type: :integer
|
|
15
|
+
|
|
16
|
+
def initialize(options)
|
|
17
|
+
super(DEFAULT_OPTION.merge(options))
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def scoped
|
|
21
|
+
@scoped ||= fetch_scoped(last_entries, scope)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def error?
|
|
25
|
+
@error ||= !count_in_range?
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def as_json
|
|
29
|
+
{
|
|
30
|
+
status: status,
|
|
31
|
+
count: count
|
|
32
|
+
}
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def count
|
|
36
|
+
scoped.count
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
private
|
|
40
|
+
|
|
41
|
+
def range
|
|
42
|
+
(minimum..maximum)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def count_in_range?
|
|
46
|
+
return range.include?(count) unless (maximum.nil? || minimum.nil?)
|
|
47
|
+
return count >= minimum unless minimum.nil?
|
|
48
|
+
return count <= maximum unless maximum.nil?
|
|
49
|
+
true
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
module Bidu
|
|
2
|
+
module Mercy
|
|
3
|
+
class ReportBuilder
|
|
4
|
+
def build(key, parameters = {})
|
|
5
|
+
config = config_for(key)
|
|
6
|
+
config.build(parameters)
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def add_config(key, config)
|
|
10
|
+
configs[key] = Bidu::Mercy::ReportConfig.new(config)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
private
|
|
14
|
+
|
|
15
|
+
def config_for(key)
|
|
16
|
+
configs[key]
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def configs
|
|
20
|
+
@configs ||= {}
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
module Bidu
|
|
2
|
+
module Mercy
|
|
3
|
+
class ReportConfig
|
|
4
|
+
attr_accessor :config
|
|
5
|
+
|
|
6
|
+
delegate :[], :[]=, :merge, to: :config
|
|
7
|
+
|
|
8
|
+
def initialize(config)
|
|
9
|
+
@config = config
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def build(parameters)
|
|
13
|
+
params = slice_parameters(parameters)
|
|
14
|
+
report_class.new(config.merge(params))
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
private
|
|
18
|
+
|
|
19
|
+
def type
|
|
20
|
+
self[:type] ||= :error
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def report_class
|
|
24
|
+
return type if type.is_a?(Class)
|
|
25
|
+
@report_class ||= Bidu::Mercy::Report.const_get(type.to_s.camelize)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def slice_parameters(parameters)
|
|
29
|
+
parameters.slice(*allowed_parameters)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def allowed_parameters
|
|
33
|
+
report_class::ALLOWED_PARAMETERS
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
module Bidu
|
|
2
|
+
module Mercy
|
|
3
|
+
class Status
|
|
4
|
+
attr_reader :reports
|
|
5
|
+
|
|
6
|
+
def initialize(reports)
|
|
7
|
+
@reports = reports
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def status
|
|
11
|
+
reports.any? { |r| r.error? } ? :error : :ok
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def as_json
|
|
15
|
+
{
|
|
16
|
+
status: status
|
|
17
|
+
}.merge(reports_jsons)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
private
|
|
21
|
+
|
|
22
|
+
def reports_jsons
|
|
23
|
+
reports.map(&:as_json).as_hash(reports.map(&:id))
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
module Bidu
|
|
2
|
+
module Mercy
|
|
3
|
+
class StatusBuilder
|
|
4
|
+
def build(key, parameters = {})
|
|
5
|
+
Bidu::Mercy::Status.new(reports_for(key, parameters))
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def add_report_config(key, config)
|
|
9
|
+
status_key = config.delete(:on) || :default
|
|
10
|
+
report_builder.add_config(key, config)
|
|
11
|
+
config_for(status_key) << key
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
private
|
|
15
|
+
|
|
16
|
+
def report_builder
|
|
17
|
+
@report_builder ||= Bidu::Mercy::ReportBuilder.new
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def reports_for(key, parameters)
|
|
21
|
+
config_for(key).map do |report_key|
|
|
22
|
+
report_builder.build(report_key, parameters)
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def configs
|
|
27
|
+
@configs ||= {}
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def config_for(key)
|
|
31
|
+
configs[key] ||= []
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
module Bidu
|
|
2
|
+
class PeriodParser
|
|
3
|
+
class << self
|
|
4
|
+
def parse(period)
|
|
5
|
+
return unless period
|
|
6
|
+
return period if period.is_a?(Integer)
|
|
7
|
+
new(period).to_seconds
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def initialize(period)
|
|
12
|
+
@period = period
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def to_seconds
|
|
16
|
+
return unless period.match(/^\d+(years|months|days|hours|minutes|seconds)?/)
|
|
17
|
+
type.blank? ? value.seconds : value.public_send(type)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
private
|
|
21
|
+
|
|
22
|
+
attr_reader :period
|
|
23
|
+
|
|
24
|
+
def value
|
|
25
|
+
@period_value ||= period.gsub(/\D+/, '').to_i
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def type
|
|
29
|
+
@period_type ||= period.gsub(/\d+/, '')
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
data/mercy.gemspec
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# coding: utf-8
|
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
4
|
+
require 'bidu/mercy/version'
|
|
5
|
+
|
|
6
|
+
Gem::Specification.new do |gem|
|
|
7
|
+
gem.name = 'mercy'
|
|
8
|
+
gem.version = Bidu::Mercy::VERSION
|
|
9
|
+
gem.authors = ["Bidu Dev's Team"]
|
|
10
|
+
gem.email = ["dev@bidu.com.br"]
|
|
11
|
+
gem.homepage = 'https://github.com/Bidu/mercy'
|
|
12
|
+
gem.description = 'Gem for easy health check'
|
|
13
|
+
gem.summary = gem.description
|
|
14
|
+
|
|
15
|
+
gem.files = `git ls-files`.split($/)
|
|
16
|
+
gem.executables = gem.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
|
17
|
+
gem.test_files = gem.files.grep(%r{^(test|gem|features)/})
|
|
18
|
+
gem.require_paths = ["lib"]
|
|
19
|
+
|
|
20
|
+
gem.add_runtime_dependency 'activesupport'
|
|
21
|
+
gem.add_runtime_dependency 'concern_builder'
|
|
22
|
+
gem.add_runtime_dependency 'darthjee-active_ext'
|
|
23
|
+
gem.add_runtime_dependency 'json_parser', '~> 1.1'
|
|
24
|
+
|
|
25
|
+
gem.add_development_dependency "activerecord"
|
|
26
|
+
gem.add_development_dependency "sqlite3"
|
|
27
|
+
|
|
28
|
+
gem.add_development_dependency "bundler"
|
|
29
|
+
gem.add_development_dependency "rake"
|
|
30
|
+
gem.add_development_dependency "rspec"
|
|
31
|
+
gem.add_development_dependency 'pry-nav'
|
|
32
|
+
gem.add_development_dependency 'simplecov'
|
|
33
|
+
end
|
|
@@ -0,0 +1,385 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe Bidu::Mercy::Report::Error do
|
|
4
|
+
let(:errors) { 1 }
|
|
5
|
+
let(:successes) { 1 }
|
|
6
|
+
let(:old_errors) { 2 }
|
|
7
|
+
let(:threshold) { 0.02 }
|
|
8
|
+
let(:period) { 1.day }
|
|
9
|
+
let(:external_key) { :external_id }
|
|
10
|
+
let(:scope) { :with_error }
|
|
11
|
+
let(:base_scope) { :all }
|
|
12
|
+
let(:options) do
|
|
13
|
+
{
|
|
14
|
+
period: period,
|
|
15
|
+
threshold: threshold,
|
|
16
|
+
scope: scope,
|
|
17
|
+
base_scope: base_scope,
|
|
18
|
+
clazz: Document,
|
|
19
|
+
external_key: external_key
|
|
20
|
+
}
|
|
21
|
+
end
|
|
22
|
+
let(:subject) { described_class.new(options) }
|
|
23
|
+
let(:types) { [:a] }
|
|
24
|
+
before do
|
|
25
|
+
Document.all.each(&:destroy)
|
|
26
|
+
types.each do |type|
|
|
27
|
+
successes.times { Document.create status: :success, doc_type: type }
|
|
28
|
+
errors.times do |i|
|
|
29
|
+
Document.create status: :error, external_id: 10 * successes + i, outter_external_id: i, doc_type: type
|
|
30
|
+
end
|
|
31
|
+
old_errors.times do
|
|
32
|
+
Document.create status: :error, created_at: 2.days.ago, updated_at: 2.days.ago, doc_type: type
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
describe '#status' do
|
|
38
|
+
context 'when are more errors than the allowed by the threshold' do
|
|
39
|
+
let(:errors) { 1 }
|
|
40
|
+
let(:successes) { 3 }
|
|
41
|
+
it { expect(subject.status).to eq(:error) }
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
context 'when the threshold is 0 and there are no errors' do
|
|
45
|
+
let(:errors) { 0 }
|
|
46
|
+
let(:threshold) { 0 }
|
|
47
|
+
it { expect(subject.status).to eq(:ok) }
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
context 'when the threshold is 100% and there is 100% error' do
|
|
51
|
+
let(:successes) { 0 }
|
|
52
|
+
let(:threshold) { 1 }
|
|
53
|
+
it { expect(subject.status).to eq(:ok) }
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
context 'when there are no documents' do
|
|
57
|
+
let(:successes) { 0 }
|
|
58
|
+
let(:errors) { 0 }
|
|
59
|
+
it { expect(subject.status).to eq(:ok) }
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
context 'when there are older errors out of the period' do
|
|
63
|
+
let(:threshold) { 0.6 }
|
|
64
|
+
|
|
65
|
+
it 'ignores the older errros' do
|
|
66
|
+
expect(subject.status).to eq(:ok)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
context 'when passing a bigger period' do
|
|
70
|
+
let(:period) { 3.days }
|
|
71
|
+
|
|
72
|
+
it 'consider the older errros' do
|
|
73
|
+
expect(subject.status).to eq(:error)
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
describe 'percentage' do
|
|
80
|
+
context 'when there are 25% erros' do
|
|
81
|
+
let(:errors) { 1 }
|
|
82
|
+
let(:successes) { 3 }
|
|
83
|
+
it { expect(subject.percentage).to eq(0.25) }
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
context 'when there are no errors' do
|
|
87
|
+
let(:errors) { 0 }
|
|
88
|
+
let(:threshold) { 0 }
|
|
89
|
+
it { expect(subject.percentage).to eq(0) }
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
context 'when there is 100% error' do
|
|
93
|
+
let(:successes) { 0 }
|
|
94
|
+
let(:threshold) { 1 }
|
|
95
|
+
it { expect(subject.percentage).to eq(1) }
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
context 'when there are no documents' do
|
|
99
|
+
let(:successes) { 0 }
|
|
100
|
+
let(:errors) { 0 }
|
|
101
|
+
it { expect(subject.percentage).to eq(0) }
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
context 'when there are older errors out of the period' do
|
|
105
|
+
let(:old_errors) { 2 }
|
|
106
|
+
|
|
107
|
+
it 'ignores the older errros' do
|
|
108
|
+
expect(subject.percentage).to eq(0.5)
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
context 'when passing a bigger period' do
|
|
112
|
+
let(:period) { 3.days }
|
|
113
|
+
|
|
114
|
+
it 'consider the older errros' do
|
|
115
|
+
expect(subject.percentage).to eq(0.75)
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
context 'when configuring with a complex scope' do
|
|
121
|
+
let(:types) { [:a, :b] }
|
|
122
|
+
let(:old_errors) { 0 }
|
|
123
|
+
let(:scope) { :'with_error.type_b' }
|
|
124
|
+
let(:errors) { 1 }
|
|
125
|
+
let(:successes) { 3 }
|
|
126
|
+
context 'as symbol' do
|
|
127
|
+
let(:scope) { :'with_error.type_b' }
|
|
128
|
+
|
|
129
|
+
it 'fetches from each scope in order' do
|
|
130
|
+
expect(subject.percentage).to eq(0.125)
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
context 'as string where scope' do
|
|
135
|
+
let(:scope) { "status = 'error' and doc_type = 'b'" }
|
|
136
|
+
|
|
137
|
+
it 'fetches from each scope in order' do
|
|
138
|
+
expect(subject.percentage).to eq(0.125)
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
context 'as hash where scope' do
|
|
143
|
+
let(:scope) { { status: :error, doc_type: :b } }
|
|
144
|
+
|
|
145
|
+
it 'fetches from each scope in order' do
|
|
146
|
+
expect(subject.percentage).to eq(0.125)
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
context 'when using a base scope' do
|
|
152
|
+
let(:types) { [:a, :b, :b, :b] }
|
|
153
|
+
let(:old_errors) { 0 }
|
|
154
|
+
let(:errors) { 1 }
|
|
155
|
+
let(:successes) { 3 }
|
|
156
|
+
|
|
157
|
+
context 'as symbol' do
|
|
158
|
+
let(:base_scope) { :type_b }
|
|
159
|
+
|
|
160
|
+
it 'fetches from each scope in order' do
|
|
161
|
+
expect(subject.percentage).to eq(0.25)
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
context 'as where clause' do
|
|
166
|
+
let(:base_scope) { "doc_type = 'b'" }
|
|
167
|
+
|
|
168
|
+
it 'fetches from each scope in order' do
|
|
169
|
+
expect(subject.percentage).to eq(0.25)
|
|
170
|
+
end
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
context 'as hash' do
|
|
174
|
+
let(:base_scope) { { doc_type: :b } }
|
|
175
|
+
|
|
176
|
+
it 'fetches from each scope in order' do
|
|
177
|
+
expect(subject.percentage).to eq(0.25)
|
|
178
|
+
end
|
|
179
|
+
end
|
|
180
|
+
end
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
describe '#scoped' do
|
|
184
|
+
context 'when there are 25% erros' do
|
|
185
|
+
let(:errors) { 1 }
|
|
186
|
+
let(:successes) { 3 }
|
|
187
|
+
it 'returns only the scoped documents' do
|
|
188
|
+
expect(subject.scoped.count).to eq(1)
|
|
189
|
+
end
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
context 'when there are no errors' do
|
|
193
|
+
let(:errors) { 0 }
|
|
194
|
+
let(:threshold) { 0 }
|
|
195
|
+
it { expect(subject.scoped).to be_empty }
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
context 'when there are no documents' do
|
|
199
|
+
let(:successes) { 0 }
|
|
200
|
+
let(:errors) { 0 }
|
|
201
|
+
it { expect(subject.scoped).to be_empty }
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
context 'when there are older errors out of the period' do
|
|
205
|
+
let(:old_errors) { 2 }
|
|
206
|
+
|
|
207
|
+
it 'ignores the older errros' do
|
|
208
|
+
expect(subject.scoped.count).to eq(1)
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
context 'when passing a bigger period' do
|
|
212
|
+
let(:period) { 3.days }
|
|
213
|
+
|
|
214
|
+
it 'consider the older errros' do
|
|
215
|
+
expect(subject.scoped.count).to eq(3)
|
|
216
|
+
end
|
|
217
|
+
end
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
context 'when configured with a complex scope' do
|
|
221
|
+
let(:types) { [:a, :b, :b] }
|
|
222
|
+
let(:old_errors) { 0 }
|
|
223
|
+
|
|
224
|
+
context 'as symbol' do
|
|
225
|
+
let(:scope) { :'with_error.type_b' }
|
|
226
|
+
|
|
227
|
+
it 'fetches from each scope in order' do
|
|
228
|
+
expect(subject.scoped.count).to eq(Document.with_error.type_b.count)
|
|
229
|
+
expect(subject.scoped.count).to eq(2 * Document.with_error.type_a.count)
|
|
230
|
+
end
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
context 'as hash' do
|
|
234
|
+
let(:scope) { { status: :error, doc_type: :b } }
|
|
235
|
+
|
|
236
|
+
it 'fetches from each scope in order' do
|
|
237
|
+
expect(subject.scoped.count).to eq(Document.with_error.type_b.count)
|
|
238
|
+
expect(subject.scoped.count).to eq(2 * Document.with_error.type_a.count)
|
|
239
|
+
end
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
context 'as string where scope' do
|
|
243
|
+
let(:scope) { "status = 'error' and doc_type = 'b'" }
|
|
244
|
+
|
|
245
|
+
it 'fetches from each scope in order' do
|
|
246
|
+
expect(subject.scoped.count).to eq(Document.with_error.type_b.count)
|
|
247
|
+
expect(subject.scoped.count).to eq(2 * Document.with_error.type_a.count)
|
|
248
|
+
end
|
|
249
|
+
end
|
|
250
|
+
end
|
|
251
|
+
|
|
252
|
+
context 'when using a base scope' do
|
|
253
|
+
let(:types) { [:a, :b, :b, :b] }
|
|
254
|
+
let(:old_errors) { 0 }
|
|
255
|
+
|
|
256
|
+
context 'as symbol' do
|
|
257
|
+
let(:base_scope) { :type_b }
|
|
258
|
+
|
|
259
|
+
it 'fetches from each scope in order' do
|
|
260
|
+
expect(subject.scoped.count).to eq(Document.with_error.type_b.count)
|
|
261
|
+
expect(subject.scoped.count).to eq(3 * Document.with_error.type_a.count)
|
|
262
|
+
end
|
|
263
|
+
end
|
|
264
|
+
|
|
265
|
+
context 'as where clause' do
|
|
266
|
+
let(:base_scope) { "doc_type = 'b'" }
|
|
267
|
+
|
|
268
|
+
it 'fetches from each scope in order' do
|
|
269
|
+
expect(subject.scoped.count).to eq(Document.with_error.type_b.count)
|
|
270
|
+
expect(subject.scoped.count).to eq(3 * Document.with_error.type_a.count)
|
|
271
|
+
end
|
|
272
|
+
end
|
|
273
|
+
|
|
274
|
+
context 'as hash' do
|
|
275
|
+
let(:base_scope) { { doc_type: :b } }
|
|
276
|
+
|
|
277
|
+
it 'fetches from each scope in order' do
|
|
278
|
+
expect(subject.scoped.count).to eq(Document.with_error.type_b.count)
|
|
279
|
+
expect(subject.scoped.count).to eq(3 * Document.with_error.type_a.count)
|
|
280
|
+
end
|
|
281
|
+
end
|
|
282
|
+
end
|
|
283
|
+
end
|
|
284
|
+
|
|
285
|
+
describe '#error?' do
|
|
286
|
+
context 'when errors percentage overcames threshold' do
|
|
287
|
+
it { expect(subject.error?).to be_truthy }
|
|
288
|
+
end
|
|
289
|
+
|
|
290
|
+
context 'when errors percentage does not overcames threshold' do
|
|
291
|
+
let(:errors) { 0 }
|
|
292
|
+
it { expect(subject.error?).to be_falsey }
|
|
293
|
+
end
|
|
294
|
+
end
|
|
295
|
+
|
|
296
|
+
describe '#status' do
|
|
297
|
+
context 'when errors percentage overcames threshold' do
|
|
298
|
+
it { expect(subject.status).to eq(:error) }
|
|
299
|
+
end
|
|
300
|
+
|
|
301
|
+
context 'when errors percentage does not overcames threshold' do
|
|
302
|
+
let(:errors) { 0 }
|
|
303
|
+
it { expect(subject.status).to eq(:ok) }
|
|
304
|
+
end
|
|
305
|
+
end
|
|
306
|
+
|
|
307
|
+
describe '#as_json' do
|
|
308
|
+
let(:expected) do
|
|
309
|
+
{ ids: ids_expected, percentage: percentage_expected, status: status_expected }
|
|
310
|
+
end
|
|
311
|
+
|
|
312
|
+
context 'when everything is ok' do
|
|
313
|
+
let(:errors) { 1 }
|
|
314
|
+
let(:successes) { 9 }
|
|
315
|
+
let(:ids_expected) { [90] }
|
|
316
|
+
let(:status_expected) { :ok }
|
|
317
|
+
let(:percentage_expected) { 0.1 }
|
|
318
|
+
let(:threshold) { 0.5 }
|
|
319
|
+
|
|
320
|
+
it 'returns the external keys, status and error percentage' do
|
|
321
|
+
expect(subject.as_json).to eq(expected)
|
|
322
|
+
end
|
|
323
|
+
|
|
324
|
+
end
|
|
325
|
+
|
|
326
|
+
context 'when there are 75% erros' do
|
|
327
|
+
let(:status_expected) { :error }
|
|
328
|
+
let(:percentage_expected) { 0.75 }
|
|
329
|
+
let(:errors) { 3 }
|
|
330
|
+
let(:successes) { 1 }
|
|
331
|
+
let(:ids_expected) { [10, 11, 12] }
|
|
332
|
+
|
|
333
|
+
it 'returns the external keys, status and error percentage' do
|
|
334
|
+
expect(subject.as_json).to eq(expected)
|
|
335
|
+
end
|
|
336
|
+
|
|
337
|
+
context 'when configurated with different external key' do
|
|
338
|
+
let(:external_key) { :outter_external_id }
|
|
339
|
+
let(:ids_expected) { [0, 1, 2] }
|
|
340
|
+
|
|
341
|
+
it 'returns the correct external keys' do
|
|
342
|
+
expect(subject.as_json).to eq(expected)
|
|
343
|
+
end
|
|
344
|
+
|
|
345
|
+
context 'when some external ids are the same' do
|
|
346
|
+
let(:ids_expected) { [10, 10, 10] }
|
|
347
|
+
before do
|
|
348
|
+
Document.update_all(outter_external_id: 10)
|
|
349
|
+
end
|
|
350
|
+
|
|
351
|
+
it 'returns the correct external keys' do
|
|
352
|
+
expect(subject.as_json).to eq(expected)
|
|
353
|
+
end
|
|
354
|
+
|
|
355
|
+
context 'and passing uniq option' do
|
|
356
|
+
before { options[:uniq] = true }
|
|
357
|
+
let(:ids_expected) { [10] }
|
|
358
|
+
|
|
359
|
+
it 'returns the correct external keys only once' do
|
|
360
|
+
expect(subject.as_json).to eq(expected)
|
|
361
|
+
end
|
|
362
|
+
end
|
|
363
|
+
end
|
|
364
|
+
|
|
365
|
+
context 'with a limit' do
|
|
366
|
+
before { options[:limit] = 2 }
|
|
367
|
+
let(:ids_expected) { [0, 1] }
|
|
368
|
+
|
|
369
|
+
it 'returns only the limited ids' do
|
|
370
|
+
expect(subject.as_json).to eq(expected)
|
|
371
|
+
end
|
|
372
|
+
end
|
|
373
|
+
end
|
|
374
|
+
|
|
375
|
+
context 'when configurated without external key' do
|
|
376
|
+
before { options.delete(:external_key) }
|
|
377
|
+
let(:ids_expected) { Document.with_error.where('created_at > ?', 30.hours.ago).map(&:id) }
|
|
378
|
+
|
|
379
|
+
it 'returns the ids as default id' do
|
|
380
|
+
expect(subject.as_json).to eq(expected)
|
|
381
|
+
end
|
|
382
|
+
end
|
|
383
|
+
end
|
|
384
|
+
end
|
|
385
|
+
end
|