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.
Files changed (40) hide show
  1. data/.travis.yml +7 -0
  2. data/Gemfile +17 -0
  3. data/Gemfile.lock +65 -0
  4. data/LICENSE +22 -0
  5. data/README.md +290 -0
  6. data/Rakefile +36 -0
  7. data/VERSION +1 -0
  8. data/bin/pk +22 -0
  9. data/lib/commands/build.rb +62 -0
  10. data/lib/commands/commands.rb +31 -0
  11. data/lib/commands/generate.rb +44 -0
  12. data/lib/commands/templates/boarding-pass.json +56 -0
  13. data/lib/commands/templates/coupon.json +33 -0
  14. data/lib/commands/templates/event-ticket.json +33 -0
  15. data/lib/commands/templates/generic.json +33 -0
  16. data/lib/commands/templates/store-card.json +33 -0
  17. data/lib/passbook.rb +15 -0
  18. data/lib/passbook/pkpass.rb +120 -0
  19. data/lib/passbook/push_notification.rb +10 -0
  20. data/lib/passbook/signer.rb +40 -0
  21. data/lib/passbook/version.rb +3 -0
  22. data/lib/rack/passbook_rack.rb +98 -0
  23. data/lib/rails/generators/passbook/config/config_generator.rb +16 -0
  24. data/lib/rails/generators/passbook/config/templates/initializer.rb +13 -0
  25. data/lib/utils/command_utils.rb +12 -0
  26. data/passbook.gemspec +110 -0
  27. data/spec/data/icon.png +0 -0
  28. data/spec/data/icon@2x.png +0 -0
  29. data/spec/data/logo.png +0 -0
  30. data/spec/data/logo@2x.png +0 -0
  31. data/spec/lib/commands/build_spec.rb +92 -0
  32. data/spec/lib/commands/commands_spec.rb +102 -0
  33. data/spec/lib/commands/commands_spec_helper.rb +69 -0
  34. data/spec/lib/commands/generate_spec.rb +72 -0
  35. data/spec/lib/passbook/pkpass_spec.rb +108 -0
  36. data/spec/lib/passbook/push_notification_spec.rb +22 -0
  37. data/spec/lib/passbook/signer_spec.rb +84 -0
  38. data/spec/lib/rack/passbook_rack_spec.rb +233 -0
  39. data/spec/spec_helper.rb +9 -0
  40. 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