betterplace-bi 0.7.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 +10 -0
- data/.semaphore/semaphore.yml +26 -0
- data/.tool-versions +2 -0
- data/.utilsrc +26 -0
- data/Gemfile +5 -0
- data/README.md +76 -0
- data/Rakefile +37 -0
- data/VERSION +1 -0
- data/betterplace-bi.gemspec +39 -0
- data/lib/betterplace-bi.rb +1 -0
- data/lib/bi/ab_test_helper.rb +81 -0
- data/lib/bi/api.rb +115 -0
- data/lib/bi/commands/base.rb +11 -0
- data/lib/bi/commands/collector.rb +32 -0
- data/lib/bi/commands/connection.rb +25 -0
- data/lib/bi/commands/delete.rb +33 -0
- data/lib/bi/commands/serializer.rb +40 -0
- data/lib/bi/commands/update.rb +29 -0
- data/lib/bi/commands.rb +10 -0
- data/lib/bi/commands_job.rb +24 -0
- data/lib/bi/event.rb +52 -0
- data/lib/bi/planning_value_parser.rb +100 -0
- data/lib/bi/planning_value_validations.rb +15 -0
- data/lib/bi/railtie.rb +13 -0
- data/lib/bi/request_analyzer.rb +64 -0
- data/lib/bi/session_id.rb +11 -0
- data/lib/bi/shared_value.rb +102 -0
- data/lib/bi/tracking.rb +28 -0
- data/lib/bi/type_generator.rb +79 -0
- data/lib/bi/update_error.rb +4 -0
- data/lib/bi/updater.rb +61 -0
- data/lib/bi/version.rb +8 -0
- data/lib/bi.rb +27 -0
- data/lib/tasks/bime.rake +55 -0
- data/spec/bi/ab_test_helper_spec.rb +145 -0
- data/spec/bi/commands/collector_spec.rb +26 -0
- data/spec/bi/commands/connection_spec.rb +15 -0
- data/spec/bi/commands_job_spec.rb +24 -0
- data/spec/bi/commands_spec.rb +46 -0
- data/spec/bi/event_spec.rb +77 -0
- data/spec/bi/planning_value_parser_spec.rb +94 -0
- data/spec/bi/request_analyzer_spec.rb +75 -0
- data/spec/bi/shared_value_spec.rb +47 -0
- data/spec/bi/tracking_spec.rb +37 -0
- data/spec/bi/type_generator_spec.rb +44 -0
- data/spec/bi/updater_spec.rb +61 -0
- data/spec/bime_dir/.keep +0 -0
- data/spec/config/bi.yml +18 -0
- data/spec/spec_helper.rb +31 -0
- data/spec/support/models.rb +106 -0
- metadata +331 -0
@@ -0,0 +1,145 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe BI::ABTestHelper do
|
4
|
+
before do
|
5
|
+
allow(Rails).to receive(:logger).and_return(double('logger').as_null_object)
|
6
|
+
end
|
7
|
+
|
8
|
+
let :helper do
|
9
|
+
double('helper').extend described_class
|
10
|
+
end
|
11
|
+
|
12
|
+
let :session do
|
13
|
+
double(id: 'deadbeef' * 5, loaded?: true)
|
14
|
+
end
|
15
|
+
|
16
|
+
let :request do
|
17
|
+
double(session: session, headers: {}, params: {}, user_agent: nil)
|
18
|
+
end
|
19
|
+
|
20
|
+
let :alternatives do
|
21
|
+
%w[ alt1 alt2 alt3 ]
|
22
|
+
end
|
23
|
+
|
24
|
+
describe '#ab_test' do
|
25
|
+
it 'can be overriden' do
|
26
|
+
allow(helper).to receive(:ab_test_override?).and_return 'alt2'
|
27
|
+
expect(helper.ab_test('foo', alternatives, request: request)).to eq 'alt2'
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'can ignore certain users' do
|
31
|
+
expect(helper).to receive(:ab_test_ignore_user?).and_return true
|
32
|
+
expect(helper.ab_test('foo', alternatives, request: request)).to eq 'alt1'
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'triggers loading of session unless loaded' do
|
36
|
+
unloaded_session = double(id: 'deadbeef' * 5, loaded?: false)
|
37
|
+
expect(unloaded_session).to receive(:load!)
|
38
|
+
allow(request).to receive(:session).and_return unloaded_session
|
39
|
+
expect(helper.ab_test('foo', alternatives, request: request)).to eq 'alt2'
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'can choose alternatives based on session id' do
|
43
|
+
allow(helper).to receive(:session).and_return session
|
44
|
+
expect(helper.ab_test('foo', alternatives, request: request)).to eq 'alt2'
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'does not default even without session_id' do
|
48
|
+
allow(request.session).to receive(:id)
|
49
|
+
allow(helper).to receive(:rand).and_return(666)
|
50
|
+
expect(helper.ab_test('foo', alternatives, request: request)).to eq 'alt1'
|
51
|
+
allow(helper).to receive(:rand).and_return(667)
|
52
|
+
expect(helper.ab_test('foo', alternatives, request: request)).to eq 'alt2'
|
53
|
+
end
|
54
|
+
|
55
|
+
it 'does not default even without session' do
|
56
|
+
allow(request).to receive(:session)
|
57
|
+
allow(helper).to receive(:rand).and_return(666)
|
58
|
+
expect(helper.ab_test('foo', alternatives, request: request)).to eq 'alt1'
|
59
|
+
allow(helper).to receive(:rand).and_return(667)
|
60
|
+
expect(helper.ab_test('foo', alternatives, request: request)).to eq 'alt2'
|
61
|
+
end
|
62
|
+
|
63
|
+
it 'can be called with block' do
|
64
|
+
allow(helper).to receive(:session).and_return session
|
65
|
+
chosen = nil
|
66
|
+
expect(helper.ab_test('foo', *alternatives, request: request) {
|
67
|
+
|choice| chosen = choice; :result
|
68
|
+
}).to eq :result
|
69
|
+
expect(chosen).to eq 'alt2'
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
describe '#ab_test_ignore_user?' do
|
74
|
+
it 'does not ignore users by default' do
|
75
|
+
expect(helper).not_to be_ab_test_ignore_user(request: request)
|
76
|
+
end
|
77
|
+
|
78
|
+
it 'does ignore some users' do
|
79
|
+
allow_any_instance_of(BI::RequestAnalyzer).to receive(:ignore?).and_return true
|
80
|
+
expect(helper).to be_ab_test_ignore_user(request: request)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
describe '#ab_test_override?' do
|
85
|
+
it 'does not override by default' do
|
86
|
+
expect(helper).not_to be_ab_test_override('foo', alternatives, request: request)
|
87
|
+
end
|
88
|
+
|
89
|
+
it 'can be used to choose alternatives for all ab tests' do
|
90
|
+
allow(request).to receive(:params).and_return({
|
91
|
+
'ab_test' => 'alternative'
|
92
|
+
})
|
93
|
+
expect(helper.ab_test_override?('foo', alternatives, request: request)).to eq 'alt2'
|
94
|
+
end
|
95
|
+
|
96
|
+
it 'can be used to choose alternatives for all ab tests with specific name' do
|
97
|
+
allow(request).to receive(:params).and_return({
|
98
|
+
'ab_test' => 'alt3'
|
99
|
+
})
|
100
|
+
expect(helper.ab_test_override?('foo', alternatives, request: request)).to eq 'alt3'
|
101
|
+
end
|
102
|
+
|
103
|
+
it 'can be used to choose alternatives for a specific ab test' do
|
104
|
+
allow(request).to receive(:params).and_return({
|
105
|
+
'ab_test' => { 'foo' => 'alternative' },
|
106
|
+
})
|
107
|
+
expect(helper.ab_test_override?('foo', alternatives, request: request)).to eq 'alt2'
|
108
|
+
expect(helper.ab_test_override?('bar', alternatives, request: request)).not_to eq 'alt2'
|
109
|
+
end
|
110
|
+
|
111
|
+
it 'can be used to choose alternatives with specific name' do
|
112
|
+
allow(request).to receive(:params).and_return({
|
113
|
+
'ab_test' => { 'foo' => 'alt3' },
|
114
|
+
})
|
115
|
+
expect(helper.ab_test_override?('foo', alternatives, request: request)).to eq 'alt3'
|
116
|
+
expect(helper.ab_test_override?('bar', alternatives, request: request)).not_to eq 'alt3'
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
describe '#ab_test_override_alternative' do
|
121
|
+
it 'can choose the control' do
|
122
|
+
expect(helper.ab_test_override_alternative('control', alternatives, request: request)).to\
|
123
|
+
eq 'alt1'
|
124
|
+
end
|
125
|
+
|
126
|
+
it 'can choose the first alternative' do
|
127
|
+
expect(helper.ab_test_override_alternative('alternative', alternatives, request: request)).to\
|
128
|
+
eq 'alt2'
|
129
|
+
end
|
130
|
+
|
131
|
+
it 'can choose the nth alternative, starting to count from 0' do
|
132
|
+
expect(helper.ab_test_override_alternative('1', alternatives, request: request)).to\
|
133
|
+
eq 'alt2'
|
134
|
+
end
|
135
|
+
|
136
|
+
it 'defaults to control if there is no nth alternative' do
|
137
|
+
expect(helper.ab_test_override_alternative('42', alternatives, request: request)).to\
|
138
|
+
eq 'alt1'
|
139
|
+
end
|
140
|
+
|
141
|
+
it 'defaults to nil in any other case' do
|
142
|
+
expect(helper.ab_test_override_alternative('nix', alternatives, request)).to be_nil
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe BI::Commands::Collector do
|
4
|
+
let :collector do
|
5
|
+
described_class.new
|
6
|
+
end
|
7
|
+
|
8
|
+
it 'can check if job should be scheduled now' do
|
9
|
+
object = double(destroyed?: false)
|
10
|
+
expect(collector).not_to be_schedule_job(object)
|
11
|
+
collector.add(object, double.as_null_object)
|
12
|
+
expect(collector).to be_schedule_job(object)
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'can add Update commands' do
|
16
|
+
object = double(destroyed?: false, id: 666)
|
17
|
+
expect(BI::Commands::Update).to receive(:new).and_call_original
|
18
|
+
collector.add(object, double.as_null_object)
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'can add Delete commands' do
|
22
|
+
object = double(destroyed?: true, id: 666)
|
23
|
+
expect(BI::Commands::Delete).to receive(:new).and_call_original
|
24
|
+
collector.add(object, double.as_null_object)
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe BI::Commands::Connection do
|
4
|
+
describe '#connect_to_server' do
|
5
|
+
it 'creates one connection' do
|
6
|
+
expect(described_class).to receive(:create_connection).once.and_return double.as_null_object
|
7
|
+
connection = nil
|
8
|
+
described_class.connect_to_server do |c|
|
9
|
+
connection = c
|
10
|
+
expect(connection).to receive(:reset)
|
11
|
+
end
|
12
|
+
expect(connection).not_to be_nil
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe BI::CommandsJob do
|
4
|
+
let :job do
|
5
|
+
described_class.new
|
6
|
+
end
|
7
|
+
|
8
|
+
it 'connects to server for commands' do
|
9
|
+
expect(job).to receive(:connect_to_server)
|
10
|
+
job.perform :something
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'does not connect to server without commands' do
|
14
|
+
expect(job).not_to receive(:connect_to_server)
|
15
|
+
job.perform
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'performs all given commands on server' do
|
19
|
+
command = double
|
20
|
+
expect(command).to receive(:perform_via)
|
21
|
+
allow(job).to receive(:create_connection).and_return(double.as_null_object)
|
22
|
+
job.perform command
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe BI::Commands do
|
4
|
+
before do
|
5
|
+
allow(Log.instance).to receive(:info)
|
6
|
+
end
|
7
|
+
|
8
|
+
let :object do
|
9
|
+
double
|
10
|
+
end
|
11
|
+
|
12
|
+
let :connection do
|
13
|
+
double
|
14
|
+
end
|
15
|
+
|
16
|
+
context BI::Commands::Update do
|
17
|
+
let :command do
|
18
|
+
described_class.new(value: object)
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'can be configured' do
|
22
|
+
expect(command.value).to eq object
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'can be performed' do
|
26
|
+
allow(object).to receive(:url)
|
27
|
+
expect(connection).to receive(:post).and_return double(status: 200)
|
28
|
+
command.perform_via(connection)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
context BI::Commands::Delete do
|
33
|
+
let :command do
|
34
|
+
described_class.new(url: object)
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'can be configured' do
|
38
|
+
expect(command.url).to eq object
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'can be performed' do
|
42
|
+
expect(connection).to receive(:delete).and_return double(status: 200)
|
43
|
+
command.perform_via(connection)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe BI::Event do
|
4
|
+
before do
|
5
|
+
allow(Rails).to receive(:logger).and_return(double('logger').as_null_object)
|
6
|
+
end
|
7
|
+
|
8
|
+
class ABEvent
|
9
|
+
class << self
|
10
|
+
def serialize(*)
|
11
|
+
end
|
12
|
+
|
13
|
+
def create!(*)
|
14
|
+
new
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def attributes
|
19
|
+
{}
|
20
|
+
end
|
21
|
+
|
22
|
+
include BI::Tracking
|
23
|
+
end
|
24
|
+
|
25
|
+
let :session_id do
|
26
|
+
SecureRandom.hex(32)
|
27
|
+
end
|
28
|
+
|
29
|
+
let :session do
|
30
|
+
double('session', loaded?: true, id: session_id)
|
31
|
+
end
|
32
|
+
|
33
|
+
subject do
|
34
|
+
described_class.new
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'offers static write method' do
|
38
|
+
args = { channel: 'default', name: 'hello', version: 'world', session: session }
|
39
|
+
expect(ABEvent).to receive(:create!).with(tracking: { session: session_id } | args).and_call_original
|
40
|
+
described_class.write(**args)
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'can handle sessions with string id' do
|
44
|
+
args = { channel: 'default', name: 'hello', version: 'world', session: session_id }
|
45
|
+
expect(ABEvent).to receive(:create!).with(tracking: args).and_call_original
|
46
|
+
described_class.write(**args)
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'ignores unexpected session objects' do
|
50
|
+
args = { channel: 'default', name: 'hello', version: 'world', session: Object.new }
|
51
|
+
expect(ABEvent).to receive(:create!).with(tracking: { session: nil } | args).and_call_original
|
52
|
+
described_class.write(**args)
|
53
|
+
end
|
54
|
+
|
55
|
+
it 'logs any errors during writing and raises again' do
|
56
|
+
error = StandardError.new
|
57
|
+
allow(ABEvent).to receive(:create!).and_raise error
|
58
|
+
expect(Log).to receive(:error).with(error, notify: true, meta: { module: 'bi' })
|
59
|
+
expect {
|
60
|
+
subject.write(channel: 'default', name: 'hello', version: 'world', session: session)
|
61
|
+
}.to raise_error error
|
62
|
+
end
|
63
|
+
|
64
|
+
it 'calls write method on configured event_class' do
|
65
|
+
event_data_input = {
|
66
|
+
channel: 'default', name: 'hello', version: 'world', session: session,
|
67
|
+
my: :data
|
68
|
+
}
|
69
|
+
event_data_output = {
|
70
|
+
channel: 'default', name: 'hello', version: 'world', session: session.id,
|
71
|
+
my: :data
|
72
|
+
}
|
73
|
+
expect(ABEvent).to receive(:create!).with(tracking: event_data_output).and_call_original
|
74
|
+
expect(Log).to receive(:info)
|
75
|
+
subject.write(**event_data_input)
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe BI::PlanningValueParser do
|
4
|
+
let :csv do
|
5
|
+
<<~end
|
6
|
+
product_tracking;amount_in_cents;yearmonth;data_type;data_version
|
7
|
+
foo;66600;2019-01;Foo;2018-06
|
8
|
+
bar;2300;2019-01;Bar;2018-06
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
let :instance do
|
13
|
+
described_class.parse(csv, class_name: 'TestPlanning')
|
14
|
+
end
|
15
|
+
|
16
|
+
context 'correct headers' do
|
17
|
+
it 'parses' do
|
18
|
+
expect(instance.first).to be_a TestPlanning
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
context 'invalid headers' do
|
23
|
+
let :csv do
|
24
|
+
<<~end
|
25
|
+
product_tracking;amount;yearmonth;data_type;data_version
|
26
|
+
foo;66600;2019-01;Foo;2018-06
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'raises ParserError' do
|
31
|
+
expect(instance.errors).to eq(
|
32
|
+
2 => 'headers need to be ["amount_in_cents", "data_type", "data_version", "product_tracking", "yearmonth"]',
|
33
|
+
)
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'is not valid' do
|
37
|
+
expect(instance).to_not be_valid
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
context 'invalid #values' do
|
42
|
+
let :csv do
|
43
|
+
<<~end
|
44
|
+
product_tracking;amount;yearmonth;data_type;data_version
|
45
|
+
foo;66600;2019-01;Foo;2018-06;nope
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'raises ParserError' do
|
50
|
+
expect(instance.errors).to eq(
|
51
|
+
2 => '#values has to match #headers = 5'
|
52
|
+
)
|
53
|
+
end
|
54
|
+
|
55
|
+
it 'is not valid' do
|
56
|
+
expect(instance).to_not be_valid
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
context 'correct values' do
|
61
|
+
it 'parses' do
|
62
|
+
expect(instance.count).to eq 2
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
context 'incorrect values' do
|
67
|
+
let :csv do
|
68
|
+
<<~end
|
69
|
+
product_tracking;amount_in_cents;yearmonth;data_type;data_version
|
70
|
+
foo;66600;2019-13;Foo;2018-06
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
let :errors do
|
75
|
+
{ 2 => [ "yearmonth: is invalid" ] }
|
76
|
+
end
|
77
|
+
|
78
|
+
before do
|
79
|
+
allow_any_instance_of(TestPlanning).to\
|
80
|
+
receive(:errors).and_return(errors)
|
81
|
+
allow(errors).to receive(:messages).and_return errors
|
82
|
+
end
|
83
|
+
|
84
|
+
it 'is not valid' do
|
85
|
+
expect(instance).to_not be_valid
|
86
|
+
end
|
87
|
+
|
88
|
+
it 'gives errorrs' do
|
89
|
+
expect(instance.errors).to eq(
|
90
|
+
2 => "2: yearmonth: is invalid",
|
91
|
+
)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe BI::RequestAnalyzer do
|
4
|
+
before do
|
5
|
+
described_class.mize_cache_clear
|
6
|
+
end
|
7
|
+
|
8
|
+
let :request do
|
9
|
+
double(
|
10
|
+
session: double,
|
11
|
+
user_agent: 'Agent Smith',
|
12
|
+
headers: {},
|
13
|
+
)
|
14
|
+
end
|
15
|
+
|
16
|
+
subject do
|
17
|
+
described_class.new(request)
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'can figure out user_agent' do
|
21
|
+
expect(subject.user_agent).to eq 'Agent Smith'
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'can ignore or not' do
|
25
|
+
expect(subject).not_to be_ignore
|
26
|
+
allow(subject).to receive(:preview?).and_return true
|
27
|
+
expect(subject).to be_ignore
|
28
|
+
allow(subject).to receive(:robot?).and_return true
|
29
|
+
expect(subject).to be_ignore
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'can figure out if request is a preview' do
|
33
|
+
request.headers['x-purpose'] = 'preview'
|
34
|
+
expect(subject).to be_preview
|
35
|
+
end
|
36
|
+
|
37
|
+
context 'mobile' do
|
38
|
+
it 'attempts to categorize mobile devices' do
|
39
|
+
allow(subject).to receive(:user_agent_configuration).and_return\
|
40
|
+
double(mobile?: %w[ Foo Mobile Baz ].map { |x| [x] * 2 }.to_h)
|
41
|
+
allow(request).to receive(:user_agent).and_return\
|
42
|
+
'Mozilla/5.0 (Linux; Android 6.0.1; SM-G920V Build/MMB29K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.98 Mobile Safari/537.36'
|
43
|
+
expect(subject).to be_mobile
|
44
|
+
expect(subject.device_type).to eq :mobile
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'attempts to categorize desktop devices' do
|
48
|
+
allow(request).to receive(:user_agent).and_return\
|
49
|
+
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.135 Safari/537.36 Edge/12.246'
|
50
|
+
expect(subject).not_to be_mobile
|
51
|
+
expect(subject.device_type).to eq :desktop
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
context 'robot' do
|
56
|
+
before do
|
57
|
+
allow(subject).to receive(:user_agent_configuration).and_return\
|
58
|
+
double(mobile?: nil, robot?: %w[ Foo Slurp Baz ].map { |x| [x] * 2 }.to_h)
|
59
|
+
end
|
60
|
+
|
61
|
+
it 'attempts to categorize non robots' do
|
62
|
+
allow(request).to receive(:user_agent).and_return\
|
63
|
+
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_2) AppleWebKit/601.3.9 (KHTML, like Gecko) Version/9.0.2 Safari/601.3.9'
|
64
|
+
expect(subject).not_to be_robot
|
65
|
+
expect(subject.device_type).to eq :desktop
|
66
|
+
end
|
67
|
+
|
68
|
+
it 'attempts to categorize robots' do
|
69
|
+
allow(request).to receive(:user_agent).and_return\
|
70
|
+
'Mozilla/5.0 (compatible; Yahoo! Slurp; http://help.yahoo.com/help/us/ysearch/slurp)'
|
71
|
+
expect(subject).to be_robot
|
72
|
+
expect(subject.device_type).to eq :bot
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe BI::SharedValue do
|
4
|
+
let :test_value do
|
5
|
+
BI::TestModelValue.new('foo')
|
6
|
+
end
|
7
|
+
|
8
|
+
it 'can warp a model' do
|
9
|
+
tv = BI::TestModelValue.new(TestModel.new('foo'))
|
10
|
+
expect(tv).to be_a described_class
|
11
|
+
expect(tv.id).to eq 'foo'
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'can be build for a model with globalid' do
|
15
|
+
tv = BI::TestModelValue.find('foo')
|
16
|
+
expect(tv).to be_a described_class
|
17
|
+
expect(tv.id).to eq 'foo'
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'can intialize for a model found via globalid' do
|
21
|
+
tv = BI::TestModelValue.new('foo')
|
22
|
+
expect(tv).to be_a described_class
|
23
|
+
expect(tv.id).to eq 'foo'
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'haz endpoint url' do
|
27
|
+
expect(test_value.url).to eq '/api/v1/tests'
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'can be converted to JSON value' do
|
31
|
+
Time.dummy('2011-11-11T11:11:11+01:00') do
|
32
|
+
expect(JSON(test_value)).to eq \
|
33
|
+
'{"id":"foo","number":10000000000,"created_at":"2011-11-11T11:11:11.000000Z","tags":["foo","bar"]}'
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'can be output as string' do
|
38
|
+
expect(test_value.to_s).to eq "#<BI::TestModelValue type=TestModel id=foo>"
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'can clear the models from bi' do
|
42
|
+
connection = double
|
43
|
+
expect(BI::Commands::Connection).to receive(:connect_to_server).and_yield(connection)
|
44
|
+
expect_any_instance_of(BI::Commands::Delete).to receive(:perform_via).with(connection)
|
45
|
+
test_value.class.clear
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe BI::Tracking do
|
4
|
+
let :tracked do
|
5
|
+
Class.new do
|
6
|
+
class << self
|
7
|
+
def serialize(*)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
include BI::Tracking
|
12
|
+
|
13
|
+
def tracking
|
14
|
+
@tracking ||= {}
|
15
|
+
end
|
16
|
+
|
17
|
+
self
|
18
|
+
end.new
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'can track foos' do
|
22
|
+
expect(tracked.respond_to?(:tracking_foo)).to eq true
|
23
|
+
tracked.tracking_foo = :foo
|
24
|
+
expect(tracked.tracking_foo).to eq :foo
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'can have a tracking_hash' do
|
28
|
+
tracked.tracking_foo = :foo
|
29
|
+
expect(tracked.tracking_hash).to eq(foo: :foo)
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'does not answer non_tracking tings' do
|
33
|
+
expect {
|
34
|
+
tracked.non_tracking_foo = :foo
|
35
|
+
}.to raise_error NoMethodError
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'fileutils'
|
3
|
+
|
4
|
+
describe BI::TypeGenerator do
|
5
|
+
before do
|
6
|
+
allow_any_instance_of(described_class).to receive(:gofmt)
|
7
|
+
end
|
8
|
+
|
9
|
+
let :go_file do
|
10
|
+
Pathname.new(cc.bi.bime_dir).join('test_model.go')
|
11
|
+
end
|
12
|
+
|
13
|
+
after do
|
14
|
+
FileUtils.rm_rf go_file
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'Can generate a GO types according to spec in a BI::*Value' do
|
18
|
+
described_class.new.generate
|
19
|
+
expect(File.read(go_file)).to eq(<<~end)
|
20
|
+
package bime
|
21
|
+
|
22
|
+
import (
|
23
|
+
"time"
|
24
|
+
"github.com/lib/pq"
|
25
|
+
)
|
26
|
+
|
27
|
+
type TestModel struct {
|
28
|
+
Id string `json:"id" gorm:"type:uuid;primary_key"`
|
29
|
+
Number *int64 `json:"number" gorm:"type:bigint"`
|
30
|
+
CreatedAt *time.Time `json:"created_at" gorm:"-" timestamp:"split"`
|
31
|
+
CreatedAtLocal *time.Time `json:"-" gorm:"type:timestamp;index"`
|
32
|
+
CreatedAtYearLocal *int `json:"-" gorm:"type:int;index"`
|
33
|
+
CreatedAtMonthLocal *int `json:"-" gorm:"type:int;index"`
|
34
|
+
CreatedAtDayLocal *int `json:"-" gorm:"type:int;index"`
|
35
|
+
CreatedAtHourLocal *int `json:"-" gorm:"type:int;index"`
|
36
|
+
CreatedAtCWLocal *int `json:"-" gorm:"type:int;index"`
|
37
|
+
CreatedAtDateLocal *string `json:"-" gorm:"type:varchar;index"`
|
38
|
+
CreatedAtYearMonthLocal *string `json:"-" gorm:"type:varchar;index"`
|
39
|
+
CreatedAtYearCWLocal *string `json:"-" gorm:"type:varchar;index"`
|
40
|
+
Tags pq.StringArray `json:"tags" gorm:"type:varchar[]"`
|
41
|
+
}
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|