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