patronus_fati 1.1.2 → 1.2.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/bin/patronus_fati +10 -2
- data/lib/patronus_fati/data_models/access_point.rb +24 -24
- data/lib/patronus_fati/data_models/client.rb +12 -23
- data/lib/patronus_fati/data_models/common_state.rb +37 -2
- data/lib/patronus_fati/data_models/connection.rb +3 -8
- data/lib/patronus_fati/data_models/ssid.rb +6 -4
- data/lib/patronus_fati/message_processor.rb +3 -2
- data/lib/patronus_fati/presence.rb +6 -2
- data/lib/patronus_fati/version.rb +1 -1
- data/spec/patronus_fati/data_models/access_point_spec.rb +282 -0
- data/spec/patronus_fati/data_models/client_spec.rb +350 -0
- data/spec/patronus_fati/data_models/connection_spec.rb +7 -0
- data/spec/patronus_fati/data_models/ssid_spec.rb +57 -0
- data/spec/shared_examples/common_model_state.rb +269 -0
- data/spec/spec_helper.rb +2 -0
- metadata +12 -2
@@ -0,0 +1,350 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe(PatronusFati::DataModels::Client) do
|
4
|
+
subject { described_class.new('00:11:22:33:44:55') }
|
5
|
+
|
6
|
+
it_behaves_like 'a common stateful model'
|
7
|
+
|
8
|
+
context '#add_access_point' do
|
9
|
+
it 'should not add an access point more than once' do
|
10
|
+
sample_mac = '33:33:33:44:44:44'
|
11
|
+
subject.access_point_bssids = [ sample_mac ]
|
12
|
+
|
13
|
+
expect { subject.add_access_point(sample_mac) }
|
14
|
+
.to_not change { subject.access_point_bssids }
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'should add an access point if it\'s not presently in the list' do
|
18
|
+
sample_mac = '99:11:22:ff:23:00'
|
19
|
+
|
20
|
+
expect(subject.access_point_bssids).to be_empty
|
21
|
+
expect { subject.add_access_point(sample_mac) }
|
22
|
+
.to change { subject.access_point_bssids }.from([]).to([sample_mac])
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
context '#announce_changes' do
|
27
|
+
before(:each) do
|
28
|
+
PatronusFati::DataModels::AccessPoint.instance_variable_set(:@instances, nil)
|
29
|
+
PatronusFati::DataModels::Client.instance_variable_set(:@instances, nil)
|
30
|
+
PatronusFati::DataModels::Connection.instance_variable_set(:@instances, nil)
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'should emit no events when the client isn\'t valid' do
|
34
|
+
expect(subject).to receive(:dirty?).and_return(true)
|
35
|
+
expect(subject).to receive(:valid?).and_return(false)
|
36
|
+
|
37
|
+
expect(PatronusFati.event_handler).to_not receive(:event)
|
38
|
+
subject.announce_changes
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'should emit no events when the instance isn\'t dirty' do
|
42
|
+
expect(subject).to receive(:dirty?).and_return(false)
|
43
|
+
|
44
|
+
expect(PatronusFati.event_handler).to_not receive(:event)
|
45
|
+
subject.announce_changes
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'should emit a new client event when dirty and unsynced' do
|
49
|
+
expect(subject).to receive(:dirty?).and_return(true)
|
50
|
+
expect(subject).to receive(:valid?).and_return(true)
|
51
|
+
subject.presence.mark_visible
|
52
|
+
|
53
|
+
expect(PatronusFati.event_handler)
|
54
|
+
.to receive(:event).with(:client, :new, anything, anything)
|
55
|
+
subject.announce_changes
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'should emit a changed client event when dirty and synced as online' do
|
59
|
+
subject.presence.mark_visible
|
60
|
+
subject.mark_synced
|
61
|
+
|
62
|
+
expect(subject).to receive(:dirty?).and_return(true)
|
63
|
+
expect(subject).to receive(:valid?).and_return(true)
|
64
|
+
|
65
|
+
expect(PatronusFati.event_handler)
|
66
|
+
.to receive(:event).with(:client, :changed, anything, anything)
|
67
|
+
subject.announce_changes
|
68
|
+
end
|
69
|
+
|
70
|
+
it 'should emit a changed client event when dirty and synced as offline' do
|
71
|
+
subject.mark_synced
|
72
|
+
expect(subject.active?).to be_falsey
|
73
|
+
|
74
|
+
subject.presence.mark_visible
|
75
|
+
|
76
|
+
expect(subject).to receive(:valid?).and_return(true)
|
77
|
+
expect(PatronusFati.event_handler)
|
78
|
+
.to receive(:event).with(:client, :changed, anything, anything)
|
79
|
+
subject.announce_changes
|
80
|
+
end
|
81
|
+
|
82
|
+
it 'should emit an offline client event when the client becomes inactive' do
|
83
|
+
subject.presence.mark_visible
|
84
|
+
subject.mark_synced
|
85
|
+
expect(subject.active?).to be_truthy
|
86
|
+
|
87
|
+
expect(subject).to receive(:valid?).and_return(true)
|
88
|
+
expect(subject).to receive(:active?).and_return(false).exactly(3).times
|
89
|
+
|
90
|
+
expect(PatronusFati.event_handler)
|
91
|
+
.to receive(:event).with(:client, :offline, anything, anything)
|
92
|
+
subject.announce_changes
|
93
|
+
end
|
94
|
+
|
95
|
+
it 'should reset the presence first_seen value when announced offline' do
|
96
|
+
subject.presence.mark_visible
|
97
|
+
subject.mark_synced
|
98
|
+
expect(subject.active?).to be_truthy
|
99
|
+
|
100
|
+
expect(subject).to receive(:valid?).and_return(true)
|
101
|
+
expect(subject).to receive(:active?).and_return(false).exactly(3).times
|
102
|
+
|
103
|
+
expect { subject.announce_changes }
|
104
|
+
.to change { subject.presence.first_seen }.to(nil)
|
105
|
+
end
|
106
|
+
|
107
|
+
it 'should remove itself from access points as a connected client when announced offline' do
|
108
|
+
bssid = 'fa:eb:dc:45:23:67'
|
109
|
+
dbl = double(PatronusFati::DataModels::AccessPoint)
|
110
|
+
PatronusFati::DataModels::AccessPoint.instances[bssid] = dbl
|
111
|
+
|
112
|
+
subject.presence.mark_visible
|
113
|
+
subject.add_access_point(bssid)
|
114
|
+
subject.mark_synced
|
115
|
+
|
116
|
+
expect(dbl).to receive(:remove_client).with(subject.local_attributes[:mac])
|
117
|
+
expect(subject).to receive(:active?).and_return(false).exactly(3).times
|
118
|
+
|
119
|
+
subject.announce_changes
|
120
|
+
end
|
121
|
+
|
122
|
+
it 'should mark active connections with a lost link when announced offline' do
|
123
|
+
bssid = 'fa:eb:dc:45:23:67'
|
124
|
+
conn_key = "#{bssid}^#{subject.local_attributes[:mac]}"
|
125
|
+
|
126
|
+
dbl = double(PatronusFati::DataModels::Connection)
|
127
|
+
PatronusFati::DataModels::Connection.instances[conn_key] = dbl
|
128
|
+
|
129
|
+
subject.presence.mark_visible
|
130
|
+
subject.add_access_point(bssid)
|
131
|
+
subject.mark_synced
|
132
|
+
|
133
|
+
expect(dbl).to receive(:link_lost=).with(true)
|
134
|
+
expect(subject).to receive(:active?).and_return(false).exactly(3).times
|
135
|
+
|
136
|
+
subject.announce_changes
|
137
|
+
end
|
138
|
+
|
139
|
+
it 'short not be dirty after being synced' do
|
140
|
+
expect(subject).to receive(:valid?).and_return(true)
|
141
|
+
subject.update(channel: 8)
|
142
|
+
|
143
|
+
expect { subject.announce_changes }.to change { subject.dirty? }.from(true).to(false)
|
144
|
+
end
|
145
|
+
|
146
|
+
it 'should announce the full state of the client with online syncs' do
|
147
|
+
subject.presence.mark_visible
|
148
|
+
subject.mark_synced
|
149
|
+
|
150
|
+
data_sample = { data: 'test' }
|
151
|
+
|
152
|
+
expect(subject).to receive(:dirty?).and_return(true)
|
153
|
+
expect(subject).to receive(:valid?).and_return(true)
|
154
|
+
expect(subject).to receive(:full_state).and_return(data_sample)
|
155
|
+
|
156
|
+
expect(PatronusFati.event_handler)
|
157
|
+
.to receive(:event).with(:client, :changed, data_sample, anything)
|
158
|
+
subject.announce_changes
|
159
|
+
end
|
160
|
+
|
161
|
+
it 'should announce a minimal state of the client with offline syncs' do
|
162
|
+
subject.presence.mark_visible
|
163
|
+
subject.mark_synced
|
164
|
+
|
165
|
+
expect(subject).to receive(:valid?).and_return(true)
|
166
|
+
expect(subject).to receive(:active?).and_return(false).exactly(3).times
|
167
|
+
|
168
|
+
expect(subject.presence).to receive(:visible_time).and_return(1234)
|
169
|
+
min_data = {
|
170
|
+
'bssid' => subject.local_attributes[:mac],
|
171
|
+
'uptime' => 1234
|
172
|
+
}
|
173
|
+
|
174
|
+
expect(PatronusFati.event_handler)
|
175
|
+
.to receive(:event).with(:client, :offline, min_data, anything)
|
176
|
+
subject.announce_changes
|
177
|
+
end
|
178
|
+
|
179
|
+
it 'should announce the diagnostic data of the client' do
|
180
|
+
sample_data = { content: 'diagnostic' }
|
181
|
+
|
182
|
+
expect(subject).to receive(:dirty?).and_return(true)
|
183
|
+
expect(subject).to receive(:valid?).and_return(true)
|
184
|
+
expect(subject).to receive(:diagnostic_data).and_return(sample_data)
|
185
|
+
|
186
|
+
expect(PatronusFati.event_handler)
|
187
|
+
.to receive(:event).with(:client, :offline, anything, sample_data)
|
188
|
+
subject.announce_changes
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
context '#cleanup_probes' do
|
193
|
+
let(:probe) { PatronusFati::Presence.new }
|
194
|
+
|
195
|
+
it 'should not modify the children flag when there are no probes' do
|
196
|
+
expect(subject.probes).to be_empty
|
197
|
+
expect { subject.cleanup_probes }.to_not change { subject.sync_status }
|
198
|
+
end
|
199
|
+
|
200
|
+
it 'should not modify the children flag when there is only active probes' do
|
201
|
+
probe.mark_visible
|
202
|
+
subject.probes = { 'probe key' => probe }
|
203
|
+
expect { subject.cleanup_probes }.to_not change { subject.sync_status }
|
204
|
+
end
|
205
|
+
|
206
|
+
it 'should modify the children flag when there is a dead probe' do
|
207
|
+
expect(probe).to be_dead
|
208
|
+
subject.probes = { 'NETGEAR32' => probe }
|
209
|
+
expect { subject.cleanup_probes }
|
210
|
+
.to change { subject.sync_flag?(:dirtyChildren) }.from(false).to(true)
|
211
|
+
end
|
212
|
+
|
213
|
+
it 'should remove all dead probes from the list' do
|
214
|
+
expect(probe).to be_dead
|
215
|
+
subject.probes = { 'linksys' => probe }
|
216
|
+
expect { subject.cleanup_probes }.to change { subject.probes }.to({})
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
220
|
+
context '#full_state' do
|
221
|
+
it 'should return a hash' do
|
222
|
+
expect(subject.full_state).to be_kind_of(Hash)
|
223
|
+
end
|
224
|
+
|
225
|
+
it 'should include the keys expected by pulse' do
|
226
|
+
[:active, :bssid, :channel, :connected_access_points, :probes, :vendor].each do |k|
|
227
|
+
expect(subject.full_state.key?(k)).to be_truthy
|
228
|
+
end
|
229
|
+
end
|
230
|
+
end
|
231
|
+
|
232
|
+
context '#initialize' do
|
233
|
+
it 'should initialize probes to an empty hash' do
|
234
|
+
expect(subject.probes).to eql({})
|
235
|
+
end
|
236
|
+
|
237
|
+
it 'should initialize the local attributes with the client\'s mac' do
|
238
|
+
expect(subject.local_attributes.keys).to eql([:mac])
|
239
|
+
expect(subject.local_attributes[:mac]).to_not be_nil
|
240
|
+
end
|
241
|
+
|
242
|
+
it 'should initialize the APs it\'s connected to, to an empty array' do
|
243
|
+
expect(subject.access_point_bssids).to eql([])
|
244
|
+
end
|
245
|
+
end
|
246
|
+
|
247
|
+
context '#remove_access_point' do
|
248
|
+
it 'should make no change if the the bssid isn\'t present' do
|
249
|
+
subject.access_point_bssids = [ '78:2b:11:ae:00:12' ]
|
250
|
+
expect { subject.remove_access_point('00:11:22:33:44:55') }
|
251
|
+
.to_not change { subject.access_point_bssids }
|
252
|
+
end
|
253
|
+
|
254
|
+
it 'should remove just the bssid provided when it is present' do
|
255
|
+
test_mac = '00:11:22:33:44:55'
|
256
|
+
subject.access_point_bssids = [ '78:2b:11:ae:00:12', test_mac ]
|
257
|
+
|
258
|
+
expect { subject.remove_access_point(test_mac) }
|
259
|
+
.to change { subject.access_point_bssids }
|
260
|
+
expect(subject.access_point_bssids).to_not include(test_mac)
|
261
|
+
end
|
262
|
+
end
|
263
|
+
|
264
|
+
context '#track_probe' do
|
265
|
+
it 'should not change anything when provided a nil value' do
|
266
|
+
expect { subject.track_probe(nil) }.to_not change { subject.probes }
|
267
|
+
end
|
268
|
+
|
269
|
+
it 'should not change anything when provided an empty string' do
|
270
|
+
expect { subject.track_probe('') }.to_not change { subject.probes }
|
271
|
+
end
|
272
|
+
|
273
|
+
it 'should track new probes as a new presence instance' do
|
274
|
+
subject.track_probe('test')
|
275
|
+
expect(subject.probes['test']).to be_instance_of(PatronusFati::Presence)
|
276
|
+
expect(subject.probes['test']).to_not be_dead
|
277
|
+
end
|
278
|
+
|
279
|
+
it 'should mark existing probes as visisble' do
|
280
|
+
dbl = double(PatronusFati::Presence)
|
281
|
+
subject.probes['pineapple'] = dbl
|
282
|
+
expect(dbl).to receive(:mark_visible)
|
283
|
+
subject.track_probe('pineapple')
|
284
|
+
end
|
285
|
+
end
|
286
|
+
|
287
|
+
context '#update' do
|
288
|
+
it 'should not set invalid keys' do
|
289
|
+
expect { subject.update(bad: 'key') }
|
290
|
+
.to_not change { subject.local_attributes }
|
291
|
+
end
|
292
|
+
|
293
|
+
it 'shouldn\'t modify the sync flags on invalid keys' do
|
294
|
+
expect { subject.update(other: 'key') }
|
295
|
+
.to_not change { subject.sync_status }
|
296
|
+
end
|
297
|
+
|
298
|
+
it 'shouldn\'t modify the sync flags if the values haven\'t changed' do
|
299
|
+
expect { subject.update(subject.local_attributes) }
|
300
|
+
.to_not change { subject.sync_status }
|
301
|
+
end
|
302
|
+
|
303
|
+
it 'should set the dirty attribute flag when a value has changed' do
|
304
|
+
expect { subject.update(channel: 5) }
|
305
|
+
.to change { subject.sync_status }
|
306
|
+
expect(subject.sync_flag?(:dirtyAttributes)).to be_truthy
|
307
|
+
end
|
308
|
+
end
|
309
|
+
|
310
|
+
context '#valid?' do
|
311
|
+
it 'should be true when all required attributes are set' do
|
312
|
+
subject.local_attributes = { mac: 'testing' }
|
313
|
+
expect(subject).to be_valid
|
314
|
+
end
|
315
|
+
|
316
|
+
it 'should be false when missing a required attribute' do
|
317
|
+
subject.local_attributes.delete(:mac)
|
318
|
+
expect(subject).to_not be_valid
|
319
|
+
end
|
320
|
+
end
|
321
|
+
|
322
|
+
context '#vendor' do
|
323
|
+
it 'should short circuit if no MAC is available' do
|
324
|
+
expect(Louis).to_not receive(:lookup)
|
325
|
+
|
326
|
+
subject.update(mac: nil)
|
327
|
+
subject.vendor
|
328
|
+
end
|
329
|
+
|
330
|
+
it 'should use the Louis gem to perform it\'s lookup' do
|
331
|
+
inst = 'test string'
|
332
|
+
subject.update(mac: inst)
|
333
|
+
|
334
|
+
expect(Louis).to receive(:lookup).with(inst).and_return({})
|
335
|
+
subject.vendor
|
336
|
+
end
|
337
|
+
|
338
|
+
it 'should default the long vendor name if it\'s available' do
|
339
|
+
result = { 'long_vendor' => 'correct', 'short_vendor' => 'bad' }
|
340
|
+
expect(Louis).to receive(:lookup).and_return(result)
|
341
|
+
expect(subject.vendor).to eql('correct')
|
342
|
+
end
|
343
|
+
|
344
|
+
it 'should fallback on the short vendor name if long isn\'t available' do
|
345
|
+
result = { 'short_vendor' => 'short' }
|
346
|
+
expect(Louis).to receive(:lookup).and_return(result)
|
347
|
+
expect(subject.vendor).to eql('short')
|
348
|
+
end
|
349
|
+
end
|
350
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe(PatronusFati::DataModels::Ssid) do
|
4
|
+
subject { described_class.new('BillWiTheScienceFi') }
|
5
|
+
|
6
|
+
it_behaves_like 'a common stateful model'
|
7
|
+
|
8
|
+
context '#initialize' do
|
9
|
+
it 'should initialize the local attributes with the essid' do
|
10
|
+
expect(subject.local_attributes.keys).to include(:essid)
|
11
|
+
expect(subject.local_attributes[:essid]).to_not be_nil
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'should initialize the local attributes with cloaked' do
|
15
|
+
expect(subject.local_attributes.keys).to include(:cloaked)
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'should initialize the cloaked attribute to false if an essid is provided' do
|
19
|
+
expect(subject.local_attributes[:essid]).to_not be_nil
|
20
|
+
expect(subject.local_attributes[:essid].size).to be > 0
|
21
|
+
expect(subject.local_attributes[:cloaked]).to be_falsey
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'should initialize the cloaked attribute to true if a nil essid is provided' do
|
25
|
+
subject = described_class.new(nil)
|
26
|
+
expect(subject.local_attributes[:cloaked]).to be_truthy
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'should initialize the cloaked attribute to true if an empty essid is provided' do
|
30
|
+
subject = described_class.new('')
|
31
|
+
expect(subject.local_attributes[:cloaked]).to be_truthy
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
context '#update' do
|
36
|
+
it 'should not set invalid keys' do
|
37
|
+
expect { subject.update(bad: 'key') }
|
38
|
+
.to_not change { subject.local_attributes }
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'shouldn\'t modify the sync flags on invalid keys' do
|
42
|
+
expect { subject.update(other: 'key') }
|
43
|
+
.to_not change { subject.sync_status }
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'shouldn\'t modify the sync flags if the values haven\'t changed' do
|
47
|
+
expect { subject.update(subject.local_attributes) }
|
48
|
+
.to_not change { subject.sync_status }
|
49
|
+
end
|
50
|
+
|
51
|
+
it 'should set the dirty attribute flag when a value has changed' do
|
52
|
+
expect { subject.update(max_rate: 5) }
|
53
|
+
.to change { subject.sync_status }
|
54
|
+
expect(subject.sync_flag?(:dirtyAttributes)).to be_truthy
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,269 @@
|
|
1
|
+
RSpec.shared_examples_for('a common stateful model') do
|
2
|
+
context 'class methods' do
|
3
|
+
# Helper to ensure tests don't interfere with each other
|
4
|
+
before(:each) do
|
5
|
+
described_class.instance_variable_set(:@instances, nil)
|
6
|
+
end
|
7
|
+
|
8
|
+
context '#[]' do
|
9
|
+
it 'should create a new instance when the key doesn\'t exist' do
|
10
|
+
expect { described_class['test'] }.to change { described_class.instances.count }.from(0).to(1)
|
11
|
+
expect(described_class['test']).to be_instance_of(described_class)
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'should return an existing instance when the key already exists' do
|
15
|
+
dbl = double(described_class)
|
16
|
+
described_class.instances['just^keyed'] = dbl
|
17
|
+
expect(described_class['just^keyed']).to eq(dbl)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
context '#exists?' do
|
22
|
+
it 'should be true when an instance matching the key exists' do
|
23
|
+
described_class.instances['test'] = double(described_class)
|
24
|
+
expect(described_class.exists?('test')).to be_truthy
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'should be false when there is no instance matching the key' do
|
28
|
+
expect(described_class.exists?('other')).to be_falsey
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
context '#instances' do
|
33
|
+
it 'should default to an empty hash' do
|
34
|
+
expect(described_class.instances).to be_kind_of(Hash)
|
35
|
+
expect(described_class.instances).to be_empty
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
context '#active?' do
|
41
|
+
it 'should use it\'s expiration time against the presence instance' do
|
42
|
+
expect(described_class).to receive(:current_expiration_threshold).and_return(137)
|
43
|
+
expect(subject.presence).to receive(:visible_since?).with(137)
|
44
|
+
|
45
|
+
subject.active?
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'should pass the presence result back' do
|
49
|
+
expect(subject.presence).to receive(:visible_since?).and_return(false)
|
50
|
+
expect(subject.active?).to be_falsey
|
51
|
+
|
52
|
+
expect(subject.presence).to receive(:visible_since?).and_return(true)
|
53
|
+
expect(subject.active?).to be_truthy
|
54
|
+
end
|
55
|
+
|
56
|
+
it 'should have an expiration time' do
|
57
|
+
expect(described_class).to respond_to(:current_expiration_threshold)
|
58
|
+
expect(described_class.current_expiration_threshold).to be_kind_of(Numeric)
|
59
|
+
end
|
60
|
+
|
61
|
+
it 'should have an expiration in the past' do
|
62
|
+
expect(described_class.current_expiration_threshold).to be <= Time.now.to_i
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
context '#data_dirty?' do
|
67
|
+
it 'should be false when no status flags have been set' do
|
68
|
+
subject.sync_status = PatronusFati::SYNC_FLAGS[:unsynced]
|
69
|
+
expect(subject.data_dirty?).to be_falsey
|
70
|
+
end
|
71
|
+
|
72
|
+
it 'should be false when only sync status flags are set' do
|
73
|
+
subject.sync_status = PatronusFati::SYNC_FLAGS[:syncedOffline]
|
74
|
+
expect(subject.data_dirty?).to be_falsey
|
75
|
+
|
76
|
+
subject.sync_status = PatronusFati::SYNC_FLAGS[:syncedOnline]
|
77
|
+
expect(subject.data_dirty?).to be_falsey
|
78
|
+
end
|
79
|
+
|
80
|
+
it 'should be true when the attributes have been marked dirty' do
|
81
|
+
subject.sync_status = PatronusFati::SYNC_FLAGS[:dirtyAttributes]
|
82
|
+
expect(subject.data_dirty?).to be_truthy
|
83
|
+
end
|
84
|
+
|
85
|
+
it 'should be true when a child has been marked dirty' do
|
86
|
+
subject.sync_status = PatronusFati::SYNC_FLAGS[:dirtyChildren]
|
87
|
+
expect(subject.data_dirty?).to be_truthy
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
context '#diagnostic_data' do
|
92
|
+
it 'should be a hash' do
|
93
|
+
expect(subject.diagnostic_data).to be_kind_of(Hash)
|
94
|
+
end
|
95
|
+
|
96
|
+
it 'should include the raw sync_status value' do
|
97
|
+
data = subject.diagnostic_data
|
98
|
+
expect(data.keys).to include(:sync_status)
|
99
|
+
expect(data[:sync_status]).to eql(subject.sync_status)
|
100
|
+
end
|
101
|
+
|
102
|
+
it 'should include the raw presence information' do
|
103
|
+
data = subject.diagnostic_data
|
104
|
+
expect(data.keys).to include(:current_presence)
|
105
|
+
expect(data.keys).to include(:last_presence)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
context '#dirty?' do
|
110
|
+
it 'should be dirty when it\'s new' do
|
111
|
+
expect(subject).to receive(:new?).and_return(true)
|
112
|
+
|
113
|
+
expect(subject.dirty?).to be_truthy
|
114
|
+
end
|
115
|
+
|
116
|
+
it 'should be dirty if data has changed' do
|
117
|
+
expect(subject).to receive(:new?).and_return(false)
|
118
|
+
expect(subject).to receive(:data_dirty?).and_return(true)
|
119
|
+
|
120
|
+
expect(subject.dirty?).to be_truthy
|
121
|
+
end
|
122
|
+
|
123
|
+
it 'should be dirty if the sync status doesn\'t match or current status' do
|
124
|
+
expect(subject).to receive(:new?).and_return(false)
|
125
|
+
expect(subject).to receive(:data_dirty?).and_return(false)
|
126
|
+
expect(subject).to receive(:status_dirty?).and_return(true)
|
127
|
+
|
128
|
+
expect(subject.dirty?).to be_truthy
|
129
|
+
end
|
130
|
+
|
131
|
+
it 'should not be dirty when nothing has changed' do
|
132
|
+
expect(subject).to receive(:new?).and_return(false)
|
133
|
+
expect(subject).to receive(:data_dirty?).and_return(false)
|
134
|
+
expect(subject).to receive(:status_dirty?).and_return(false)
|
135
|
+
|
136
|
+
expect(subject.dirty?).to be_falsey
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
context '#initialize' do
|
141
|
+
it 'should initialize sync_status to zero' do
|
142
|
+
expect(subject.sync_status).to eql(0)
|
143
|
+
end
|
144
|
+
|
145
|
+
it 'should initialize a new presence instance' do
|
146
|
+
expect(subject.presence).to be_kind_of(PatronusFati::Presence)
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
context '#mark_synced' do
|
151
|
+
it 'should set the sync status to syncedOnline when active' do
|
152
|
+
expect(subject).to receive(:active?).and_return(true)
|
153
|
+
expect { subject.mark_synced }
|
154
|
+
.to change { subject.sync_flag?(:syncedOnline) }.from(false).to(true)
|
155
|
+
end
|
156
|
+
|
157
|
+
it 'should set the sync status to syncedOffline when not active' do
|
158
|
+
expect(subject).to receive(:active?).and_return(false)
|
159
|
+
expect { subject.mark_synced }
|
160
|
+
.to change { subject.sync_flag?(:syncedOffline) }.from(false).to(true)
|
161
|
+
end
|
162
|
+
|
163
|
+
it 'should clear the dirty attribute flags' do
|
164
|
+
subject.set_sync_flag(:dirtyAttributes)
|
165
|
+
expect { subject.mark_synced }
|
166
|
+
.to change { subject.sync_flag?(:dirtyAttributes) }.from(true).to(false)
|
167
|
+
|
168
|
+
subject.set_sync_flag(:dirtyChildren)
|
169
|
+
expect { subject.mark_synced }
|
170
|
+
.to change { subject.sync_flag?(:dirtyChildren) }.from(true).to(false)
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
context 'new?' do
|
175
|
+
it 'should be true when unsynced' do
|
176
|
+
expect(subject.sync_status).to eql(PatronusFati::SYNC_FLAGS[:unsynced])
|
177
|
+
expect(subject.new?).to be_truthy
|
178
|
+
end
|
179
|
+
|
180
|
+
it 'should be false when syncedOnline is set' do
|
181
|
+
subject.set_sync_flag(:syncedOnline)
|
182
|
+
expect(subject.new?).to be_falsey
|
183
|
+
end
|
184
|
+
|
185
|
+
it 'should be false when syncedOffline is set' do
|
186
|
+
subject.set_sync_flag(:syncedOffline)
|
187
|
+
expect(subject.new?).to be_falsey
|
188
|
+
end
|
189
|
+
|
190
|
+
it 'should be true when just the dirty data attributes are set' do
|
191
|
+
expect(subject.sync_status).to eql(PatronusFati::SYNC_FLAGS[:unsynced])
|
192
|
+
|
193
|
+
subject.set_sync_flag(:dirtyAttributes)
|
194
|
+
subject.set_sync_flag(:dirtyChildren)
|
195
|
+
|
196
|
+
expect(subject.new?).to be_truthy
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
context '#presence' do
|
201
|
+
it { expect(subject).to respond_to(:presence) }
|
202
|
+
it { expect(subject.presence).to be_instance_of(PatronusFati::Presence) }
|
203
|
+
end
|
204
|
+
|
205
|
+
context '#set_sync_flag' do
|
206
|
+
it 'should not change the value when it\'s already set' do
|
207
|
+
subject.set_sync_flag(:syncedOnline)
|
208
|
+
expect { subject.set_sync_flag(:syncedOnline) }
|
209
|
+
.to_not change { subject.sync_status }
|
210
|
+
end
|
211
|
+
|
212
|
+
it 'should set the flag if it\'s not already set' do
|
213
|
+
expect(subject.sync_flag?(:dirtyAttributes)).to be_falsey
|
214
|
+
subject.set_sync_flag(:dirtyAttributes)
|
215
|
+
expect(subject.sync_flag?(:dirtyAttributes)).to be_truthy
|
216
|
+
end
|
217
|
+
|
218
|
+
it 'should not change other flags when being set' do
|
219
|
+
subject.set_sync_flag(:dirtyChildren)
|
220
|
+
subject.set_sync_flag(:syncedOnline)
|
221
|
+
|
222
|
+
expect(subject.sync_flag?(:dirtyChildren)).to be_truthy
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
226
|
+
context '#status_dirty?' do
|
227
|
+
it 'should be true when inactive and marked as active' do
|
228
|
+
expect(subject).to receive(:active?).and_return(false)
|
229
|
+
subject.set_sync_flag(:syncedOnline)
|
230
|
+
expect(subject.status_dirty?).to be_truthy
|
231
|
+
end
|
232
|
+
|
233
|
+
it 'should be true when active and marked as inactive' do
|
234
|
+
expect(subject).to receive(:active?).and_return(true)
|
235
|
+
subject.set_sync_flag(:syncedOffline)
|
236
|
+
expect(subject.status_dirty?).to be_truthy
|
237
|
+
end
|
238
|
+
|
239
|
+
it 'should be false when status and marking are active' do
|
240
|
+
expect(subject).to receive(:active?).and_return(true)
|
241
|
+
subject.set_sync_flag(:syncedOnline)
|
242
|
+
expect(subject.status_dirty?).to be_falsey
|
243
|
+
end
|
244
|
+
|
245
|
+
it 'should be false when status and marking are inactive' do
|
246
|
+
expect(subject).to receive(:active?).and_return(false)
|
247
|
+
subject.set_sync_flag(:syncedOffline)
|
248
|
+
expect(subject.status_dirty?).to be_falsey
|
249
|
+
end
|
250
|
+
end
|
251
|
+
|
252
|
+
context '#sync_flag?' do
|
253
|
+
it 'should be true when the provided flag is set' do
|
254
|
+
expect { subject.set_sync_flag(:dirtyAttributes) }
|
255
|
+
.to change { subject.sync_flag?(:dirtyAttributes) }.from(false).to(true)
|
256
|
+
end
|
257
|
+
|
258
|
+
it 'should be false when the provided flag isn\'t set' do
|
259
|
+
subject.sync_status = 0
|
260
|
+
expect(subject.sync_flag?(:dirtyChildren)).to be_falsey
|
261
|
+
end
|
262
|
+
|
263
|
+
it 'should be false when just another flag is set' do
|
264
|
+
subject.sync_status = 0
|
265
|
+
subject.set_sync_flag(:dirtyAttributes)
|
266
|
+
expect(subject.sync_flag?(:syncedOffline)).to be_falsey
|
267
|
+
end
|
268
|
+
end
|
269
|
+
end
|