passbook-iid 0.4.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/.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
|