passbook-iid 0.4.0

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