groem 0.0.4
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/.autotest +24 -0
- data/.gitignore +3 -0
- data/Gemfile +3 -0
- data/HISTORY.markdown +9 -0
- data/README.markdown +185 -0
- data/TODO.markdown +7 -0
- data/groem.gemspec +24 -0
- data/lib/groem.rb +10 -0
- data/lib/groem/app.rb +197 -0
- data/lib/groem/client.rb +169 -0
- data/lib/groem/constants.rb +74 -0
- data/lib/groem/marshal.rb +349 -0
- data/lib/groem/notification.rb +140 -0
- data/lib/groem/response.rb +86 -0
- data/lib/groem/route.rb +37 -0
- data/lib/groem/version.rb +3 -0
- data/spec/functional/app_notify_adhoc_spec.rb +73 -0
- data/spec/functional/app_notify_spec.rb +390 -0
- data/spec/functional/app_register_spec.rb +113 -0
- data/spec/functional/client_spec.rb +361 -0
- data/spec/integration/notify.rb +318 -0
- data/spec/integration/register.rb +133 -0
- data/spec/shared/dummy_server.rb +198 -0
- data/spec/shared/dummy_server_helper.rb +31 -0
- data/spec/shared/marshal_helper.rb +40 -0
- data/spec/spec_helper.rb +14 -0
- data/spec/unit/app_spec.rb +77 -0
- data/spec/unit/marshal_request_spec.rb +380 -0
- data/spec/unit/marshal_response_spec.rb +162 -0
- data/spec/unit/notification_spec.rb +205 -0
- data/spec/unit/response_spec.rb +7 -0
- data/spec/unit/route_spec.rb +93 -0
- metadata +141 -0
@@ -0,0 +1,198 @@
|
|
1
|
+
require 'eventmachine'
|
2
|
+
|
3
|
+
module Groem
|
4
|
+
module Dummy
|
5
|
+
class Server < EM::Connection
|
6
|
+
include Groem::Constants
|
7
|
+
|
8
|
+
DEFAULT_HOST = 'localhost'
|
9
|
+
DEFAULT_PORT = 23052 # note one off to avoid port conflict
|
10
|
+
|
11
|
+
DEFAULT_RESPONSES = { :register => ['-OK', 0],
|
12
|
+
:notify => ['-OK', 0],
|
13
|
+
:callback => ['CLICK', 10]
|
14
|
+
}
|
15
|
+
|
16
|
+
class << self
|
17
|
+
def canned_responses
|
18
|
+
@canned_responses ||= DEFAULT_RESPONSES.dup
|
19
|
+
end
|
20
|
+
|
21
|
+
def reset_canned_responses
|
22
|
+
@canned_responses = nil
|
23
|
+
end
|
24
|
+
|
25
|
+
def respond_to_register_with(meth, err = 0)
|
26
|
+
canned_responses[:register] = [meth.to_s.upcase, err.to_i]
|
27
|
+
end
|
28
|
+
|
29
|
+
def respond_to_notify_with(meth, err = 0)
|
30
|
+
canned_responses[:notify] = [meth.to_s.upcase, err.to_i]
|
31
|
+
end
|
32
|
+
|
33
|
+
def callback_with(rslt, secs)
|
34
|
+
canned_responses[:callback] = [rslt.to_s.upcase, secs.to_i]
|
35
|
+
end
|
36
|
+
|
37
|
+
def listen(host = DEFAULT_HOST, port = DEFAULT_PORT)
|
38
|
+
svr = EM.start_server host, port, self
|
39
|
+
puts "SERVER: Dummy GNTP server listening on #{host}:#{port}"
|
40
|
+
canned_responses.each_pair do |k, v|
|
41
|
+
if k == :register || k == :notify
|
42
|
+
puts " #{k.to_s.upcase} responds #{v[0]} with status #{v[1]}"
|
43
|
+
elsif k == :callback
|
44
|
+
puts " #{k.to_s.upcase} with result #{v[0]} after #{v[1]} secs"
|
45
|
+
end
|
46
|
+
end
|
47
|
+
svr
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def post_init
|
52
|
+
reset_state
|
53
|
+
end
|
54
|
+
|
55
|
+
def receive_data data
|
56
|
+
@buffer.extract(data).each do |line|
|
57
|
+
update_message_state!(line)
|
58
|
+
@lines << line
|
59
|
+
end
|
60
|
+
receive_message @lines.join("\r\n") + "\r\n" if eof?
|
61
|
+
end
|
62
|
+
|
63
|
+
protected
|
64
|
+
|
65
|
+
def reset_state
|
66
|
+
@buffer = BufferedTokenizer.new("\r\n")
|
67
|
+
@lines = []
|
68
|
+
@response = nil
|
69
|
+
@sections_left = 0
|
70
|
+
end
|
71
|
+
|
72
|
+
# Horribly kludgy but that's GNTP for you...
|
73
|
+
def update_message_state!(line)
|
74
|
+
if line[/^\s*Notifications-Count\s*:\s*(\d+)/i]
|
75
|
+
@sections_left += $1.to_i
|
76
|
+
end
|
77
|
+
if line[/^.+:\s*x-growl-resource:/i]
|
78
|
+
@sections_left += 1
|
79
|
+
end
|
80
|
+
if line.empty?
|
81
|
+
@sections_left -= 1
|
82
|
+
end
|
83
|
+
#puts line
|
84
|
+
#puts "Note: #{@sections_left} more sections"
|
85
|
+
end
|
86
|
+
|
87
|
+
def eof?
|
88
|
+
if @lines[-1].empty? && @sections_left > 0
|
89
|
+
puts "SERVER: CRLFCRLF received, but expecting #{@sections_left} more sections"
|
90
|
+
end
|
91
|
+
@lines[-1].empty? && @sections_left <= 0
|
92
|
+
end
|
93
|
+
|
94
|
+
def receive_message message
|
95
|
+
puts "SERVER: Received message"
|
96
|
+
klass = Class.new { include Groem::Marshal::Request }
|
97
|
+
raw = klass.load(message, false)
|
98
|
+
#puts "Parsed message:\n#{raw.inspect}"
|
99
|
+
prepare_responses_for(raw)
|
100
|
+
if @response
|
101
|
+
send_data @response
|
102
|
+
puts "SERVER: Sent response"
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def canned_responses; self.class.canned_responses; end
|
107
|
+
|
108
|
+
def prepare_responses_for(req)
|
109
|
+
case req['environment']['request_method']
|
110
|
+
when 'REGISTER'
|
111
|
+
prepare_register_response_for(req,
|
112
|
+
canned_responses[:register][0],
|
113
|
+
canned_responses[:register][1]
|
114
|
+
)
|
115
|
+
when 'NOTIFY'
|
116
|
+
prepare_notify_response_for(req,
|
117
|
+
canned_responses[:notify][0],
|
118
|
+
canned_responses[:notify][1]
|
119
|
+
)
|
120
|
+
schedule_callback_response_for(req,
|
121
|
+
canned_responses[:callback][0],
|
122
|
+
canned_responses[:callback][1]
|
123
|
+
) \
|
124
|
+
if req['headers']['Notification-Callback-Context']
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
# eventually replace with Response#dump, quick & dirty for now
|
129
|
+
def prepare_register_response_for(req, meth, err)
|
130
|
+
env, hdrs = req[ENVIRONMENT_KEY], req[HEADERS_KEY]
|
131
|
+
out = []
|
132
|
+
out << "#{env[(GNTP_PROTOCOL_KEY)]}" +
|
133
|
+
"/#{env[(GNTP_VERSION_KEY)]} "+
|
134
|
+
"#{meth} "+
|
135
|
+
"#{env[(GNTP_ENCRYPTION_ID_KEY)]}"
|
136
|
+
out << "#{GNTP_RESPONSE_ACTION_KEY}: #{GNTP_REGISTER_METHOD}"
|
137
|
+
if meth == GNTP_ERROR_RESPONSE
|
138
|
+
out << "#{GNTP_ERROR_CODE_KEY}: #{err}"
|
139
|
+
out << "Error-Description: An error occurred"
|
140
|
+
end
|
141
|
+
out << nil
|
142
|
+
out << nil
|
143
|
+
out << nil
|
144
|
+
puts "SERVER: Prepared REGISTER response: #{meth}, #{err}"
|
145
|
+
@response = out.join("\r\n")
|
146
|
+
end
|
147
|
+
|
148
|
+
def prepare_notify_response_for(req, meth, err)
|
149
|
+
env, hdrs = req[ENVIRONMENT_KEY], req[HEADERS_KEY]
|
150
|
+
out = []
|
151
|
+
out << "#{env[(GNTP_PROTOCOL_KEY)]}" +
|
152
|
+
"/#{env[(GNTP_VERSION_KEY)]} "+
|
153
|
+
"#{meth} "+
|
154
|
+
"#{env[(GNTP_ENCRYPTION_ID_KEY)]}"
|
155
|
+
out << "#{GNTP_RESPONSE_ACTION_KEY}: #{GNTP_NOTIFY_METHOD}"
|
156
|
+
out << "#{GNTP_NOTIFICATION_ID_KEY}: #{hdrs[(GNTP_NOTIFICATION_ID_KEY)]}"
|
157
|
+
if meth == GNTP_ERROR_RESPONSE
|
158
|
+
out << "#{GNTP_ERROR_CODE_KEY}: #{err}"
|
159
|
+
out << "Error-Description: An error occurred"
|
160
|
+
end
|
161
|
+
out << nil
|
162
|
+
out << nil
|
163
|
+
out << nil
|
164
|
+
puts "SERVER: Prepared NOTIFY response: #{meth}, #{err}"
|
165
|
+
@response = out.join("\r\n")
|
166
|
+
end
|
167
|
+
|
168
|
+
def schedule_callback_response_for(req, rslt, secs)
|
169
|
+
EM.add_timer(secs) do
|
170
|
+
msg = callback_response_for(req, rslt)
|
171
|
+
send_data msg
|
172
|
+
puts "SERVER: Sent CALLBACK response: #{rslt}"
|
173
|
+
end
|
174
|
+
puts "SERVER: Scheduled CALLBACK response in #{secs} secs: #{rslt}"
|
175
|
+
end
|
176
|
+
|
177
|
+
def callback_response_for(req, rslt)
|
178
|
+
env, hdrs = req[ENVIRONMENT_KEY], req[HEADERS_KEY]
|
179
|
+
out = []
|
180
|
+
out << "#{env[(GNTP_PROTOCOL_KEY)]}" +
|
181
|
+
"/#{env[(GNTP_VERSION_KEY)]} "+
|
182
|
+
"#{GNTP_CALLBACK_RESPONSE} "+
|
183
|
+
"#{env[(GNTP_ENCRYPTION_ID_KEY)]}"
|
184
|
+
out << "#{GNTP_APPLICATION_NAME_KEY}: #{hdrs[(GNTP_APPLICATION_NAME_KEY)]}"
|
185
|
+
out << "#{GNTP_NOTIFICATION_ID_KEY}: #{hdrs[(GNTP_NOTIFICATION_ID_KEY)]}"
|
186
|
+
out << "#{GNTP_NOTIFICATION_CALLBACK_RESULT_KEY}: #{rslt}"
|
187
|
+
out << "#{GNTP_NOTIFICATION_CALLBACK_TIMESTAMP_KEY}: #{Time.now.strftime('%Y-%m-%d %H:%M:%SZ')}"
|
188
|
+
out << "#{GNTP_NOTIFICATION_CALLBACK_CONTEXT_KEY}: #{hdrs[(GNTP_NOTIFICATION_CALLBACK_CONTEXT_KEY)]}"
|
189
|
+
out << "#{GNTP_NOTIFICATION_CALLBACK_CONTEXT_TYPE_KEY}: #{hdrs[(GNTP_NOTIFICATION_CALLBACK_CONTEXT_TYPE_KEY)]}"
|
190
|
+
out << nil
|
191
|
+
out << nil
|
192
|
+
out << nil
|
193
|
+
out.join("\r\n")
|
194
|
+
end
|
195
|
+
|
196
|
+
end
|
197
|
+
end
|
198
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
|
2
|
+
module DummyServerHelper
|
3
|
+
|
4
|
+
DEFAULT_PORT = Groem::Dummy::Server::DEFAULT_PORT
|
5
|
+
|
6
|
+
def self.fork_server(opts = {})
|
7
|
+
fork {
|
8
|
+
puts '-------------- server process fork ------------------'
|
9
|
+
Groem::Dummy::Server.reset_canned_responses
|
10
|
+
if opts[:register]
|
11
|
+
Groem::Dummy::Server.respond_to_register_with *opts[:register]
|
12
|
+
end
|
13
|
+
if opts[:notify]
|
14
|
+
Groem::Dummy::Server.respond_to_notify_with *opts[:notify]
|
15
|
+
end
|
16
|
+
if opts[:callback]
|
17
|
+
Groem::Dummy::Server.callback_with *opts[:callback]
|
18
|
+
end
|
19
|
+
EM.run {
|
20
|
+
Signal.trap("INT") { EM.next_tick { EM.stop } }
|
21
|
+
Groem::Dummy::Server.listen
|
22
|
+
}
|
23
|
+
puts '-------------- server process exiting -----------'
|
24
|
+
}
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.kill_server(pid)
|
28
|
+
Process.kill("INT", pid)
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
|
2
|
+
module MarshalHelper
|
3
|
+
|
4
|
+
def self.dummy_request_class
|
5
|
+
Class.new {
|
6
|
+
include(Groem::Marshal::Request)
|
7
|
+
require 'forwardable'
|
8
|
+
extend Forwardable
|
9
|
+
def_delegators :@raw, :[], :[]=
|
10
|
+
def raw; @raw ||= {}; end
|
11
|
+
def initialize(input = {})
|
12
|
+
@raw = input
|
13
|
+
end
|
14
|
+
}
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.dummy_response_class
|
18
|
+
Class.new {
|
19
|
+
include(Groem::Marshal::Response)
|
20
|
+
require 'forwardable'
|
21
|
+
extend Forwardable
|
22
|
+
def_delegators :@raw, :[], :[]=
|
23
|
+
def raw; @raw ||= []; end
|
24
|
+
def initialize(input = [])
|
25
|
+
@raw = input
|
26
|
+
end
|
27
|
+
}
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.dummy_request(env = {}, hdrs = {}, notifs = {})
|
31
|
+
klass = dummy_request_class
|
32
|
+
klass.new({'environment' => env,
|
33
|
+
'headers' => hdrs,
|
34
|
+
'notifications' => notifs
|
35
|
+
})
|
36
|
+
end
|
37
|
+
|
38
|
+
|
39
|
+
|
40
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
$LOAD_PATH.unshift File.expand_path(File.dirname(__FILE__))
|
2
|
+
require 'rubygems'
|
3
|
+
require 'bundler'
|
4
|
+
Bundler.setup :default
|
5
|
+
|
6
|
+
require 'lib/groem'
|
7
|
+
|
8
|
+
Dir[File.expand_path(File.join(File.dirname(__FILE__), 'shared', '**', '*.rb'))].each do |f|
|
9
|
+
load f
|
10
|
+
end
|
11
|
+
|
12
|
+
Bundler.setup :development
|
13
|
+
require 'minitest/spec'
|
14
|
+
MiniTest::Unit.autorun
|
@@ -0,0 +1,77 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__),'..','spec_helper')
|
2
|
+
|
3
|
+
describe 'Groem::App #[]' do
|
4
|
+
|
5
|
+
describe 'after initializing' do
|
6
|
+
|
7
|
+
it 'should set the application_name header based on input' do
|
8
|
+
@subject = Groem::App.new('thing')
|
9
|
+
@subject['headers']['Application-Name'].must_equal 'thing'
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'should default the environment when no options passed' do
|
13
|
+
@subject = Groem::App.new('thing')
|
14
|
+
@subject['environment'].must_equal Groem::App::DEFAULT_ENV
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'should merge keys from input environment option into default environment' do
|
18
|
+
input = {'version' => '1.2',
|
19
|
+
'request_method' => 'HELLO',
|
20
|
+
'encryption_id' => 'ABC'
|
21
|
+
}
|
22
|
+
@subject = Groem::App.new('thing', {:environment => input})
|
23
|
+
@subject['environment'].must_equal Groem::App::DEFAULT_ENV.merge(input)
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'should set each option in headers hash besides environment, host, and port' do
|
27
|
+
opts = {:environment => {},
|
28
|
+
:host => 'foo',
|
29
|
+
:port => 12345,
|
30
|
+
:x_option_1 => '1',
|
31
|
+
:x_option_2 => '2',
|
32
|
+
:x_option_3 => '3'
|
33
|
+
}
|
34
|
+
@subject = Groem::App.new('thing', opts)
|
35
|
+
@subject['headers'].must_equal(
|
36
|
+
{'Application-Name' => 'thing',
|
37
|
+
'X-Option-1' => '1',
|
38
|
+
'X-Option-2' => '2',
|
39
|
+
'X-Option-3' => '3'
|
40
|
+
}
|
41
|
+
)
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'should initialize the notifications hash to empty' do
|
45
|
+
@subject = Groem::App.new('thing')
|
46
|
+
@subject['notifications'].must_equal({})
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
|
51
|
+
describe 'after setting header' do
|
52
|
+
|
53
|
+
it 'should add the header to the headers hash based on input' do
|
54
|
+
@subject = Groem::App.new('thing')
|
55
|
+
@subject.header('x_header', 'boo')
|
56
|
+
@subject['headers']['X-Header'].must_equal 'boo'
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
|
61
|
+
|
62
|
+
describe 'after setting notification' do
|
63
|
+
|
64
|
+
it 'should set the notifications hash defaulting enabled to true' do
|
65
|
+
@subject = Groem::App.new('thing')
|
66
|
+
@subject.notification 'action' do end
|
67
|
+
@subject['notifications'].keys.must_include 'action'
|
68
|
+
@subject['notifications']['action'].keys.must_include 'Notification-Enabled'
|
69
|
+
@subject['notifications']['action']['Notification-Enabled'].must_equal 'True'
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
73
|
+
|
74
|
+
|
75
|
+
end
|
76
|
+
|
77
|
+
|
@@ -0,0 +1,380 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__),'..','spec_helper')
|
2
|
+
|
3
|
+
describe 'Groem::Marshal::Request.load' do
|
4
|
+
|
5
|
+
#------ REGISTER requests --------#
|
6
|
+
|
7
|
+
describe 'when valid REGISTER request with one notification and no binaries' do
|
8
|
+
|
9
|
+
before do
|
10
|
+
@input = <<-__________
|
11
|
+
GNTP/1.0 REGISTER NONE
|
12
|
+
Application-Name: SurfWriter
|
13
|
+
Application-Icon: http://www.site.org/image.jpg
|
14
|
+
X-Creator: Apple Software
|
15
|
+
X-Application-ID:08d6c05a21512a79a1dfeb9d2a8f262f
|
16
|
+
Notifications-Count: 1
|
17
|
+
|
18
|
+
Notification-Name: Download Complete
|
19
|
+
Notification-Display-Name: Download completed
|
20
|
+
Notification-Enabled:True
|
21
|
+
X-Language : English
|
22
|
+
X-Timezone: PST
|
23
|
+
|
24
|
+
|
25
|
+
__________
|
26
|
+
dummy = Class.new { include(Groem::Marshal::Request) }
|
27
|
+
@subject = dummy.load(@input, false)
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'should return a hash with keys for environment, headers, and notifications' do
|
31
|
+
@subject.has_key?('environment').must_equal true
|
32
|
+
@subject.has_key?('headers').must_equal true
|
33
|
+
@subject.has_key?('notifications').must_equal true
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'environment hash should have version == 1.0' do
|
37
|
+
@subject['environment']['version'].must_equal '1.0'
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'environment hash should have request_method == REGISTER' do
|
41
|
+
@subject['environment']['request_method'].must_equal 'REGISTER'
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'headers hash should have application_name == SurfWriter' do
|
45
|
+
@subject['headers']['Application-Name'].must_equal 'SurfWriter'
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'headers hash should have notifications_count == 1' do
|
49
|
+
@subject['headers']['Notifications-Count'].must_equal '1'
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'headers hash should have x_application_id == 08d6c05a21512a79a1dfeb9d2a8f262f' do
|
53
|
+
@subject['headers']['X-Application-ID'].must_equal '08d6c05a21512a79a1dfeb9d2a8f262f'
|
54
|
+
end
|
55
|
+
|
56
|
+
it 'notifications hash should have 1 key' do
|
57
|
+
@subject['notifications'].keys.size.must_equal 1
|
58
|
+
end
|
59
|
+
|
60
|
+
it 'notifications hash should have key \'Download Complete\'' do
|
61
|
+
@subject['notifications']['Download Complete'].wont_be_nil
|
62
|
+
end
|
63
|
+
|
64
|
+
it '\'Download Complete\' notification should have notification_display_name == \'Download completed\'' do
|
65
|
+
@subject['notifications']['Download Complete']['Notification-Display-Name'].must_equal 'Download completed'
|
66
|
+
end
|
67
|
+
|
68
|
+
it '\'Download Complete\' notification should have notification_enabled == \'True\'' do
|
69
|
+
@subject['notifications']['Download Complete']['Notification-Enabled'].must_equal 'True'
|
70
|
+
end
|
71
|
+
|
72
|
+
it '\'Download Complete\' notification should have x_timezone == \'PST\'' do
|
73
|
+
@subject['notifications']['Download Complete']['X-Timezone'].must_equal 'PST'
|
74
|
+
end
|
75
|
+
|
76
|
+
it '\'Download Complete\' notification should have x_language == \'English\'' do
|
77
|
+
@subject['notifications']['Download Complete']['X-Language'].must_equal 'English'
|
78
|
+
end
|
79
|
+
|
80
|
+
it 'headers hash should not have x_timezone key' do
|
81
|
+
@subject['headers'].has_key?('X-Timezone').must_equal false
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
85
|
+
|
86
|
+
describe 'when valid REGISTER request with three notifications and no binaries' do
|
87
|
+
|
88
|
+
before do
|
89
|
+
@input = <<-__________
|
90
|
+
GNTP/1.0 REGISTER NONE
|
91
|
+
Application-Name: SurfWriter
|
92
|
+
Application-Icon: http://www.site.org/image.jpg
|
93
|
+
X-Creator: Apple Software
|
94
|
+
X-Application-ID: 08d6c05a21512a79a1dfeb9d2a8f262f
|
95
|
+
Notifications-Count: 3
|
96
|
+
|
97
|
+
Notification-Name: Download Complete
|
98
|
+
Notification-Display-Name: Download completed
|
99
|
+
Notification-Enabled: True
|
100
|
+
X-Language: English
|
101
|
+
X-Timezone: PST
|
102
|
+
|
103
|
+
Notification-Name: Download Started
|
104
|
+
Notification-Display-Name: Download starting
|
105
|
+
Notification-Enabled: False
|
106
|
+
X-Timezone: GMT
|
107
|
+
|
108
|
+
Notification-Name: Download Error
|
109
|
+
Notification-Display-Name: Error downloading
|
110
|
+
Notification-Enabled: True
|
111
|
+
X-Language: Spanish
|
112
|
+
|
113
|
+
__________
|
114
|
+
dummy = Class.new { include(Groem::Marshal::Request) }
|
115
|
+
@subject = dummy.load(@input, false)
|
116
|
+
end
|
117
|
+
|
118
|
+
it 'should return a hash with keys for environment, headers, and notifications' do
|
119
|
+
@subject.has_key?('environment').must_equal true
|
120
|
+
@subject.has_key?('headers').must_equal true
|
121
|
+
@subject.has_key?('notifications').must_equal true
|
122
|
+
end
|
123
|
+
|
124
|
+
it 'notifications hash should have 3 keys' do
|
125
|
+
@subject['notifications'].keys.size.must_equal 3
|
126
|
+
end
|
127
|
+
|
128
|
+
it 'notifications hash should have key \'Download Complete\'' do
|
129
|
+
@subject['notifications']['Download Complete'].wont_be_nil
|
130
|
+
end
|
131
|
+
|
132
|
+
it 'notifications hash should have key \'Download Started\'' do
|
133
|
+
@subject['notifications']['Download Started'].wont_be_nil
|
134
|
+
end
|
135
|
+
|
136
|
+
it 'notifications hash should have key \'Download Error\'' do
|
137
|
+
@subject['notifications']['Download Error'].wont_be_nil
|
138
|
+
end
|
139
|
+
|
140
|
+
it '\'Download Complete\' notification should have notification_display_name == \'Download completed\'' do
|
141
|
+
@subject['notifications']['Download Complete']['Notification-Display-Name'].must_equal 'Download completed'
|
142
|
+
end
|
143
|
+
|
144
|
+
it '\'Download Started\' notification should have notification_enabled == \'False\'' do
|
145
|
+
@subject['notifications']['Download Started']['Notification-Enabled'].must_equal 'False'
|
146
|
+
end
|
147
|
+
|
148
|
+
it '\'Download Error\' notification should have x_language == \'Spanish\'' do
|
149
|
+
@subject['notifications']['Download Error']['X-Language'].must_equal 'Spanish'
|
150
|
+
end
|
151
|
+
|
152
|
+
it '\'Download Started\' notification should not have X-Language key' do
|
153
|
+
@subject['notifications']['Download Started'].has_key?('X-Language').must_equal false
|
154
|
+
end
|
155
|
+
|
156
|
+
it '\'Download Error\' notification should not have X-Timezone key' do
|
157
|
+
@subject['notifications']['Download Error'].has_key?('X-Timezone').must_equal false
|
158
|
+
end
|
159
|
+
|
160
|
+
end
|
161
|
+
|
162
|
+
describe 'when valid REGISTER request with one binary referenced once in headers' do
|
163
|
+
|
164
|
+
end
|
165
|
+
|
166
|
+
describe 'when valid REGISTER request with one binary referenced once in notifications' do
|
167
|
+
|
168
|
+
end
|
169
|
+
|
170
|
+
describe 'when valid REGISTER request with three binaries referenced once each in headers and notifications' do
|
171
|
+
|
172
|
+
end
|
173
|
+
|
174
|
+
describe 'when valid REGISTER request with three binaries referenced twice each in headers and notifications' do
|
175
|
+
|
176
|
+
end
|
177
|
+
|
178
|
+
|
179
|
+
#------ NOTIFY requests --------#
|
180
|
+
|
181
|
+
describe 'when valid NOTIFY request with no binaries' do
|
182
|
+
|
183
|
+
before do
|
184
|
+
@input = <<-__________
|
185
|
+
GNTP/1.0 NOTIFY NONE
|
186
|
+
Application-Name: SurfWriter
|
187
|
+
Notification-Name: Download Complete
|
188
|
+
Notification-ID: 999
|
189
|
+
Notification-Title:XYZ finished downloading
|
190
|
+
Notification-Sticky: True
|
191
|
+
Notification-Icon : http://www.whatever.com/poo.jpg
|
192
|
+
|
193
|
+
__________
|
194
|
+
dummy = Class.new { include(Groem::Marshal::Request) }
|
195
|
+
@subject = dummy.load(@input, false)
|
196
|
+
end
|
197
|
+
|
198
|
+
it 'should return a hash with keys for environment, headers, and notifications' do
|
199
|
+
@subject.has_key?('environment').must_equal true
|
200
|
+
@subject.has_key?('headers').must_equal true
|
201
|
+
@subject.has_key?('notifications').must_equal true
|
202
|
+
end
|
203
|
+
|
204
|
+
it 'environment hash should have version == 1.0' do
|
205
|
+
@subject['environment']['version'].must_equal '1.0'
|
206
|
+
end
|
207
|
+
|
208
|
+
it 'environment hash should have request_method == NOTIFY' do
|
209
|
+
@subject['environment']['request_method'].must_equal 'NOTIFY'
|
210
|
+
end
|
211
|
+
|
212
|
+
it 'headers hash should have application_name == SurfWriter' do
|
213
|
+
@subject['headers']['Application-Name'].must_equal 'SurfWriter'
|
214
|
+
end
|
215
|
+
|
216
|
+
it 'headers hash should have notification_title == \'XYZ finished downloading\'' do
|
217
|
+
@subject['headers']['Notification-Title'].must_equal 'XYZ finished downloading'
|
218
|
+
end
|
219
|
+
|
220
|
+
it 'headers hash should have notification_sticky == \'True\'' do
|
221
|
+
@subject['headers']['Notification-Sticky'].must_equal 'True'
|
222
|
+
end
|
223
|
+
|
224
|
+
it 'headers hash should have notification_icon == \'http://www.whatever.com/poo.jpg\'' do
|
225
|
+
@subject['headers']['Notification-Icon'].must_equal 'http://www.whatever.com/poo.jpg'
|
226
|
+
end
|
227
|
+
|
228
|
+
it 'notifications hash should be empty' do
|
229
|
+
@subject['notifications'].empty?.must_equal true
|
230
|
+
end
|
231
|
+
|
232
|
+
end
|
233
|
+
|
234
|
+
describe 'when valid NOTIFY request with one binary referenced once' do
|
235
|
+
|
236
|
+
end
|
237
|
+
|
238
|
+
describe 'when valid NOTIFY request with three binaries referenced once each' do
|
239
|
+
|
240
|
+
end
|
241
|
+
|
242
|
+
describe 'when valid NOTIFY request with three binaries referenced twice each' do
|
243
|
+
|
244
|
+
end
|
245
|
+
|
246
|
+
end
|
247
|
+
|
248
|
+
|
249
|
+
|
250
|
+
#---------- dump -----------#
|
251
|
+
|
252
|
+
|
253
|
+
describe 'Groem::Marshal::Request#dump' do
|
254
|
+
|
255
|
+
describe 'when valid REGISTER request with one notification, no binaries' do
|
256
|
+
|
257
|
+
before do
|
258
|
+
@input_env = { 'protocol' => 'GNTP',
|
259
|
+
'version' => '1.0',
|
260
|
+
'request_method' => 'REGISTER',
|
261
|
+
'encryption_id' => 'NONE'
|
262
|
+
}
|
263
|
+
@input_hdrs = {'Application-Name' => 'SurfWriter',
|
264
|
+
'Application-Icon' => 'http://www.site.org/image.jpg'
|
265
|
+
}
|
266
|
+
@input_notifs = { 'Download Complete' => {
|
267
|
+
'Notification-Display-Name' => 'Download completed',
|
268
|
+
'Notification-Enabled' => 'True',
|
269
|
+
'X-Language' => 'English',
|
270
|
+
'X-Timezone' => 'PST'
|
271
|
+
}
|
272
|
+
}
|
273
|
+
|
274
|
+
@subject = MarshalHelper.dummy_request(
|
275
|
+
@input_env, @input_hdrs, @input_notifs).dump
|
276
|
+
end
|
277
|
+
|
278
|
+
it 'should output a string' do
|
279
|
+
@subject.class.must_be_same_as String
|
280
|
+
puts
|
281
|
+
puts '------------Groem::Marshal::Request#dump when valid REGISTER request with one notification, no binaries ------------'
|
282
|
+
puts @subject
|
283
|
+
end
|
284
|
+
|
285
|
+
it 'should output 10 lines terminated by CRLF' do
|
286
|
+
lines = @subject.split("\r\n")
|
287
|
+
lines.size.must_equal 10
|
288
|
+
end
|
289
|
+
|
290
|
+
it 'should end with 2 CRLF' do
|
291
|
+
@subject[-4,4].must_equal "\r\n\r\n"
|
292
|
+
end
|
293
|
+
|
294
|
+
it 'should output the first line as the standard GNTP first header' do
|
295
|
+
lines = @subject.split("\r\n")
|
296
|
+
lines[0].must_match(/^#{@input_env['protocol']}\/#{@input_env['version']}\s+#{@input_env['request_method']}\s+#{@input_env['encryption_id']}\s*$/i)
|
297
|
+
end
|
298
|
+
|
299
|
+
it 'should output the notification count == count of input notifications' do
|
300
|
+
@subject.must_match(/^\s*Notifications-Count\s*:\s*#{@input_notifs.keys.count}\s*$/i)
|
301
|
+
end
|
302
|
+
|
303
|
+
it 'should have a notification-name line for each input notification' do
|
304
|
+
@input_notifs.each_pair do |name, pairs|
|
305
|
+
@subject.must_match(/^\s*Notification-Name\s*:\s*#{name}\s*$/i)
|
306
|
+
end
|
307
|
+
end
|
308
|
+
|
309
|
+
end
|
310
|
+
|
311
|
+
describe 'when valid NOTIFY request with one notification, no binaries' do
|
312
|
+
|
313
|
+
before do
|
314
|
+
@input_env = { 'protocol' => 'GNTP',
|
315
|
+
'version' => '1.0',
|
316
|
+
'request_method' => 'NOTIFY',
|
317
|
+
'encryption_id' => 'NONE'
|
318
|
+
}
|
319
|
+
@input_hdrs = {'Application-Name' => 'SurfWriter',
|
320
|
+
'Notification-Name'=> 'Download Complete',
|
321
|
+
'Notification-Id' => '999',
|
322
|
+
'Notification-Title' => 'XYZ finished downloading',
|
323
|
+
'Notification-Sticky' => 'True',
|
324
|
+
'Notification-Icon' => 'http://www.whatever.com/poo.jpg'
|
325
|
+
}
|
326
|
+
@subject = MarshalHelper.dummy_request(
|
327
|
+
@input_env, @input_hdrs, {}).dump
|
328
|
+
end
|
329
|
+
|
330
|
+
it 'should output a string' do
|
331
|
+
@subject.class.must_be_same_as String
|
332
|
+
puts
|
333
|
+
puts '------------Groem::Marshal::Request#dump when valid NOTIFY request with one notification, no binaries ------------'
|
334
|
+
puts @subject
|
335
|
+
end
|
336
|
+
|
337
|
+
it 'should output the first line as the standard GNTP first header for NOTIFY request' do
|
338
|
+
lines = @subject.split("\r\n")
|
339
|
+
lines[0].must_match(/^GNTP\/1.0\s+NOTIFY\s+NONE\s*$/i)
|
340
|
+
end
|
341
|
+
|
342
|
+
it 'should not output a notification-count line' do
|
343
|
+
@subject.wont_match(/^\s*Notifications-Count\s*:\s*\d+\s*$/i)
|
344
|
+
end
|
345
|
+
|
346
|
+
end
|
347
|
+
|
348
|
+
describe 'when no environment' do
|
349
|
+
|
350
|
+
before do
|
351
|
+
@input_hdrs = {'Application-Name' => 'SurfWriter',
|
352
|
+
'Notification-Name'=> 'Download Complete',
|
353
|
+
'Notification-ID' => '999',
|
354
|
+
'Notification-Title' => 'XYZ finished downloading',
|
355
|
+
'Notification-Sticky' => 'True',
|
356
|
+
'Notification-Icon' => 'http://www.whatever.com/poo.jpg'
|
357
|
+
}
|
358
|
+
@subject = MarshalHelper.dummy_request(
|
359
|
+
{}, @input_hdrs, {}).dump
|
360
|
+
end
|
361
|
+
|
362
|
+
it 'should output a string' do
|
363
|
+
@subject.class.must_be_same_as String
|
364
|
+
puts
|
365
|
+
puts '------------Groem::Marshal::Request#dump when no environment ------------'
|
366
|
+
puts @subject
|
367
|
+
end
|
368
|
+
|
369
|
+
it 'should output the first line as the default GNTP first header for NOTIFY request' do
|
370
|
+
lines = @subject.split("\r\n")
|
371
|
+
lines[0].must_match(/^GNTP\/1.0\s+NOTIFY\s+NONE\s*$/i)
|
372
|
+
end
|
373
|
+
|
374
|
+
it 'should not output a notification-count line' do
|
375
|
+
@subject.wont_match(/^\s*Notifications-Count\s*:\s*\d+\s*$/i)
|
376
|
+
end
|
377
|
+
|
378
|
+
end
|
379
|
+
|
380
|
+
end
|