socky-server 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,151 @@
1
+ require 'spec_helper'
2
+
3
+ describe Socky::Connection do
4
+
5
+ context "class" do
6
+ it "should have connections array" do
7
+ described_class.connections.should_not be_nil
8
+ described_class.connections.class.should eql(Array)
9
+ end
10
+ it "should have empty connections array at start" do
11
+ described_class.connections.should be_empty
12
+ end
13
+ it "should create instance and assign socket to self" do
14
+ socket = mock(:socket)
15
+ connection = described_class.new(socket)
16
+ connection.socket.should equal(socket)
17
+ end
18
+ end
19
+
20
+ context "instance" do
21
+ before(:each) do
22
+ described_class.connections.clear
23
+ socket = mock(:socket, :request => { "Query" => {}})
24
+ @connection = described_class.new(socket)
25
+ end
26
+
27
+ context "#admin" do
28
+ it "should return true for socket request data admin equal to '1' and 'true'" do
29
+ ["1","true"].each do |value|
30
+ @connection.socket.request["Query"]["admin"] = value
31
+ @connection.admin.should be_true
32
+ end
33
+ end
34
+ it "should return false for socket request data admin equal to anything except '1' and 'true'" do
35
+ [nil,"0","false","abstract"].each do |value|
36
+ @connection.socket.request["Query"]["admin"] = value
37
+ @connection.admin.should be_false
38
+ end
39
+ end
40
+ it "should return false if socket request data is nil" do
41
+ @connection.socket.request["Query"] = nil
42
+ @connection.admin.should be_false
43
+ end
44
+ end
45
+ it "#client should return client_id from socket request data" do
46
+ @connection.socket.request["Query"]["client_id"] = "abstract"
47
+ @connection.client.should eql("abstract")
48
+ end
49
+ it "#secret should return client_secret from socket request data" do
50
+ @connection.socket.request["Query"]["client_secret"] = "abstract"
51
+ @connection.secret.should eql("abstract")
52
+ end
53
+ context "#channels" do
54
+ it "should ba array" do
55
+ @connection.channels.should_not be_nil
56
+ @connection.channels.class.should eql(Array)
57
+ end
58
+ it "should return table with nil if socket request data 'channels' is nil" do
59
+ @connection.socket.request["Query"]["channels"] = nil
60
+ @connection.channels.should eql([nil])
61
+ end
62
+ it "should return table of channels if provided and separated by comma" do
63
+ @connection.socket.request["Query"]["channels"] = "aaa,bbb,ccc"
64
+ @connection.channels.should eql(["aaa","bbb","ccc"])
65
+ end
66
+ it "should not allow empty names of channels" do
67
+ @connection.socket.request["Query"]["channels"] = "aaa,,ccc"
68
+ @connection.channels.should eql(["aaa","ccc"])
69
+ end
70
+ it "should strip trailing spaces in channel names" do
71
+ @connection.socket.request["Query"]["channels"] = " aaa\n , \n , ccc "
72
+ @connection.channels.should eql(["aaa","ccc"])
73
+ end
74
+ end
75
+ it "#subscribe should send subscribe request" do
76
+ @connection.stub!(:subscribe_request)
77
+ @connection.should_receive(:subscribe_request)
78
+ @connection.subscribe
79
+ end
80
+ it "#unsubscribe should send unsubscribe request" do
81
+ @connection.stub!(:unsubscribe_request)
82
+ @connection.should_receive(:unsubscribe_request)
83
+ @connection.unsubscribe
84
+ end
85
+ context "#process_message" do
86
+ it "should send process request to socky message class if connection is by admin" do
87
+ @connection.stub!(:admin).and_return(true)
88
+ Socky::Message.stub!(:process)
89
+ Socky::Message.should_receive(:process).with(@connection, "abstract")
90
+ @connection.process_message("abstract")
91
+ end
92
+ it "should send user message that he can not send messages if connection is not admin" do
93
+ @connection.stub!(:admin).and_return(false)
94
+ @connection.stub!(:send_message)
95
+ @connection.should_receive(:send_message).with("You are not authorized to post messages")
96
+ @connection.process_message("abstract")
97
+ end
98
+ end
99
+ it "#send_message should call #send_data with hashed form of message" do
100
+ @connection.should_receive(:send_data).with({:type => :message, :body => "abstract"})
101
+ @connection.send_message("abstract")
102
+ end
103
+ it "#send_data should send message by socket in json format" do
104
+ @connection.socket.stub!(:send)
105
+ @connection.socket.should_receive(:send).with({:test => "abstract"}.to_json)
106
+ @connection.send(:send_data, {:test => "abstract"})
107
+ end
108
+ it "#disconnect should close connection after writing" do
109
+ @connection.socket.stub!(:close_connection_after_writing)
110
+ @connection.socket.should_receive(:close_connection_after_writing)
111
+ @connection.disconnect
112
+ end
113
+ context "#add_to_pool" do
114
+ it "should add self to class connection list if self isn't already on list or self isn't admin" do
115
+ @connection.stub!(:admin).and_return(false)
116
+ @connection.send(:add_to_pool)
117
+ described_class.connections.should include(@connection)
118
+ described_class.connections.should have(1).item
119
+ end
120
+ it "should not add self to class connection list if self is already on it" do
121
+ described_class.connections << @connection
122
+ described_class.connections.should have(1).item
123
+ @connection.stub!(:admin).and_return(false)
124
+ @connection.send(:add_to_pool)
125
+ described_class.connections.should include(@connection)
126
+ described_class.connections.should have(1).item
127
+ end
128
+ it "should not add self to class connection list if self is admin" do
129
+ @connection.stub!(:admin).and_return(true)
130
+ @connection.send(:add_to_pool)
131
+ described_class.connections.should_not include(@connection)
132
+ end
133
+ end
134
+ it "#remove_from_pool should delete self from class connection list" do
135
+ described_class.connections << @connection
136
+ described_class.connections.should have(1).item
137
+ @connection.send(:remove_from_pool)
138
+ described_class.connections.should_not include(@connection)
139
+ described_class.connections.should have(0).items
140
+ end
141
+ it "#to_json should return self as hash of object_id, client_id and channels" do
142
+ @connection.socket.request["Query"]["client_id"] = "abstract"
143
+ @connection.socket.request["Query"]["channels"] = "first,second,third"
144
+ json = @connection.to_json
145
+ JSON.parse(json).should eql({ "id" => @connection.object_id,
146
+ "client_id" => @connection.client,
147
+ "channels" => @connection.channels})
148
+ end
149
+ end
150
+
151
+ end
@@ -0,0 +1,102 @@
1
+ require 'spec_helper'
2
+
3
+ describe Socky::Message do
4
+ before(:each) do
5
+ @connection = mock(:connection, :name => "connection", :send_message => nil)
6
+ end
7
+
8
+ context "class" do
9
+ context "#process" do
10
+ before(:each) { @message = mock(:message, :process => nil) }
11
+ it "should create new instance of self with provided params" do
12
+ described_class.stub!(:new).and_return(@message)
13
+ described_class.should_receive(:new).with(@connection, "test")
14
+ described_class.process(@connection, "test")
15
+ end
16
+ it "should send #process to instance if valid message" do
17
+ described_class.stub!(:new).and_return(@message)
18
+ @message.should_receive(:process)
19
+ described_class.process(@connection, "test")
20
+ end
21
+ it "should rescue from error if creating instance raise error" do
22
+ described_class.stub!(:new).and_raise(Socky::SockyError)
23
+ lambda{ described_class.process(@connection, "test") }.should_not raise_error
24
+ end
25
+ it "should rescue from error if parsing instance raise error" do
26
+ @message.stub!(:process).and_raise(Socky::SockyError)
27
+ described_class.stub!(:new).and_return(@message)
28
+ lambda{ described_class.process(@connection, "test") }.should_not raise_error
29
+ end
30
+ end
31
+ context "#new" do
32
+ it "should create message with provided creator and message is message is JSON hash" do
33
+ message = described_class.new(@connection, {:test => true}.to_json)
34
+ message.params.should eql(:test => true)
35
+ message.creator.should eql(@connection)
36
+ end
37
+ it "should raise error if message is not JSON" do
38
+ lambda{described_class.new(@connection, {:test => true})}.should raise_error Socky::SockyError
39
+ lambda{described_class.new(@connection, "test")}.should raise_error Socky::SockyError
40
+ end
41
+ it "should raise error if message is JSON but not JSON hash" do
42
+ lambda{described_class.new(@connection, "test".to_json)}.should raise_error Socky::SockyError
43
+ end
44
+ end
45
+ end
46
+
47
+ context "instance" do
48
+ before(:each) { @message = described_class.new(@connection, {}.to_json) }
49
+ context "#process" do
50
+ before(:each) do
51
+ @message.stub!(:send_message)
52
+ end
53
+ it "should raise error if message command is nil" do
54
+ @message.stub!(:params).and_return({:command => nil})
55
+ lambda {@message.process}.should raise_error(Socky::SockyError, "unknown command")
56
+ end
57
+ it "should raise error if message command is neither :broadcast nor :query" do
58
+ @message.stub!(:params).and_return({:command => :invalid})
59
+ lambda {@message.process}.should raise_error(Socky::SockyError, "unknown command")
60
+ end
61
+ it "should not distinguish between string and symbol in command" do
62
+ @message.stub!(:params).and_return({:command => 'broadcast'})
63
+ lambda {@message.process}.should_not raise_error(Socky::SockyError, "unknown command")
64
+ end
65
+ context ":broadcast" do
66
+ it "should select target connections basing on params" do
67
+ @message.stub!(:params).and_return({:command => :broadcast, :some => :abstract})
68
+ Socky::Connection.should_receive(:find).with(:some => :abstract)
69
+ @message.process
70
+ end
71
+ it "should send_message with message body and connection list" do
72
+ @message.stub!(:params).and_return({:command => :broadcast, :body => "some message"})
73
+ Socky::Connection.stub!(:find).and_return(["first","second"])
74
+ @message.should_receive(:send_message).with("some message", ["first", "second"])
75
+ @message.process
76
+ end
77
+ end
78
+ context ":query" do
79
+ it "should raise error if query type is nil" do
80
+ @message.stub!(:params).and_return(:command => :query, :type => nil)
81
+ lambda{ @message.process }.should raise_error(Socky::SockyError, "unknown query type")
82
+ end
83
+ it "should raise error if query type is invalid" do
84
+ @message.stub!(:params).and_return(:command => :query, :type => :invalid)
85
+ lambda{ @message.process }.should raise_error(Socky::SockyError, "unknown query type")
86
+ end
87
+ it "should not distinguish between string and symbol in command" do
88
+ @message.stub!(:params).and_return({:command => :query, :type => "show_connections"})
89
+ lambda {@message.process}.should_not raise_error(Socky::SockyError, "unknown query type")
90
+ end
91
+ context "=> :show_connections" do
92
+ it "should return current connection list to creator" do
93
+ @message.stub!(:params).and_return(:command => :query, :type => :show_connections)
94
+ Socky::Connection.stub!(:find_all).and_return(["find results"])
95
+ @connection.should_receive(:send_message).with(["find results"])
96
+ @message.process
97
+ end
98
+ end
99
+ end
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,74 @@
1
+ require 'spec_helper'
2
+
3
+ describe Socky::Misc do
4
+ include Socky::Misc
5
+
6
+ shared_examples_for "socky_misc" do
7
+ it "#option should return Socky.options" do
8
+ Socky.stub!(:options).and_return({:test => true})
9
+ @object.options.should eql({:test => true})
10
+ end
11
+ it "#options= should set Socky.options" do
12
+ default_options = Socky.options
13
+ begin
14
+ Socky.options = {}
15
+ @object.options = {:test => true}
16
+ Socky.options.should eql({:test => true})
17
+ ensure
18
+ Socky.options = default_options
19
+ end
20
+ end
21
+ it "#name should return self formatted name" do
22
+ @object.name.should eql("#{@object.class.to_s.split("::").last}(#{@object.object_id})")
23
+ end
24
+ it "#log_path should return Socky.log_path" do
25
+ Socky.stub!(:log_path).and_return("abstract")
26
+ @object.log_path.should eql("abstract")
27
+ end
28
+ it "#config_path should return Socky.config_path" do
29
+ Socky.stub!(:config_path).and_return("abstract")
30
+ @object.config_path.should eql("abstract")
31
+ end
32
+ it "#info should send joined args to Socky.logger.info" do
33
+ Socky.logger.stub!(:info)
34
+ Socky.logger.should_receive(:info).with("first second third")
35
+ @object.info(["first","second","third"])
36
+ end
37
+ it "#debug should send joined args to Socky.logger.debug" do
38
+ Socky.logger.stub!(:debug)
39
+ Socky.logger.should_receive(:debug).with("first second third")
40
+ @object.debug(["first","second","third"])
41
+ end
42
+ it "#error should send self name and error message to self.debug" do
43
+ @object.stub!(:debug)
44
+ error = mock(:abstract_error, :class => "AbstractError", :message => "abstract")
45
+ @object.should_receive(:debug).with([@object.name,"raised:","AbstractError","abstract"])
46
+ @object.error(@object.name, error)
47
+ end
48
+ context "#symbolize_keys" do
49
+ it "return args if args are not hash" do
50
+ @object.symbolize_keys("abstract").should eql("abstract")
51
+ end
52
+ it "return hash with symbolized keys if args is hash" do
53
+ hash = {"aaa" => "a", 123 => "b"}
54
+ @object.symbolize_keys(hash).should eql({:aaa => "a", 123 => "b"})
55
+ end
56
+ end
57
+ end
58
+
59
+ context "class" do
60
+ before(:each) do
61
+ @object = self.class
62
+ end
63
+ it_should_behave_like "socky_misc"
64
+ end
65
+
66
+ context "instance" do
67
+ before(:each) do
68
+ @object = self
69
+ end
70
+ it_should_behave_like "socky_misc"
71
+ end
72
+
73
+ end
74
+
@@ -0,0 +1,42 @@
1
+ require 'spec_helper'
2
+
3
+ describe Socky::NetRequest do
4
+
5
+ context "class" do
6
+ context "#post" do
7
+ it "should send EventMachine::HttpRequest to url" do
8
+ EventMachine::HttpRequest.should_receive(:new).with("some url")
9
+ described_class.post("some url")
10
+ end
11
+ it "should send EventMachine::HttpRequest post request with specified params" do
12
+ request = mock(:request)
13
+ EventMachine::HttpRequest.stub!(:new).and_return(request)
14
+ request.should_receive(:post).with({:body => {:test => true}, :timeout => 3})
15
+ described_class.post("some url", :test => true)
16
+ end
17
+ it "should rescue from EM::HttpRequest and return false" do
18
+ EM.run do
19
+ described_class.post("inexistent").should be_false
20
+ EM.stop
21
+ end
22
+ end
23
+ it "should call block with false if request return status other than 200" do
24
+ EM.run do
25
+ described_class.post("http://127.0.0.1:8765/fail") do |response|
26
+ response.should be_false
27
+ EM.stop
28
+ end.should be_true
29
+ end
30
+ end
31
+ it "should call block with true if request return status 200" do
32
+ EM.run do
33
+ described_class.post("http://127.0.0.1:8765/") do |response|
34
+ response.should be_true
35
+ EM.stop
36
+ end.should be_true
37
+ end
38
+ end
39
+ end
40
+ end
41
+
42
+ end
@@ -0,0 +1,72 @@
1
+ require 'spec_helper'
2
+
3
+ FILES_DIR = File.join(File.dirname(__FILE__), '..', '..', 'files')
4
+
5
+ describe Socky::Options::Config do
6
+
7
+ context "class" do
8
+ before(:each) do
9
+ described_class.stub!(:puts)
10
+ end
11
+
12
+ context "#read" do
13
+ it "should raise error if file doesn't exists" do
14
+ described_class.should_receive(:puts).with("You must generate a config file (socky -g filename.yml)")
15
+ lambda { described_class.read("abstract") }.should raise_error SystemExit
16
+ end
17
+ it "should raise error if file is corrupted" do
18
+ described_class.should_receive(:puts).with("Provided config file is invalid.")
19
+ lambda { described_class.read(FILES_DIR + "/invalid.yml") }.should raise_error SystemExit
20
+ end
21
+ it "should not raise error if :kill option is provided" do
22
+ described_class.should_not_receive(:puts).with("Provided config file is invalid.")
23
+ lambda { described_class.read(FILES_DIR + "/invalid.yml", :kill => true) }.should_not raise_error SystemExit
24
+ end
25
+ it "should return valid options if file is valid" do
26
+ lambda { described_class.read(FILES_DIR + "/default.yml") }.should_not raise_error SystemExit
27
+ described_class.read(FILES_DIR + "/default.yml").should eql(default_options)
28
+ end
29
+ end
30
+
31
+ context "#generate" do
32
+ it "should raise error if file exists" do
33
+ described_class.should_receive(:puts).with("Config file already exists. You must remove it before generating a new one.")
34
+ lambda { described_class.generate(FILES_DIR + "/invalid.yml") }.should raise_error SystemExit
35
+ end
36
+ it "should raise error if path is not valid" do
37
+ described_class.should_receive(:puts).with("Config file is unavailable - please choose another.")
38
+ lambda { described_class.generate("/proc/inexistent") }.should raise_error SystemExit
39
+ end
40
+ it "should create file and exit if path is valid" do
41
+ path = FILES_DIR + "/inexistent.yml"
42
+ begin
43
+ described_class.should_receive(:puts).with("Config file generated at #{path}")
44
+ lambda { described_class.generate(path) }.should raise_error SystemExit
45
+ ensure
46
+ FileUtils.rm(path, :force => true)
47
+ end
48
+ end
49
+ it "should generate file that after parsing will equal default config" do
50
+ path = FILES_DIR + "/inexistent.yml"
51
+ begin
52
+ described_class.should_receive(:puts).with("Config file generated at #{path}")
53
+ lambda { described_class.generate(path) }.should raise_error SystemExit
54
+ lambda { described_class.read(path) }.should_not raise_error SystemExit
55
+ described_class.read(path).should eql(default_options)
56
+ ensure
57
+ FileUtils.rm(path, :force => true)
58
+ end
59
+ end
60
+ end
61
+ end
62
+
63
+ def default_options
64
+ {
65
+ :port => 8080,
66
+ :debug => false,
67
+ :secret => "my_secret_key",
68
+ :secure => false
69
+ }
70
+ end
71
+
72
+ end
@@ -0,0 +1,76 @@
1
+ require 'spec_helper'
2
+
3
+ describe Socky::Options::Parser do
4
+
5
+ context "class" do
6
+ context "#parse" do
7
+ before(:each) do
8
+ described_class.stub!(:puts)
9
+ end
10
+
11
+ it "should return empty hash on empty array" do
12
+ described_class.parse([]).should eql({})
13
+ end
14
+ it "should inform about inexistent option and exit" do
15
+ described_class.should_receive(:puts).with("invalid option: -z")
16
+ lambda { described_class.parse(["-z"]) }.should raise_error SystemExit
17
+ end
18
+ it "on -g or --generate should set config_path and generate default config file in specified destination" do
19
+ Socky::Options::Config.stub!(:generate)
20
+ ["-g","--generate"].each do |function|
21
+ Socky::Options::Config.should_receive(:generate).with("/tmp/socky.yml")
22
+ described_class.parse([function,"/tmp/socky.yml"]).should eql({:config_path => "/tmp/socky.yml"})
23
+ end
24
+ end
25
+ it "on -c or --config should set config_path to provided path" do
26
+ ["-c","--config"].each do |function|
27
+ described_class.parse([function,"/tmp/socky.yml"]).should eql({:config_path => "/tmp/socky.yml"})
28
+ end
29
+ end
30
+ it "on -p or --port should set port" do
31
+ ["-p","--port"].each do |function|
32
+ described_class.parse([function,"222"]).should eql({:port => 222})
33
+ end
34
+ end
35
+ it "on -s or --secure should set secure mode on" do
36
+ ["-s","--secure"].each do |function|
37
+ described_class.parse([function]).should eql({:secure => true})
38
+ end
39
+ end
40
+ it "on -P or --pid should set pid_path to provided path" do
41
+ ["-P","--pid"].each do |function|
42
+ described_class.parse([function,"/tmp/socky.pid"]).should eql({:pid_path => "/tmp/socky.pid"})
43
+ end
44
+ end
45
+ it "on -d or --daemon should set daemon mode on" do
46
+ ["-d","--daemon"].each do |function|
47
+ described_class.parse([function]).should eql({:daemonize => true})
48
+ end
49
+ end
50
+ it "on -l or --log should set log_path to provided path" do
51
+ ["-l","--log"].each do |function|
52
+ described_class.parse([function,"/tmp/socky.log"]).should eql({:log_path => "/tmp/socky.log"})
53
+ end
54
+ end
55
+ it "on --debug should set debug to true" do
56
+ described_class.parse(["--debug"]).should eql({:debug => true})
57
+ end
58
+ it "on --deep-debug should set debug and deep_debug to true" do
59
+ described_class.parse(["--deep-debug"]).should eql({:debug => true, :deep_debug => true})
60
+ end
61
+ it "on -? or --help should display usage" do
62
+ ["-?","--help"].each do |function|
63
+ described_class.should_receive(:puts).with(/Usage: socky \[options\]/)
64
+ lambda { described_class.parse([function]) }.should raise_error SystemExit
65
+ end
66
+ end
67
+ it "on -v or --version should dosplay current version" do
68
+ ["-v","--version"].each do |function|
69
+ described_class.should_receive(:puts).with("Socky #{Socky::VERSION}")
70
+ lambda { described_class.parse([function]) }.should raise_error SystemExit
71
+ end
72
+ end
73
+
74
+ end
75
+ end
76
+ end