pling 0.1.0 → 0.2.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.
data/lib/pling/message.rb CHANGED
@@ -12,6 +12,58 @@ module Pling
12
12
  body &&= body.to_s
13
13
  @body = body
14
14
  end
15
+
16
+ ##
17
+ # The message subject - not supported by all gateways
18
+ #
19
+ # @overload subject
20
+ # @overload subject=(subject)
21
+ # @param [#to_s] subject
22
+ attr_reader :subject
23
+
24
+ def subject=(subject)
25
+ subject &&= subject.to_s
26
+ @subject = subject
27
+ end
28
+
29
+ ##
30
+ # The message badge - not supported by all gateways
31
+ #
32
+ # @overload badge
33
+ # @overload badge=(badge)
34
+ # @param [#to_s] badge
35
+ attr_reader :badge
36
+
37
+ def badge=(badge)
38
+ badge &&= badge.to_s
39
+ @badge = badge
40
+ end
41
+
42
+ ##
43
+ # The message sound - not supported by all gateways
44
+ #
45
+ # @overload sound
46
+ # @overload sound=(sound)
47
+ # @param [#to_s] sound
48
+ attr_reader :sound
49
+
50
+ def sound=(sound)
51
+ sound &&= sound.to_s
52
+ @sound = sound
53
+ end
54
+
55
+ ##
56
+ # The message payload - not supported by all gateways
57
+ #
58
+ # @overload payload
59
+ # @overload payload=(sound)
60
+ # @param [#to_hash] payload
61
+ attr_reader :payload
62
+
63
+ def payload=(payload)
64
+ payload &&= payload.to_hash
65
+ @payload = payload
66
+ end
15
67
 
16
68
  ##
17
69
  # Creates a new Pling::Message instance with the given body
@@ -1,12 +1,43 @@
1
1
  module Pling
2
2
  module Middleware
3
+ ##
4
+ # This is the base class to implement custom middleware for pling.
5
+ #
6
+ # Middleware should inherit from this base class and implement a {#deliver} method.
7
+ # To call the next middleware on the stack this method must yield passing the given
8
+ # message and device.
9
+ #
10
+ # @example
11
+ #
12
+ # class Pling::Middleware::TimeFilter < Pling::Middleware::Base
13
+ # def deliver(message, device)
14
+ # yield(message, device) if configuration[:range].include? Time.now.hour
15
+ # end
16
+ #
17
+ # protected
18
+ #
19
+ # def default_configuration
20
+ # super.merge({
21
+ # :range => 8..22
22
+ # })
23
+ # end
24
+ # end
3
25
  class Base
4
26
  include Pling::Configurable
5
27
 
28
+ ##
29
+ # Initializes a new middleware instance
30
+ #
31
+ # @param [Hash] configuration
6
32
  def initialize(configuration = {})
7
33
  setup_configuration(configuration)
8
34
  end
9
35
 
36
+ ##
37
+ # Processes the given message and device and passes it to the next
38
+ # middleware on the stack.
39
+ #
40
+ # @yield [message, device] Call the next middleware on the stack
10
41
  def deliver(message, device)
11
42
  yield(message, device)
12
43
  end
data/lib/pling/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Pling
2
- VERSION = "0.1.0"
2
+ VERSION = "0.2.0"
3
3
  end
data/lib/pling.rb CHANGED
@@ -1,10 +1,13 @@
1
- require "pling/version"
1
+ require 'pling/version'
2
+ require 'logger'
2
3
 
3
4
  module Pling
4
5
 
5
6
  autoload :Device, 'pling/device'
6
7
  autoload :Message, 'pling/message'
7
8
  autoload :Gateway, 'pling/gateway'
9
+ autoload :APN, 'pling/apn'
10
+ autoload :C2DM, 'pling/c2dm'
8
11
  autoload :Middleware, 'pling/middleware'
9
12
  autoload :Adapter, 'pling/adapter'
10
13
  autoload :Configurable, 'pling/configurable'
@@ -13,12 +16,23 @@ module Pling
13
16
  @gateways = Pling::DelayedInitializer.new
14
17
  @middlewares = Pling::DelayedInitializer.new
15
18
  @adapter = Pling::Adapter::Base.new
19
+ @logger = Logger.new(nil)
16
20
 
17
21
  class Error < StandardError; end
18
22
  class AuthenticationFailed < Error; end
19
- class DeliveryFailed < Error; end
20
23
  class NoGatewayFound < Error; end
21
24
 
25
+ class DeliveryFailed < Error
26
+ attr_reader :pling_message, :pling_device
27
+
28
+ def initialize(message = nil, pling_message = nil, pling_device = nil)
29
+ super(message)
30
+ @pling_message = pling_message
31
+ @pling_device = pling_device
32
+ end
33
+ end
34
+
35
+
22
36
  class << self
23
37
  ##
24
38
  # Stores the list of available gateway instances
@@ -46,6 +60,12 @@ module Pling
46
60
  # @return [Pling::Adapter]
47
61
  attr_accessor :adapter
48
62
 
63
+ ##
64
+ # Stores the logger. Defaults to Logger.new(nil)
65
+ #
66
+ # @return [Logger]
67
+ attr_accessor :logger
68
+
49
69
  ##
50
70
  # Allows configuration of Pling by passing a config object to the given block
51
71
  #
@@ -66,6 +86,8 @@ module Pling
66
86
  message = Pling._convert(message, :message)
67
87
  device = Pling._convert(device, :device)
68
88
 
89
+ Pling.logger.info "#{self.class} -- Delivering #{message.inspect} to #{device.inspect}"
90
+
69
91
  stack ||= middlewares.initialize! + [adapter]
70
92
 
71
93
  stack.shift.deliver(message, device) do |m, d|
data/pling.gemspec CHANGED
@@ -7,7 +7,7 @@ Gem::Specification.new do |s|
7
7
  s.version = Pling::VERSION
8
8
  s.authors = ["Benedikt Deicke", "Konstantin Tennhard", "Christian Bäuerlein"]
9
9
  s.email = ["benedikt@synatic.net", "me@t6d.de", "fabrik42@gmail.com"]
10
- s.homepage = "https://flinc.github.com/pling"
10
+ s.homepage = "http://flinc.github.com/pling"
11
11
  s.summary = %q{Pling is a notification framework that supports multiple gateways}
12
12
  s.description = %q{Pling is a notification framework that supports multiple gateways. Currently supported are Android Push and SMS.}
13
13
 
Binary file
Binary file
@@ -0,0 +1,124 @@
1
+ require 'spec_helper'
2
+
3
+ describe Pling::APN::Connection do
4
+
5
+ let(:configuration) do
6
+ {
7
+ :host => "localhost",
8
+ :port => 2195,
9
+ :certificate => "/path/to/certificate.pem"
10
+ }
11
+ end
12
+
13
+ let(:ssl_context) { double(OpenSSL::SSL::SSLContext).as_null_object }
14
+ let(:x509_certificate) { double(OpenSSL::X509::Certificate).as_null_object }
15
+ let(:pkey_rsa) { double(OpenSSL::PKey::RSA).as_null_object }
16
+ let(:tcp_socket) { double(TCPSocket).as_null_object }
17
+ let(:ssl_socket) { double(OpenSSL::SSL::SSLSocket, :closed? => false).as_null_object }
18
+
19
+ before do
20
+ File.stub(:read).and_return('')
21
+ OpenSSL::SSL::SSLContext.stub(:new).and_return(ssl_context)
22
+ OpenSSL::X509::Certificate.stub(:new).and_return(x509_certificate)
23
+ OpenSSL::PKey::RSA.stub(:new).and_return(pkey_rsa)
24
+ TCPSocket.stub(:new).and_return(tcp_socket)
25
+ OpenSSL::SSL::SSLSocket.stub(:new).and_return(ssl_socket)
26
+ end
27
+
28
+ context "when creating it from a valid configuration" do
29
+
30
+ it "should read the certificate" do
31
+ File.should_receive(:read).
32
+ with('/path/to/certificate.pem').
33
+ and_return("--- CERT CONTENT ---")
34
+ end
35
+
36
+ it "should create a tcp socket" do
37
+ TCPSocket.should_receive(:new).with("localhost", 2195)
38
+ end
39
+
40
+ it "should create a ssl socket" do
41
+ OpenSSL::SSL::SSLSocket.should_receive(:new).with(tcp_socket, ssl_context)
42
+ end
43
+
44
+ it "should create a ssl context" do
45
+ OpenSSL::SSL::SSLContext.should_receive(:new).and_return(ssl_context)
46
+ ssl_context.should_receive(:cert=).with(x509_certificate)
47
+ ssl_context.should_receive(:key=).with(pkey_rsa)
48
+ end
49
+
50
+ it "should connect the ssl socket" do
51
+ ssl_socket.should_receive(:connect)
52
+ end
53
+
54
+ after do
55
+ Pling::APN::Connection.new(configuration)
56
+ end
57
+
58
+ end
59
+
60
+ context 'when created with an invalid configuration' do
61
+
62
+ it "should raise an error when :certificate is missing" do
63
+ expect { Pling::APN::Connection.new({}) }.to raise_error(ArgumentError, /:certificate is missing/)
64
+ end
65
+
66
+ end
67
+
68
+ context "when writing data" do
69
+
70
+ subject do
71
+ Pling::APN::Connection.new(configuration)
72
+ end
73
+
74
+ it "should simply pass on the data to the underlying SSL socket" do
75
+ data = 'Pass this through!'
76
+ ssl_socket.should_receive(:write).with(data)
77
+ subject.write(data)
78
+ end
79
+
80
+ it "should raise an exception it is closed" do
81
+ ssl_socket.stub(:closed? => true)
82
+ subject.close
83
+ expect { subject.write("Waahhhh!") }.to raise_error(IOError, "Connection closed")
84
+ end
85
+
86
+ it "should retry three times on Errno::EPIPE" do
87
+ ssl_socket.should_receive(:write).exactly(3).times.and_raise(Errno::EPIPE)
88
+ expect { subject.write("Whoops!") }.to raise_error(IOError)
89
+ end
90
+
91
+ it 'should reconnect between retries' do
92
+ ssl_socket.stub(:write).and_raise(Errno::EPIPE)
93
+ ssl_socket.should_receive(:connect).exactly(3).times
94
+ ssl_socket.should_receive(:close).exactly(2).times
95
+ expect { subject.write("Whoops!") }.to raise_error(IOError)
96
+ end
97
+
98
+ end
99
+
100
+ describe "#gets" do
101
+ subject do
102
+ Pling::APN::Connection.new(configuration)
103
+ end
104
+
105
+ it "should simply pass on the data" do
106
+ data = 'Pass this through!'
107
+ ssl_socket.should_receive(:gets).and_return(data)
108
+ subject.gets.should eq(data)
109
+ end
110
+ end
111
+
112
+ describe "#read" do
113
+ subject do
114
+ Pling::APN::Connection.new(configuration)
115
+ end
116
+
117
+ it "should simply pass on the data" do
118
+ data = 'Pass this through!'
119
+ ssl_socket.should_receive(:read).and_return(data)
120
+ subject.read.should eq(data)
121
+ end
122
+ end
123
+
124
+ end
@@ -0,0 +1,44 @@
1
+ require 'spec_helper'
2
+
3
+ describe Pling::APN::Feedback do
4
+
5
+ let(:connection) { double(Pling::APN::Connection, :closed? => false).as_null_object }
6
+
7
+ let(:time) { Time.now.to_i }
8
+
9
+ # Simulate device tokens of _different_ length! A device token is _not_
10
+ # necessarily 32 byte long. Check the documentation if you don't belive it. :)
11
+ # http://bit.ly/apple-apn-feedback-documentation
12
+ let(:token_0) { "0" * 64 }
13
+ let(:token_1) { "00000000000000000000000000000001" }
14
+
15
+ let(:feedback_0) { [time, 32, token_0].pack("NnH*") }
16
+ let(:feedback_1) { [time, 16, token_1].pack("NnH*") }
17
+
18
+ subject do
19
+ described_class.new(:certificate => '/path/to/certificate.pem')
20
+ end
21
+
22
+ before do
23
+ Pling::APN::Connection.stub(:new).and_return(connection)
24
+ connection.stub(:gets).and_return(nil)
25
+ end
26
+
27
+ it { should respond_to(:get) }
28
+
29
+ context "when getting feedback" do
30
+
31
+ it "should be in form of a list" do
32
+ subject.get.should be_kind_of(Array)
33
+ end
34
+
35
+ it "should contain all tokens send by Apple" do
36
+ connection.stub(:gets).and_return(feedback_0, feedback_1, nil)
37
+ tokens = subject.get
38
+
39
+ tokens.should be == [token_0, token_1]
40
+ end
41
+
42
+ end
43
+
44
+ end
@@ -0,0 +1,141 @@
1
+ require 'spec_helper'
2
+
3
+ describe Pling::APN::Gateway do
4
+
5
+ let(:valid_configuration) { { :certificate => '/path/to/certificate.pem' } }
6
+
7
+ let(:message) { Pling::Message.new('Hello from Pling') }
8
+
9
+ let(:device) { Pling::Device.new(:identifier => '0' * 64, :type => :iphone) }
10
+
11
+ let(:connection) { double(Pling::APN::Connection).as_null_object }
12
+
13
+ before do
14
+ Pling::APN::Connection.stub(:new).and_return(connection)
15
+ end
16
+
17
+ it 'should handle various apn related device types' do
18
+ Pling::APN::Gateway.handled_types.should =~ [:apple, :apn, :ios, :ipad, :iphone, :ipod]
19
+ end
20
+
21
+ describe '#deliver' do
22
+ subject { Pling::APN::Gateway.new(valid_configuration) }
23
+
24
+ it 'should raise an error if no message is given' do
25
+ expect { subject.deliver(nil, device) }.to raise_error
26
+ end
27
+
28
+ it 'should raise an error the device is given' do
29
+ expect { subject.deliver(message, nil) }.to raise_error
30
+ end
31
+
32
+ it 'should call #to_pling_message on the given message' do
33
+ message.should_receive(:to_pling_message).and_return(message)
34
+ subject.deliver(message, device)
35
+ end
36
+
37
+ it 'should call #to_pling_device on the given device' do
38
+ device.should_receive(:to_pling_device).and_return(device)
39
+ subject.deliver(message, device)
40
+ end
41
+
42
+ it 'should raise an exception when the payload exceeds 256 bytes' do
43
+ message.body = "X" * 256
44
+ expect { subject.deliver(message, device) }.to raise_error(Pling::DeliveryFailed, /Payload size of \d+ exceeds allowed size of 256 bytes/)
45
+ end
46
+
47
+ it 'should try to deliver the given message' do
48
+ expected_header = "\x00\x00 \x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00$"
49
+ expected_payload = {
50
+ 'aps' => {
51
+ 'alert' => 'Hello from Pling'
52
+ }
53
+ }
54
+
55
+ connection.stub(:write) do |packet|
56
+ header, payload = packet[0..36], packet[37..-1]
57
+ header.should eq(expected_header)
58
+ JSON.parse(payload).should eq(expected_payload)
59
+ end
60
+
61
+ subject.deliver(message, device)
62
+ end
63
+
64
+ it 'should include the given badge' do
65
+ expected_payload = {
66
+ 'aps' => {
67
+ 'alert' => 'Hello from Pling',
68
+ 'badge' => 10
69
+ }
70
+ }
71
+
72
+ connection.stub(:write) do |packet|
73
+ JSON.parse(packet[37..-1]).should eq(expected_payload)
74
+ end
75
+
76
+ message.badge = 10
77
+
78
+ subject.deliver(message, device)
79
+ end
80
+
81
+ it 'should include the given badge' do
82
+ expected_payload = {
83
+ 'aps' => {
84
+ 'alert' => 'Hello from Pling',
85
+ 'sound' => 'pling'
86
+ }
87
+ }
88
+
89
+ connection.stub(:write) do |packet|
90
+ JSON.parse(packet[37..-1]).should eq(expected_payload)
91
+ end
92
+
93
+ message.sound = :pling
94
+
95
+ subject.deliver(message, device)
96
+ end
97
+
98
+ context 'when configured to include payload' do
99
+ before do
100
+ valid_configuration.merge!(:payload => true)
101
+ message.payload = { :data => 'available' }
102
+ end
103
+
104
+ it 'should include the given payload' do
105
+ expected_payload = {
106
+ 'aps' => {
107
+ 'alert' => 'Hello from Pling'
108
+ },
109
+ 'data' => 'available'
110
+ }
111
+
112
+ connection.stub(:write) do |packet|
113
+ JSON.parse(packet[37..-1]).should eq(expected_payload)
114
+ end
115
+
116
+ subject.deliver(message, device)
117
+ end
118
+ end
119
+
120
+ context 'when configured to not include payload' do
121
+ before do
122
+ valid_configuration.merge!(:payload => false)
123
+ message.payload = { :data => 'available' }
124
+ end
125
+
126
+ it 'should not include the given payload' do
127
+ expected_payload = {
128
+ 'aps' => {
129
+ 'alert' => 'Hello from Pling'
130
+ }
131
+ }
132
+
133
+ connection.stub(:write) do |packet|
134
+ JSON.parse(packet[37..-1]).should eq(expected_payload)
135
+ end
136
+
137
+ subject.deliver(message, device)
138
+ end
139
+ end
140
+ end
141
+ end
@@ -1,6 +1,6 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe Pling::Gateway::C2DM do
3
+ describe Pling::C2DM::Gateway do
4
4
 
5
5
  let(:valid_configuration) do
6
6
  { :email => 'someone@gmail.com', :password => 'random', :source => 'some-source' }
@@ -28,12 +28,16 @@ describe Pling::Gateway::C2DM do
28
28
 
29
29
  before { Faraday.stub(:new).and_return(connection_mock) }
30
30
 
31
+ it 'should handle various apn related device types' do
32
+ Pling::C2DM::Gateway.handled_types.should =~ [:android, :c2dm]
33
+ end
34
+
31
35
  context 'when created with an invalid configuration' do
32
36
  [:email, :password, :source].each do |attribute|
33
37
  it "should raise an error when :#{attribute} is missing" do
34
38
  configuration = valid_configuration
35
39
  configuration.delete(attribute)
36
- expect { Pling::Gateway::C2DM.new(configuration) }.to raise_error(ArgumentError, /:#{attribute} is missing/)
40
+ expect { Pling::C2DM::Gateway.new(configuration) }.to raise_error(ArgumentError, /:#{attribute} is missing/)
37
41
  end
38
42
  end
39
43
  end
@@ -52,7 +56,7 @@ describe Pling::Gateway::C2DM do
52
56
  end
53
57
 
54
58
  it 'should not raise an error' do
55
- expect { Pling::Gateway::C2DM.new(valid_configuration) }.to_not raise_error
59
+ expect { Pling::C2DM::Gateway.new(valid_configuration) }.to_not raise_error
56
60
  end
57
61
 
58
62
  it 'should try to authenticate' do
@@ -60,31 +64,31 @@ describe Pling::Gateway::C2DM do
60
64
  with('https://www.google.com/accounts/ClientLogin', valid_authentication_params).
61
65
  and_return(authentication_response_mock)
62
66
 
63
- Pling::Gateway::C2DM.new(valid_configuration)
67
+ Pling::C2DM::Gateway.new(valid_configuration)
64
68
  end
65
69
 
66
70
  it 'should extract the token from the response body' do
67
- gateway = Pling::Gateway::C2DM.new(valid_configuration)
71
+ gateway = Pling::C2DM::Gateway.new(valid_configuration)
68
72
  gateway.token.should eq('S0ME-ToKeN123')
69
73
  end
70
74
 
71
75
  it 'should raise an error if authentication was not successful' do
72
76
  authentication_response_mock.stub(:status => 403, :success? => false, :body => 'Error=BadAuthentication')
73
77
 
74
- expect { Pling::Gateway::C2DM.new(valid_configuration) }.to raise_error(Pling::AuthenticationFailed, /Authentication failed: \[403\] Error=BadAuthentication/)
78
+ expect { Pling::C2DM::Gateway.new(valid_configuration) }.to raise_error(Pling::AuthenticationFailed, /Authentication failed: \[403\] Error=BadAuthentication/)
75
79
  end
76
80
 
77
81
  it 'should raise an error if it could not extract a token from the response' do
78
82
  authentication_response_mock.stub(:body).and_return('SOMERANDOMBODY')
79
83
 
80
- expect { Pling::Gateway::C2DM.new(valid_configuration) }.to raise_error(Pling::AuthenticationFailed, /Token extraction failed/)
84
+ expect { Pling::C2DM::Gateway.new(valid_configuration) }.to raise_error(Pling::AuthenticationFailed, /Token extraction failed/)
81
85
  end
82
86
  end
83
87
 
84
88
  context 'configuration' do
85
89
  it 'should allow configuring Faraday\'s :connection settings' do
86
90
  Faraday.should_receive(:new).with(:ssl => { :verify => false })
87
- Pling::Gateway::C2DM.new(valid_configuration.merge(:connection => { :ssl => { :verify => false }}))
91
+ Pling::C2DM::Gateway.new(valid_configuration.merge(:connection => { :ssl => { :verify => false }}))
88
92
  end
89
93
 
90
94
  it 'should use Faraday::Response::Logger when :debug is set to true' do
@@ -92,7 +96,7 @@ describe Pling::Gateway::C2DM do
92
96
  builder.should_receive(:use).with(Faraday::Response::Logger)
93
97
  Faraday.stub(:new).and_yield(builder).and_return(connection_mock)
94
98
 
95
- Pling::Gateway::C2DM.new(valid_configuration.merge(:debug => true))
99
+ Pling::C2DM::Gateway.new(valid_configuration.merge(:debug => true))
96
100
  end
97
101
 
98
102
  it 'should use the adapter set with :adapter' do
@@ -100,7 +104,7 @@ describe Pling::Gateway::C2DM do
100
104
  builder.should_receive(:adapter).with(:typheus)
101
105
  Faraday.stub(:new).and_yield(builder).and_return(connection_mock)
102
106
 
103
- Pling::Gateway::C2DM.new(valid_configuration.merge(:adapter => :typheus))
107
+ Pling::C2DM::Gateway.new(valid_configuration.merge(:adapter => :typheus))
104
108
  end
105
109
 
106
110
  it 'should allow configuring the authentication_url' do
@@ -108,13 +112,13 @@ describe Pling::Gateway::C2DM do
108
112
  with('http://example.com/authentication', anything).
109
113
  and_return(authentication_response_mock)
110
114
 
111
- Pling::Gateway::C2DM.new(valid_configuration.merge(:authentication_url => 'http://example.com/authentication'))
115
+ Pling::C2DM::Gateway.new(valid_configuration.merge(:authentication_url => 'http://example.com/authentication'))
112
116
  end
113
117
  end
114
118
  end
115
119
 
116
120
  describe '#deliver' do
117
- subject { Pling::Gateway::C2DM.new(valid_configuration) }
121
+ subject { Pling::C2DM::Gateway.new(valid_configuration) }
118
122
 
119
123
  let(:message) { Pling::Message.new('Hello from Pling') }
120
124
  let(:device) { Pling::Device.new(:identifier => 'DEVICEIDENTIFIER', :type => :android) }
@@ -160,23 +164,78 @@ describe Pling::Gateway::C2DM do
160
164
  end
161
165
 
162
166
  it 'should raise a Pling::DeliveryFailed exception if the delivery was not successful' do
163
- connection_mock.should_receive(:post).
164
- with('https://android.apis.google.com/c2dm/send', valid_push_params, valid_push_headers).
165
- and_return(push_response_mock)
166
-
167
+ connection_mock.should_receive(:post).and_return(push_response_mock)
167
168
  push_response_mock.stub(:status => 401, :success? => false, :body => "Something went wrong")
168
169
 
169
170
  expect { subject.deliver(message, device) }.to raise_error Pling::DeliveryFailed, /Something went wrong/
170
171
  end
171
172
 
172
173
  it 'should raise a Pling::DeliveryFailed exception if the response body contained an error' do
174
+ connection_mock.should_receive(:post).and_return(push_response_mock)
175
+ push_response_mock.stub(:status => 200, :success? => true, :body => "Error=SomeError")
176
+
177
+ expect { subject.deliver(message, device) }.to raise_error Pling::DeliveryFailed, /Error=SomeError/
178
+ end
179
+
180
+ it 'should send data.badge if the given message has a badge' do
173
181
  connection_mock.should_receive(:post).
174
- with('https://android.apis.google.com/c2dm/send', valid_push_params, valid_push_headers).
182
+ with(anything, hash_including(:'data.badge' => '10'), anything).
175
183
  and_return(push_response_mock)
184
+ message.badge = 10
185
+ subject.deliver(message, device)
186
+ end
176
187
 
177
- push_response_mock.stub(:status => 200, :success? => true, :body => "Error=SomeError")
188
+ it 'should send data.sound if the given message has a sound' do
189
+ connection_mock.should_receive(:post).
190
+ with(anything, hash_including(:'data.sound' => 'pling'), anything).
191
+ and_return(push_response_mock)
192
+ message.sound = :pling
193
+ subject.deliver(message, device)
194
+ end
178
195
 
179
- expect { subject.deliver(message, device) }.to raise_error Pling::DeliveryFailed, /Error=SomeError/
196
+ it 'should send data.subject if the given message has a subject' do
197
+ connection_mock.should_receive(:post).
198
+ with(anything, hash_including(:'data.subject' => 'Important!'), anything).
199
+ and_return(push_response_mock)
200
+ message.subject = 'Important!'
201
+ subject.deliver(message, device)
202
+ end
203
+
204
+ context 'when configured to include payload' do
205
+ before do
206
+ valid_configuration.merge!(:payload => true)
207
+ message.payload = { :data => 'available' }
208
+ end
209
+
210
+ it 'should include the given payload' do
211
+ connection_mock.should_receive(:post).
212
+ with(anything, hash_including(:'data.data' => 'available'), anything).
213
+ and_return(push_response_mock)
214
+ subject.deliver(message, device)
215
+ end
216
+ end
217
+
218
+ context 'when configured to not include payload' do
219
+ before do
220
+ valid_configuration.merge!(:payload => false)
221
+ message.payload = { :data => 'available' }
222
+ end
223
+
224
+ it 'should include the given payload' do
225
+ connection_mock.should_receive(:post).
226
+ with(anything, hash_not_including(:'data.data' => 'available'), anything).
227
+ and_return(push_response_mock)
228
+ subject.deliver(message, device)
229
+ end
230
+ end
231
+
232
+ [:QuotaExceeded, :DeviceQuotaExceeded,
233
+ :InvalidRegistration, :NotRegistered,
234
+ :MessageTooBig, :MissingCollapseKey].each do |exception|
235
+ it "should raise a Pling::C2DM::#{exception} when the response body is ''" do
236
+ push_response_mock.stub(:body => "Error=#{exception}")
237
+ expect { subject.deliver(message, device) }.to raise_error Pling::C2DM.const_get(exception)
238
+ end
180
239
  end
181
240
  end
182
- end
241
+ end