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,72 @@
1
+ require 'spec_helper'
2
+
3
+ describe Bidu::Mercy::ReportBuilder do
4
+ let(:errors) { 1 }
5
+ let(:successes) { 3 }
6
+ let(:old_errors) { 2 }
7
+ let(:key) { :errors }
8
+ let(:threshold) { 0.02 }
9
+ let(:period) { 1.day }
10
+ let(:external_key) { :external_id }
11
+ let(:config) do
12
+ {
13
+ period: period,
14
+ threshold: threshold,
15
+ external_key: :external_id,
16
+ scope: :with_error,
17
+ id: key,
18
+ clazz: Document
19
+ }
20
+ end
21
+ let(:parameters) { {} }
22
+ let(:report) { subject.build(key, parameters) }
23
+ before do
24
+ subject.add_config(key, config)
25
+ Document.all.each(&:destroy)
26
+ successes.times { |i| Document.create status: :success, external_id: 30+i }
27
+ errors.times { |i| Document.create status: :error, external_id: 10+i }
28
+ old_errors.times do |i|
29
+ Document.create status: :error, external_id: 20+i, created_at: 2.days.ago, updated_at: 2.days.ago
30
+ end
31
+ end
32
+
33
+ describe '#build' do
34
+ let(:ids) { [ 10 ] }
35
+ it do
36
+ expect(report).to be_a(Bidu::Mercy::Report::Error)
37
+ end
38
+
39
+ it 'builds the report using the given configuration' do
40
+ expect(report.as_json).to eq( ids: ids, percentage: 0.25, status: :error )
41
+ expect(report.error?).to be_truthy
42
+ end
43
+
44
+ context 'when passing a custom threshold parameter' do
45
+ let(:parameters) { { threshold: 1 } }
46
+
47
+ it 'uses custom threshold parameter' do
48
+ expect(report.error?).to be_falsey
49
+ end
50
+ end
51
+
52
+ context 'when passing a custom period parameter' do
53
+ let(:parameters) { { threshold: 0.4, period: 10.days, status: :error } }
54
+
55
+ it 'uses custom period parameter' do
56
+ expect(report.error?).to be_truthy
57
+ end
58
+ end
59
+
60
+ context 'when passing a custom other parameters' do
61
+ let(:parameters) do
62
+ { scope: :with_success, clazz: Bidu::Mercy::Report::Error, external_key: :id, id: :failures }
63
+ end
64
+
65
+ it 'ignores the non customizable parameters' do
66
+ expect(report.as_json).to eq( ids: ids, percentage: 0.25, status: :error )
67
+ expect(report.error?).to be_truthy
68
+ expect(report.id).to eq(:errors)
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,44 @@
1
+ require 'spec_helper'
2
+
3
+ describe Bidu::Mercy::Report do
4
+ class Bidu::Mercy::Report::Dummy1 < Bidu::Mercy::Report
5
+ DEFAULT_OPTION = {
6
+ option_value: 1,
7
+ other_option: 10
8
+ }
9
+ json_parse :option_value, :other_option, case: :snake
10
+ end
11
+ class Bidu::Mercy::Report::Dummy2 < Bidu::Mercy::Report::Dummy1; end
12
+ class Bidu::Mercy::Report::Dummy3 < Bidu::Mercy::Report::Dummy1
13
+ DEFAULT_OPTION = { option_value: 5 }
14
+ end
15
+
16
+ describe 'default_options' do
17
+ let(:report_class) { described_class::Dummy1 }
18
+ let(:subject) { report_class.new }
19
+
20
+ it 'setup the attributes using class default options' do
21
+ expect(subject.option_value).to eq(1)
22
+ expect(subject.other_option).to eq(10)
23
+ end
24
+
25
+ context 'when class inherit options' do
26
+ let(:report_class) { described_class::Dummy2 }
27
+
28
+ it 'setup the attributes using superclass default options' do
29
+ expect(subject.option_value).to eq(1)
30
+ expect(subject.other_option).to eq(10)
31
+ end
32
+ end
33
+
34
+ context 'when class inherit options but overrides some' do
35
+ let(:report_class) { described_class::Dummy3 }
36
+
37
+ it 'setup the attributes using superclass default options' do
38
+ expect(subject.option_value).to eq(5)
39
+ expect(subject.other_option).to eq(10)
40
+ end
41
+ end
42
+ end
43
+ end
44
+
@@ -0,0 +1,84 @@
1
+ require 'spec_helper'
2
+
3
+ describe Bidu::Mercy::StatusBuilder do
4
+ let(:errors) { 1 }
5
+ let(:successes) { 3 }
6
+ let(:old_errors) { 2 }
7
+ let(:key) { :errors }
8
+ let(:threshold) { 0.02 }
9
+ let(:period) { 1.day }
10
+ let(:external_key) { :external_id }
11
+ let(:config) do
12
+ {
13
+ period: period,
14
+ threshold: threshold,
15
+ scope: :with_error,
16
+ clazz: Document,
17
+ id: :failures,
18
+ external_key: :external_id,
19
+ on: key
20
+ }
21
+ end
22
+ let(:parameters) { {} }
23
+ let(:status) { subject.build(key, parameters) }
24
+ before do
25
+ subject.add_report_config(key, config)
26
+ Document.all.each(&:destroy)
27
+ successes.times { |i| Document.create status: :success, external_id: 30+i }
28
+ errors.times { |i| Document.create status: :error, external_id: 10+i }
29
+ old_errors.times do |i|
30
+ Document.create status: :error, external_id: 20+i, created_at: 2.days.ago, updated_at: 2.days.ago
31
+ end
32
+ end
33
+
34
+ describe '#build' do
35
+ let(:ids) { [ 10 ] }
36
+ let(:status_expected) { :error }
37
+ let(:percentage) { 0.25 }
38
+ let(:json_expected) do
39
+ {
40
+ status: status_expected,
41
+ failures: {
42
+ ids: ids,
43
+ percentage: percentage,
44
+ status: status_expected
45
+ }
46
+ }
47
+ end
48
+
49
+ it do
50
+ expect(status).to be_a(Bidu::Mercy::Status)
51
+ end
52
+
53
+ context 'when not specifying where to report' do
54
+ let(:key) {}
55
+
56
+ it 'register report under default' do
57
+ expect(subject.build(:default).as_json).to eq(json_expected)
58
+ end
59
+ end
60
+
61
+ it 'builds the report using the given configuration' do
62
+ expect(status.as_json).to eq(json_expected)
63
+ end
64
+
65
+ context 'when passing a custom threshold parameter' do
66
+ let(:parameters) { { threshold: 1 } }
67
+ let(:status_expected) { :ok }
68
+
69
+ it 'uses custom threshold parameter' do
70
+ expect(status.as_json).to eq(json_expected)
71
+ end
72
+ end
73
+
74
+ context 'when passing a custom period parameter' do
75
+ let(:ids) { [ 10, 20, 21 ] }
76
+ let(:percentage) { 0.5 }
77
+ let(:parameters) { { threshold: 0.4, period: 10.days } }
78
+
79
+ it 'uses custom period parameter' do
80
+ expect(status.as_json).to eq(json_expected)
81
+ end
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,135 @@
1
+ require 'spec_helper'
2
+
3
+ describe Bidu::Mercy::Status do
4
+ let(:threshold) { 0.02 }
5
+ let(:period) { 1.day }
6
+ let(:report_options) do
7
+ {
8
+ period: period,
9
+ threshold: threshold,
10
+ scope: :with_error,
11
+ clazz: Document,
12
+ id: :errors
13
+ }
14
+ end
15
+ let(:success_options) do
16
+ report_options.merge(
17
+ scope: :with_success,
18
+ id: :success
19
+ )
20
+ end
21
+ let(:errors) { 0 }
22
+ let(:successes) { 1 }
23
+ let(:error_report) { Bidu::Mercy::Report::Error.new(report_options) }
24
+ let(:success_report) do
25
+ Bidu::Mercy::Report::Error.new(success_options)
26
+ end
27
+ let(:reports) { [ error_report ] }
28
+ let(:subject) { described_class.new(reports) }
29
+ before do
30
+ Document.all.each(&:destroy)
31
+ errors.times { Document.create status: :error }
32
+ successes.times { Document.create status: :success }
33
+ end
34
+
35
+ describe '#as_json' do
36
+ let(:status_json) { subject.as_json }
37
+ let(:status) { status_json[:status] }
38
+
39
+ context 'when report is ok' do
40
+ it 'returns a json with ok' do
41
+ expect(status).to eq(:ok)
42
+ end
43
+
44
+ it 'returns the report json' do
45
+
46
+ end
47
+ end
48
+
49
+ context 'when report is not ok' do
50
+ let(:errors) { 1 }
51
+
52
+ it 'returns a json with error' do
53
+ expect(status).to eq(:error)
54
+ end
55
+ end
56
+
57
+ context 'when there are both success and error reports' do
58
+ let(:success_report) do
59
+ Bidu::Mercy::Report::Error.new(report_options.merge(scope: :with_success))
60
+ end
61
+ let(:reports) { [ success_report, error_report ] }
62
+
63
+ it 'returns a json with error' do
64
+ expect(status).to eq(:error)
65
+ end
66
+ end
67
+ end
68
+
69
+ describe '#status' do
70
+ context 'when report is ok' do
71
+ it do
72
+ expect(subject.status).to eq(:ok)
73
+ end
74
+ end
75
+
76
+ context 'when report is not ok' do
77
+ let(:errors) { 1 }
78
+
79
+ it do
80
+ expect(subject.status).to eq(:error)
81
+ end
82
+ end
83
+
84
+ context 'when there are both success and error reports' do
85
+ let(:reports) { [ success_report, error_report ] }
86
+
87
+ it do
88
+ expect(subject.status).to eq(:error)
89
+ end
90
+ end
91
+ end
92
+
93
+ describe '#as_json' do
94
+ let(:errors) { 3 }
95
+ let(:ids) { Document.with_error.map(&:id) }
96
+ let(:expected) do
97
+ {
98
+ status: :error,
99
+ errors: {
100
+ ids: ids,
101
+ percentage: 0.75,
102
+ status: :error
103
+ }
104
+ }
105
+ end
106
+
107
+ it 'creates a summary of the reports' do
108
+ expect(subject.as_json).to eq(expected)
109
+ end
110
+
111
+ context 'when there are both success and error reports' do
112
+ let(:success_ids) { Document.with_success.map(&:id) }
113
+ let(:expected) do
114
+ {
115
+ status: :error,
116
+ errors: {
117
+ ids: ids,
118
+ percentage: 0.75,
119
+ status: :error
120
+ },
121
+ success: {
122
+ ids: success_ids,
123
+ percentage: 0.25,
124
+ status: :error
125
+ }
126
+ }
127
+ end
128
+ let(:reports) { [ success_report, error_report ] }
129
+
130
+ it 'creates a summary of all the reports' do
131
+ expect(subject.as_json).to eq(expected)
132
+ end
133
+ end
134
+ end
135
+ end
@@ -0,0 +1,27 @@
1
+ require 'spec_helper'
2
+
3
+ describe Bidu::PeriodParser do
4
+ shared_examples 'a class who knows how to parse time' do |tests|
5
+ tests.each do |string, expected|
6
+ it "parses #{string} into #{expected} seconds" do
7
+ expect(described_class.parse(string)).to eq(expected)
8
+ end
9
+ end
10
+ end
11
+
12
+ it_behaves_like 'a class who knows how to parse time', {
13
+ '3' => 3.seconds,
14
+ '3seconds' => 3.seconds,
15
+ '3minutes' => 3.minutes,
16
+ '3hours' => 3.hours,
17
+ '3days' => 3.days,
18
+ '3months' => 3.months,
19
+ '3years' => 3.years
20
+ }
21
+
22
+ context 'when value is already a period' do
23
+ it 'returns the value itself' do
24
+ expect(described_class.parse(3.minutes)).to eq(3.minutes)
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,32 @@
1
+ require 'simplecov'
2
+ SimpleCov.profiles.define 'gem' do
3
+ add_filter '/spec/'
4
+ end
5
+
6
+ if ENV['CODECLIMATE_REPO_TOKEN']
7
+ require "codeclimate-test-reporter"
8
+ CodeClimate::TestReporter.start
9
+ end
10
+
11
+ SimpleCov.start 'gem'
12
+
13
+ require 'pry-nav'
14
+ require 'bidu/mercy'
15
+
16
+ require 'active_record'
17
+ ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: ":memory:")
18
+
19
+ support_files = File.expand_path("spec/support/**/*.rb")
20
+ Dir[support_files].each { |file| require file }
21
+
22
+ RSpec.configure do |config|
23
+ config.treat_symbols_as_metadata_keys_with_true_values = true
24
+ config.run_all_when_everything_filtered = true
25
+ config.filter_run :focus
26
+ config.filter_run_excluding :integration unless ENV['ALL']
27
+
28
+ config.order = 'random'
29
+
30
+ config.before do
31
+ end
32
+ end
@@ -0,0 +1,19 @@
1
+ require 'active_support'
2
+
3
+ module FixtureHelpers
4
+ def load_fixture_file(filename)
5
+ File.read (['./spec/', 'fixtures', filename].join('/'))
6
+ end
7
+
8
+ def load_json_fixture_file(filename)
9
+ cached_json_fixture_file(filename)
10
+ end
11
+
12
+ private
13
+
14
+ def cached_json_fixture_file(filename)
15
+ ActiveSupport::JSON.decode(load_fixture_file(filename))
16
+ end
17
+ end
18
+
19
+ RSpec.configuration.include FixtureHelpers
@@ -0,0 +1,6 @@
1
+ class Document < ActiveRecord::Base
2
+ scope :with_error, proc { where(status: :error) }
3
+ scope :with_success, proc { where(status: :success) }
4
+ scope :type_a, proc { where(doc_type: :a) }
5
+ scope :type_b, proc { where(doc_type: :b) }
6
+ end
@@ -0,0 +1,17 @@
1
+ module Bidu
2
+ module Mercy
3
+ class Report
4
+ class Dummy < Report
5
+ ALLOWED_PARAMETERS=[:period, :threshold]
6
+ def initialize(options)
7
+ end
8
+ end
9
+ end
10
+ end
11
+ end
12
+
13
+ class Dummy
14
+ ALLOWED_PARAMETERS=[:period, :threshold]
15
+ def initialize(options)
16
+ end
17
+ end