rapns 3.0.0-java

Sign up to get free protection for your applications and to get access to all the features.
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