ruby_skynet 0.1.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.
- 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
|