# encoding: US-ASCII require "unit_spec_helper" require 'unit/notification_shared.rb' describe Rpush::Client::ActiveRecord::Apns::Notification do it_should_behave_like 'an Notification subclass' let(:app) { Rpush::Client::ActiveRecord::Apns::App.create!(name: 'my_app', environment: 'development', certificate: TEST_CERT) } let(:notification_class) { Rpush::Client::ActiveRecord::Apns::Notification } let(:notification) { notification_class.new } it "should validate the format of the device_token" do notification = Rpush::Client::ActiveRecord::Apns::Notification.new(device_token: "{$%^&*()}") expect(notification.valid?).to be_falsey expect(notification.errors[:device_token].include?("is invalid")).to be_truthy end it "should validate the length of the binary conversion of the notification" do notification.device_token = "a" * 64 notification.alert = "way too long!" * 200 expect(notification.valid?).to be_falsey expect(notification.errors[:base].include?("APN notification cannot be larger than 2048 bytes. Try condensing your alert and device attributes.")).to be_truthy end it "should default the sound to 'default'" do expect(notification.sound).to eq('default') end it "should default the expiry to 1 day" do expect(notification.expiry).to eq 1.day.to_i end end describe Rpush::Client::ActiveRecord::Apns::Notification, "when assigning the device token" do it "should strip spaces from the given string" do notification = Rpush::Client::ActiveRecord::Apns::Notification.new(device_token: "o m g") expect(notification.device_token).to eq "omg" end it "should strip chevrons from the given string" do notification = Rpush::Client::ActiveRecord::Apns::Notification.new(device_token: "") expect(notification.device_token).to eq "omg" end end describe Rpush::Client::ActiveRecord::Apns::Notification, "as_json" do it "should include the alert if present" do notification = Rpush::Client::ActiveRecord::Apns::Notification.new(alert: "hi mom") expect(notification.as_json["aps"]["alert"]).to eq "hi mom" end it "should not include the alert key if the alert is not present" do notification = Rpush::Client::ActiveRecord::Apns::Notification.new(alert: nil) expect(notification.as_json["aps"].key?("alert")).to be_falsey end it "should encode the alert as JSON if it is a Hash" do notification = Rpush::Client::ActiveRecord::Apns::Notification.new(alert: { 'body' => "hi mom", 'alert-loc-key' => "View" }) expect(notification.as_json["aps"]["alert"]).to eq('body' => "hi mom", 'alert-loc-key' => "View") end it "should include the badge if present" do notification = Rpush::Client::ActiveRecord::Apns::Notification.new(badge: 6) expect(notification.as_json["aps"]["badge"]).to eq 6 end it "should not include the badge key if the badge is not present" do notification = Rpush::Client::ActiveRecord::Apns::Notification.new(badge: nil) expect(notification.as_json["aps"].key?("badge")).to be_falsey end it "should include the sound if present" do notification = Rpush::Client::ActiveRecord::Apns::Notification.new(alert: "my_sound.aiff") expect(notification.as_json["aps"]["alert"]).to eq "my_sound.aiff" end it "should not include the sound key if the sound is not present" do notification = Rpush::Client::ActiveRecord::Apns::Notification.new(sound: nil) expect(notification.as_json["aps"].key?("sound")).to be_falsey end it "should include attributes for the device" do notification = Rpush::Client::ActiveRecord::Apns::Notification.new notification.data = { omg: :lol, wtf: :dunno } expect(notification.as_json["omg"]).to eq "lol" expect(notification.as_json["wtf"]).to eq "dunno" end it "should allow attributes to include a hash" do notification = Rpush::Client::ActiveRecord::Apns::Notification.new notification.data = { omg: { ilike: :hashes } } expect(notification.as_json["omg"]["ilike"]).to eq "hashes" end end describe Rpush::Client::ActiveRecord::Apns::Notification, 'MDM' do let(:magic) { 'abc123' } let(:notification) { Rpush::Client::ActiveRecord::Apns::Notification.new } it 'includes the mdm magic in the payload' do notification.mdm = magic expect(notification.as_json).to eq('mdm' => magic) end it 'does not include aps attribute' do notification.alert = "i'm doomed" notification.mdm = magic expect(notification.as_json.key?('aps')).to be_falsey end end describe Rpush::Client::ActiveRecord::Apns::Notification, 'content-available' do let(:notification) { Rpush::Client::ActiveRecord::Apns::Notification.new } it 'includes content-available in the payload' do notification.content_available = true expect(notification.as_json['aps']['content-available']).to eq 1 end it 'does not include content-available in the payload if not set' do expect(notification.as_json['aps'].key?('content-available')).to be_falsey end it 'does not include content-available as a non-aps attribute' do notification.content_available = true expect(notification.as_json.key?('content-available')).to be_falsey end it 'does not overwrite existing attributes for the device' do notification.data = { hi: :mom } notification.content_available = true expect(notification.as_json['aps']['content-available']).to eq 1 expect(notification.as_json['hi']).to eq 'mom' end it 'does not overwrite the content-available flag when setting attributes for the device' do notification.content_available = true notification.data = { hi: :mom } expect(notification.as_json['aps']['content-available']).to eq 1 expect(notification.as_json['hi']).to eq 'mom' end end describe Rpush::Client::ActiveRecord::Apns::Notification, 'url-args' do let(:notification) { Rpush::Client::ActiveRecord::Apns::Notification.new } it 'includes url-args in the payload' do notification.url_args = ['url-arg-1'] expect(notification.as_json['aps']['url-args']).to eq ['url-arg-1'] end it 'does not include url-args in the payload if not set' do expect(notification.as_json['aps'].key?('url-args')).to be_falsey end end describe Rpush::Client::ActiveRecord::Apns::Notification, 'category' do let(:notification) { Rpush::Client::ActiveRecord::Apns::Notification.new } it 'includes category in the payload' do notification.category = 'INVITE_CATEGORY' expect(notification.as_json['aps']['category']).to eq 'INVITE_CATEGORY' end it 'does not include category in the payload if not set' do expect(notification.as_json['aps'].key?('category')).to be_falsey end end describe Rpush::Client::ActiveRecord::Apns::Notification, 'to_binary' do let(:notification) { Rpush::Client::ActiveRecord::Apns::Notification.new } before do notification.device_token = "a" * 64 notification.id = 1234 end it 'uses APNS_PRIORITY_CONSERVE_POWER if content-available is the only key' do notification.alert = nil notification.badge = nil notification.sound = nil notification.content_available = true bytes = notification.to_binary.bytes.to_a[-4..-1] expect(bytes.first).to eq 5 # priority item ID expect(bytes.last).to eq Rpush::Client::ActiveRecord::Apns::Notification::APNS_PRIORITY_CONSERVE_POWER end it 'uses APNS_PRIORITY_IMMEDIATE if content-available is not the only key' do notification.alert = "New stuff!" notification.badge = nil notification.sound = nil notification.content_available = true bytes = notification.to_binary.bytes.to_a[-4..-1] expect(bytes.first).to eq 5 # priority item ID expect(bytes.last).to eq Rpush::Client::ActiveRecord::Apns::Notification::APNS_PRIORITY_IMMEDIATE end it "should correctly convert the notification to binary" do notification.sound = "1.aiff" notification.badge = 3 notification.alert = "Don't panic Mr Mainwaring, don't panic!" notification.data = { hi: :mom } notification.expiry = 86_400 # 1 day, \x00\x01Q\x80 notification.priority = Rpush::Client::ActiveRecord::Apns::Notification::APNS_PRIORITY_IMMEDIATE notification.app = Rpush::Client::ActiveRecord::Apns::App.new(name: 'my_app', environment: 'development', certificate: TEST_CERT) expect(notification.to_binary).to eq "\x02\x00\x00\x00\x99\x01\x00 \xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\x02\x00a{\"aps\":{\"alert\":\"Don't panic Mr Mainwaring, don't panic!\",\"badge\":3,\"sound\":\"1.aiff\"},\"hi\":\"mom\"}\x03\x00\x04\x00\x00\x04\xD2\x04\x00\x04\x00\x01Q\x80\x05\x00\x01\n" end end describe Rpush::Client::ActiveRecord::Apns::Notification, "bug #31" do it 'does not confuse a JSON looking string as JSON' do notification = Rpush::Client::ActiveRecord::Apns::Notification.new notification.alert = "{\"one\":2}" expect(notification.alert).to eq "{\"one\":2}" end it 'does confuse a JSON looking string as JSON if the alert_is_json attribute is not present' do notification = Rpush::Client::ActiveRecord::Apns::Notification.new allow(notification).to receive_messages(:has_attribute? => false) notification.alert = "{\"one\":2}" expect(notification.alert).to eq('one' => 2) end end describe Rpush::Client::ActiveRecord::Apns::Notification, "bug #35" do it "should limit payload size to 256 bytes but not the entire packet" do notification = Rpush::Client::ActiveRecord::Apns::Notification.new do |n| n.device_token = "a" * 64 n.alert = "a" * 210 n.app = Rpush::Client::ActiveRecord::Apns::App.create!(name: 'my_app', environment: 'development', certificate: TEST_CERT) end expect(notification.to_binary(for_validation: true).bytesize).to be > 256 expect(notification.payload.bytesize).to be < 256 expect(notification).to be_valid end end describe Rpush::Client::ActiveRecord::Apns::Notification, "multi_json usage" do describe Rpush::Client::ActiveRecord::Apns::Notification, "alert" do it "should call MultiJson.load when multi_json version is 1.3.0" do notification = Rpush::Client::ActiveRecord::Apns::Notification.new(alert: { a: 1 }, alert_is_json: true) allow(Gem).to receive(:loaded_specs).and_return('multi_json' => Gem::Specification.new('multi_json', '1.3.0')) expect(MultiJson).to receive(:load).with(any_args) notification.alert end it "should call MultiJson.decode when multi_json version is 1.2.9" do notification = Rpush::Client::ActiveRecord::Apns::Notification.new(alert: { a: 1 }, alert_is_json: true) allow(Gem).to receive(:loaded_specs).and_return('multi_json' => Gem::Specification.new('multi_json', '1.2.9')) expect(MultiJson).to receive(:decode).with(any_args) notification.alert end end end