passbook-iid 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.travis.yml +7 -0
- data/Gemfile +17 -0
- data/Gemfile.lock +65 -0
- data/LICENSE +22 -0
- data/README.md +290 -0
- data/Rakefile +36 -0
- data/VERSION +1 -0
- data/bin/pk +22 -0
- data/lib/commands/build.rb +62 -0
- data/lib/commands/commands.rb +31 -0
- data/lib/commands/generate.rb +44 -0
- data/lib/commands/templates/boarding-pass.json +56 -0
- data/lib/commands/templates/coupon.json +33 -0
- data/lib/commands/templates/event-ticket.json +33 -0
- data/lib/commands/templates/generic.json +33 -0
- data/lib/commands/templates/store-card.json +33 -0
- data/lib/passbook.rb +15 -0
- data/lib/passbook/pkpass.rb +120 -0
- data/lib/passbook/push_notification.rb +10 -0
- data/lib/passbook/signer.rb +40 -0
- data/lib/passbook/version.rb +3 -0
- data/lib/rack/passbook_rack.rb +98 -0
- data/lib/rails/generators/passbook/config/config_generator.rb +16 -0
- data/lib/rails/generators/passbook/config/templates/initializer.rb +13 -0
- data/lib/utils/command_utils.rb +12 -0
- data/passbook.gemspec +110 -0
- data/spec/data/icon.png +0 -0
- data/spec/data/icon@2x.png +0 -0
- data/spec/data/logo.png +0 -0
- data/spec/data/logo@2x.png +0 -0
- data/spec/lib/commands/build_spec.rb +92 -0
- data/spec/lib/commands/commands_spec.rb +102 -0
- data/spec/lib/commands/commands_spec_helper.rb +69 -0
- data/spec/lib/commands/generate_spec.rb +72 -0
- data/spec/lib/passbook/pkpass_spec.rb +108 -0
- data/spec/lib/passbook/push_notification_spec.rb +22 -0
- data/spec/lib/passbook/signer_spec.rb +84 -0
- data/spec/lib/rack/passbook_rack_spec.rb +233 -0
- data/spec/spec_helper.rb +9 -0
- metadata +216 -0
@@ -0,0 +1,69 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'terminal-table'
|
3
|
+
require 'commander/import'
|
4
|
+
require 'utils/command_utils'
|
5
|
+
|
6
|
+
def load_commands
|
7
|
+
Dir['lib/commands/**/*.rb'].each {|f|
|
8
|
+
load File.join(File.dirname(__FILE__), '../../..', f)
|
9
|
+
require File.join(File.dirname(__FILE__), '../../..', f.gsub(/.rb/, ''))
|
10
|
+
}
|
11
|
+
end
|
12
|
+
|
13
|
+
def mock_terminal
|
14
|
+
@input = StringIO.new
|
15
|
+
@output = StringIO.new
|
16
|
+
$terminal = HighLine.new @input, @output
|
17
|
+
end
|
18
|
+
|
19
|
+
def new_command_runner *args, &block
|
20
|
+
Commander::Runner.instance_variable_set :"@singleton", Commander::Runner.new(args)
|
21
|
+
program :version, '1.2.3'
|
22
|
+
program :description, "Honey Badger Don't Care"
|
23
|
+
yield if block
|
24
|
+
Commander::Runner.instance
|
25
|
+
end
|
26
|
+
|
27
|
+
def run *args
|
28
|
+
runner = new_command_runner(*args) do
|
29
|
+
load_commands
|
30
|
+
end
|
31
|
+
runner.run!
|
32
|
+
@output.string
|
33
|
+
end
|
34
|
+
|
35
|
+
RSpec::Matchers.define :exit_with_code do |exp_code|
|
36
|
+
actual = nil
|
37
|
+
match do |block|
|
38
|
+
begin
|
39
|
+
block.call
|
40
|
+
rescue SystemExit => e
|
41
|
+
actual = e.status
|
42
|
+
end
|
43
|
+
actual and actual == exp_code
|
44
|
+
end
|
45
|
+
failure_message_for_should do |block|
|
46
|
+
"expected block to call exit(#{exp_code}) but exit" +
|
47
|
+
(actual.nil? ? " not called" : "(#{actual}) was called")
|
48
|
+
end
|
49
|
+
failure_message_for_should_not do |block|
|
50
|
+
"expected block not to call exit(#{exp_code})"
|
51
|
+
end
|
52
|
+
description do
|
53
|
+
"expect block to call exit(#{exp_code})"
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def run_raw_command(*args)
|
58
|
+
lambda{
|
59
|
+
run(*args)
|
60
|
+
}.should raise_error(SystemExit, ' ')
|
61
|
+
end
|
62
|
+
|
63
|
+
def run_command(*args, &block)
|
64
|
+
begin
|
65
|
+
run_raw_command *args
|
66
|
+
rescue
|
67
|
+
yield
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
require 'lib/commands/commands_spec_helper'
|
2
|
+
require 'passbook'
|
3
|
+
|
4
|
+
describe 'Generate' do
|
5
|
+
|
6
|
+
before :each do
|
7
|
+
$stderr = StringIO.new
|
8
|
+
mock_terminal
|
9
|
+
end
|
10
|
+
|
11
|
+
context 'command' do
|
12
|
+
specify 'no options' do
|
13
|
+
run_command 'generate' do
|
14
|
+
@output.string.should eq 'Enter a passbook name: '
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
|
19
|
+
specify 'passbook entered directory already exists' do
|
20
|
+
@input << "my_awesome_passbook\n"
|
21
|
+
@input.rewind
|
22
|
+
File.should_receive(:directory?).with('my_awesome_passbook').and_return true
|
23
|
+
run_command 'generate' do
|
24
|
+
@output.string.should eq "Enter a passbook name: \e[31mDirectory my_awesome_passbook already exists\e[0m\n"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
specify 'passbook entered file already exists' do
|
29
|
+
@input << "my_awesome_passbook\n"
|
30
|
+
@input.rewind
|
31
|
+
File.should_receive(:directory?).with('my_awesome_passbook').and_return false
|
32
|
+
File.should_receive(:exist?).with('my_awesome_passbook').and_return true
|
33
|
+
run_command 'generate' do
|
34
|
+
@output.string.should eq "Enter a passbook name: \e[31mFile exists at my_awesome_passbook\e[0m\n"
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
context 'valid pass directory' do
|
39
|
+
|
40
|
+
before :each do
|
41
|
+
File.should_receive(:directory?).with('my_awesome_passbook').and_return false
|
42
|
+
File.should_receive(:exist?).with('my_awesome_passbook').and_return false
|
43
|
+
end
|
44
|
+
|
45
|
+
specify 'invalid type' do
|
46
|
+
@input << "my_awesome_passbook\n"
|
47
|
+
@input.rewind
|
48
|
+
run_command 'generate', '-T', 'honey_badger' do
|
49
|
+
@output.string.should eq "Enter a passbook name: \e[31mInvalid type: \"honey_badger\", expected one of: [boarding-pass, coupon, event-ticket, store-card, generic]\e[0m\n"
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
specify 'valid type' do
|
54
|
+
CommandUtils.should_receive(:get_current_directory).and_return('')
|
55
|
+
FileUtils.should_receive(:mkdir_p).with('my_awesome_passbook')
|
56
|
+
FileUtils.should_receive(:cp).with("/../commands/templates/boarding-pass.json", "my_awesome_passbook/pass.json")
|
57
|
+
FileUtils.should_receive(:touch).with("my_awesome_passbook/icon.png")
|
58
|
+
FileUtils.should_receive(:touch).with("my_awesome_passbook/icon@2x.png")
|
59
|
+
@input << "1\n"
|
60
|
+
@input.rewind
|
61
|
+
run_command 'generate', 'my_awesome_passbook' do
|
62
|
+
@output.string.should eq "Select a pass type\n1. boarding-pass\n2. coupon\n3. event-ticket\n4. store-card\n5. generic\n? \e[32mPass generated in my_awesome_passbook\e[0m\n"
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
|
68
|
+
end
|
69
|
+
|
70
|
+
|
71
|
+
end
|
72
|
+
|
@@ -0,0 +1,108 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Passbook do
|
4
|
+
|
5
|
+
let (:content) {{
|
6
|
+
:formatVersion => 1,
|
7
|
+
:passTypeIdentifier => "pass.passbook.test",
|
8
|
+
:serialNumber => "001",
|
9
|
+
:teamIdentifier => ENV['APPLE_TEAM_ID'],
|
10
|
+
:relevantDate => "2012-10-02",
|
11
|
+
:locations => [ #TODO
|
12
|
+
{
|
13
|
+
:longitude => 2.35403,
|
14
|
+
:latitude => 48.893855
|
15
|
+
}
|
16
|
+
],
|
17
|
+
:organizationName => "WorldCo",
|
18
|
+
:description => "description",
|
19
|
+
:foregroundColor => "rgb(227,210,18)",
|
20
|
+
:backgroundColor => "rgb(60, 65, 76)",
|
21
|
+
:logoText => "Event",
|
22
|
+
:eventTicket => {
|
23
|
+
:primaryFields => [
|
24
|
+
{
|
25
|
+
:key => "date",
|
26
|
+
:label => "DATE",
|
27
|
+
:value => "date"
|
28
|
+
}
|
29
|
+
],
|
30
|
+
:backFields => [
|
31
|
+
{
|
32
|
+
:key => "description",
|
33
|
+
:label => "DESCRIPTION",
|
34
|
+
:value => "description"
|
35
|
+
},
|
36
|
+
{
|
37
|
+
:key => "aboutUs",
|
38
|
+
:label => "MORE",
|
39
|
+
:value => "about us"
|
40
|
+
}
|
41
|
+
]
|
42
|
+
}
|
43
|
+
}}
|
44
|
+
|
45
|
+
let (:signer) {double 'signer'}
|
46
|
+
let (:pass) {Passbook::PKPass.new content.to_json, signer}
|
47
|
+
|
48
|
+
context 'outputs' do
|
49
|
+
let (:base_path) {'spec/data'}
|
50
|
+
let (:entries) {["pass.json", "manifest.json", "signature", "icon.png", "icon@2x.png", "logo.png", "logo@2x.png"]}
|
51
|
+
|
52
|
+
before :each do
|
53
|
+
pass.addFiles ["#{base_path}/icon.png","#{base_path}/icon@2x.png","#{base_path}/logo.png","#{base_path}/logo@2x.png"]
|
54
|
+
signer.should_receive(:sign).and_return('Signed by the Honey Badger')
|
55
|
+
@file_entries = []
|
56
|
+
Zip::InputStream::open(zip_path) {|io|
|
57
|
+
while (entry = io.get_next_entry)
|
58
|
+
@file_entries << entry.name
|
59
|
+
end
|
60
|
+
}
|
61
|
+
end
|
62
|
+
|
63
|
+
context 'zip file' do
|
64
|
+
let(:zip_path) {pass.file.path}
|
65
|
+
|
66
|
+
subject {entries}
|
67
|
+
it {should eq @file_entries}
|
68
|
+
end
|
69
|
+
|
70
|
+
context 'StringIO' do
|
71
|
+
let (:temp_file) {Tempfile.new("pass.pkpass")}
|
72
|
+
let (:zip_path) {
|
73
|
+
zip_out = pass.stream
|
74
|
+
zip_out.class.should eq(Class::StringIO)
|
75
|
+
#creating file, re-reading zip to see if correctly formed
|
76
|
+
temp_file.write zip_out.string
|
77
|
+
temp_file.close
|
78
|
+
temp_file.path
|
79
|
+
}
|
80
|
+
|
81
|
+
subject {entries}
|
82
|
+
it {should eq @file_entries}
|
83
|
+
|
84
|
+
after do
|
85
|
+
temp_file.delete
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
# TODO: find a proper way to do this
|
91
|
+
context 'Error catcher' do
|
92
|
+
context 'formatVersion' do
|
93
|
+
let (:base_path) {'spec/data'}
|
94
|
+
|
95
|
+
before :each do
|
96
|
+
pass.addFiles ["#{base_path}/icon.png","#{base_path}/icon@2x.png","#{base_path}/logo.png","#{base_path}/logo@2x.png"]
|
97
|
+
tpass = JSON.parse(pass.pass)
|
98
|
+
tpass['formatVersion'] = 'It should be a numeric'
|
99
|
+
pass.pass = tpass.to_json
|
100
|
+
end
|
101
|
+
|
102
|
+
it "raise an error" do
|
103
|
+
expect { pass.build }.to raise_error('Format Version should be a numeric')
|
104
|
+
end
|
105
|
+
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'grocer'
|
3
|
+
|
4
|
+
describe Passbook::PushNotification do
|
5
|
+
|
6
|
+
context 'send notification' do
|
7
|
+
let(:grocer_pusher) {double 'Grocer'}
|
8
|
+
let(:notification) {double 'Grocer::Notification'}
|
9
|
+
let(:notification_settings) {{:certificate => './notification_cert.pem', :gateway => 'honeybadger.apple.com'}}
|
10
|
+
|
11
|
+
before :each do
|
12
|
+
Passbook.should_receive(:notification_cert).and_return './notification_cert.pem'
|
13
|
+
Grocer::PassbookNotification.should_receive(:new).with(:device_token => 'my token').and_return notification
|
14
|
+
grocer_pusher.should_receive(:push).with(notification).and_return 55
|
15
|
+
Grocer.should_receive(:pusher).with(notification_settings).and_return grocer_pusher
|
16
|
+
Passbook.should_receive(:notification_gateway).and_return 'honeybadger.apple.com'
|
17
|
+
end
|
18
|
+
|
19
|
+
subject {Passbook::PushNotification.send_notification('my token')}
|
20
|
+
it {should eq 55}
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'Signer' do
|
4
|
+
context 'signatures' do
|
5
|
+
|
6
|
+
context 'p12_cert_and_key' do
|
7
|
+
context 'pem p12 certs' do
|
8
|
+
context 'using config file certificates' do
|
9
|
+
before do
|
10
|
+
Passbook.should_receive(:p12_password).and_return 'password'
|
11
|
+
Passbook.should_receive(:p12_key).and_return 'my_p12_key'
|
12
|
+
Passbook.should_receive(:p12_certificate).and_return 'my_p12_certificate'
|
13
|
+
Passbook.should_receive(:wwdc_cert).and_return 'i_love_robots'
|
14
|
+
File.should_receive(:read).with('my_p12_key').and_return 'my_p12_key_file'
|
15
|
+
File.should_receive(:read).with('my_p12_certificate').and_return 'my_p12_certificate_file'
|
16
|
+
OpenSSL::PKey::RSA.should_receive(:new).with('my_p12_key_file', 'password').and_return 'my_rsa_key'
|
17
|
+
OpenSSL::X509::Certificate.should_receive(:new).with('my_p12_certificate_file').and_return 'my_ssl_p12_cert'
|
18
|
+
end
|
19
|
+
|
20
|
+
subject {Passbook::Signer.new.key_hash}
|
21
|
+
its([:key]) {should eq 'my_rsa_key'}
|
22
|
+
its([:cert]) {should eq 'my_ssl_p12_cert'}
|
23
|
+
end
|
24
|
+
|
25
|
+
context 'using passed in certificates' do
|
26
|
+
before do
|
27
|
+
Passbook.should_receive(:p12_password).never
|
28
|
+
Passbook.should_receive(:p12_key).never
|
29
|
+
Passbook.should_receive(:p12_certificate).never
|
30
|
+
Passbook.should_receive(:wwdc_cert).never
|
31
|
+
File.should_receive(:read).with('my_p12_key').and_return 'my_p12_key_file'
|
32
|
+
File.should_receive(:read).with('my_p12_certificate').and_return 'my_p12_certificate_file'
|
33
|
+
OpenSSL::PKey::RSA.should_receive(:new).with('my_p12_key_file', 'password').and_return 'my_rsa_key'
|
34
|
+
OpenSSL::X509::Certificate.should_receive(:new).with('my_p12_certificate_file').and_return 'my_ssl_p12_cert'
|
35
|
+
end
|
36
|
+
|
37
|
+
subject {Passbook::Signer.new(certificate: 'my_p12_certificate', password: 'password',
|
38
|
+
key: 'my_p12_key', wwdc_cert: 'i_love_robots').key_hash}
|
39
|
+
its([:key]) {should eq 'my_rsa_key'}
|
40
|
+
its([:cert]) {should eq 'my_ssl_p12_cert'}
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
context 'p12 files' do
|
45
|
+
let (:p12) { double('OpenSSL::PKCS12') }
|
46
|
+
let (:final_hash) {{:key => 'my_final_p12_key', :cert => 'my_final_p12_cert'}}
|
47
|
+
context 'using config file certificates' do
|
48
|
+
before do
|
49
|
+
p12.should_receive(:key).and_return final_hash[:key]
|
50
|
+
p12.should_receive(:certificate).and_return final_hash[:cert]
|
51
|
+
Passbook.should_receive(:p12_password).and_return 'password'
|
52
|
+
Passbook.should_receive(:wwdc_cert).and_return 'i_love_robots'
|
53
|
+
Passbook.should_receive(:p12_certificate).and_return 'my_p12_cert'
|
54
|
+
Passbook.should_receive(:p12_key).and_return nil
|
55
|
+
File.should_receive(:read).with('my_p12_cert').and_return 'my_p12_cert_file'
|
56
|
+
OpenSSL::PKCS12.should_receive(:new).with('my_p12_cert_file', 'password').and_return p12
|
57
|
+
end
|
58
|
+
|
59
|
+
subject {Passbook::Signer.new.key_hash}
|
60
|
+
its([:key]) {should eq final_hash[:key]}
|
61
|
+
its([:cert]) {should eq final_hash[:cert]}
|
62
|
+
end
|
63
|
+
|
64
|
+
context 'using passed in certificates' do
|
65
|
+
before do
|
66
|
+
p12.should_receive(:key).and_return final_hash[:key]
|
67
|
+
p12.should_receive(:certificate).and_return final_hash[:cert]
|
68
|
+
Passbook.should_receive(:p12_password).never
|
69
|
+
Passbook.should_receive(:p12_key).never
|
70
|
+
Passbook.should_receive(:p12_certificate).never
|
71
|
+
Passbook.should_receive(:wwdc_cert).never
|
72
|
+
File.should_receive(:read).with('my_p12_cert').and_return 'my_p12_cert_file'
|
73
|
+
OpenSSL::PKCS12.should_receive(:new).with('my_p12_cert_file', 'password').and_return p12
|
74
|
+
end
|
75
|
+
|
76
|
+
subject {Passbook::Signer.new(certificate: 'my_p12_cert', password: 'password',
|
77
|
+
wwdc_cert: 'i_love_robots').key_hash}
|
78
|
+
its([:key]) {should eq final_hash[:key]}
|
79
|
+
its([:cert]) {should eq final_hash[:cert]}
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,233 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Rack::PassbookRack do
|
4
|
+
|
5
|
+
let(:register_delete_path) {'/v1/devices/fe772e610be3efafb65ed77772ca311a/registrations/pass.com.polyglotprogramminginc.testpass/27-1'}
|
6
|
+
let(:register_delete_params) {{'deviceLibraryIdentifier' => 'fe772e610be3efafb65ed77772ca311a',
|
7
|
+
'passTypeIdentifier' => 'pass.com.polyglotprogramminginc.testpass',
|
8
|
+
'serialNumber' => '27-1'}}
|
9
|
+
let(:passes_for_device_path) {'/v1/devices/fe772e610be3efafb65ed77772ca311a/registrations/pass.com.polyglotprogramminginc.testpass'}
|
10
|
+
let(:passes_for_device_params) {{'deviceLibraryIdentifier' => 'fe772e610be3efafb65ed77772ca311a',
|
11
|
+
'passTypeIdentifier' => 'pass.com.polyglotprogramminginc.testpass'}}
|
12
|
+
let(:latest_pass_path) {'/v1/passes/pass.com.polyglotprogramminginc.testpass/27-1'}
|
13
|
+
let(:latest_pass_params) {{'passTypeIdentifier' => 'pass.com.polyglotprogramminginc.testpass',
|
14
|
+
'serialNumber' => '27-1'}}
|
15
|
+
let(:log_path) {'/v1/log'}
|
16
|
+
let(:push_token) {"8c56f2e787d9c089963960ace834bc2875e3f0cf7745da5b98d58bc6be05b4dc"}
|
17
|
+
let(:auth_token) {"3c0adc9ccbcf3e733edeb897043a4835"}
|
18
|
+
|
19
|
+
context 'find method' do
|
20
|
+
let(:passbook_rack) {Rack::PassbookRack.new nil}
|
21
|
+
|
22
|
+
shared_examples_for 'a method that can handle non passbook urls' do
|
23
|
+
context 'incomplete passbook api path' do
|
24
|
+
subject {passbook_rack.find_method('/v1/devices/fe772e610be3efafb65ed77772ca311a/registrations')}
|
25
|
+
it {should eq nil}
|
26
|
+
end
|
27
|
+
|
28
|
+
context 'no version api path' do
|
29
|
+
subject {passbook_rack.find_method('/devices/fe772e610be3efafb65ed77772ca311a/registrations')}
|
30
|
+
it {should eq nil}
|
31
|
+
end
|
32
|
+
|
33
|
+
context 'no devices api path' do
|
34
|
+
subject {passbook_rack.find_method('/v1/fe772e610be3efafb65ed77772ca311a/registrations')}
|
35
|
+
it {should eq nil}
|
36
|
+
end
|
37
|
+
|
38
|
+
context 'no registrations api path' do
|
39
|
+
subject {passbook_rack.find_method('/v1/devices/fe772e610be3efafb65ed77772ca311a')}
|
40
|
+
it {should eq nil}
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
context 'device register delete' do
|
45
|
+
context 'a valid path' do
|
46
|
+
subject {passbook_rack.find_method(register_delete_path)}
|
47
|
+
its([:method]) {should eq 'device_register_delete'}
|
48
|
+
its([:params]) {should eq(register_delete_params) }
|
49
|
+
end
|
50
|
+
|
51
|
+
it_behaves_like 'a method that can handle non passbook urls'
|
52
|
+
|
53
|
+
end
|
54
|
+
|
55
|
+
context 'passes for device' do
|
56
|
+
subject {passbook_rack.find_method(passes_for_device_path)}
|
57
|
+
its([:method]) {should eq 'passes_for_device'}
|
58
|
+
its([:params]) {should eq passes_for_device_params }
|
59
|
+
end
|
60
|
+
|
61
|
+
context 'latest pass' do
|
62
|
+
subject {passbook_rack.find_method(latest_pass_path)}
|
63
|
+
its([:method]) {should eq 'latest_pass'}
|
64
|
+
its([:params]) {should eq(latest_pass_params) }
|
65
|
+
end
|
66
|
+
|
67
|
+
context 'latest pass' do
|
68
|
+
subject {passbook_rack.find_method(log_path)}
|
69
|
+
its([:method]) {should eq 'log'}
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
context 'rack middleware' do
|
74
|
+
|
75
|
+
context 'register pass without authToken' do
|
76
|
+
before do
|
77
|
+
Passbook::PassbookNotification.should_receive(:register_pass).
|
78
|
+
with(register_delete_params.merge!('pushToken' => push_token)).and_return({:status => 201})
|
79
|
+
post register_delete_path, {"pushToken" => push_token}.to_json
|
80
|
+
end
|
81
|
+
|
82
|
+
subject {last_response}
|
83
|
+
its(:status) {should eq 201}
|
84
|
+
end
|
85
|
+
|
86
|
+
context 'register pass with authToken' do
|
87
|
+
before do
|
88
|
+
Passbook::PassbookNotification.should_receive(:register_pass).
|
89
|
+
with(register_delete_params.merge!('pushToken' => push_token,'authToken' => auth_token)).and_return({:status => 201})
|
90
|
+
post register_delete_path, {"pushToken" => push_token}.to_json, rack_env = {'HTTP_AUTHORIZATION' => auth_token}
|
91
|
+
end
|
92
|
+
|
93
|
+
subject {last_response}
|
94
|
+
its(:status) {should eq 201}
|
95
|
+
end
|
96
|
+
|
97
|
+
context 'passes for device' do
|
98
|
+
context 'with passes' do
|
99
|
+
let(:passes_for_device_response) {{'last_updated' => 1, 'serial_numbers' => [343, 234]}}
|
100
|
+
before do
|
101
|
+
Passbook::PassbookNotification.should_receive(:passes_for_device).
|
102
|
+
with(passes_for_device_params).and_return(passes_for_device_response)
|
103
|
+
get passes_for_device_path
|
104
|
+
end
|
105
|
+
|
106
|
+
context 'status' do
|
107
|
+
subject {last_response.status}
|
108
|
+
it {should eq 200}
|
109
|
+
end
|
110
|
+
|
111
|
+
context 'body' do
|
112
|
+
subject{JSON.parse(last_response.body)}
|
113
|
+
it {should eq passes_for_device_response}
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
context 'with passes modified since' do
|
118
|
+
before do
|
119
|
+
Passbook::PassbookNotification.should_receive(:passes_for_device).
|
120
|
+
with(passes_for_device_params.merge!('passesUpdatedSince' => '1371189712')).and_return(nil)
|
121
|
+
path_with_update_since = passes_for_device_path + "?passesUpdatedSince=1371189712"
|
122
|
+
get path_with_update_since
|
123
|
+
end
|
124
|
+
|
125
|
+
context 'status' do
|
126
|
+
subject {last_response.status}
|
127
|
+
it {should eq 204}
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
context 'without passes' do
|
132
|
+
before do
|
133
|
+
Passbook::PassbookNotification.should_receive(:passes_for_device).
|
134
|
+
with(passes_for_device_params).and_return(nil)
|
135
|
+
get passes_for_device_path
|
136
|
+
end
|
137
|
+
|
138
|
+
context 'status' do
|
139
|
+
subject {last_response.status}
|
140
|
+
it {should eq 204}
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
context 'get latest pass' do
|
146
|
+
context 'valid pass' do
|
147
|
+
let(:raw_pass) {'some url encoded text'}
|
148
|
+
|
149
|
+
before do
|
150
|
+
Passbook::PassbookNotification.should_receive(:latest_pass).with(latest_pass_params).
|
151
|
+
and_return(raw_pass)
|
152
|
+
get latest_pass_path
|
153
|
+
end
|
154
|
+
|
155
|
+
subject {last_response}
|
156
|
+
its(:status) {should eq 200}
|
157
|
+
its(:header) {should eq({'Content-Type' => 'application/vnd.apple.pkpass',
|
158
|
+
'Content-Disposition' => 'attachment', 'filename' => '27-1.pkpass', 'Content-Length' => '21'})}
|
159
|
+
its(:body) {should eq raw_pass}
|
160
|
+
end
|
161
|
+
|
162
|
+
context 'no pass' do
|
163
|
+
before do
|
164
|
+
Passbook::PassbookNotification.should_receive(:latest_pass).with(latest_pass_params).
|
165
|
+
and_return(nil)
|
166
|
+
get latest_pass_path
|
167
|
+
end
|
168
|
+
|
169
|
+
subject {last_response}
|
170
|
+
its(:status) {should eq 204}
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
context 'unregister pass without authToken' do
|
175
|
+
before do
|
176
|
+
Passbook::PassbookNotification.should_receive(:unregister_pass).
|
177
|
+
with(register_delete_params).and_return({:status => 200})
|
178
|
+
delete register_delete_path
|
179
|
+
end
|
180
|
+
|
181
|
+
subject {last_response}
|
182
|
+
its(:status) {should eq 200}
|
183
|
+
end
|
184
|
+
|
185
|
+
context 'unregister pass with authToken' do
|
186
|
+
before do
|
187
|
+
Passbook::PassbookNotification.should_receive(:unregister_pass).
|
188
|
+
with(register_delete_params.merge!('authToken' => auth_token)).and_return({:status => 200})
|
189
|
+
delete register_delete_path, {}, rack_env = {'HTTP_AUTHORIZATION' => auth_token}
|
190
|
+
end
|
191
|
+
|
192
|
+
subject {last_response}
|
193
|
+
its(:status) {should eq 200}
|
194
|
+
end
|
195
|
+
|
196
|
+
context 'log' do
|
197
|
+
let(:log_params) {{'logs' => ['some error']}}
|
198
|
+
before do
|
199
|
+
Passbook::PassbookNotification.should_receive(:passbook_log).
|
200
|
+
with(log_params)
|
201
|
+
post log_path, log_params.to_json
|
202
|
+
end
|
203
|
+
|
204
|
+
subject {last_response}
|
205
|
+
its(:status) {should eq 200}
|
206
|
+
end
|
207
|
+
|
208
|
+
context 'non passbook requests' do
|
209
|
+
before do
|
210
|
+
get '/foo'
|
211
|
+
end
|
212
|
+
|
213
|
+
subject {last_response}
|
214
|
+
its(:status) {should eq 200}
|
215
|
+
its(:body) {should eq 'test app'}
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
219
|
+
end
|
220
|
+
|
221
|
+
require 'rack/test'
|
222
|
+
include Rack::Test::Methods
|
223
|
+
|
224
|
+
def app
|
225
|
+
test_app = lambda do |env|
|
226
|
+
[200, {}, 'test app']
|
227
|
+
end
|
228
|
+
|
229
|
+
Rack::PassbookRack.new test_app
|
230
|
+
end
|
231
|
+
|
232
|
+
class Passbook::PassbookNotification
|
233
|
+
end
|