launchdarkly-server-sdk 5.6.2 → 5.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 +4 -4
- data/CHANGELOG.md +4 -0
- data/Gemfile.lock +4 -6
- data/launchdarkly-server-sdk.gemspec +1 -2
- data/lib/ldclient-rb/config.rb +64 -0
- data/lib/ldclient-rb/events.rb +94 -86
- data/lib/ldclient-rb/flags_state.rb +1 -1
- data/lib/ldclient-rb/impl/diagnostic_events.rb +130 -0
- data/lib/ldclient-rb/impl/event_sender.rb +72 -0
- data/lib/ldclient-rb/impl/util.rb +19 -0
- data/lib/ldclient-rb/ldclient.rb +17 -4
- data/lib/ldclient-rb/requestor.rb +1 -2
- data/lib/ldclient-rb/stream.rb +18 -5
- data/lib/ldclient-rb/version.rb +1 -1
- data/spec/diagnostic_events_spec.rb +163 -0
- data/spec/evaluation_spec.rb +1 -1
- data/spec/event_sender_spec.rb +179 -0
- data/spec/events_spec.rb +437 -524
- data/spec/file_data_source_spec.rb +1 -1
- data/spec/http_util.rb +15 -2
- data/spec/integrations/consul_feature_store_spec.rb +0 -2
- data/spec/integrations/dynamodb_feature_store_spec.rb +0 -2
- data/spec/ldclient_spec.rb +1 -1
- data/spec/polling_spec.rb +1 -1
- data/spec/redis_feature_store_spec.rb +0 -3
- data/spec/requestor_spec.rb +20 -4
- data/spec/spec_helper.rb +3 -0
- metadata +11 -19
- data/Rakefile +0 -5
data/spec/evaluation_spec.rb
CHANGED
@@ -17,7 +17,7 @@ describe LaunchDarkly::Evaluation do
|
|
17
17
|
}
|
18
18
|
}
|
19
19
|
|
20
|
-
let(:logger) {
|
20
|
+
let(:logger) { $null_log }
|
21
21
|
|
22
22
|
def boolean_flag_with_rules(rules)
|
23
23
|
{ key: 'feature', on: true, rules: rules, fallthrough: { variation: 0 }, variations: [ false, true ] }
|
@@ -0,0 +1,179 @@
|
|
1
|
+
require "ldclient-rb/impl/event_sender"
|
2
|
+
|
3
|
+
require "http_util"
|
4
|
+
require "spec_helper"
|
5
|
+
|
6
|
+
require "time"
|
7
|
+
|
8
|
+
module LaunchDarkly
|
9
|
+
module Impl
|
10
|
+
describe EventSender do
|
11
|
+
subject { EventSender }
|
12
|
+
|
13
|
+
let(:sdk_key) { "sdk_key" }
|
14
|
+
let(:fake_data) { '{"things":[]}' }
|
15
|
+
|
16
|
+
def make_sender(server)
|
17
|
+
subject.new(sdk_key, Config.new(events_uri: server.base_uri.to_s, logger: $null_log), nil, 0.1)
|
18
|
+
end
|
19
|
+
|
20
|
+
def with_sender_and_server
|
21
|
+
with_server do |server|
|
22
|
+
yield make_sender(server), server
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
it "sends analytics event data" do
|
27
|
+
with_sender_and_server do |es, server|
|
28
|
+
server.setup_ok_response("/bulk", "")
|
29
|
+
|
30
|
+
result = es.send_event_data(fake_data, false)
|
31
|
+
|
32
|
+
expect(result.success).to be true
|
33
|
+
expect(result.must_shutdown).to be false
|
34
|
+
expect(result.time_from_server).not_to be_nil
|
35
|
+
|
36
|
+
req = server.await_request
|
37
|
+
expect(req.body).to eq fake_data
|
38
|
+
expect(req.header).to include({
|
39
|
+
"authorization" => [ sdk_key ],
|
40
|
+
"content-type" => [ "application/json" ],
|
41
|
+
"user-agent" => [ "RubyClient/" + LaunchDarkly::VERSION ],
|
42
|
+
"x-launchdarkly-event-schema" => [ "3" ]
|
43
|
+
})
|
44
|
+
expect(req.header['x-launchdarkly-payload-id']).not_to eq []
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
it "generates a new payload ID for each payload" do
|
49
|
+
with_sender_and_server do |es, server|
|
50
|
+
server.setup_ok_response("/bulk", "")
|
51
|
+
|
52
|
+
result1 = es.send_event_data(fake_data, false)
|
53
|
+
result2 = es.send_event_data(fake_data, false)
|
54
|
+
expect(result1.success).to be true
|
55
|
+
expect(result2.success).to be true
|
56
|
+
|
57
|
+
req1, body1 = server.await_request_with_body
|
58
|
+
req2, body2 = server.await_request_with_body
|
59
|
+
expect(body1).to eq fake_data
|
60
|
+
expect(body2).to eq fake_data
|
61
|
+
expect(req1.header['x-launchdarkly-payload-id']).not_to eq req2.header['x-launchdarkly-payload-id']
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
it "sends diagnostic event data" do
|
66
|
+
with_sender_and_server do |es, server|
|
67
|
+
server.setup_ok_response("/diagnostic", "")
|
68
|
+
|
69
|
+
result = es.send_event_data(fake_data, true)
|
70
|
+
|
71
|
+
expect(result.success).to be true
|
72
|
+
expect(result.must_shutdown).to be false
|
73
|
+
expect(result.time_from_server).not_to be_nil
|
74
|
+
|
75
|
+
req, body = server.await_request_with_body
|
76
|
+
expect(body).to eq fake_data
|
77
|
+
expect(req.header).to include({
|
78
|
+
"authorization" => [ sdk_key ],
|
79
|
+
"content-type" => [ "application/json" ],
|
80
|
+
"user-agent" => [ "RubyClient/" + LaunchDarkly::VERSION ],
|
81
|
+
})
|
82
|
+
expect(req.header['x-launchdarkly-event-schema']).to eq []
|
83
|
+
expect(req.header['x-launchdarkly-payload-id']).to eq []
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
it "can use a proxy server" do
|
88
|
+
with_server do |server|
|
89
|
+
server.setup_ok_response("/bulk", "")
|
90
|
+
|
91
|
+
with_server(StubProxyServer.new) do |proxy|
|
92
|
+
begin
|
93
|
+
ENV["http_proxy"] = proxy.base_uri.to_s
|
94
|
+
|
95
|
+
es = make_sender(server)
|
96
|
+
|
97
|
+
result = es.send_event_data(fake_data, false)
|
98
|
+
|
99
|
+
expect(result.success).to be true
|
100
|
+
|
101
|
+
req, body = server.await_request_with_body
|
102
|
+
expect(body).to eq fake_data
|
103
|
+
ensure
|
104
|
+
ENV["http_proxy"] = nil
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
[400, 408, 429, 500].each do |status|
|
111
|
+
it "handles recoverable error #{status}" do
|
112
|
+
with_sender_and_server do |es, server|
|
113
|
+
req_count = 0
|
114
|
+
server.setup_response("/bulk") do |req, res|
|
115
|
+
req_count = req_count + 1
|
116
|
+
res.status = req_count == 2 ? 200 : status
|
117
|
+
end
|
118
|
+
|
119
|
+
result = es.send_event_data(fake_data, false)
|
120
|
+
|
121
|
+
expect(result.success).to be true
|
122
|
+
expect(result.must_shutdown).to be false
|
123
|
+
expect(result.time_from_server).not_to be_nil
|
124
|
+
|
125
|
+
expect(server.requests.count).to eq 2
|
126
|
+
req1, body1 = server.await_request_with_body
|
127
|
+
req2, body2 = server.await_request_with_body
|
128
|
+
expect(body1).to eq fake_data
|
129
|
+
expect(body2).to eq fake_data
|
130
|
+
expect(req1.header['x-launchdarkly-payload-id']).to eq req2.header['x-launchdarkly-payload-id']
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
[400, 408, 429, 500].each do |status|
|
136
|
+
it "only retries error #{status} once" do
|
137
|
+
with_sender_and_server do |es, server|
|
138
|
+
req_count = 0
|
139
|
+
server.setup_response("/bulk") do |req, res|
|
140
|
+
req_count = req_count + 1
|
141
|
+
res.status = req_count == 3 ? 200 : status
|
142
|
+
end
|
143
|
+
|
144
|
+
result = es.send_event_data(fake_data, false)
|
145
|
+
|
146
|
+
expect(result.success).to be false
|
147
|
+
expect(result.must_shutdown).to be false
|
148
|
+
expect(result.time_from_server).to be_nil
|
149
|
+
|
150
|
+
expect(server.requests.count).to eq 2
|
151
|
+
req1, body1 = server.await_request_with_body
|
152
|
+
req2, body2 = server.await_request_with_body
|
153
|
+
expect(body1).to eq fake_data
|
154
|
+
expect(body2).to eq fake_data
|
155
|
+
expect(req1.header['x-launchdarkly-payload-id']).to eq req2.header['x-launchdarkly-payload-id']
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
[401, 403].each do |status|
|
161
|
+
it "gives up after unrecoverable error #{status}" do
|
162
|
+
with_sender_and_server do |es, server|
|
163
|
+
server.setup_response("/bulk") do |req, res|
|
164
|
+
res.status = status
|
165
|
+
end
|
166
|
+
|
167
|
+
result = es.send_event_data(fake_data, false)
|
168
|
+
|
169
|
+
expect(result.success).to be false
|
170
|
+
expect(result.must_shutdown).to be true
|
171
|
+
expect(result.time_from_server).to be_nil
|
172
|
+
|
173
|
+
expect(server.requests.count).to eq 1
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|
data/spec/events_spec.rb
CHANGED
@@ -5,8 +5,8 @@ require "time"
|
|
5
5
|
describe LaunchDarkly::EventProcessor do
|
6
6
|
subject { LaunchDarkly::EventProcessor }
|
7
7
|
|
8
|
-
let(:
|
9
|
-
let(:
|
8
|
+
let(:default_config_opts) { { diagnostic_opt_out: true, logger: $null_log } }
|
9
|
+
let(:default_config) { LaunchDarkly::Config.new(default_config_opts) }
|
10
10
|
let(:user) { { key: "userkey", name: "Red" } }
|
11
11
|
let(:filtered_user) { { key: "userkey", privateAttrs: [ "name" ] } }
|
12
12
|
let(:numeric_user) { { key: 1, secondary: 2, ip: 3, country: 4, email: 5, firstName: 6, lastName: 7,
|
@@ -14,546 +14,508 @@ describe LaunchDarkly::EventProcessor do
|
|
14
14
|
let(:stringified_numeric_user) { { key: '1', secondary: '2', ip: '3', country: '4', email: '5', firstName: '6',
|
15
15
|
lastName: '7', avatar: '8', name: '9', anonymous: false, custom: { age: 99 } } }
|
16
16
|
|
17
|
-
|
18
|
-
|
19
|
-
|
17
|
+
def with_processor_and_sender(config)
|
18
|
+
sender = FakeEventSender.new
|
19
|
+
ep = subject.new("sdk_key", config, nil, nil, { event_sender: sender })
|
20
|
+
begin
|
21
|
+
yield ep, sender
|
22
|
+
ensure
|
23
|
+
ep.stop
|
20
24
|
end
|
21
25
|
end
|
22
26
|
|
23
27
|
it "queues identify event" do
|
24
|
-
|
25
|
-
|
26
|
-
|
28
|
+
with_processor_and_sender(default_config) do |ep, sender|
|
29
|
+
e = { kind: "identify", key: user[:key], user: user }
|
30
|
+
ep.add_event(e)
|
27
31
|
|
28
|
-
|
29
|
-
|
32
|
+
output = flush_and_get_events(ep, sender)
|
33
|
+
expect(output).to contain_exactly(e)
|
34
|
+
end
|
30
35
|
end
|
31
36
|
|
32
37
|
it "filters user in identify event" do
|
33
|
-
config = LaunchDarkly::Config.new(all_attributes_private: true)
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
38
|
+
config = LaunchDarkly::Config.new(default_config_opts.merge(all_attributes_private: true))
|
39
|
+
with_processor_and_sender(config) do |ep, sender|
|
40
|
+
e = { kind: "identify", key: user[:key], user: user }
|
41
|
+
ep.add_event(e)
|
42
|
+
|
43
|
+
output = flush_and_get_events(ep, sender)
|
44
|
+
expect(output).to contain_exactly({
|
45
|
+
kind: "identify",
|
46
|
+
key: user[:key],
|
47
|
+
creationDate: e[:creationDate],
|
48
|
+
user: filtered_user
|
49
|
+
})
|
50
|
+
end
|
45
51
|
end
|
46
52
|
|
47
53
|
it "stringifies built-in user attributes in identify event" do
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
54
|
+
with_processor_and_sender(default_config) do |ep, sender|
|
55
|
+
flag = { key: "flagkey", version: 11 }
|
56
|
+
e = { kind: "identify", key: numeric_user[:key], user: numeric_user }
|
57
|
+
ep.add_event(e)
|
58
|
+
|
59
|
+
output = flush_and_get_events(ep, sender)
|
60
|
+
expect(output).to contain_exactly(
|
61
|
+
kind: "identify",
|
62
|
+
key: numeric_user[:key].to_s,
|
63
|
+
creationDate: e[:creationDate],
|
64
|
+
user: stringified_numeric_user
|
65
|
+
)
|
66
|
+
end
|
60
67
|
end
|
61
68
|
|
62
69
|
it "queues individual feature event with index event" do
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
70
|
+
with_processor_and_sender(default_config) do |ep, sender|
|
71
|
+
flag = { key: "flagkey", version: 11 }
|
72
|
+
fe = {
|
73
|
+
kind: "feature", key: "flagkey", version: 11, user: user,
|
74
|
+
variation: 1, value: "value", trackEvents: true
|
75
|
+
}
|
76
|
+
ep.add_event(fe)
|
77
|
+
|
78
|
+
output = flush_and_get_events(ep, sender)
|
79
|
+
expect(output).to contain_exactly(
|
80
|
+
eq(index_event(fe, user)),
|
81
|
+
eq(feature_event(fe, flag, false, nil)),
|
82
|
+
include(:kind => "summary")
|
83
|
+
)
|
84
|
+
end
|
77
85
|
end
|
78
86
|
|
79
87
|
it "filters user in index event" do
|
80
|
-
config = LaunchDarkly::Config.new(all_attributes_private: true)
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
88
|
+
config = LaunchDarkly::Config.new(default_config_opts.merge(all_attributes_private: true))
|
89
|
+
with_processor_and_sender(config) do |ep, sender|
|
90
|
+
flag = { key: "flagkey", version: 11 }
|
91
|
+
fe = {
|
92
|
+
kind: "feature", key: "flagkey", version: 11, user: user,
|
93
|
+
variation: 1, value: "value", trackEvents: true
|
94
|
+
}
|
95
|
+
ep.add_event(fe)
|
96
|
+
|
97
|
+
output = flush_and_get_events(ep, sender)
|
98
|
+
expect(output).to contain_exactly(
|
99
|
+
eq(index_event(fe, filtered_user)),
|
100
|
+
eq(feature_event(fe, flag, false, nil)),
|
101
|
+
include(:kind => "summary")
|
102
|
+
)
|
103
|
+
end
|
95
104
|
end
|
96
105
|
|
97
106
|
it "stringifies built-in user attributes in index event" do
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
107
|
+
with_processor_and_sender(default_config) do |ep, sender|
|
108
|
+
flag = { key: "flagkey", version: 11 }
|
109
|
+
fe = {
|
110
|
+
kind: "feature", key: "flagkey", version: 11, user: numeric_user,
|
111
|
+
variation: 1, value: "value", trackEvents: true
|
112
|
+
}
|
113
|
+
ep.add_event(fe)
|
114
|
+
|
115
|
+
output = flush_and_get_events(ep, sender)
|
116
|
+
expect(output).to contain_exactly(
|
117
|
+
eq(index_event(fe, stringified_numeric_user)),
|
118
|
+
eq(feature_event(fe, flag, false, nil)),
|
119
|
+
include(:kind => "summary")
|
120
|
+
)
|
121
|
+
end
|
112
122
|
end
|
113
123
|
|
114
124
|
it "can include inline user in feature event" do
|
115
|
-
config = LaunchDarkly::Config.new(inline_users_in_events: true)
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
125
|
+
config = LaunchDarkly::Config.new(default_config_opts.merge(inline_users_in_events: true))
|
126
|
+
with_processor_and_sender(config) do |ep, sender|
|
127
|
+
flag = { key: "flagkey", version: 11 }
|
128
|
+
fe = {
|
129
|
+
kind: "feature", key: "flagkey", version: 11, user: user,
|
130
|
+
variation: 1, value: "value", trackEvents: true
|
131
|
+
}
|
132
|
+
ep.add_event(fe)
|
133
|
+
|
134
|
+
output = flush_and_get_events(ep, sender)
|
135
|
+
expect(output).to contain_exactly(
|
136
|
+
eq(feature_event(fe, flag, false, user)),
|
137
|
+
include(:kind => "summary")
|
138
|
+
)
|
139
|
+
end
|
129
140
|
end
|
130
141
|
|
131
142
|
it "stringifies built-in user attributes in feature event" do
|
132
|
-
config = LaunchDarkly::Config.new(inline_users_in_events: true)
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
143
|
+
config = LaunchDarkly::Config.new(default_config_opts.merge(inline_users_in_events: true))
|
144
|
+
with_processor_and_sender(config) do |ep, sender|
|
145
|
+
flag = { key: "flagkey", version: 11 }
|
146
|
+
fe = {
|
147
|
+
kind: "feature", key: "flagkey", version: 11, user: numeric_user,
|
148
|
+
variation: 1, value: "value", trackEvents: true
|
149
|
+
}
|
150
|
+
ep.add_event(fe)
|
151
|
+
|
152
|
+
output = flush_and_get_events(ep, sender)
|
153
|
+
expect(output).to contain_exactly(
|
154
|
+
eq(feature_event(fe, flag, false, stringified_numeric_user)),
|
155
|
+
include(:kind => "summary")
|
156
|
+
)
|
157
|
+
end
|
146
158
|
end
|
147
159
|
|
148
160
|
it "filters user in feature event" do
|
149
|
-
config = LaunchDarkly::Config.new(all_attributes_private: true, inline_users_in_events: true)
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
161
|
+
config = LaunchDarkly::Config.new(default_config_opts.merge(all_attributes_private: true, inline_users_in_events: true))
|
162
|
+
with_processor_and_sender(config) do |ep, sender|
|
163
|
+
flag = { key: "flagkey", version: 11 }
|
164
|
+
fe = {
|
165
|
+
kind: "feature", key: "flagkey", version: 11, user: user,
|
166
|
+
variation: 1, value: "value", trackEvents: true
|
167
|
+
}
|
168
|
+
ep.add_event(fe)
|
169
|
+
|
170
|
+
output = flush_and_get_events(ep, sender)
|
171
|
+
expect(output).to contain_exactly(
|
172
|
+
eq(feature_event(fe, flag, false, filtered_user)),
|
173
|
+
include(:kind => "summary")
|
174
|
+
)
|
175
|
+
end
|
163
176
|
end
|
164
177
|
|
165
178
|
it "still generates index event if inline_users is true but feature event was not tracked" do
|
166
|
-
config = LaunchDarkly::Config.new(inline_users_in_events: true)
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
179
|
+
config = LaunchDarkly::Config.new(default_config_opts.merge(inline_users_in_events: true))
|
180
|
+
with_processor_and_sender(config) do |ep, sender|
|
181
|
+
flag = { key: "flagkey", version: 11 }
|
182
|
+
fe = {
|
183
|
+
kind: "feature", key: "flagkey", version: 11, user: user,
|
184
|
+
variation: 1, value: "value", trackEvents: false
|
185
|
+
}
|
186
|
+
ep.add_event(fe)
|
187
|
+
|
188
|
+
output = flush_and_get_events(ep, sender)
|
189
|
+
expect(output).to contain_exactly(
|
190
|
+
eq(index_event(fe, user)),
|
191
|
+
include(:kind => "summary")
|
192
|
+
)
|
193
|
+
end
|
180
194
|
end
|
181
195
|
|
182
196
|
it "sets event kind to debug if flag is temporarily in debug mode" do
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
197
|
+
with_processor_and_sender(default_config) do |ep, sender|
|
198
|
+
flag = { key: "flagkey", version: 11 }
|
199
|
+
future_time = (Time.now.to_f * 1000).to_i + 1000000
|
200
|
+
fe = {
|
201
|
+
kind: "feature", key: "flagkey", version: 11, user: user,
|
202
|
+
variation: 1, value: "value", trackEvents: false, debugEventsUntilDate: future_time
|
203
|
+
}
|
204
|
+
ep.add_event(fe)
|
205
|
+
|
206
|
+
output = flush_and_get_events(ep, sender)
|
207
|
+
expect(output).to contain_exactly(
|
208
|
+
eq(index_event(fe, user)),
|
209
|
+
eq(feature_event(fe, flag, true, user)),
|
210
|
+
include(:kind => "summary")
|
211
|
+
)
|
212
|
+
end
|
198
213
|
end
|
199
214
|
|
200
215
|
it "can be both debugging and tracking an event" do
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
216
|
+
with_processor_and_sender(default_config) do |ep, sender|
|
217
|
+
flag = { key: "flagkey", version: 11 }
|
218
|
+
future_time = (Time.now.to_f * 1000).to_i + 1000000
|
219
|
+
fe = {
|
220
|
+
kind: "feature", key: "flagkey", version: 11, user: user,
|
221
|
+
variation: 1, value: "value", trackEvents: true, debugEventsUntilDate: future_time
|
222
|
+
}
|
223
|
+
ep.add_event(fe)
|
224
|
+
|
225
|
+
output = flush_and_get_events(ep, sender)
|
226
|
+
expect(output).to contain_exactly(
|
227
|
+
eq(index_event(fe, user)),
|
228
|
+
eq(feature_event(fe, flag, false, nil)),
|
229
|
+
eq(feature_event(fe, flag, true, user)),
|
230
|
+
include(:kind => "summary")
|
231
|
+
)
|
232
|
+
end
|
217
233
|
end
|
218
234
|
|
219
235
|
it "ends debug mode based on client time if client time is later than server time" do
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
)
|
236
|
+
with_processor_and_sender(default_config) do |ep, sender|
|
237
|
+
# Pick a server time that is somewhat behind the client time
|
238
|
+
server_time = Time.now - 20
|
239
|
+
|
240
|
+
# Send and flush an event we don't care about, just to set the last server time
|
241
|
+
sender.result = LaunchDarkly::Impl::EventSenderResult.new(true, false, server_time)
|
242
|
+
ep.add_event({ kind: "identify", user: user })
|
243
|
+
flush_and_get_events(ep, sender)
|
244
|
+
|
245
|
+
# Now send an event with debug mode on, with a "debug until" time that is further in
|
246
|
+
# the future than the server time, but in the past compared to the client.
|
247
|
+
flag = { key: "flagkey", version: 11 }
|
248
|
+
debug_until = (server_time.to_f * 1000).to_i + 1000
|
249
|
+
fe = {
|
250
|
+
kind: "feature", key: "flagkey", version: 11, user: user,
|
251
|
+
variation: 1, value: "value", trackEvents: false, debugEventsUntilDate: debug_until
|
252
|
+
}
|
253
|
+
ep.add_event(fe)
|
254
|
+
|
255
|
+
# Should get a summary event only, not a full feature event
|
256
|
+
output = flush_and_get_events(ep, sender)
|
257
|
+
expect(output).to contain_exactly(
|
258
|
+
include(:kind => "summary")
|
259
|
+
)
|
260
|
+
end
|
246
261
|
end
|
247
262
|
|
248
263
|
it "ends debug mode based on server time if server time is later than client time" do
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
)
|
264
|
+
with_processor_and_sender(default_config) do |ep, sender|
|
265
|
+
# Pick a server time that is somewhat ahead of the client time
|
266
|
+
server_time = Time.now + 20
|
267
|
+
|
268
|
+
# Send and flush an event we don't care about, just to set the last server time
|
269
|
+
sender.result = LaunchDarkly::Impl::EventSenderResult.new(true, false, server_time)
|
270
|
+
ep.add_event({ kind: "identify", user: user })
|
271
|
+
flush_and_get_events(ep, sender)
|
272
|
+
|
273
|
+
# Now send an event with debug mode on, with a "debug until" time that is further in
|
274
|
+
# the future than the server time, but in the past compared to the client.
|
275
|
+
flag = { key: "flagkey", version: 11 }
|
276
|
+
debug_until = (server_time.to_f * 1000).to_i - 1000
|
277
|
+
fe = {
|
278
|
+
kind: "feature", key: "flagkey", version: 11, user: user,
|
279
|
+
variation: 1, value: "value", trackEvents: false, debugEventsUntilDate: debug_until
|
280
|
+
}
|
281
|
+
ep.add_event(fe)
|
282
|
+
|
283
|
+
# Should get a summary event only, not a full feature event
|
284
|
+
output = flush_and_get_events(ep, sender)
|
285
|
+
expect(output).to contain_exactly(
|
286
|
+
include(:kind => "summary")
|
287
|
+
)
|
288
|
+
end
|
275
289
|
end
|
276
290
|
|
277
291
|
it "generates only one index event for multiple events with same user" do
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
292
|
+
with_processor_and_sender(default_config) do |ep, sender|
|
293
|
+
flag1 = { key: "flagkey1", version: 11 }
|
294
|
+
flag2 = { key: "flagkey2", version: 22 }
|
295
|
+
future_time = (Time.now.to_f * 1000).to_i + 1000000
|
296
|
+
fe1 = {
|
297
|
+
kind: "feature", key: "flagkey1", version: 11, user: user,
|
298
|
+
variation: 1, value: "value", trackEvents: true
|
299
|
+
}
|
300
|
+
fe2 = {
|
301
|
+
kind: "feature", key: "flagkey2", version: 22, user: user,
|
302
|
+
variation: 1, value: "value", trackEvents: true
|
303
|
+
}
|
304
|
+
ep.add_event(fe1)
|
305
|
+
ep.add_event(fe2)
|
306
|
+
|
307
|
+
output = flush_and_get_events(ep, sender)
|
308
|
+
expect(output).to contain_exactly(
|
309
|
+
eq(index_event(fe1, user)),
|
310
|
+
eq(feature_event(fe1, flag1, false, nil)),
|
311
|
+
eq(feature_event(fe2, flag2, false, nil)),
|
312
|
+
include(:kind => "summary")
|
313
|
+
)
|
314
|
+
end
|
300
315
|
end
|
301
316
|
|
302
317
|
it "summarizes non-tracked events" do
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
318
|
+
with_processor_and_sender(default_config) do |ep, sender|
|
319
|
+
flag1 = { key: "flagkey1", version: 11 }
|
320
|
+
flag2 = { key: "flagkey2", version: 22 }
|
321
|
+
future_time = (Time.now.to_f * 1000).to_i + 1000000
|
322
|
+
fe1 = {
|
323
|
+
kind: "feature", key: "flagkey1", version: 11, user: user,
|
324
|
+
variation: 1, value: "value1", default: "default1"
|
325
|
+
}
|
326
|
+
fe2 = {
|
327
|
+
kind: "feature", key: "flagkey2", version: 22, user: user,
|
328
|
+
variation: 2, value: "value2", default: "default2"
|
329
|
+
}
|
330
|
+
ep.add_event(fe1)
|
331
|
+
ep.add_event(fe2)
|
332
|
+
|
333
|
+
output = flush_and_get_events(ep, sender)
|
334
|
+
expect(output).to contain_exactly(
|
335
|
+
eq(index_event(fe1, user)),
|
336
|
+
eq({
|
337
|
+
kind: "summary",
|
338
|
+
startDate: fe1[:creationDate],
|
339
|
+
endDate: fe2[:creationDate],
|
340
|
+
features: {
|
341
|
+
flagkey1: {
|
342
|
+
default: "default1",
|
343
|
+
counters: [
|
344
|
+
{ version: 11, variation: 1, value: "value1", count: 1 }
|
345
|
+
]
|
346
|
+
},
|
347
|
+
flagkey2: {
|
348
|
+
default: "default2",
|
349
|
+
counters: [
|
350
|
+
{ version: 22, variation: 2, value: "value2", count: 1 }
|
351
|
+
]
|
352
|
+
}
|
337
353
|
}
|
338
|
-
}
|
339
|
-
|
340
|
-
|
354
|
+
})
|
355
|
+
)
|
356
|
+
end
|
341
357
|
end
|
342
358
|
|
343
359
|
it "queues custom event with user" do
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
360
|
+
with_processor_and_sender(default_config) do |ep, sender|
|
361
|
+
e = { kind: "custom", key: "eventkey", user: user, data: { thing: "stuff" }, metricValue: 1.5 }
|
362
|
+
ep.add_event(e)
|
363
|
+
|
364
|
+
output = flush_and_get_events(ep, sender)
|
365
|
+
expect(output).to contain_exactly(
|
366
|
+
eq(index_event(e, user)),
|
367
|
+
eq(custom_event(e, nil))
|
368
|
+
)
|
369
|
+
end
|
353
370
|
end
|
354
371
|
|
355
372
|
it "can include inline user in custom event" do
|
356
|
-
config = LaunchDarkly::Config.new(inline_users_in_events: true)
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
373
|
+
config = LaunchDarkly::Config.new(default_config_opts.merge(inline_users_in_events: true))
|
374
|
+
with_processor_and_sender(config) do |ep, sender|
|
375
|
+
e = { kind: "custom", key: "eventkey", user: user, data: { thing: "stuff" } }
|
376
|
+
ep.add_event(e)
|
377
|
+
|
378
|
+
output = flush_and_get_events(ep, sender)
|
379
|
+
expect(output).to contain_exactly(
|
380
|
+
eq(custom_event(e, user))
|
381
|
+
)
|
382
|
+
end
|
365
383
|
end
|
366
384
|
|
367
385
|
it "filters user in custom event" do
|
368
|
-
config = LaunchDarkly::Config.new(all_attributes_private: true, inline_users_in_events: true)
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
386
|
+
config = LaunchDarkly::Config.new(default_config_opts.merge(all_attributes_private: true, inline_users_in_events: true))
|
387
|
+
with_processor_and_sender(config) do |ep, sender|
|
388
|
+
e = { kind: "custom", key: "eventkey", user: user, data: { thing: "stuff" } }
|
389
|
+
ep.add_event(e)
|
390
|
+
|
391
|
+
output = flush_and_get_events(ep, sender)
|
392
|
+
expect(output).to contain_exactly(
|
393
|
+
eq(custom_event(e, filtered_user))
|
394
|
+
)
|
395
|
+
end
|
377
396
|
end
|
378
397
|
|
379
398
|
it "stringifies built-in user attributes in custom event" do
|
380
|
-
config = LaunchDarkly::Config.new(inline_users_in_events: true)
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
399
|
+
config = LaunchDarkly::Config.new(default_config_opts.merge(inline_users_in_events: true))
|
400
|
+
with_processor_and_sender(config) do |ep, sender|
|
401
|
+
e = { kind: "custom", key: "eventkey", user: numeric_user }
|
402
|
+
ep.add_event(e)
|
403
|
+
|
404
|
+
output = flush_and_get_events(ep, sender)
|
405
|
+
expect(output).to contain_exactly(
|
406
|
+
eq(custom_event(e, stringified_numeric_user))
|
407
|
+
)
|
408
|
+
end
|
389
409
|
end
|
390
410
|
|
391
411
|
it "does a final flush when shutting down" do
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
412
|
+
with_processor_and_sender(default_config) do |ep, sender|
|
413
|
+
e = { kind: "identify", key: user[:key], user: user }
|
414
|
+
ep.add_event(e)
|
415
|
+
|
416
|
+
ep.stop
|
417
|
+
|
418
|
+
output = sender.analytics_payloads.pop
|
419
|
+
expect(output).to contain_exactly(e)
|
420
|
+
end
|
400
421
|
end
|
401
422
|
|
402
423
|
it "sends nothing if there are no events" do
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
it "sends SDK key" do
|
409
|
-
@ep = subject.new("sdk_key", default_config, hc)
|
410
|
-
e = { kind: "identify", user: user }
|
411
|
-
@ep.add_event(e)
|
412
|
-
|
413
|
-
@ep.flush
|
414
|
-
@ep.wait_until_inactive
|
415
|
-
|
416
|
-
expect(hc.get_request["authorization"]).to eq "sdk_key"
|
417
|
-
end
|
418
|
-
|
419
|
-
it "sends unique payload IDs" do
|
420
|
-
@ep = subject.new("sdk_key", default_config, hc)
|
421
|
-
e = { kind: "identify", user: user }
|
422
|
-
|
423
|
-
@ep.add_event(e)
|
424
|
-
@ep.flush
|
425
|
-
@ep.wait_until_inactive
|
426
|
-
req0 = hc.get_request
|
427
|
-
|
428
|
-
@ep.add_event(e)
|
429
|
-
@ep.flush
|
430
|
-
@ep.wait_until_inactive
|
431
|
-
req1 = hc.get_request
|
432
|
-
|
433
|
-
id0 = req0["x-launchdarkly-payload-id"]
|
434
|
-
id1 = req1["x-launchdarkly-payload-id"]
|
435
|
-
expect(id0).not_to be_nil
|
436
|
-
expect(id0).not_to eq ""
|
437
|
-
expect(id1).not_to be nil
|
438
|
-
expect(id1).not_to eq ""
|
439
|
-
expect(id1).not_to eq id0
|
440
|
-
end
|
441
|
-
|
442
|
-
def verify_unrecoverable_http_error(status)
|
443
|
-
@ep = subject.new("sdk_key", default_config, hc)
|
444
|
-
e = { kind: "identify", user: user }
|
445
|
-
@ep.add_event(e)
|
446
|
-
|
447
|
-
hc.set_response_status(status)
|
448
|
-
@ep.flush
|
449
|
-
@ep.wait_until_inactive
|
450
|
-
expect(hc.get_request).not_to be_nil
|
451
|
-
hc.reset
|
452
|
-
|
453
|
-
@ep.add_event(e)
|
454
|
-
@ep.flush
|
455
|
-
@ep.wait_until_inactive
|
456
|
-
expect(hc.get_request).to be_nil
|
457
|
-
end
|
458
|
-
|
459
|
-
def verify_recoverable_http_error(status)
|
460
|
-
@ep = subject.new("sdk_key", default_config, hc)
|
461
|
-
e = { kind: "identify", user: user }
|
462
|
-
@ep.add_event(e)
|
463
|
-
|
464
|
-
hc.set_response_status(503)
|
465
|
-
@ep.flush
|
466
|
-
@ep.wait_until_inactive
|
467
|
-
|
468
|
-
req0 = hc.get_request
|
469
|
-
expect(req0).not_to be_nil
|
470
|
-
req1 = hc.get_request
|
471
|
-
expect(req1).not_to be_nil
|
472
|
-
id0 = req0["x-launchdarkly-payload-id"]
|
473
|
-
expect(id0).not_to be_nil
|
474
|
-
expect(id0).not_to eq ""
|
475
|
-
expect(req1["x-launchdarkly-payload-id"]).to eq id0
|
476
|
-
|
477
|
-
expect(hc.get_request).to be_nil # no 3rd request
|
478
|
-
|
479
|
-
# now verify that a subsequent flush still generates a request
|
480
|
-
hc.reset
|
481
|
-
@ep.add_event(e)
|
482
|
-
@ep.flush
|
483
|
-
@ep.wait_until_inactive
|
484
|
-
expect(hc.get_request).not_to be_nil
|
424
|
+
with_processor_and_sender(default_config) do |ep, sender|
|
425
|
+
ep.flush
|
426
|
+
ep.wait_until_inactive
|
427
|
+
expect(sender.analytics_payloads.empty?).to be true
|
428
|
+
end
|
485
429
|
end
|
486
430
|
|
487
|
-
it "stops posting events after
|
488
|
-
|
489
|
-
|
431
|
+
it "stops posting events after unrecoverable error" do
|
432
|
+
with_processor_and_sender(default_config) do |ep, sender|
|
433
|
+
sender.result = LaunchDarkly::Impl::EventSenderResult.new(false, true, nil)
|
434
|
+
e = { kind: "identify", key: user[:key], user: user }
|
435
|
+
ep.add_event(e)
|
436
|
+
flush_and_get_events(ep, sender)
|
490
437
|
|
491
|
-
|
492
|
-
|
438
|
+
e = { kind: "identify", key: user[:key], user: user }
|
439
|
+
ep.add_event(e)
|
440
|
+
ep.flush
|
441
|
+
ep.wait_until_inactive
|
442
|
+
expect(sender.analytics_payloads.empty?).to be true
|
443
|
+
end
|
493
444
|
end
|
494
445
|
|
495
|
-
|
496
|
-
|
497
|
-
|
446
|
+
describe "diagnostic events" do
|
447
|
+
let(:default_id) { LaunchDarkly::Impl::DiagnosticAccumulator.create_diagnostic_id('sdk_key') }
|
448
|
+
let(:diagnostic_config) { LaunchDarkly::Config.new(diagnostic_opt_out: false, logger: $null_log) }
|
498
449
|
|
499
|
-
|
500
|
-
|
501
|
-
|
450
|
+
def with_diagnostic_processor_and_sender(config)
|
451
|
+
sender = FakeEventSender.new
|
452
|
+
acc = LaunchDarkly::Impl::DiagnosticAccumulator.new(default_id)
|
453
|
+
ep = subject.new("sdk_key", config, nil, acc,
|
454
|
+
{ diagnostic_recording_interval: 0.2, event_sender: sender })
|
455
|
+
begin
|
456
|
+
yield ep, sender
|
457
|
+
ensure
|
458
|
+
ep.stop
|
459
|
+
end
|
460
|
+
end
|
502
461
|
|
503
|
-
|
504
|
-
|
505
|
-
|
462
|
+
it "sends init event" do
|
463
|
+
with_diagnostic_processor_and_sender(diagnostic_config) do |ep, sender|
|
464
|
+
event = sender.diagnostic_payloads.pop
|
465
|
+
expect(event).to include({
|
466
|
+
kind: 'diagnostic-init',
|
467
|
+
id: default_id
|
468
|
+
})
|
469
|
+
end
|
470
|
+
end
|
506
471
|
|
507
|
-
|
508
|
-
|
509
|
-
|
510
|
-
|
472
|
+
it "sends periodic event" do
|
473
|
+
with_diagnostic_processor_and_sender(diagnostic_config) do |ep, sender|
|
474
|
+
init_event = sender.diagnostic_payloads.pop
|
475
|
+
periodic_event = sender.diagnostic_payloads.pop
|
476
|
+
expect(periodic_event).to include({
|
477
|
+
kind: 'diagnostic',
|
478
|
+
id: default_id,
|
479
|
+
droppedEvents: 0,
|
480
|
+
deduplicatedUsers: 0,
|
481
|
+
eventsInLastBatch: 0,
|
482
|
+
streamInits: []
|
483
|
+
})
|
484
|
+
end
|
485
|
+
end
|
511
486
|
|
512
|
-
|
513
|
-
|
514
|
-
|
487
|
+
it "counts events in queue from last flush and dropped events" do
|
488
|
+
config = LaunchDarkly::Config.new(diagnostic_opt_out: false, capacity: 2, logger: $null_log)
|
489
|
+
with_diagnostic_processor_and_sender(config) do |ep, sender|
|
490
|
+
init_event = sender.diagnostic_payloads.pop
|
491
|
+
|
492
|
+
ep.add_event({ kind: 'identify', user: user })
|
493
|
+
ep.add_event({ kind: 'identify', user: user })
|
494
|
+
ep.add_event({ kind: 'identify', user: user })
|
495
|
+
flush_and_get_events(ep, sender)
|
496
|
+
|
497
|
+
periodic_event = sender.diagnostic_payloads.pop
|
498
|
+
expect(periodic_event).to include({
|
499
|
+
kind: 'diagnostic',
|
500
|
+
droppedEvents: 1,
|
501
|
+
eventsInLastBatch: 2
|
502
|
+
})
|
503
|
+
end
|
504
|
+
end
|
515
505
|
|
516
|
-
|
517
|
-
|
518
|
-
|
519
|
-
end
|
506
|
+
it "counts deduplicated users" do
|
507
|
+
with_diagnostic_processor_and_sender(diagnostic_config) do |ep, sender|
|
508
|
+
init_event = sender.diagnostic_payloads.pop
|
520
509
|
|
521
|
-
|
522
|
-
|
523
|
-
|
524
|
-
server.setup_ok_response("/bulk", "")
|
525
|
-
|
526
|
-
@ep = subject.new("sdk_key", LaunchDarkly::Config.new(events_uri: server.base_uri.to_s))
|
527
|
-
@ep.add_event(e)
|
528
|
-
@ep.flush
|
529
|
-
|
530
|
-
req = server.await_request
|
531
|
-
expect(req.header).to include({
|
532
|
-
"authorization" => [ "sdk_key" ],
|
533
|
-
"content-type" => [ "application/json" ],
|
534
|
-
"user-agent" => [ "RubyClient/" + LaunchDarkly::VERSION ],
|
535
|
-
"x-launchdarkly-event-schema" => [ "3" ]
|
536
|
-
})
|
537
|
-
end
|
538
|
-
end
|
510
|
+
ep.add_event({ kind: 'custom', key: 'event1', user: user })
|
511
|
+
ep.add_event({ kind: 'custom', key: 'event2', user: user })
|
512
|
+
events = flush_and_get_events(ep, sender)
|
539
513
|
|
540
|
-
|
541
|
-
|
542
|
-
|
543
|
-
|
544
|
-
|
545
|
-
with_server(StubProxyServer.new) do |proxy|
|
546
|
-
begin
|
547
|
-
ENV["http_proxy"] = proxy.base_uri.to_s
|
548
|
-
@ep = subject.new("sdk_key", LaunchDarkly::Config.new(events_uri: server.base_uri.to_s))
|
549
|
-
@ep.add_event(e)
|
550
|
-
@ep.flush
|
551
|
-
|
552
|
-
req = server.await_request
|
553
|
-
expect(req["content-type"]).to eq("application/json")
|
554
|
-
ensure
|
555
|
-
ENV["http_proxy"] = nil
|
556
|
-
end
|
514
|
+
periodic_event = sender.diagnostic_payloads.pop
|
515
|
+
expect(periodic_event).to include({
|
516
|
+
kind: 'diagnostic',
|
517
|
+
deduplicatedUsers: 1
|
518
|
+
})
|
557
519
|
end
|
558
520
|
end
|
559
521
|
end
|
@@ -599,75 +561,26 @@ describe LaunchDarkly::EventProcessor do
|
|
599
561
|
out
|
600
562
|
end
|
601
563
|
|
602
|
-
def flush_and_get_events
|
603
|
-
|
604
|
-
|
605
|
-
|
564
|
+
def flush_and_get_events(ep, sender)
|
565
|
+
ep.flush
|
566
|
+
ep.wait_until_inactive
|
567
|
+
sender.analytics_payloads.pop
|
606
568
|
end
|
607
569
|
|
608
|
-
|
609
|
-
|
610
|
-
|
611
|
-
|
570
|
+
class FakeEventSender
|
571
|
+
attr_accessor :result
|
572
|
+
attr_reader :analytics_payloads
|
573
|
+
attr_reader :diagnostic_payloads
|
612
574
|
|
613
|
-
class FakeHttpClient
|
614
575
|
def initialize
|
615
|
-
|
616
|
-
|
617
|
-
|
618
|
-
def set_response_status(status)
|
619
|
-
@status = status
|
620
|
-
end
|
621
|
-
|
622
|
-
def set_server_time(time_millis)
|
623
|
-
@server_time = Time.at(time_millis.to_f / 1000)
|
624
|
-
end
|
625
|
-
|
626
|
-
def set_exception(e)
|
627
|
-
@exception = e
|
628
|
-
end
|
629
|
-
|
630
|
-
def reset
|
631
|
-
@requests = []
|
632
|
-
@status = 200
|
633
|
-
end
|
634
|
-
|
635
|
-
def request(req)
|
636
|
-
@requests.push(req)
|
637
|
-
if @exception
|
638
|
-
raise @exception
|
639
|
-
else
|
640
|
-
headers = {}
|
641
|
-
if @server_time
|
642
|
-
headers["Date"] = @server_time.httpdate
|
643
|
-
end
|
644
|
-
FakeResponse.new(@status ? @status : 200, headers)
|
645
|
-
end
|
576
|
+
@result = LaunchDarkly::Impl::EventSenderResult.new(true, false, nil)
|
577
|
+
@analytics_payloads = Queue.new
|
578
|
+
@diagnostic_payloads = Queue.new
|
646
579
|
end
|
647
580
|
|
648
|
-
def
|
649
|
-
|
650
|
-
|
651
|
-
def started?
|
652
|
-
false
|
653
|
-
end
|
654
|
-
|
655
|
-
def finish
|
656
|
-
end
|
657
|
-
|
658
|
-
def get_request
|
659
|
-
@requests.shift
|
660
|
-
end
|
661
|
-
end
|
662
|
-
|
663
|
-
class FakeResponse
|
664
|
-
include Net::HTTPHeader
|
665
|
-
|
666
|
-
attr_reader :code
|
667
|
-
|
668
|
-
def initialize(status, headers)
|
669
|
-
@code = status.to_s
|
670
|
-
initialize_http_header(headers)
|
581
|
+
def send_event_data(data, is_diagnostic)
|
582
|
+
(is_diagnostic ? @diagnostic_payloads : @analytics_payloads).push(JSON.parse(data, symbolize_names: true))
|
583
|
+
@result
|
671
584
|
end
|
672
585
|
end
|
673
586
|
end
|