pokan 0.1.0rc1

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