pokan 0.1.0rc1

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.
@@ -0,0 +1,67 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ describe Pokan::Query do
4
+ before :all do
5
+ Pokan::Connection.redis.flushall
6
+ @query = Pokan::Query.new(Pokan::Peer)
7
+ p1 = Pokan::Peer.new
8
+ p1.address = p1.udp_port = '1'
9
+ p1.act_as_seed
10
+ p1.kill
11
+ p1.save
12
+ p2 = Pokan::Peer.new
13
+ p2.address = p2.udp_port = '2'
14
+ p2.act_as_seed
15
+ p2.save
16
+ p3 = Pokan::Peer.new
17
+ p3.address = p3.udp_port = '3'
18
+ p3.save
19
+ p4 = Pokan::Peer.new
20
+ p4.address = p4.udp_port = '4'
21
+ p4.kill
22
+ p4.save
23
+ end
24
+
25
+ context 'when getting all peers' do
26
+ it 'should return all alive peers' do
27
+ peers = @query.where(role:'peer', status:'alive').collect { |p| p.id }
28
+ peers.should =~ ['3:3']
29
+ end
30
+
31
+ it 'should return all alive seeds' do
32
+ peers = @query.where(role:'seed', status:'alive').collect { |p| p.id }
33
+ peers.should == ['2:2']
34
+ end
35
+ end
36
+
37
+ context 'when search a specific peer' do
38
+ it'should return a empty array if it does not exist' do
39
+ peers = @query.where(id:'5:5').collect! { |p| p.id }
40
+ peers.should == []
41
+ end
42
+
43
+ it 'should return the peer, if it exists' do
44
+ peers = @query.where(id:'4:4').collect! { |p| p.id }
45
+ peers.should == ['4:4']
46
+ end
47
+ end
48
+
49
+ context 'when choosing a peer' do
50
+ it 'should return a random seed' do
51
+ seeds = @query.where(role:'seed').collect! { |p| p.id }
52
+ id = @query.where(random:true, role:'seed')[0].id
53
+ seeds.include?(id).should be_true
54
+ end
55
+
56
+ it 'should return a random peer' do
57
+ peers = @query.where(role:'peer').collect! { |p| p.id }
58
+ id = @query.where(random:true, role:'peer')[0].id
59
+ peers.include?(id).should be_true
60
+ end
61
+
62
+ it 'should not return the peer with the specific id' do
63
+ peers = @query.where(:not => ['1:1', '2:2', '3:3', '4:4'])
64
+ peers.include?('1:1').should be_false
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,253 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+
4
+ describe Pokan::RequestHandler do
5
+ before :all do
6
+ Pokan::RequestHandler.address = 'ip'
7
+ Pokan::RequestHandler.port = '1111'
8
+ @req_handler = Pokan::RequestHandler.new nil
9
+
10
+ class Pokan::RequestHandler
11
+ def send_datagram a, b, c
12
+ nil
13
+ end
14
+ end
15
+ # @req_handler.stub!(:send_datagram).and_return(0)
16
+ end
17
+
18
+ before :each do
19
+ Pokan::Connection.redis.flushall
20
+ @req_handler.merge({
21
+ p1: {
22
+ status: { value: 'alive', timestamp: 100 },
23
+ role: { value: 'peer', timestamp: 100 },
24
+ answer: { value: 'FALSE', timestamp: 100 },
25
+ foo: { value: 'baz', timestamp: 101 }
26
+ },
27
+ p2: {
28
+ status: { value: 'alive', timestamp: 100 },
29
+ role: { value: 'peer', timestamp: 100 },
30
+ answer: { value: '+/-', timestamp: 102 },
31
+ foo: { value: 'baz', timestamp: 103 }
32
+ }
33
+ })
34
+ end
35
+
36
+ context 'when merging data' do
37
+ it 'should merge with a new set preserving newer values' do
38
+ @req_handler.merge({
39
+ p2: { answer: { value: 'GREAT', timestamp: 200 },
40
+ foo: {value: 'null', timestamp: 50 }
41
+ }
42
+ })
43
+ query = Pokan::Query.new(Pokan::Peer)
44
+ peer2 = query.where(id:'p2')[0]
45
+ peer2.value(:answer).should == 'GREAT'
46
+ end
47
+
48
+ it 'should not merge with a older set preserving newer values' do
49
+ @req_handler.merge({
50
+ p2: {
51
+ answer: {value: 'GREAT', timestamp: 200},
52
+ foo: {value: 'null', timestamp: 50}
53
+ }
54
+ })
55
+ query = Pokan::Query.new(Pokan::Peer)
56
+ peer2 = query.where(id:'p2')[0]
57
+ peer2.value(:foo).should == 'baz'
58
+ end
59
+
60
+ it 'should not merge a key of a dead peer' do
61
+ # need a better test for this
62
+ query = Pokan::Query.new(Pokan::Peer)
63
+ peer = query.where(id:'p2')[0]
64
+ peer.kill
65
+ @req_handler.merge({
66
+ p2: { answer: { value: 'GREAT', timestamp: 200 },
67
+ foo: { value: 'null', timestamp: 50 }
68
+ }
69
+ })
70
+ peer.dead?.should be_true
71
+ end
72
+ end
73
+
74
+ context 'when checking the given keys' do
75
+ it 'should return all older keys of all peers' do
76
+ keys = { p1: { answer: 100, foo: 300 } }
77
+ older = @req_handler.older(keys)
78
+ older.should == { p1: [:foo] }
79
+ end
80
+
81
+ it 'should return all newer key/value/timestamps of all peers' do
82
+ keys = { p1: { answer: 109, foo: 91 } }
83
+ newer = @req_handler.newer(keys)
84
+ newer.should == { p1: { foo: { value: 'baz', timestamp: 101 } } }
85
+ end
86
+ end
87
+
88
+ context 'when returning its newer data' do
89
+ it 'should retrieve all requested keys/values/timestamps' do
90
+ query = Pokan::Query.new(Pokan::Peer)
91
+ query.where(id:'p1')[0].kill.save
92
+
93
+ request = { p1: [:status], p2: [:foo] }
94
+ data = @req_handler.retrieve(request)
95
+ data.should == {
96
+ p2: { foo: { value: 'baz', timestamp: 103 } }
97
+ }
98
+ end
99
+ end
100
+
101
+
102
+ context 'when generating a pull message' do
103
+ before :each do
104
+ query = Pokan::Query.new(Pokan::Peer)
105
+ @req_handler.merge({
106
+ p10: {
107
+ answer: { value: '+/-', timestamp: 102 },
108
+ foo: { value: 'baz', timestamp: 103 }
109
+ }
110
+ })
111
+ query.where(id:'p10')[0].store(:role, 'seed', 99317585671)
112
+ query.where(id:'p1')[0].store(:status, 'dead', 99317585671)
113
+ query.where(id:'p2')[0].store(:role, 'peer', 99317585671)
114
+ digest = {
115
+ action: 'digest',
116
+ origin: 'ip:1111',
117
+ data: { keys: { p1: { answer: 12.0, foo: 123.0 } } }
118
+ }.to_json
119
+ pull_json = @req_handler.receive_data(digest)
120
+ @pull = JSON.parse(pull_json)
121
+ end
122
+
123
+ it 'should set action as pull' do
124
+ @pull['action'].should == 'pull'
125
+ end
126
+
127
+ it 'should set the newer keys from all peers with values and timestamps' do
128
+ @pull['data']['newer'] == {
129
+ 'p1' => { 'answer' => { 'value' => 'FALSE', 'timestamp' => 100.0 } },
130
+ 'p10' => { 'answer' => { 'value' => '+/-', 'timestamp' => 102.0 },
131
+ 'foo' => { 'value' => 'baz', 'timestamp' => 103.0 } }
132
+ }
133
+ end
134
+
135
+ it 'should set the older keys from all peers in arrays' do
136
+ @pull['data']['older'] == {'older' => {'p1' => ['foo']}}
137
+ end
138
+ end
139
+
140
+ context 'when generating a push message' do
141
+ before :each do
142
+ @req_handler.merge({
143
+ p10: {
144
+ status: { value: 'alive', timestamp: 101 },
145
+ role: { value: 'seed', timestamp: 101 },
146
+ answer: { value: '+/-', timestamp: 102 },
147
+ foo: { value: 'baz', timestamp: 103 }
148
+ },
149
+ p1: {
150
+ status: { value: 'alive', timestamp: 101 },
151
+ role: { value: 'peer', timestamp: 101 }
152
+ },
153
+ p2: {
154
+ status: { value: 'dead', timestamp: 101 },
155
+ role: { value: 'peer', timestamp: 101 }
156
+ }
157
+ })
158
+ pull_message = {
159
+ action: 'pull', origin: 'ip:1111',
160
+ data: {
161
+ newer: { p1: { status: { value: 'FALSE', timestamp: 1000.0 } },
162
+ p10: { answer: { value: '+/-', timestamp: 1020.0 } } },
163
+ older: { p1: [:foo], p10: [:foo]}
164
+ }
165
+ }
166
+ push_message = @req_handler.receive_data(JSON.generate(pull_message))
167
+ @push = JSON.parse(push_message)
168
+ end
169
+
170
+ it 'should set action as push' do
171
+ @push['action'].should == 'push'
172
+ end
173
+
174
+ it 'should set the requested keys from all peers with values and timestamps' do
175
+ @push['data'].should == {
176
+ 'p1' => {'foo' => {'value' => 'baz', 'timestamp' => 101.0}},
177
+ 'p10' => {'foo' => {'value' => 'baz', 'timestamp' => 103.0}}
178
+ }
179
+ end
180
+ end
181
+
182
+ context 'when interpreting messages' do
183
+ context 'when receiving a pull message' do
184
+ before :each do
185
+ pull_message = {
186
+ action: 'pull', origin: 'ip:1111',
187
+ data: {
188
+ newer: {
189
+ p1: { status: { value: 'dead', timestamp: 100000.0 },
190
+ answer: { value: 'ZOMG!!', timestamp: 1000.0 } },
191
+ p10: { answer: { value: 'Hi!!', timestamp: 1000.0 } }
192
+ },
193
+ older: { p1: ['foo'] }
194
+ }
195
+ }
196
+ @req_handler.receive_data(pull_message.to_json)
197
+ end
198
+
199
+ it 'should kill the peer if the timestamp is greater than the stored' do
200
+ Pokan::Query.new(Pokan::Peer).where(id: 'p1')[0].value(:status).should == 'dead'
201
+ end
202
+
203
+ it 'should create the peer locally if it does not exist' do
204
+ Pokan::Query.new(Pokan::Peer).where(id: 'p10')[0].value(:role).should == 'peer'
205
+ end
206
+
207
+ it 'should update the keys with the newers in message' do
208
+ Pokan::Query.new(Pokan::Peer).where(id: 'p1')[0].value(:answer).should == 'ZOMG!!'
209
+ end
210
+
211
+ it 'should update the timestamp of keys with the newers in message' do
212
+ Pokan::Query.new(Pokan::Peer).where(id: 'p1')[0].timestamp(:answer).to_f.should == 1000.0
213
+ end
214
+ end
215
+
216
+ context 'when receiving a push message' do
217
+ before :each do
218
+ push_message = {
219
+ 'action' => 'push',
220
+ 'origin' => 'ip:1111',
221
+ 'data' => {
222
+ 'p1' => {'answer' => {'value' => 'ZOMG!!', 'timestamp' => 1000.0}},
223
+ 'p2' => {'status' => {'value' => 'dead', 'timestamp' => 1000.0}},
224
+ 'p10' => {'role' => {'value' => 'peer', 'timestamp' => 1000.0}}
225
+ }
226
+ }
227
+ @req_handler.receive_data(JSON.generate(push_message))
228
+ end
229
+
230
+ it 'should kill the peer if the timestamp is greater than the stored' do
231
+ peer = Pokan::Query.new(Pokan::Peer).where(id:'p2')[0]
232
+ peer.value(:status).should == 'dead'
233
+ end
234
+
235
+ it 'should create the peer locally if it does not exist' do
236
+ peer = Pokan::Query.new(Pokan::Peer).where(id:'p10')[0]
237
+ peer.value(:role).should == 'peer'
238
+ end
239
+
240
+ it 'should update the keys with the newers in message' do
241
+ peer = Pokan::Query.new(Pokan::Peer).where(id:'p1')[0]
242
+ peer.value(:answer).should == 'ZOMG!!'
243
+ end
244
+
245
+ it 'should update the timestamp of keys with the newers in message' do
246
+ peer = Pokan::Query.new(Pokan::Peer).where(id:'p1')[0]
247
+ peer.timestamp(:answer).to_f.should == 1000.0
248
+ end
249
+ end
250
+
251
+
252
+ end
253
+ end
@@ -0,0 +1,81 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ describe Pokan::ServerMessages do
4
+ before :all do
5
+ class Messenger
6
+ include Pokan::ServerMessages
7
+ attr_accessor :address, :port
8
+ end
9
+ @messenger = Messenger.new
10
+ @messenger.port = 'port'
11
+ @messenger.address = 'ip'
12
+ Pokan::RequestHandler.address = 'ip'
13
+ Pokan::RequestHandler.port = 'port'
14
+ @req_handler = Pokan::RequestHandler.new nil
15
+ end
16
+
17
+ before :each do
18
+ Pokan::Connection.redis.flushall
19
+ @req_handler.merge({
20
+ p1: {
21
+ status: { value: 'alive', timestamp: 100 },
22
+ role: { value: 'peer', timestamp: 100 },
23
+ answer: { value: 'FALSE', timestamp: 100 },
24
+ foo: { value: 'baz', timestamp: 101 }
25
+ },
26
+ p2: {
27
+ status: { value: 'alive', timestamp: 100 },
28
+ role: { value: 'peer', timestamp: 100 },
29
+ answer: { value: '+/-', timestamp: 102 },
30
+ foo: { value: 'baz', timestamp: 103 }
31
+ }
32
+ })
33
+ end
34
+
35
+ context 'when generating a digest message' do
36
+ before :each do
37
+ @digest = JSON.parse(@messenger.digest_message)
38
+ end
39
+
40
+ it 'should set action as digest' do
41
+ @digest['action'].should == 'digest'
42
+ end
43
+
44
+ it 'should set the timestamps of all keys of all known ids' do
45
+ @digest['data'].should == {
46
+ 'p1' => { 'answer' => 100, 'foo' => 101, 'status' => 100, 'role' => 100 },
47
+ 'p2' => { 'answer' => 102, 'foo' => 103, 'status' => 100, 'role' => 100 }
48
+ }
49
+ end
50
+ end
51
+
52
+ context 'when receiving a hello message' do
53
+ it 'should create the peer and set its role' do
54
+ hello_message = {
55
+ 'action' => 'hello',
56
+ 'role' => 'peer',
57
+ 'origin' => 'ip:port'
58
+ }
59
+ @req_handler.receive_data(hello_message.to_json)
60
+ peer = Pokan::Query.new(Pokan::Peer).where(id:'ip:port')[0]
61
+ peer.value(:role).should == 'peer'
62
+ end
63
+ end
64
+
65
+ context 'when receiving a goodbye message' do
66
+ it 'should kill the origin peer' do
67
+ peer = Pokan::Peer.new
68
+ peer.address = 'ip'
69
+ peer.udp_port = 'port'
70
+ peer.save
71
+ goodbye_message = {
72
+ 'action' => 'goodbye',
73
+ 'timestamp' => 97317585671,
74
+ 'origin' => 'ip:port'
75
+ }
76
+ @req_handler.receive_data(goodbye_message.to_json)
77
+ peer = Pokan::Query.new(Pokan::Peer).where(id: 'ip:port')[0]
78
+ peer.value(:status).should == 'dead'
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,46 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ describe Pokan::Server do
4
+
5
+ before :each do
6
+ Pokan::Connection.redis.flushall
7
+ end
8
+
9
+ context 'when server is being configured manually' do
10
+
11
+ before { @server = Pokan::Server.new('10.10.10.8') }
12
+
13
+ it 'should gossip every specified interval' do
14
+ @server.gossip_every(3)
15
+ @server.gossip_interval.should == 3
16
+ end
17
+
18
+ it 'should use epoll when it is told to' do
19
+ @server.use_epoll
20
+ @server.using_epoll?.should be_true
21
+ end
22
+
23
+ end
24
+
25
+ context 'when behavior is added to the server' do
26
+
27
+ before { @server = Pokan::Server.new('10.10.10.8') }
28
+
29
+ it 'should properly call registered callbacks on events' do
30
+ @block_called = false
31
+ add_behavior_to_event(:new_key)
32
+
33
+ @server.store 'k', 'v'
34
+
35
+ @block_called.should be_true
36
+ end
37
+
38
+ def add_behavior_to_event(event)
39
+ @server.on event do |key, value|
40
+ @block_called = true
41
+ end
42
+ end
43
+
44
+ end
45
+
46
+ end