ruby_skynet 0.1.0

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