rapns 3.0.0-java

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.
Files changed (89) hide show
  1. data/CHANGELOG.md +31 -0
  2. data/LICENSE +7 -0
  3. data/README.md +138 -0
  4. data/bin/rapns +41 -0
  5. data/config/database.yml +39 -0
  6. data/lib/generators/rapns_generator.rb +34 -0
  7. data/lib/generators/templates/add_alert_is_json_to_rapns_notifications.rb +9 -0
  8. data/lib/generators/templates/add_app_to_rapns.rb +11 -0
  9. data/lib/generators/templates/add_gcm.rb +94 -0
  10. data/lib/generators/templates/create_rapns_apps.rb +16 -0
  11. data/lib/generators/templates/create_rapns_feedback.rb +15 -0
  12. data/lib/generators/templates/create_rapns_notifications.rb +26 -0
  13. data/lib/generators/templates/rapns.rb +39 -0
  14. data/lib/rapns/apns/app.rb +8 -0
  15. data/lib/rapns/apns/binary_notification_validator.rb +12 -0
  16. data/lib/rapns/apns/device_token_format_validator.rb +12 -0
  17. data/lib/rapns/apns/feedback.rb +14 -0
  18. data/lib/rapns/apns/notification.rb +86 -0
  19. data/lib/rapns/apns/required_fields_validator.rb +14 -0
  20. data/lib/rapns/app.rb +29 -0
  21. data/lib/rapns/configuration.rb +46 -0
  22. data/lib/rapns/daemon/apns/app_runner.rb +36 -0
  23. data/lib/rapns/daemon/apns/connection.rb +113 -0
  24. data/lib/rapns/daemon/apns/delivery.rb +63 -0
  25. data/lib/rapns/daemon/apns/delivery_handler.rb +21 -0
  26. data/lib/rapns/daemon/apns/disconnection_error.rb +20 -0
  27. data/lib/rapns/daemon/apns/feedback_receiver.rb +74 -0
  28. data/lib/rapns/daemon/app_runner.rb +135 -0
  29. data/lib/rapns/daemon/database_reconnectable.rb +57 -0
  30. data/lib/rapns/daemon/delivery.rb +43 -0
  31. data/lib/rapns/daemon/delivery_error.rb +19 -0
  32. data/lib/rapns/daemon/delivery_handler.rb +46 -0
  33. data/lib/rapns/daemon/delivery_queue.rb +42 -0
  34. data/lib/rapns/daemon/delivery_queue_18.rb +44 -0
  35. data/lib/rapns/daemon/delivery_queue_19.rb +42 -0
  36. data/lib/rapns/daemon/feeder.rb +37 -0
  37. data/lib/rapns/daemon/gcm/app_runner.rb +13 -0
  38. data/lib/rapns/daemon/gcm/delivery.rb +206 -0
  39. data/lib/rapns/daemon/gcm/delivery_handler.rb +20 -0
  40. data/lib/rapns/daemon/interruptible_sleep.rb +18 -0
  41. data/lib/rapns/daemon/logger.rb +68 -0
  42. data/lib/rapns/daemon.rb +136 -0
  43. data/lib/rapns/gcm/app.rb +7 -0
  44. data/lib/rapns/gcm/expiry_collapse_key_mutual_inclusion_validator.rb +11 -0
  45. data/lib/rapns/gcm/notification.rb +31 -0
  46. data/lib/rapns/gcm/payload_size_validator.rb +13 -0
  47. data/lib/rapns/multi_json_helper.rb +16 -0
  48. data/lib/rapns/notification.rb +54 -0
  49. data/lib/rapns/patches/rails/3.1.0/postgresql_adapter.rb +12 -0
  50. data/lib/rapns/patches/rails/3.1.1/postgresql_adapter.rb +17 -0
  51. data/lib/rapns/patches.rb +6 -0
  52. data/lib/rapns/version.rb +3 -0
  53. data/lib/rapns.rb +21 -0
  54. data/lib/tasks/cane.rake +18 -0
  55. data/lib/tasks/test.rake +33 -0
  56. data/spec/acceptance/gcm_upgrade_spec.rb +34 -0
  57. data/spec/acceptance_spec_helper.rb +85 -0
  58. data/spec/support/simplecov_helper.rb +13 -0
  59. data/spec/support/simplecov_quality_formatter.rb +8 -0
  60. data/spec/unit/apns/app_spec.rb +15 -0
  61. data/spec/unit/apns/feedback_spec.rb +12 -0
  62. data/spec/unit/apns/notification_spec.rb +198 -0
  63. data/spec/unit/app_spec.rb +18 -0
  64. data/spec/unit/configuration_spec.rb +38 -0
  65. data/spec/unit/daemon/apns/app_runner_spec.rb +39 -0
  66. data/spec/unit/daemon/apns/connection_spec.rb +234 -0
  67. data/spec/unit/daemon/apns/delivery_handler_spec.rb +48 -0
  68. data/spec/unit/daemon/apns/delivery_spec.rb +160 -0
  69. data/spec/unit/daemon/apns/disconnection_error_spec.rb +18 -0
  70. data/spec/unit/daemon/apns/feedback_receiver_spec.rb +118 -0
  71. data/spec/unit/daemon/app_runner_shared.rb +66 -0
  72. data/spec/unit/daemon/app_runner_spec.rb +129 -0
  73. data/spec/unit/daemon/database_reconnectable_spec.rb +109 -0
  74. data/spec/unit/daemon/delivery_error_spec.rb +13 -0
  75. data/spec/unit/daemon/delivery_handler_shared.rb +28 -0
  76. data/spec/unit/daemon/delivery_queue_spec.rb +29 -0
  77. data/spec/unit/daemon/feeder_spec.rb +95 -0
  78. data/spec/unit/daemon/gcm/app_runner_spec.rb +17 -0
  79. data/spec/unit/daemon/gcm/delivery_handler_spec.rb +36 -0
  80. data/spec/unit/daemon/gcm/delivery_spec.rb +236 -0
  81. data/spec/unit/daemon/interruptible_sleep_spec.rb +40 -0
  82. data/spec/unit/daemon/logger_spec.rb +156 -0
  83. data/spec/unit/daemon_spec.rb +139 -0
  84. data/spec/unit/gcm/app_spec.rb +5 -0
  85. data/spec/unit/gcm/notification_spec.rb +55 -0
  86. data/spec/unit/notification_shared.rb +38 -0
  87. data/spec/unit/notification_spec.rb +6 -0
  88. data/spec/unit_spec_helper.rb +145 -0
  89. metadata +240 -0
@@ -0,0 +1,40 @@
1
+ require "unit_spec_helper"
2
+
3
+ describe Rapns::Daemon::InterruptibleSleep do
4
+ class SleepTest
5
+ extend Rapns::Daemon::InterruptibleSleep
6
+ end
7
+
8
+ let(:rd) { stub(:close => nil) }
9
+ let(:wr) { stub(:close => nil) }
10
+
11
+ before do
12
+ IO.stub(:pipe)
13
+ IO.stub(:select)
14
+ end
15
+
16
+ it 'creates a new pipe' do
17
+ IO.should_receive(:pipe)
18
+ SleepTest.interruptible_sleep 1
19
+ end
20
+
21
+ it 'selects on the reader' do
22
+ IO.stub(:pipe => [rd, wr])
23
+ IO.should_receive(:select).with([rd], nil, nil, 1)
24
+ SleepTest.interruptible_sleep 1
25
+ end
26
+
27
+ it 'closes both ends of the pipe after the timeout' do
28
+ IO.stub(:pipe => [rd, wr])
29
+ rd.should_receive(:close)
30
+ wr.should_receive(:close)
31
+ SleepTest.interruptible_sleep 1
32
+ end
33
+
34
+ it 'closes the writer' do
35
+ IO.stub(:pipe => [rd, wr])
36
+ SleepTest.interruptible_sleep 1
37
+ wr.should_receive(:close)
38
+ SleepTest.interrupt_sleep
39
+ end
40
+ end
@@ -0,0 +1,156 @@
1
+ require "unit_spec_helper"
2
+
3
+ module Rails
4
+ def self.logger
5
+ @logger
6
+ end
7
+
8
+ def self.logger=(logger)
9
+ @logger = logger
10
+ end
11
+ end
12
+
13
+ module HoptoadNotifier
14
+ def self.notify(e)
15
+ end
16
+ end
17
+
18
+ module Airbrake
19
+ def self.notify(e)
20
+ end
21
+ end
22
+
23
+ describe Rapns::Daemon::Logger do
24
+ let(:log) { stub(:sync= => true) }
25
+ let(:config) { stub(:airbrake_notify => true) }
26
+
27
+ before do
28
+ Rails.stub(:root).and_return("/rails_root")
29
+ @buffered_logger = mock("BufferedLogger", :info => nil, :error => nil, :level => 0, :auto_flushing => 1, :auto_flushing= => nil)
30
+ Rails.logger = @buffered_logger
31
+ ActiveSupport::BufferedLogger.stub(:new).and_return(@buffered_logger)
32
+ Rapns::Daemon.stub(:config => config)
33
+ File.stub(:open => log)
34
+ STDERR.stub(:puts)
35
+ end
36
+
37
+ it "disables logging if the log file cannot be opened" do
38
+ File.stub(:open).and_raise(Errno::ENOENT)
39
+ STDERR.should_receive(:puts).with(/No such file or directory/)
40
+ STDERR.should_receive(:puts).with(/Logging disabled/)
41
+ Rapns::Daemon::Logger.new(:foreground => true)
42
+ end
43
+
44
+ it "should open the a log file in the Rails log directory" do
45
+ File.should_receive(:open).with('/rails_root/log/rapns.log', 'a')
46
+ Rapns::Daemon::Logger.new(:foreground => true)
47
+ end
48
+
49
+ it 'sets sync mode on the log descriptor' do
50
+ log.should_receive(:sync=).with(true)
51
+ Rapns::Daemon::Logger.new(:foreground => true)
52
+ end
53
+
54
+ it 'instantiates the BufferedLogger' do
55
+ ActiveSupport::BufferedLogger.should_receive(:new).with(log, Rails.logger.level)
56
+ Rapns::Daemon::Logger.new(:foreground => true)
57
+ end
58
+
59
+ it "should print out the msg if running in the foreground" do
60
+ logger = Rapns::Daemon::Logger.new(:foreground => true)
61
+ STDOUT.should_receive(:puts).with(/hi mom/)
62
+ logger.info("hi mom")
63
+ end
64
+
65
+ it "should not print out the msg if not running in the foreground" do
66
+ logger = Rapns::Daemon::Logger.new(:foreground => false)
67
+ STDOUT.should_not_receive(:puts).with(/hi mom/)
68
+ logger.info("hi mom")
69
+ end
70
+
71
+ it "should prefix log lines with the current time" do
72
+ now = Time.now
73
+ Time.stub(:now).and_return(now)
74
+ logger = Rapns::Daemon::Logger.new(:foreground => false)
75
+ @buffered_logger.should_receive(:info).with(/#{Regexp.escape("[#{now.to_s(:db)}]")}/)
76
+ logger.info("blah")
77
+ end
78
+
79
+ it "should prefix error logs with the ERROR label" do
80
+ logger = Rapns::Daemon::Logger.new(:foreground => false)
81
+ @buffered_logger.should_receive(:error).with(/#{Regexp.escape("[ERROR]")}/)
82
+ logger.error("eeek")
83
+ end
84
+
85
+ it "should prefix warn logs with the WARNING label" do
86
+ logger = Rapns::Daemon::Logger.new(:foreground => false)
87
+ @buffered_logger.should_receive(:warn).with(/#{Regexp.escape("[WARNING]")}/)
88
+ logger.warn("eeek")
89
+ end
90
+
91
+ it "should handle an Exception instance" do
92
+ e = RuntimeError.new("hi mom")
93
+ e.stub(:backtrace => [])
94
+ logger = Rapns::Daemon::Logger.new(:foreground => false)
95
+ @buffered_logger.should_receive(:error).with(/RuntimeError, hi mom/)
96
+ logger.error(e)
97
+ end
98
+
99
+ it "should notify Airbrake of the exception" do
100
+ e = RuntimeError.new("hi mom")
101
+ e.stub(:backtrace => [])
102
+ logger = Rapns::Daemon::Logger.new(:foreground => false, :airbrake_notify => true)
103
+ Airbrake.should_receive(:notify_or_ignore).with(e)
104
+ logger.error(e)
105
+ end
106
+
107
+ context "without Airbrake defined" do
108
+ before do
109
+ Object.send(:remove_const, :Airbrake)
110
+ end
111
+
112
+ after do
113
+ module Airbrake
114
+ def self.notify(e)
115
+ end
116
+ end
117
+ end
118
+
119
+ it "should notify using HoptoadNotifier" do
120
+ e = RuntimeError.new("hi mom")
121
+ e.stub(:backtrace => [])
122
+ logger = Rapns::Daemon::Logger.new(:foreground => false, :airbrake_notify => true)
123
+ HoptoadNotifier.should_receive(:notify_or_ignore).with(e)
124
+ logger.error(e)
125
+ end
126
+ end
127
+
128
+ it "should not notify Airbrake of the exception if the airbrake_notify option is false" do
129
+ e = RuntimeError.new("hi mom")
130
+ e.stub(:backtrace => [])
131
+ logger = Rapns::Daemon::Logger.new(:foreground => false, :airbrake_notify => false)
132
+ Airbrake.should_not_receive(:notify_or_ignore).with(e)
133
+ logger.error(e)
134
+ end
135
+
136
+ it "should not notify Airbrake if explicitly disabled in the call to error" do
137
+ e = RuntimeError.new("hi mom")
138
+ e.stub(:backtrace => [])
139
+ logger = Rapns::Daemon::Logger.new(:foreground => false, :airbrake_notify => true)
140
+ Airbrake.should_not_receive(:notify_or_ignore).with(e)
141
+ logger.error(e, :airbrake_notify => false)
142
+ end
143
+
144
+ it "should not attempt to notify Airbrake of the error is not an Exception" do
145
+ logger = Rapns::Daemon::Logger.new(:foreground => false)
146
+ Airbrake.should_not_receive(:notify_or_ignore)
147
+ logger.error("string error message")
148
+ end
149
+
150
+ it 'defaults auto_flushing to true if the Rails logger does not respond to auto_flushing' do
151
+ rails_logger = mock(:info => nil, :error => nil, :level => 0)
152
+ Rails.logger = rails_logger
153
+ logger = Rapns::Daemon::Logger.new({})
154
+ @buffered_logger.auto_flushing.should be_true
155
+ end
156
+ end
@@ -0,0 +1,139 @@
1
+ require "unit_spec_helper"
2
+
3
+ describe Rapns::Daemon, "when starting" do
4
+ module Rails; end
5
+
6
+ let(:certificate) { stub }
7
+ let(:password) { stub }
8
+ let(:config) { stub(:pid_file => nil, :push_poll => 2, :airbrake_notify => false, :foreground => true) }
9
+ let(:logger) { stub(:info => nil, :error => nil, :warn => nil) }
10
+
11
+ before do
12
+ Rapns.stub(:config => config)
13
+ Rapns::Daemon::Feeder.stub(:start)
14
+ Rapns::Daemon::Logger.stub(:new).and_return(logger)
15
+ Rapns::Daemon::AppRunner.stub(:sync => nil, :stop => nil)
16
+ Rapns::Daemon.stub(:daemonize => nil, :reconnect_database => nil, :exit => nil, :puts => nil)
17
+ File.stub(:open)
18
+ Rails.stub(:root).and_return("/rails_root")
19
+ end
20
+
21
+ it "forks into a daemon if the foreground option is false" do
22
+ config.stub(:foreground => false)
23
+ ActiveRecord::Base.stub(:establish_connection)
24
+ Rapns::Daemon.should_receive(:daemonize)
25
+ Rapns::Daemon.start
26
+ end
27
+
28
+ it "does not fork into a daemon if the foreground option is true" do
29
+ config.stub(:foreground => true)
30
+ Rapns::Daemon.should_not_receive(:daemonize)
31
+ Rapns::Daemon.start
32
+ end
33
+
34
+ it "writes the process ID to the PID file" do
35
+ Rapns::Daemon.should_receive(:write_pid_file)
36
+ Rapns::Daemon.start
37
+ end
38
+
39
+ it "logs an error if the PID file could not be written" do
40
+ config.stub(:pid_file => '/rails_root/rapns.pid')
41
+ File.stub(:open).and_raise(Errno::ENOENT)
42
+ logger.should_receive(:error).with("Failed to write PID to '/rails_root/rapns.pid': #<Errno::ENOENT: No such file or directory>")
43
+ Rapns::Daemon.start
44
+ end
45
+
46
+ it "starts the feeder" do
47
+ Rapns::Daemon::Feeder.should_receive(:start).with(2)
48
+ Rapns::Daemon.start
49
+ end
50
+
51
+ it "syncs apps" do
52
+ Rapns::Daemon::AppRunner.should_receive(:sync)
53
+ Rapns::Daemon.start
54
+ end
55
+
56
+ it "sets up the logger" do
57
+ config.stub(:airbrake_notify => true)
58
+ Rapns::Daemon::Logger.should_receive(:new).with(:foreground => true, :airbrake_notify => true)
59
+ Rapns::Daemon.start
60
+ end
61
+
62
+ it "makes the logger accessible" do
63
+ Rapns::Daemon.start
64
+ Rapns::Daemon.logger.should == logger
65
+ end
66
+
67
+ it 'prints a warning if there are no apps' do
68
+ Rapns::App.stub(:count => 0)
69
+ logger.should_receive(:warn).any_number_of_times
70
+ Rapns::Daemon.start
71
+ end
72
+
73
+ it 'prints a warning exists if rapns has not been upgraded' do
74
+ Rapns::App.stub(:count).and_raise(ActiveRecord::StatementInvalid)
75
+ Rapns::Daemon.should_receive(:puts).any_number_of_times
76
+ Rapns::Daemon.should_receive(:exit).with(1)
77
+ Rapns::Daemon.start
78
+ end
79
+
80
+ it 'warns if rapns.yml still exists' do
81
+ File.should_receive(:exists?).with('/rails_root/config/rapns/rapns.yml').and_return(true)
82
+ logger.should_receive(:warn).with("Since 2.0.0 rapns uses command-line options and a Ruby based configuration file.\nPlease run 'rails g rapns' to generate a new configuration file into config/initializers.\nRemove config/rapns/rapns.yml to avoid this warning.\n")
83
+ Rapns::Daemon.start
84
+ end
85
+ end
86
+
87
+ describe Rapns::Daemon, "when being shutdown" do
88
+ let(:config) { stub(:pid_file => '/rails_root/rapns.pid') }
89
+
90
+ before do
91
+ Rapns.stub(:config => config)
92
+ Rapns::Daemon.stub(:puts => nil)
93
+ Rapns::Daemon::Feeder.stub(:stop)
94
+ Rapns::Daemon::AppRunner.stub(:stop)
95
+ end
96
+
97
+ # These tests do not work on JRuby.
98
+ unless defined? JRUBY_VERSION
99
+ it "shuts down when signaled signaled SIGINT" do
100
+ Rapns::Daemon.setup_signal_hooks
101
+ Rapns::Daemon.should_receive(:shutdown)
102
+ Process.kill("SIGINT", Process.pid)
103
+ end
104
+
105
+ it "shuts down when signaled signaled SIGTERM" do
106
+ Rapns::Daemon.setup_signal_hooks
107
+ Rapns::Daemon.should_receive(:shutdown)
108
+ Process.kill("SIGTERM", Process.pid)
109
+ end
110
+ end
111
+
112
+ it "stops the feeder" do
113
+ Rapns::Daemon::Feeder.should_receive(:stop)
114
+ Rapns::Daemon.send(:shutdown)
115
+ end
116
+
117
+ it "stops the app runners" do
118
+ Rapns::Daemon::AppRunner.should_receive(:stop)
119
+ Rapns::Daemon.send(:shutdown)
120
+ end
121
+
122
+ it "removes the PID file if one was written" do
123
+ File.stub(:exists?).and_return(true)
124
+ File.should_receive(:delete).with("/rails_root/rapns.pid")
125
+ Rapns::Daemon.send(:shutdown)
126
+ end
127
+
128
+ it "does not attempt to remove the PID file if it does not exist" do
129
+ File.stub(:exists?).and_return(false)
130
+ File.should_not_receive(:delete)
131
+ Rapns::Daemon.send(:shutdown)
132
+ end
133
+
134
+ it "does not attempt to remove the PID file if one was not written" do
135
+ config.stub(:pid_file).and_return(nil)
136
+ File.should_not_receive(:delete)
137
+ Rapns::Daemon.send(:shutdown)
138
+ end
139
+ end
@@ -0,0 +1,5 @@
1
+ require 'unit_spec_helper'
2
+
3
+ describe Rapns::Gcm::App do
4
+ it { should validate_presence_of(:auth_key) }
5
+ end
@@ -0,0 +1,55 @@
1
+ require 'unit_spec_helper'
2
+ require 'unit/notification_shared.rb'
3
+
4
+ describe Rapns::Gcm::Notification do
5
+ it_should_behave_like 'an Notification subclass'
6
+
7
+ let(:app) { Rapns::Gcm::App.create!(:name => 'test', :auth_key => 'abc') }
8
+ let(:notification_class) { Rapns::Gcm::Notification }
9
+ let(:notification) { notification_class.new }
10
+ let(:data_setter) { 'data=' }
11
+ let(:data_getter) { 'data' }
12
+
13
+ it { should validate_presence_of :registration_ids }
14
+
15
+ it 'has a payload limit of 4096 bytes' do
16
+ notification.data = { :key => "a" * 4096 }
17
+ notification.valid?.should be_false
18
+ notification.errors[:base].should == ["GCM notification payload cannot be larger than 4096 bytes."]
19
+ end
20
+
21
+ it 'allows assignment of many registration IDs' do
22
+ notification.app = app
23
+ notification.registration_ids = ['a', 'b']
24
+ notification.save!
25
+ reloaded_notification = notification_class.find(notification.id)
26
+ reloaded_notification.registration_ids.should == ['a', 'b']
27
+ end
28
+
29
+ it 'allows assignment of a single registration ID' do
30
+ notification.app = app
31
+ notification.registration_ids = 'a'
32
+ notification.save!
33
+ reloaded_notification = notification_class.find(notification.id)
34
+ reloaded_notification.registration_ids.should == ['a']
35
+ end
36
+
37
+ it 'validates expiry is present if collapse_key is set' do
38
+ notification.collapse_key = 'test'
39
+ notification.expiry = nil
40
+ notification.valid?.should be_false
41
+ notification.errors[:expiry].should == ['must be set when using a collapse_key']
42
+ end
43
+
44
+ it 'does not include time_to_live in the payload if collapse_key is not set' do
45
+ notification.expiry = 100
46
+ notification.collapse_key = nil
47
+ notification.as_json.key?('time_to_live').should be_false
48
+ end
49
+
50
+ it 'includes time_to_live in the payload if collapse_key is set' do
51
+ notification.expiry = 100
52
+ notification.collapse_key = 'sync'
53
+ notification.as_json['time_to_live'].should == 100
54
+ end
55
+ end
@@ -0,0 +1,38 @@
1
+ shared_examples_for "an Notification subclass" do
2
+ describe "when assigning data for the device" do
3
+ it "calls MultiJson.dump when multi_json responds to :dump" do
4
+ notification = notification_class.new
5
+ MultiJson.stub(:respond_to?).with(:dump).and_return(true)
6
+ MultiJson.should_receive(:dump).with(any_args())
7
+ notification.send(data_setter, { :pirates => 1 })
8
+ end
9
+
10
+ it "calls MultiJson.encode when multi_json does not respond to :dump" do
11
+ notification = notification_class.new
12
+ MultiJson.stub(:respond_to?).with(:dump).and_return(false)
13
+ MultiJson.should_receive(:encode).with(any_args())
14
+ notification.send(data_setter, { :ninjas => 1 })
15
+ end
16
+
17
+ it "raises an ArgumentError if something other than a Hash is assigned" do
18
+ expect do
19
+ notification.send(data_setter, Array.new)
20
+ end.to raise_error(ArgumentError, "must be a Hash")
21
+ end
22
+
23
+ it "encodes the given Hash as JSON" do
24
+ notification.send(data_setter, { :hi => "mom" })
25
+ notification.read_attribute(:data).should == "{\"hi\":\"mom\"}"
26
+ end
27
+
28
+ it "decodes the JSON when using the reader method" do
29
+ notification.send(data_setter, { :hi => "mom" })
30
+ notification.send(data_getter).should == {"hi" => "mom"}
31
+ end
32
+
33
+ it 'warns if attributes_for_device is assigned via mass-assignment' do
34
+ ActiveSupport::Deprecation.should_receive(:warn)
35
+ notification_class.new(:attributes_for_device => {:hi => 'mom'})
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,6 @@
1
+ require "unit_spec_helper"
2
+
3
+ describe Rapns::Notification do
4
+ it { should validate_numericality_of(:expiry) }
5
+ it { should validate_presence_of(:app) }
6
+ end
@@ -0,0 +1,145 @@
1
+ ENV['RAILS_ENV'] = 'test'
2
+
3
+ begin
4
+ require './spec/support/simplecov_helper'
5
+ include SimpleCovHelper
6
+ start_simple_cov("unit-#{RUBY_VERSION}")
7
+ rescue LoadError
8
+ puts "Coverage disabled."
9
+ end
10
+
11
+ require 'active_record'
12
+
13
+ jruby = defined?(RUBY_ENGINE) && RUBY_ENGINE == 'jruby'
14
+
15
+ $adapter = ENV['ADAPTER'] || 'postgresql'
16
+ $adapter = 'jdbc' + $adapter if jruby
17
+
18
+ DATABASE_CONFIG = YAML.load_file(File.expand_path("../config/database.yml", File.dirname(__FILE__)))
19
+
20
+ if DATABASE_CONFIG[$adapter].nil?
21
+ puts "No such adapter '#{$adapter}'. Valid adapters are #{DATABASE_CONFIG.keys.join(', ')}."
22
+ exit 1
23
+ end
24
+
25
+ if ENV['TRAVIS']
26
+ DATABASE_CONFIG[$adapter]['username'] = 'postgres'
27
+ else
28
+ require 'etc'
29
+ DATABASE_CONFIG[$adapter]['username'] = Etc.getlogin
30
+ end
31
+
32
+ puts "Using #{$adapter} adapter."
33
+
34
+ ActiveRecord::Base.establish_connection(DATABASE_CONFIG[$adapter])
35
+
36
+ require 'generators/templates/create_rapns_notifications'
37
+ require 'generators/templates/create_rapns_feedback'
38
+ require 'generators/templates/add_alert_is_json_to_rapns_notifications'
39
+ require 'generators/templates/add_app_to_rapns'
40
+ require 'generators/templates/create_rapns_apps'
41
+ require 'generators/templates/add_gcm'
42
+
43
+ [CreateRapnsNotifications, CreateRapnsFeedback,
44
+ AddAlertIsJsonToRapnsNotifications, AddAppToRapns, CreateRapnsApps, AddGcm].each do |migration|
45
+ migration.down rescue ActiveRecord::StatementInvalid
46
+ migration.up
47
+ end
48
+
49
+ require 'bundler'
50
+ Bundler.require(:default)
51
+
52
+ require 'shoulda'
53
+ require 'database_cleaner'
54
+
55
+ DatabaseCleaner.strategy = :truncation
56
+
57
+ require 'rapns'
58
+ require 'rapns/daemon'
59
+
60
+ Rapns::Notification.reset_column_information
61
+ Rapns::App.reset_column_information
62
+ Rapns::Apns::Feedback.reset_column_information
63
+
64
+ RSpec.configure do |config|
65
+ # config.before :suite do
66
+ # PerfTools::CpuProfiler.start('/tmp/rapns_profile')
67
+ # end
68
+ # config.after :suite do
69
+ # PerfTools::CpuProfiler.stop
70
+ # end
71
+
72
+ config.before(:each) { DatabaseCleaner.clean }
73
+ end
74
+
75
+ # a test certificate that contains both an X509 certificate and
76
+ # a private key, similar to those used for connecting to Apple
77
+ # push notification servers.
78
+ #
79
+ # Note that we cannot validate the certificate and private key
80
+ # because we are missing the certificate chain used to validate
81
+ # the certificate, and this is private to Apple. So if the app
82
+ # has a certificate and a private key in it, the only way to find
83
+ # out if it really is valid is to connect to Apple's servers.
84
+ #
85
+ TEST_CERT = <<EOF
86
+ Bag Attributes
87
+ friendlyName: test certificate
88
+ localKeyID: 00 93 8F E4 A3 C3 75 64 3D 7E EA 14 0B 0A EA DD 15 85 8A D5
89
+ subject=/CN=test certificate/O=Example/OU=Example/ST=QLD/C=AU/L=Example/emailAddress=user@example.com
90
+ issuer=/CN=test certificate/O=Example/OU=Example/ST=QLD/C=AU/L=Example/emailAddress=user@example.com
91
+ -----BEGIN CERTIFICATE-----
92
+ MIID5jCCAs6gAwIBAgIBATALBgkqhkiG9w0BAQswgY0xGTAXBgNVBAMMEHRlc3Qg
93
+ Y2VydGlmaWNhdGUxEDAOBgNVBAoMB0V4YW1wbGUxEDAOBgNVBAsMB0V4YW1wbGUx
94
+ DDAKBgNVBAgMA1FMRDELMAkGA1UEBhMCQVUxEDAOBgNVBAcMB0V4YW1wbGUxHzAd
95
+ BgkqhkiG9w0BCQEWEHVzZXJAZXhhbXBsZS5jb20wHhcNMTIwOTA5MDMxODMyWhcN
96
+ MjIwOTA3MDMxODMyWjCBjTEZMBcGA1UEAwwQdGVzdCBjZXJ0aWZpY2F0ZTEQMA4G
97
+ A1UECgwHRXhhbXBsZTEQMA4GA1UECwwHRXhhbXBsZTEMMAoGA1UECAwDUUxEMQsw
98
+ CQYDVQQGEwJBVTEQMA4GA1UEBwwHRXhhbXBsZTEfMB0GCSqGSIb3DQEJARYQdXNl
99
+ ckBleGFtcGxlLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKF+
100
+ UDsN1sLen8g+97PNTiWju9+wkSv+H5rQlvb6YFLPx11YvqpK8ms6kFU1OmWeLfmh
101
+ cpsT+bZtKupC7aGPoSG3RXzzf/YUMgs/ZSXA0idZHA6tkReAEzIX6jL5otfPWbaP
102
+ luCTUoVMeP4u9ywk628zlqh9IQHC1Agl0R1xGCpULDk8kn1gPyEisl38wI5aDbzy
103
+ 6lYQGNUKOqt1xfVjtIFe/jyY/v0sxFjIJlRLcAFBuJx4sRV+PwRBkusOQtYwcwpI
104
+ loMxJj+GQe66ueATW81aC4iOU66DAFFEuGzwIwm3bOilimGGQbGb92F339RfmSOo
105
+ TPAvVhsakI3mzESb4lkCAwEAAaNRME8wDgYDVR0PAQH/BAQDAgeAMCAGA1UdJQEB
106
+ /wQWMBQGCCsGAQUFBwMCBggrBgEFBQcDATAbBgNVHREEFDASgRB1c2VyQGV4YW1w
107
+ bGUuY29tMA0GCSqGSIb3DQEBCwUAA4IBAQA5UbNR+83ZdI2DiaB4dRmy0V5RDAqJ
108
+ k9+QskcTV4gBTjsOBS46Dw1tI6iTrfTyjYJdnyH0Y2Y2YVWBnvtON41UCZak+4ed
109
+ /IqyzU0dtfZ+frWa0RY4reyl80TwqnzyJfni0nDo4zGGvz70cxyaz2u1BWqwLjqb
110
+ dh8Dxvt+aHW2MQi0iGKh/HNbgwVanR4+ubNwziK9sR1Rnq9MkHWtwBw16SXQG6ao
111
+ SZKASWNaH8VL08Zz0E98cwd137UJkPsldCwJ8kHR5OzkcjPdXvnGD3d64yy2TC1Z
112
+ Gy1Aazt98wPcTYBytlhK8Rvzg9OoY9QmsdpmWxz1ZCXECJNqCa3IKsqO
113
+ -----END CERTIFICATE-----
114
+ Bag Attributes
115
+ friendlyName: test certificate
116
+ localKeyID: 00 93 8F E4 A3 C3 75 64 3D 7E EA 14 0B 0A EA DD 15 85 8A D5
117
+ Key Attributes: <No Attributes>
118
+ -----BEGIN RSA PRIVATE KEY-----
119
+ MIIEpQIBAAKCAQEAoX5QOw3Wwt6fyD73s81OJaO737CRK/4fmtCW9vpgUs/HXVi+
120
+ qkryazqQVTU6ZZ4t+aFymxP5tm0q6kLtoY+hIbdFfPN/9hQyCz9lJcDSJ1kcDq2R
121
+ F4ATMhfqMvmi189Zto+W4JNShUx4/i73LCTrbzOWqH0hAcLUCCXRHXEYKlQsOTyS
122
+ fWA/ISKyXfzAjloNvPLqVhAY1Qo6q3XF9WO0gV7+PJj+/SzEWMgmVEtwAUG4nHix
123
+ FX4/BEGS6w5C1jBzCkiWgzEmP4ZB7rq54BNbzVoLiI5TroMAUUS4bPAjCbds6KWK
124
+ YYZBsZv3YXff1F+ZI6hM8C9WGxqQjebMRJviWQIDAQABAoIBAQCTiLIDQUFSBdAz
125
+ QFNLD+S0vkCEuunlJuP4q1c/ir006l1YChsluBJ/o6D4NwiCjV+zDquEwVsALftm
126
+ yH4PewfZpXT2Ef508T5GyEO/mchj6iSXxDkpHvhqay6qIyWBwwxSnBtaTzy0Soi+
127
+ rmlhCtmLXbXld2sQEM1kJChGnWtWPtvSyrn+mapNPZviGRtgRNK+YsrAti1nUext
128
+ 2syO5mTdHf1D8GR7I98OaX6odREuSocEV9PzfapWZx2GK5tvRiS1skiug5ciieTd
129
+ Am5/C+bb31h4drFslihLb5BRGO5SFQJvMJL2Sx1f19BCC4XikS01P4/zZbxQNq79
130
+ kxEQuDGBAoGBANP4pIYZ5xshCkx7cTYqmxzWLClGKE2S7Oa8N89mtOwfmqT9AFun
131
+ t9Us9Ukbi8BaKlKhGpQ1HlLf/KVcpyW0x2qLou6AyIWYH+/5VaR3graNgUnzpK9f
132
+ 1F5HoaNHbhlAoebqhzhASFlJI2aqUdQjdOv73z+s9szJU4gpILNwGDFnAoGBAMMJ
133
+ j+vIxtG9J2jldyoXzpg5mbMXSj9u/wFLBVdjXWyOoiqVMMBto53RnoqAom7Ifr9D
134
+ 49LxRAT1Q3l4vs/YnM3ziMsIg2vQK1EbrLsY9OnD/kvPaLXOlNIOdfLM8UeVWZMc
135
+ I4LPbbZrhv/7CC8RjbRhMoWWdGYPvxmvD6V4ZDY/AoGBALoI6OxA45Htx4okdNHj
136
+ RstiNNPsnQaoQn6nBhxiubraafEPkzbd1fukP4pwQJELEUX/2sHkdL6rkqLW1GPF
137
+ a5dZAiBsqpCFWNJWdBGqSfBJ9QSgbxLz+gDcwUH6OOi0zuNJRm/aCyVBiW5bYQHc
138
+ NIvAPMk31ksZDtTbs7WIVdNVAoGBALZ1+KWNxKqs+fSBT5UahpUUtfy8miJz9a7A
139
+ /3M8q0cGvSF3Rw+OwpW/aEGMi+l2OlU27ykFuyukRAac9m296RwnbF79TO2M5ylO
140
+ 6a5zb5ROXlWP6RbE96b4DlIidssQJqegmHwlEC+rsrVBpOtb0aThlYEyOxzMOGyP
141
+ wOR9l8rDAoGADZ4TUHFM6VrvPlUZBkGbqiyXH9IM/y9JWk+22JQCEGnM6RFZemSs
142
+ jxWqQiPAdJtb3xKryJSCMtFPH9azedoCrSgaMflJ1QgoXgpiKZyoEXWraVUggh/0
143
+ CEavgZcTZ6SvMuayqJdGGB+zb1V8XwXMtCjApR/kTm47DjxO4DmpOPs=
144
+ -----END RSA PRIVATE KEY-----
145
+ EOF