ruby_skynet 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +13 -0
- data/Gemfile.lock +49 -0
- data/LICENSE.txt +201 -0
- data/README.md +119 -0
- data/Rakefile +40 -0
- data/lib/ruby_skynet/client.rb +293 -0
- data/lib/ruby_skynet/doozer/client.rb +202 -0
- data/lib/ruby_skynet/doozer/exceptions.rb +5 -0
- data/lib/ruby_skynet/doozer/msg.pb.rb +118 -0
- data/lib/ruby_skynet/exceptions.rb +6 -0
- data/lib/ruby_skynet/version.rb +3 -0
- data/lib/ruby_skynet.rb +11 -0
- data/nbproject/private/config.properties +0 -0
- data/nbproject/private/private.properties +1 -0
- data/nbproject/private/rake-d.txt +4 -0
- data/nbproject/project.properties +6 -0
- data/nbproject/project.xml +15 -0
- data/skynet-0.1.0.gem +0 -0
- data/test/doozer_client_test.rb +73 -0
- data/test/ruby_skynet_client_test.rb +77 -0
- data/test/simple_server.rb +134 -0
- data/test.log +5887 -0
- metadata +131 -0
@@ -0,0 +1,73 @@
|
|
1
|
+
# Allow test to be run in-place without requiring a gem install
|
2
|
+
$LOAD_PATH.unshift File.dirname(__FILE__) + '/../lib'
|
3
|
+
|
4
|
+
require 'rubygems'
|
5
|
+
require 'test/unit'
|
6
|
+
require 'shoulda'
|
7
|
+
require 'ruby_skynet/doozer/client'
|
8
|
+
|
9
|
+
SemanticLogger::Logger.default_level = :trace
|
10
|
+
SemanticLogger::Logger.appenders << SemanticLogger::Appender::File.new('test.log')
|
11
|
+
|
12
|
+
# NOTE:
|
13
|
+
# This test assumes that doozerd is running locally on the default port of 8046
|
14
|
+
|
15
|
+
# Unit Test for RubySkynet::Doozer::Client
|
16
|
+
class DoozerClientTest < Test::Unit::TestCase
|
17
|
+
context RubySkynet::Doozer::Client do
|
18
|
+
|
19
|
+
context "without server" do
|
20
|
+
should "raise exception when cannot reach doozer server after 5 retries" do
|
21
|
+
exception = assert_raise ResilientSocket::ConnectionFailure do
|
22
|
+
RubySkynet::Doozer::Client.new(
|
23
|
+
# Bad server address to test exception is raised
|
24
|
+
:server => 'localhost:9999',
|
25
|
+
:connect_retry_interval => 0.1,
|
26
|
+
:connect_retry_count => 5)
|
27
|
+
end
|
28
|
+
assert_match /After 5 attempts: Errno::ECONNREFUSED/, exception.message
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
|
33
|
+
context "with client connection" do
|
34
|
+
setup do
|
35
|
+
@client = RubySkynet::Doozer::Client.new(:server => 'localhost:8046')
|
36
|
+
end
|
37
|
+
|
38
|
+
def teardown
|
39
|
+
if @client
|
40
|
+
@client.close
|
41
|
+
@client.delete('/test/foo')
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
should "return current revision" do
|
46
|
+
assert @client.current_revision >= 0
|
47
|
+
end
|
48
|
+
|
49
|
+
should "successfully set and get data" do
|
50
|
+
new_revision = @client.set('/test/foo', 'value')
|
51
|
+
result = @client.get('/test/foo')
|
52
|
+
assert_equal 'value', result.value
|
53
|
+
assert_equal new_revision, result.rev
|
54
|
+
end
|
55
|
+
|
56
|
+
should "successfully set and get data using array operators" do
|
57
|
+
@client['/test/foo'] = 'value2'
|
58
|
+
result = @client['/test/foo']
|
59
|
+
assert_equal 'value2', result
|
60
|
+
end
|
61
|
+
|
62
|
+
should "fetch directories in a path" do
|
63
|
+
@path = '/'
|
64
|
+
count = 0
|
65
|
+
until @client.directory(@path, count).nil?
|
66
|
+
count += 1
|
67
|
+
end
|
68
|
+
assert count > 0
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
# Allow test to be run in-place without requiring a gem install
|
2
|
+
$LOAD_PATH.unshift File.dirname(__FILE__) + '/../lib'
|
3
|
+
$LOAD_PATH.unshift File.dirname(__FILE__)
|
4
|
+
|
5
|
+
require 'rubygems'
|
6
|
+
require 'test/unit'
|
7
|
+
require 'shoulda'
|
8
|
+
require 'ruby_skynet'
|
9
|
+
require 'simple_server'
|
10
|
+
|
11
|
+
SemanticLogger::Logger.default_level = :trace
|
12
|
+
SemanticLogger::Logger.appenders << SemanticLogger::Appender::File.new('test.log')
|
13
|
+
|
14
|
+
# Unit Test for ResilientSocket::TCPClient
|
15
|
+
class RubySkynetClientTest < Test::Unit::TestCase
|
16
|
+
context RubySkynet::Client do
|
17
|
+
|
18
|
+
context "without server" do
|
19
|
+
should "raise exception when cannot reach server after 5 retries" do
|
20
|
+
exception = assert_raise ResilientSocket::ConnectionFailure do
|
21
|
+
RubySkynet::Client.new('SomeService',
|
22
|
+
:server => 'localhost:3300',
|
23
|
+
:connect_retry_interval => 0.1,
|
24
|
+
:connect_retry_count => 5)
|
25
|
+
end
|
26
|
+
assert_match /After 5 attempts: Errno::ECONNREFUSED/, exception.message
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
context "with server" do
|
32
|
+
setup do
|
33
|
+
@read_timeout = 3.0
|
34
|
+
@server = SimpleServer.new(2000)
|
35
|
+
@server_name = 'localhost:2000'
|
36
|
+
end
|
37
|
+
|
38
|
+
teardown do
|
39
|
+
@server.stop if @server
|
40
|
+
end
|
41
|
+
|
42
|
+
context "using blocks" do
|
43
|
+
should "call server" do
|
44
|
+
RubySkynet::Client.connect('TutorialService', :read_timeout => @read_timeout, :server => @server_name) do |tutorial_service|
|
45
|
+
assert_equal 'test1', tutorial_service.call(:test1, 'some' => 'parameters')['result']
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
context "with client connection" do
|
51
|
+
setup do
|
52
|
+
@client = RubySkynet::Client.new('TutorialService', :read_timeout => @read_timeout, :server => @server_name)
|
53
|
+
end
|
54
|
+
|
55
|
+
def teardown
|
56
|
+
@client.close if @client
|
57
|
+
end
|
58
|
+
|
59
|
+
should "successfully send and receive data" do
|
60
|
+
reply = @client.call(:test1, 'some' => 'parameters')
|
61
|
+
assert_equal 'test1', reply['result']
|
62
|
+
end
|
63
|
+
|
64
|
+
should "timeout on receive" do
|
65
|
+
request = { 'duration' => @read_timeout + 0.5}
|
66
|
+
|
67
|
+
exception = assert_raise ResilientSocket::ReadTimeout do
|
68
|
+
# Read 4 bytes from server
|
69
|
+
@client.call('sleep', request)
|
70
|
+
end
|
71
|
+
assert_match /Timedout after #{@read_timeout} seconds trying to read/, exception.message
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,134 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'socket'
|
3
|
+
require 'bson'
|
4
|
+
require 'semantic_logger'
|
5
|
+
|
6
|
+
# This a simple stand-alone server that does not use the Skynet code so that
|
7
|
+
# the Skynet code can be tested
|
8
|
+
|
9
|
+
|
10
|
+
# Read the bson document, returning nil if the IO is closed
|
11
|
+
# before receiving any data or a complete BSON document
|
12
|
+
def read_bson_document(io)
|
13
|
+
bytebuf = BSON::ByteBuffer.new
|
14
|
+
# Read 4 byte size of following BSON document
|
15
|
+
bytes = io.read(4)
|
16
|
+
return unless bytes
|
17
|
+
# Read BSON document
|
18
|
+
sz = bytes.unpack("V")[0]
|
19
|
+
bytebuf.append!(bytes)
|
20
|
+
bytes = io.read(sz-4)
|
21
|
+
return unless bytes
|
22
|
+
bytebuf.append!(bytes)
|
23
|
+
return BSON.deserialize(bytebuf)
|
24
|
+
end
|
25
|
+
|
26
|
+
# Simple single threaded server for testing purposes using a local socket
|
27
|
+
# Sends and receives BSON Messages
|
28
|
+
class SimpleServer
|
29
|
+
attr_reader :thread
|
30
|
+
def initialize(port = 2000)
|
31
|
+
start(port)
|
32
|
+
end
|
33
|
+
|
34
|
+
def start(port)
|
35
|
+
@server = TCPServer.open(port)
|
36
|
+
@logger = SemanticLogger::Logger.new(self.class)
|
37
|
+
|
38
|
+
@thread = Thread.new do
|
39
|
+
loop do
|
40
|
+
@logger.debug "Waiting for a client to connect"
|
41
|
+
|
42
|
+
# Wait for a client to connect
|
43
|
+
on_request(@server.accept)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def stop
|
49
|
+
if @thread
|
50
|
+
@thread.kill
|
51
|
+
@thread.join
|
52
|
+
@thread = nil
|
53
|
+
end
|
54
|
+
begin
|
55
|
+
@server.close if @server
|
56
|
+
rescue IOError
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
# Called for each message received from the client
|
61
|
+
# Returns a Hash that is sent back to the caller
|
62
|
+
def on_message(method, params)
|
63
|
+
case method
|
64
|
+
when 'test1'
|
65
|
+
{ 'result' => 'test1' }
|
66
|
+
when 'sleep'
|
67
|
+
sleep params['duration'] || 1
|
68
|
+
{ 'result' => 'sleep' }
|
69
|
+
when 'fail'
|
70
|
+
if params['attempt'].to_i >= 2
|
71
|
+
{ 'result' => 'fail' }
|
72
|
+
else
|
73
|
+
nil
|
74
|
+
end
|
75
|
+
else
|
76
|
+
{ 'result' => "Unknown method: #{method}" }
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
# Called for each client connection
|
81
|
+
# In a real server each request would be handled in a separate thread
|
82
|
+
def on_request(client)
|
83
|
+
@logger.debug "Client connected, waiting for data from client"
|
84
|
+
|
85
|
+
# Process handshake
|
86
|
+
handshake = {
|
87
|
+
'registered' => true,
|
88
|
+
'clientid' => '123'
|
89
|
+
}
|
90
|
+
client.print(BSON.serialize(handshake))
|
91
|
+
read_bson_document(client)
|
92
|
+
|
93
|
+
while(header = read_bson_document(client)) do
|
94
|
+
@logger.debug "\n******************"
|
95
|
+
@logger.debug "Received Request"
|
96
|
+
@logger.trace 'Header', header
|
97
|
+
|
98
|
+
request = read_bson_document(client)
|
99
|
+
@logger.trace 'Request', request
|
100
|
+
break unless request
|
101
|
+
|
102
|
+
if reply = on_message(request['method'], BSON.deserialize(request['in']))
|
103
|
+
@logger.debug "Sending Header"
|
104
|
+
# For this test we just send back the received header
|
105
|
+
client.print(BSON.serialize(header))
|
106
|
+
|
107
|
+
@logger.debug "Sending Reply"
|
108
|
+
@logger.trace 'Reply', reply
|
109
|
+
client.print(BSON.serialize({'out' => BSON.serialize(reply).to_s}))
|
110
|
+
else
|
111
|
+
@logger.debug "Closing client since no reply is being sent back"
|
112
|
+
@server.close
|
113
|
+
client.close
|
114
|
+
@logger.debug "Server closed"
|
115
|
+
#@thread.kill
|
116
|
+
@logger.debug "thread killed"
|
117
|
+
start(2000)
|
118
|
+
@logger.debug "Server Restarted"
|
119
|
+
break
|
120
|
+
end
|
121
|
+
end
|
122
|
+
# Disconnect from the client
|
123
|
+
client.close
|
124
|
+
@logger.debug "Disconnected from the client"
|
125
|
+
end
|
126
|
+
|
127
|
+
end
|
128
|
+
|
129
|
+
if $0 == __FILE__
|
130
|
+
SemanticLogger::Logger.default_level = :trace
|
131
|
+
SemanticLogger::Logger.appenders << SemanticLogger::Appender::File.new(STDOUT)
|
132
|
+
server = SimpleServer.new(2000)
|
133
|
+
server.thread.join
|
134
|
+
end
|