rocket-server 0.0.1
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/.gitignore +22 -0
- data/LICENSE +20 -0
- data/README.md +0 -0
- data/Rakefile +60 -0
- data/bin/rocket-server +13 -0
- data/etc/testing.yml +38 -0
- data/lib/rocket-server.rb +2 -0
- data/lib/rocket/server.rb +87 -0
- data/lib/rocket/server/app.rb +44 -0
- data/lib/rocket/server/channel.rb +22 -0
- data/lib/rocket/server/cli.rb +52 -0
- data/lib/rocket/server/connection.rb +137 -0
- data/lib/rocket/server/helpers.rb +22 -0
- data/lib/rocket/server/misc.rb +17 -0
- data/lib/rocket/server/runner.rb +78 -0
- data/lib/rocket/server/session.rb +58 -0
- data/lib/rocket/server/templates/config.yml.tpl +40 -0
- data/lib/rocket/server/version.rb +14 -0
- data/rocket-server.gemspec +96 -0
- data/spec/apps_spec.rb +54 -0
- data/spec/channel_spec.rb +31 -0
- data/spec/cli_spec.rb +85 -0
- data/spec/connection_spec.rb +257 -0
- data/spec/helpers_spec.rb +23 -0
- data/spec/misc_spec.rb +31 -0
- data/spec/runner_spec.rb +138 -0
- data/spec/server_spec.rb +146 -0
- data/spec/session_spec.rb +110 -0
- data/spec/spec_helper.rb +22 -0
- metadata +237 -0
@@ -0,0 +1,257 @@
|
|
1
|
+
require File.expand_path("../spec_helper", __FILE__)
|
2
|
+
|
3
|
+
class Rocket::Server::Connection
|
4
|
+
def send(*args); end
|
5
|
+
end
|
6
|
+
|
7
|
+
describe Rocket::Server::Connection do
|
8
|
+
subject do
|
9
|
+
Rocket::Server::Connection
|
10
|
+
end
|
11
|
+
|
12
|
+
before do
|
13
|
+
Rocket::Server.instance_variable_set("@apps", {"test-app" => {"secret" => "my-secret"}})
|
14
|
+
end
|
15
|
+
|
16
|
+
describe "#initialize" do
|
17
|
+
it "should set all callbacks" do
|
18
|
+
conn = subject.new({})
|
19
|
+
conn.instance_variable_get("@onopen").should == conn.method(:onopen)
|
20
|
+
conn.instance_variable_get("@onclose").should == conn.method(:onclose)
|
21
|
+
conn.instance_variable_get("@onerror").should == conn.method(:onerror)
|
22
|
+
conn.instance_variable_get("@onmessage").should == conn.method(:onmessage)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
describe "#onopen" do
|
27
|
+
context "when valid app specified in request" do
|
28
|
+
before do
|
29
|
+
@conn = subject.new(1)
|
30
|
+
@conn.request.expects(:"[]").with("Path").returns("/app/test-app")
|
31
|
+
@conn.request.stubs(:"[]").with("Query").returns({})
|
32
|
+
end
|
33
|
+
|
34
|
+
it "should start new session for it" do
|
35
|
+
@conn.stubs(:send)
|
36
|
+
@conn.onopen
|
37
|
+
session = @conn.session
|
38
|
+
session.should be_kind_of(Rocket::Server::Session)
|
39
|
+
session.app_id.should == "test-app"
|
40
|
+
end
|
41
|
+
|
42
|
+
it "should send message to connected client" do
|
43
|
+
@conn.expects(:send).with({:event => "rocket:connected", :data => { :session_id => 1 }}.to_json)
|
44
|
+
@conn.onopen.should == true
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
context "when invalid app specified in request" do
|
49
|
+
it "should close connection with client" do
|
50
|
+
conn = subject.new(1)
|
51
|
+
conn.request.expects(:"[]").with("Path").returns("/app/non-existing-app")
|
52
|
+
conn.request.stubs(:"[]").with("Query").returns({})
|
53
|
+
conn.expects(:close_connection)
|
54
|
+
conn.onopen
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
context "when valid secret given in request query" do
|
59
|
+
it "should start new session and authenticate it" do
|
60
|
+
conn = subject.new(1)
|
61
|
+
conn.request.expects(:"[]").with("Path").returns("/app/test-app")
|
62
|
+
conn.request.stubs(:"[]").with("Query").returns("secret" => "my-secret")
|
63
|
+
conn.expects(:send).with({:event => "rocket:connected", :data => { :session_id => 1 }}.to_json)
|
64
|
+
conn.onopen
|
65
|
+
session = conn.session
|
66
|
+
session.should be_authenticated
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
context "when invalid or no secret given" do
|
71
|
+
it "shouldn't authenticate session" do
|
72
|
+
conn = subject.new(1)
|
73
|
+
conn.request.expects(:"[]").with("Path").returns("/app/test-app")
|
74
|
+
conn.request.stubs(:"[]").with("Query").returns("secret" => "fooobar")
|
75
|
+
conn.onopen
|
76
|
+
session = conn.session
|
77
|
+
session.should_not be_authenticated
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
describe "#onclose" do
|
83
|
+
context "when session is active" do
|
84
|
+
it "should close it" do
|
85
|
+
conn = subject.new(1)
|
86
|
+
conn.request.expects(:"[]").with("Path").returns("/app/test-app")
|
87
|
+
conn.request.stubs(:"[]").with("Query").returns({})
|
88
|
+
conn.onopen
|
89
|
+
conn.session.expects(:close)
|
90
|
+
conn.onclose
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
context "when session is not active" do
|
95
|
+
it "should do nothing" do
|
96
|
+
conn = subject.new(1)
|
97
|
+
conn.onclose.should be_nil
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
describe "#onerror" do
|
103
|
+
it "should log given error" do
|
104
|
+
conn = Rocket::Server::Connection.new(1)
|
105
|
+
conn.request.expects(:"[]").with("Path").returns("/app/test-app")
|
106
|
+
conn.request.stubs(:"[]").with("Query").returns({})
|
107
|
+
conn.onopen
|
108
|
+
conn.onerror("test error here")
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
describe "#onmessage" do
|
113
|
+
subject do
|
114
|
+
conn = Rocket::Server::Connection.new(1)
|
115
|
+
conn.request.expects(:"[]").with("Path").returns("/app/test-app")
|
116
|
+
conn.request.stubs(:"[]").with("Query").returns({})
|
117
|
+
conn.onopen
|
118
|
+
conn
|
119
|
+
end
|
120
|
+
|
121
|
+
context "when given message is valid JSON" do
|
122
|
+
context "for rocket:subscribe message" do
|
123
|
+
it "should process it" do
|
124
|
+
conn = subject
|
125
|
+
message = {"event" => "rocket:subscribe", "channel" => "my-test-one"}
|
126
|
+
conn.expects(:subscribe!).with(instance_of(Hash))
|
127
|
+
conn.onmessage(message.to_json)
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
context "for rocket:unsubscribe message" do
|
132
|
+
it "should process it" do
|
133
|
+
conn = subject
|
134
|
+
message = {"event" => "rocket:unsubscribe", "channel" => "my-test-one"}
|
135
|
+
conn.expects(:unsubscribe!).with(instance_of(Hash))
|
136
|
+
conn.onmessage(message.to_json)
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
context "for other message" do
|
141
|
+
it "should process it" do
|
142
|
+
conn = subject
|
143
|
+
message = {"channel" => "my-test-chan", "event" => "my-test-even", "data" => {"foo" => "bar"}}
|
144
|
+
conn.expects(:trigger!).with(instance_of(Hash))
|
145
|
+
conn.onmessage(message.to_json)
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
context "when given message is invalid JSON" do
|
151
|
+
it "should log proper error" do
|
152
|
+
conn = subject
|
153
|
+
conn.onmessage("{invalid JSON here}")
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
describe "#session?" do
|
159
|
+
context "when current session exists" do
|
160
|
+
it "should be true" do
|
161
|
+
conn = subject.new(1)
|
162
|
+
conn.request.expects(:"[]").with("Path").returns("/app/test-app")
|
163
|
+
conn.request.stubs(:"[]").with("Query").returns({})
|
164
|
+
conn.onopen
|
165
|
+
conn.should be_session
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
context "when there is no session" do
|
170
|
+
it "should be false" do
|
171
|
+
conn = subject.new(1)
|
172
|
+
conn.should_not be_session
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
describe "#subscribe!" do
|
178
|
+
before do
|
179
|
+
@conn = subject.new(1)
|
180
|
+
@conn.request.expects(:"[]").with("Path").returns("/app/test-app")
|
181
|
+
@conn.request.stubs(:"[]").with("Query").returns({})
|
182
|
+
@conn.onopen
|
183
|
+
end
|
184
|
+
|
185
|
+
context "when given valid event data" do
|
186
|
+
it "should subscribe channel" do
|
187
|
+
@conn.subscribe!({"channel" => "test-channel"}).should be
|
188
|
+
@conn.session.subscriptions.should have(1).item
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
context "when no valid event data given" do
|
193
|
+
it "should return false" do
|
194
|
+
@conn.subscribe!({"channello" => "test-channel"}).should be_false
|
195
|
+
end
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
describe "#unsubscribe!" do
|
200
|
+
before do
|
201
|
+
@conn = subject.new(1)
|
202
|
+
@conn.request.expects(:"[]").with("Path").returns("/app/test-app")
|
203
|
+
@conn.request.stubs(:"[]").with("Query").returns({})
|
204
|
+
@conn.onopen
|
205
|
+
end
|
206
|
+
|
207
|
+
context "when given valid event data" do
|
208
|
+
it "should unsubscribe channel" do
|
209
|
+
@conn.unsubscribe!({"channel" => "test-channel"})
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
context "when no valid event data given" do
|
214
|
+
it "should return false" do
|
215
|
+
@conn.unsubscribe!({"channello" => "test-channel"}).should be_false
|
216
|
+
end
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
220
|
+
describe "#trigger!" do
|
221
|
+
before do
|
222
|
+
@conn = subject.new(1)
|
223
|
+
@conn.request.expects(:"[]").with("Path").returns("/app/test-app")
|
224
|
+
@conn.request.stubs(:"[]").with("Query").returns({})
|
225
|
+
@conn.onopen
|
226
|
+
end
|
227
|
+
|
228
|
+
context "when session is not authenticated" do
|
229
|
+
it "should log proper error" do
|
230
|
+
data = {"event" => "test", "channel" => "testing"}
|
231
|
+
@conn.trigger!(data)
|
232
|
+
end
|
233
|
+
end
|
234
|
+
|
235
|
+
context "when session is authenticated" do
|
236
|
+
before do
|
237
|
+
@conn.session.authenticate!("my-secret")
|
238
|
+
end
|
239
|
+
|
240
|
+
context "when event data is valid" do
|
241
|
+
it "should broadcast it on given channel" do
|
242
|
+
data = {"event" => "test", "channel" => "testing"}
|
243
|
+
Rocket::Server::Channel["test-app" => "testing"].expects(:push).with(data.to_json)
|
244
|
+
@conn.trigger!(data)
|
245
|
+
end
|
246
|
+
end
|
247
|
+
|
248
|
+
context "when event data is invalid" do
|
249
|
+
it "should log proper error" do
|
250
|
+
data = {"evento" => "test", "channello" => "testing"}
|
251
|
+
@conn.trigger!(data)
|
252
|
+
end
|
253
|
+
end
|
254
|
+
end
|
255
|
+
|
256
|
+
end
|
257
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require File.expand_path("../spec_helper", __FILE__)
|
2
|
+
|
3
|
+
class HelpersPoweredClass
|
4
|
+
include Rocket::Server::Helpers
|
5
|
+
end
|
6
|
+
|
7
|
+
describe Rocket::Server::Helpers do
|
8
|
+
subject do
|
9
|
+
HelpersPoweredClass.new
|
10
|
+
end
|
11
|
+
|
12
|
+
it "#log should be proxy to Rocket::Server.logger" do
|
13
|
+
subject.log.should == Rocket::Server.logger
|
14
|
+
end
|
15
|
+
|
16
|
+
it "should respond to all log level-specific methods" do
|
17
|
+
subject.should respond_to(:log_info)
|
18
|
+
subject.should respond_to(:log_debug)
|
19
|
+
subject.should respond_to(:log_warn)
|
20
|
+
subject.should respond_to(:log_error)
|
21
|
+
subject.should respond_to(:log_fatal)
|
22
|
+
end
|
23
|
+
end
|
data/spec/misc_spec.rb
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
require File.expand_path("../spec_helper", __FILE__)
|
2
|
+
|
3
|
+
describe Rocket::Server::Misc do
|
4
|
+
subject do
|
5
|
+
Rocket::Server::Misc
|
6
|
+
end
|
7
|
+
|
8
|
+
describe ".generate_config_file" do
|
9
|
+
context "when everything's ok" do
|
10
|
+
it "should create default config under given path" do
|
11
|
+
begin
|
12
|
+
subject.generate_config_file(fname = File.expand_path("../rocket.yml", __FILE__))
|
13
|
+
File.should be_exist(fname)
|
14
|
+
expect { YAML.load_file(fname) }.to_not raise_error
|
15
|
+
ensure
|
16
|
+
FileUtils.rm_rf(fname)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
context "when error raised" do
|
22
|
+
it "should rescue it and display, and exit program" do
|
23
|
+
out, err = capture_output {
|
24
|
+
File.expects(:open).raises(Errno::EACCES)
|
25
|
+
expect { subject.generate_config_file("rocket.yml") }.to raise_error(SystemExit)
|
26
|
+
}
|
27
|
+
out.should == "Permission denied\n"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
data/spec/runner_spec.rb
ADDED
@@ -0,0 +1,138 @@
|
|
1
|
+
require File.expand_path("../spec_helper", __FILE__)
|
2
|
+
|
3
|
+
describe Rocket::Server::Runner do
|
4
|
+
subject do
|
5
|
+
Rocket::Server::Runner
|
6
|
+
end
|
7
|
+
|
8
|
+
describe "#initialize" do
|
9
|
+
it "should set options" do
|
10
|
+
subject.new(1=>2).options.should == {1=>2}
|
11
|
+
end
|
12
|
+
|
13
|
+
context "when host and port given" do
|
14
|
+
it "should set it properly" do
|
15
|
+
serv = subject.new(:host => 'test.host', :port => 999)
|
16
|
+
serv.host.should == 'test.host'
|
17
|
+
serv.port.should == 999
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
context "when no host or port given" do
|
22
|
+
it "should set default values for it" do
|
23
|
+
serv = subject.new
|
24
|
+
serv.host.should == 'localhost'
|
25
|
+
serv.port.should == 9772
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
it "should remove unnecessary options" do
|
30
|
+
serv = subject.new(:verbose => true, :quiet => false, :log => 1, :apps => [], :plugins => [])
|
31
|
+
serv.options.values.should be_empty
|
32
|
+
end
|
33
|
+
|
34
|
+
context "when :pid option given" do
|
35
|
+
it "should set it" do
|
36
|
+
serv = subject.new(:pid => "test.pid")
|
37
|
+
serv.pidfile.should == "test.pid"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
context "when :daemon option given" do
|
42
|
+
it "should set it" do
|
43
|
+
serv = subject.new(:daemon => true)
|
44
|
+
serv.daemon.should == true
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
describe "#stop!" do
|
50
|
+
it "should stop EventMachine reactor" do
|
51
|
+
EM.expects(:stop)
|
52
|
+
subject.new.stop!
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
describe "#start!" do
|
57
|
+
context "when daemon mode disabled" do
|
58
|
+
it "should start server on given host and port" do
|
59
|
+
EM.expects(:start_server).with("test.host", 9992, Rocket::Server::Connection, instance_of(Hash))
|
60
|
+
t = Thread.new { subject.new(:host => "test.host", :port => 9992).start! }
|
61
|
+
t.kill
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
context "when daemon mode enabled" do
|
66
|
+
it "should daemonize server" do
|
67
|
+
serv = subject.new(:host => "test.host", :port => 9992, :daemon => true)
|
68
|
+
serv.expects(:daemonize!).returns(:test)
|
69
|
+
serv.start!.should == :test
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
describe "#save_pid" do
|
75
|
+
context "when pid file given" do
|
76
|
+
subject do
|
77
|
+
Rocket::Server::Runner.new(:pid => "test.pid")
|
78
|
+
end
|
79
|
+
|
80
|
+
context "and everything's ok" do
|
81
|
+
it "should save process id to this file" do
|
82
|
+
mock_file = mock
|
83
|
+
mock_file.expects(:write).with("#{Process.pid}\n")
|
84
|
+
File.expects(:open).with("test.pid", "w+").yields(mock_file)
|
85
|
+
subject.save_pid
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
context "and some errors raises" do
|
90
|
+
it "should display error and quit" do
|
91
|
+
out, err = capture_output {
|
92
|
+
FileUtils.expects(:mkdir_p).with(".").raises(Errno::EACCES)
|
93
|
+
expect { subject.save_pid }.to raise_error(SystemExit)
|
94
|
+
}
|
95
|
+
out.should == "Permission denied\n"
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
describe "#kill!" do
|
102
|
+
context "when pid file given" do
|
103
|
+
subject do
|
104
|
+
Rocket::Server::Runner.new(:pid => "test.pid")
|
105
|
+
end
|
106
|
+
|
107
|
+
context "and it doesn't exist" do
|
108
|
+
it "should do nothing" do
|
109
|
+
File.expects(:exists?).with("test.pid").returns(false)
|
110
|
+
subject.kill!.should_not be
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
context "and it exists" do
|
115
|
+
it "should remove it and kill given process" do
|
116
|
+
File.expects(:exists?).with("test.pid").returns(true)
|
117
|
+
File.expects(:read).with("test.pid").returns("123\n")
|
118
|
+
FileUtils.expects(:rm).with("test.pid")
|
119
|
+
Process.expects(:kill).with(9, 123)
|
120
|
+
subject.kill!.should == 123
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
describe "#daemonize!" do
|
127
|
+
subject do
|
128
|
+
Rocket::Server::Runner.new(:pid => "test.pid")
|
129
|
+
end
|
130
|
+
|
131
|
+
it "should switch to deamon mode and start server" do
|
132
|
+
serv = subject
|
133
|
+
serv.expects(:daemonize)
|
134
|
+
serv.expects(:start!)
|
135
|
+
serv.daemonize!
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|