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.
@@ -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