cassette 1.0.18 → 1.1.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 (41) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +89 -12
  3. data/lib/cassette/authentication/authorities.rb +34 -30
  4. data/lib/cassette/authentication/cache.rb +22 -18
  5. data/lib/cassette/authentication/filter.rb +52 -33
  6. data/lib/cassette/authentication/user.rb +20 -16
  7. data/lib/cassette/authentication.rb +39 -27
  8. data/lib/cassette/cache.rb +2 -2
  9. data/lib/cassette/client/cache.rb +39 -35
  10. data/lib/cassette/client.rb +5 -4
  11. data/lib/cassette/http/parsed_response.rb +20 -0
  12. data/lib/cassette/http/request.rb +44 -0
  13. data/lib/cassette/http/ticket_response.rb +48 -0
  14. data/lib/cassette/http.rb +8 -0
  15. data/lib/cassette/rubycas/helper.rb +2 -8
  16. data/lib/cassette/rubycas/routing_constraint.rb +23 -0
  17. data/lib/cassette/rubycas/single_sign_out_constraint.rb +8 -8
  18. data/lib/cassette/rubycas/user_factory.rb +14 -0
  19. data/lib/cassette/rubycas.rb +2 -0
  20. data/lib/cassette/version.rb +2 -2
  21. data/lib/cassette.rb +12 -50
  22. data/spec/cassette/authentication/authorities_spec.rb +1 -1
  23. data/spec/cassette/authentication/cache_spec.rb +40 -4
  24. data/spec/cassette/authentication/filter_spec.rb +106 -36
  25. data/spec/cassette/authentication/user_factory_spec.rb +42 -0
  26. data/spec/cassette/authentication/user_spec.rb +4 -3
  27. data/spec/cassette/authentication_spec.rb +24 -12
  28. data/spec/cassette/cache_spec.rb +0 -2
  29. data/spec/cassette/client/cache_spec.rb +1 -1
  30. data/spec/cassette/client_spec.rb +319 -0
  31. data/spec/cassette/errors_spec.rb +1 -1
  32. data/spec/cassette/http/parsed_response_spec.rb +27 -0
  33. data/spec/cassette/http/request_spec.rb +41 -0
  34. data/spec/cassette/http/ticket_response_spec.rb +41 -0
  35. data/spec/cassette/rubycas/routing_constraint_spec.rb +84 -0
  36. data/spec/cassette_spec.rb +36 -0
  37. data/spec/integration/cas/client_spec.rb +0 -3
  38. data/spec/spec_helper.rb +5 -0
  39. data/spec/support/controllers/controller_mock.rb +19 -0
  40. metadata +98 -36
  41. data/spec/cas_spec.rb +0 -78
@@ -1,27 +1,39 @@
1
1
  # encoding: utf-8
2
-
3
- require 'spec_helper'
4
-
5
2
  describe Cassette::Authentication do
6
3
  let(:cache) { instance_double(Cassette::Authentication::Cache) }
7
4
  let(:http) { class_double(Cassette) }
8
5
 
9
- subject do
6
+ subject(:authentication) do
10
7
  Cassette::Authentication.new(cache: cache, http_client: http)
11
8
  end
12
9
 
13
10
  describe '#ticket_user' do
11
+ subject(:ticket_user) { authentication.ticket_user(ticket) }
12
+
13
+ let(:ticket) { 'ticket' }
14
+ let(:cached_value) { 'cached_value' }
15
+ let(:service) { 'test-api.example.org' }
16
+
14
17
  context 'when cached' do
15
- it 'returns the cached value when cached' do
16
- cached = double('cached')
18
+ before do
19
+ allow(cache).to receive(:fetch_authentication)
20
+ .and_return(cached_value)
21
+ end
22
+
23
+ it { is_expected.to eql(cached_value) }
24
+ it 'calls Cache#fetch_authentication with ticket and service' do
25
+ expect(cache).to receive(:fetch_authentication)
26
+ .with(ticket, service)
27
+
28
+ ticket_user
29
+ end
17
30
 
18
- expect(cache).to receive(:fetch_authentication) do |ticket, &block|
19
- expect(ticket).to eql('ticket')
31
+ it 'passes a block to Cache#fetch_authentication' do
32
+ expect(cache).to receive(:fetch_authentication) do |*, &block|
20
33
  expect(block).to be_present
21
- cached
22
34
  end
23
35
 
24
- expect(subject.ticket_user('ticket')).to eql(cached)
36
+ ticket_user
25
37
  end
26
38
  end
27
39
 
@@ -44,7 +56,7 @@ describe Cassette::Authentication do
44
56
  end
45
57
 
46
58
  it 'returns nil' do
47
- expect(subject.ticket_user('ticket')).to be_nil
59
+ expect(ticket_user).to be_nil
48
60
  end
49
61
  end
50
62
 
@@ -55,7 +67,7 @@ describe Cassette::Authentication do
55
67
  end
56
68
 
57
69
  it 'returns an User' do
58
- expect(subject.ticket_user('ticket')).to be_instance_of(Cassette::Authentication::User)
70
+ expect(ticket_user).to be_instance_of(Cassette::Authentication::User)
59
71
  end
60
72
  end
61
73
  end
@@ -1,7 +1,5 @@
1
1
  # encoding: utf-8
2
2
 
3
- require 'spec_helper'
4
-
5
3
  describe Cassette::Cache do
6
4
  subject do
7
5
  c = Class.new
@@ -1,6 +1,6 @@
1
1
  # encoding: utf-8
2
2
 
3
- require 'spec_helper'
3
+
4
4
 
5
5
  describe Cassette::Client::Cache do
6
6
  pending
@@ -0,0 +1,319 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Cassette::Client do
6
+ let(:http) { class_double(Cassette) }
7
+ let(:cache) { instance_double(Cassette::Client::Cache) }
8
+ let(:options) do
9
+ {
10
+ http_client: http,
11
+ cache: cache,
12
+ }
13
+ end
14
+
15
+ let(:client) do
16
+ Cassette::Client.new(options)
17
+ end
18
+
19
+ describe '#health_check' do
20
+ subject { client.health_check }
21
+
22
+ it 'raises any Error' do
23
+ expect(client).to receive(:st_for).with(anything).and_raise('Failure')
24
+
25
+ expect { subject }.to raise_error('Failure')
26
+ end
27
+
28
+ it 'tries to generate a ST' do
29
+ expect(client).to receive(:st_for).with(an_instance_of(String)).and_return('ST-Something')
30
+
31
+ expect(subject).to eq 'ST-Something'
32
+ end
33
+ end
34
+
35
+ describe '#tgt' do
36
+ subject { client.tgt('user', 'pass', force) }
37
+
38
+ context 'http client interactions' do
39
+ let(:force) { true }
40
+ let(:response) { Faraday::Response.new }
41
+
42
+ before do
43
+ allow(cache).to receive(:fetch_tgt).and_yield
44
+ allow(http).to receive(:post) { response }
45
+ end
46
+
47
+ it 'extracts the tgt from the Location header' do
48
+ tgt = 'TGT-something'
49
+ allow(response).to receive(:headers).and_return('Location' => "/tickets/#{tgt}")
50
+
51
+ expect(subject).to eq tgt
52
+ end
53
+
54
+ it 'posts the username and password' do
55
+ subject
56
+ expect(http).to have_received(:post).with(anything, username: 'user', password: 'pass')
57
+ end
58
+
59
+ it 'posts to a /v1/tickets uri' do
60
+ subject
61
+ expect(http).to have_received(:post).with(%r{/v1/tickets\z}, instance_of(Hash))
62
+ end
63
+ end
64
+
65
+ context 'when tgt is not cached' do
66
+ let(:tgt) { 'TGT-Something-example' }
67
+ let(:force) { false }
68
+
69
+ before do
70
+ allow(cache).to receive(:fetch_tgt) do |opts, &_block|
71
+ expect(opts[:force]).to eq false
72
+ tgt
73
+ end
74
+
75
+ allow(http).to receive(:post)
76
+ end
77
+
78
+ it { is_expected.to eq tgt }
79
+ end
80
+
81
+ context 'with a cached tgt' do
82
+ let(:tgt) { 'TGT-Something-example' }
83
+
84
+ before do
85
+ allow(cache).to receive(:fetch_tgt).with(hash_including(force: force))
86
+ .and_return(tgt)
87
+
88
+ allow(http).to receive(:post)
89
+ end
90
+
91
+ shared_context 'force control' do
92
+ it { is_expected.to eq tgt }
93
+
94
+ it 'forwards force to the cache' do
95
+ subject
96
+ expect(cache).to have_received(:fetch_tgt).with(hash_including(force: force))
97
+ end
98
+ end
99
+
100
+ context 'and no force' do
101
+ let(:force) { false }
102
+
103
+ include_context 'force control'
104
+ end
105
+
106
+ context 'and using the force' do
107
+ let(:force) { true }
108
+
109
+ include_context 'force control'
110
+ end
111
+ end
112
+ end
113
+
114
+ describe '#st' do
115
+ subject { client.st(tgt_param, service, force) }
116
+ let(:service) { 'example.org' }
117
+ let(:tgt) { 'TGT-Example' }
118
+ let(:st) { 'ST-Something-example' }
119
+ let(:tgt_param) { tgt }
120
+
121
+ shared_context 'http client interactions' do
122
+ let(:force) { true }
123
+ let(:response) { Faraday::Response.new }
124
+
125
+ before do
126
+ allow(cache).to receive(:fetch_st).and_yield
127
+ allow(http).to receive(:post) { response }
128
+ end
129
+
130
+ it 'extracts the tgt from the Location header' do
131
+ allow(response).to receive(:body) { st }
132
+
133
+ expect(subject).to eq st
134
+ end
135
+
136
+ it 'posts the service' do
137
+ subject
138
+
139
+ expect(http).to have_received(:post).with(anything, service: service)
140
+ end
141
+
142
+ it 'posts to the tgt uri' do
143
+ subject
144
+
145
+ expect(http).to have_received(:post).with(%r{/#{tgt}\z}, instance_of(Hash))
146
+ end
147
+ end
148
+
149
+ context 'when tgt is a string' do
150
+ let(:tgt_param) { tgt }
151
+
152
+ it_behaves_like 'http client interactions'
153
+ end
154
+
155
+ context 'when tgt is a callable' do
156
+ let(:tgt_param) { ->{ tgt } }
157
+
158
+ it_behaves_like 'http client interactions'
159
+ end
160
+
161
+ context 'cache control' do
162
+ before do
163
+ allow(cache).to receive(:fetch_st).with(service, hash_including(force: force))
164
+ .and_return(st)
165
+ end
166
+
167
+ shared_context 'controlling the force' do
168
+ it { is_expected.to eq st }
169
+
170
+ it 'forwards force to the cache' do
171
+ subject
172
+
173
+ expect(cache).to have_received(:fetch_st).with(service, hash_including(force: force))
174
+ end
175
+ end
176
+
177
+ context 'not using the force' do
178
+ let(:force) { false }
179
+
180
+ include_context 'controlling the force'
181
+ end
182
+
183
+ context 'using the force' do
184
+ let(:force) { true }
185
+
186
+ include_context 'controlling the force'
187
+ end
188
+ end
189
+ end
190
+
191
+ describe '#st_for' do
192
+ subject { client.st_for(service) }
193
+ let(:service) { 'example.org' }
194
+ let(:cached_tgt) { 'TGT-Something' }
195
+ let(:tgt) { 'TGT-Something-NEW' }
196
+ let(:st) { 'ST-For-Something' }
197
+
198
+ context 'when tgt and st are not cached' do
199
+ before do
200
+ allow(cache).to receive(:fetch_tgt).with(hash_including(force: false)).and_yield
201
+ allow(cache).to receive(:fetch_st).with(service, hash_including(force: false)).and_yield
202
+
203
+ allow(http).to receive(:post)
204
+ .with(%r{/v1/tickets\z}, username: Cassette.config.username, password: Cassette.config.password)
205
+ .and_return(tgt_response)
206
+
207
+ allow(http).to receive(:post).with(%r{/v1/tickets/#{tgt}\z}, service: service)
208
+ .and_return(st_response)
209
+ end
210
+
211
+ let(:st_response) { Faraday::Response.new(body: st) }
212
+ let(:tgt_response) { Faraday::Response.new(response_headers: {'Location' => "/v1/tickets/#{tgt}"}) }
213
+
214
+ it 'returns the generated st' do
215
+ expect(subject).to eq st
216
+ end
217
+
218
+ it 'generates an ST' do
219
+ subject
220
+
221
+ expect(http).to have_received(:post).with(%r{/v1/tickets/#{tgt}\z}, service: service)
222
+ end
223
+
224
+ it 'generates a TGT' do
225
+ subject
226
+
227
+ expect(http).to have_received(:post)
228
+ .with(%r{/v1/tickets\z}, username: Cassette.config.username, password: Cassette.config.password)
229
+ end
230
+ end
231
+
232
+ context 'when tgt is cached but st is not' do
233
+ before do
234
+ allow(cache).to receive(:fetch_tgt).with(hash_including(force: false)).and_return(tgt)
235
+ allow(cache).to receive(:fetch_st).with(service, hash_including(force: false)).and_yield
236
+
237
+ allow(http).to receive(:post).with(%r{/v1/tickets/#{tgt}\z}, service: service)
238
+ .and_return(st_response)
239
+ end
240
+
241
+ let(:st_response) { Faraday::Response.new(body: st) }
242
+
243
+ it 'returns the generated st' do
244
+ expect(subject).to eq st
245
+ end
246
+
247
+ it 'generates an ST' do
248
+ subject
249
+
250
+ expect(http).to have_received(:post).with(%r{/v1/tickets/#{tgt}\z}, service: service)
251
+ end
252
+ end
253
+
254
+ context 'when st is cached' do
255
+ before do
256
+ allow(cache).to receive(:fetch_st).with(service, hash_including(force: false)).and_return(st)
257
+ end
258
+
259
+ it 'returns the cached value' do
260
+ expect(subject).to eq st
261
+ end
262
+ end
263
+
264
+ context 'when tgt is expired' do
265
+ before do
266
+ allow(cache).to receive(:fetch_tgt).with(hash_including(force: false)).and_return(cached_tgt)
267
+ allow(cache).to receive(:fetch_tgt).with(hash_including(force: true)).and_yield
268
+ allow(cache).to receive(:fetch_st).and_yield
269
+
270
+ allow(http).to receive(:post).with(%r{/v1/tickets/#{cached_tgt}\z}, service: service)
271
+ .and_raise(Cassette::Errors::NotFound)
272
+
273
+ allow(http).to receive(:post)
274
+ .with(%r{/v1/tickets\z}, username: Cassette.config.username, password: Cassette.config.password)
275
+ .and_return(tgt_response)
276
+
277
+ allow(http).to receive(:post).with(%r{/v1/tickets/#{tgt}\z}, service: service)
278
+ .and_return(st_response)
279
+ end
280
+
281
+ let(:tgt_response) { Faraday::Response.new(response_headers: {'Location' => "/v1/tickets/#{tgt}"}) }
282
+ let(:st_response) { Faraday::Response.new(body: st) }
283
+
284
+ it 'calls #fetch_st twice' do
285
+ subject
286
+
287
+ expect(cache).to have_received(:fetch_st).twice
288
+ end
289
+
290
+ it 'calls #fetch_tgt without forcing' do
291
+ subject
292
+
293
+ expect(cache).to have_received(:fetch_tgt).with(force: false)
294
+ end
295
+
296
+ it 'calls #fetch_tgt forcing' do
297
+ subject
298
+
299
+ expect(cache).to have_received(:fetch_tgt).with(force: true)
300
+ end
301
+
302
+ it 'tries to generate a ST with the expired TGT' do
303
+ subject
304
+
305
+ expect(http).to have_received(:post).with(%r{/v1/tickets/#{cached_tgt}\z}, service: service)
306
+ end
307
+
308
+ it 'retries to generate a ST with the new TGT' do
309
+ subject
310
+
311
+ expect(http).to have_received(:post).with(%r{/v1/tickets/#{tgt}\z}, service: service)
312
+ end
313
+
314
+ it 'returns a brand new tgt' do
315
+ expect(subject).to eq st
316
+ end
317
+ end
318
+ end
319
+ end
@@ -1,6 +1,6 @@
1
1
  # encoding: UTF-8
2
2
 
3
- require 'spec_helper'
3
+
4
4
 
5
5
  describe Cassette::Errors do
6
6
  describe Cassette::Errors::Base do
@@ -0,0 +1,27 @@
1
+ describe Cassette::Http::ParsedResponse do
2
+ subject(:parsed_response) { described_class.new(xml_response) }
3
+
4
+ let(:xml_response) { fixture('cas/success.xml') }
5
+
6
+ let(:hash_response) do
7
+ {
8
+ "serviceResponse" => {
9
+ "authenticationSuccess" => {
10
+ "user"=> {
11
+ "__content__" => "test-user"
12
+ },
13
+ "attributes" => {
14
+ "authorities" => {
15
+ "__content__" => "[CUPOM, AUDITING,]"
16
+ },
17
+ "cn" => {
18
+ "__content__" => "Test System"
19
+ }
20
+ }
21
+ }
22
+ }
23
+ }
24
+ end
25
+
26
+ it { is_expected.to eq(hash_response) }
27
+ end
@@ -0,0 +1,41 @@
1
+ describe Cassette::Http::Request do
2
+ subject(:request) { described_class }
3
+
4
+ describe '.post' do
5
+ subject(:post) { request.post(uri, payload) }
6
+
7
+ let(:uri) { 'http://example.org/' }
8
+ let(:payload) { { ping: :pong } }
9
+ let(:response) do
10
+ {
11
+ headers: { 'Content-Type' => 'application/json' },
12
+ body: { ok: :true }.to_json,
13
+ status: 200
14
+ }
15
+ end
16
+
17
+ before { stub_request(:post, uri).to_return(response) }
18
+
19
+ it 'performs a http post request with the proper params'do
20
+ post
21
+
22
+ expect(a_request(:post, uri).with(body: 'ping=pong')).to have_been_made
23
+ end
24
+
25
+ it do
26
+ is_expected.to have_attributes(
27
+ headers: { 'Content-Type' => 'application/json' },
28
+ body: '{"ok":"true"}',
29
+ status: 200
30
+ )
31
+ end
32
+
33
+ context 'when response has an error status code' do
34
+ let(:response) { { status: 500 } }
35
+
36
+ it 'raises an exception' do
37
+ expect { post }.to raise_error(Cassette::Errors::InternalServerError)
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,41 @@
1
+ describe Cassette::Http::TicketResponse do
2
+ subject(:ticket_response) { described_class.new(xml_response) }
3
+
4
+ let(:xml_response) { fixture('cas/success.xml') }
5
+
6
+ describe '#login' do
7
+ subject(:login) { ticket_response.login }
8
+
9
+ it { is_expected.to eq('test-user') }
10
+
11
+ context "when response isn't successful" do
12
+ let(:xml_response) { fixture('cas/fail.xml') }
13
+
14
+ it { is_expected.to be_nil }
15
+ end
16
+ end
17
+
18
+ describe '#name' do
19
+ subject(:name) { ticket_response.name }
20
+
21
+ it { is_expected.to eq('Test System') }
22
+
23
+ context "when response isn't successful" do
24
+ let(:xml_response) { fixture('cas/fail.xml') }
25
+
26
+ it { is_expected.to be_nil }
27
+ end
28
+ end
29
+
30
+ describe '#authorities' do
31
+ subject(:authorities) { ticket_response.authorities }
32
+
33
+ it { is_expected.to eq('[CUPOM, AUDITING,]') }
34
+
35
+ context "when response isn't successful" do
36
+ let(:xml_response) { fixture('cas/fail.xml') }
37
+
38
+ it { is_expected.to be_nil }
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,84 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Cassette::Rubycas::RoutingConstraint do
6
+ describe '#matches?' do
7
+ let(:request) do
8
+ OpenStruct.new(session: session)
9
+ end
10
+
11
+ let(:user) { instance_double(Cassette::Authentication::User) }
12
+ let(:constraint) { described_class.new(role, options) }
13
+
14
+ subject { constraint.matches?(request) }
15
+
16
+ before do
17
+ allow(constraint).to receive(:from_session).with(session).and_return(user)
18
+ end
19
+
20
+ let(:session) do
21
+ {
22
+ cas_user: 'test.user',
23
+ cas_extra_attributes: {
24
+ cn: 'test user',
25
+ email: 'test.user@example.org'
26
+ }
27
+ }
28
+ end
29
+
30
+ context 'with no options' do
31
+ let(:role) { :admin }
32
+ let(:options) { {} }
33
+ let(:has_role) { true }
34
+
35
+ before do
36
+ allow(user).to receive(:has_role?).with(role).and_return(has_role)
37
+ end
38
+
39
+ it 'checks the User role' do
40
+ subject
41
+ expect(user).to have_received(:has_role?).with(role)
42
+ end
43
+
44
+ context 'when user has the role' do
45
+ let(:has_role) { true }
46
+
47
+ it { is_expected.to eq(true) }
48
+ end
49
+
50
+ context 'when user does not have the role' do
51
+ let(:has_role) { false }
52
+
53
+ it { is_expected.to eq(false) }
54
+ end
55
+ end
56
+
57
+ context 'when options[:raw] = true' do
58
+ let(:role) { 'API_ADMIN' }
59
+ let(:options) { { raw: true } }
60
+ let(:has_role) { true }
61
+
62
+ before do
63
+ allow(user).to receive(:has_raw_role?).with(role).and_return(has_role)
64
+ end
65
+
66
+ it 'checks the User role' do
67
+ subject
68
+ expect(user).to have_received(:has_raw_role?).with(role)
69
+ end
70
+
71
+ context 'when user has the role' do
72
+ let(:has_role) { true }
73
+
74
+ it { is_expected.to eq(true) }
75
+ end
76
+
77
+ context 'when user does not have the role' do
78
+ let(:has_role) { false }
79
+
80
+ it { is_expected.to eq(false) }
81
+ end
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,36 @@
1
+
2
+
3
+ describe Cassette do
4
+ def keeping_logger(&block)
5
+ original_logger = Cassette.logger
6
+ block.call
7
+ Cassette.logger = original_logger
8
+ end
9
+
10
+ describe '.logger' do
11
+ it 'returns a default instance' do
12
+ expect(Cassette.logger).not_to be_nil
13
+ expect(Cassette.logger.is_a?(Logger)).to eql(true)
14
+ end
15
+
16
+ it 'returns rails logger when Rails is available' do
17
+ keeping_logger do
18
+ Cassette.logger = nil
19
+ rails = double('Rails')
20
+ expect(rails).to receive(:logger).and_return(rails).at_least(:once)
21
+ stub_const('Rails', rails)
22
+ expect(Cassette.logger).to eql(rails)
23
+ end
24
+ end
25
+ end
26
+
27
+ describe '.logger=' do
28
+ let(:logger) { Logger.new(STDOUT) }
29
+ it 'defines the logger instance' do
30
+ keeping_logger do
31
+ Cassette.logger = logger
32
+ expect(Cassette.logger).to eq(logger)
33
+ end
34
+ end
35
+ end
36
+ end
@@ -1,7 +1,4 @@
1
1
  # encoding: utf-8
2
-
3
- require 'spec_helper'
4
-
5
2
  RSpec.describe 'Cassette::Client, Cassette::Authentication integration' do
6
3
  shared_examples_for 'a Cassette client and validator' do
7
4
  let(:config) { fail 'implement config!' }
data/spec/spec_helper.rb CHANGED
@@ -2,6 +2,11 @@ require 'simplecov'
2
2
  require 'simplecov-rcov'
3
3
  require 'simplecov-gem-adapter'
4
4
  require 'yaml'
5
+ require 'webmock/rspec'
6
+ require 'rspec/its'
7
+ require 'faker'
8
+
9
+ Dir['spec/support/**/*.rb'].each { |f| load f }
5
10
 
6
11
  module Fixtures
7
12
  def fixture(name)
@@ -0,0 +1,19 @@
1
+ # encoding: utf-8
2
+
3
+ require 'active_support/core_ext/hash/indifferent_access'
4
+
5
+ def ControllerMock(*mods)
6
+ mods.inject(Class.new(ControllerMock)) do |c, mod|
7
+ c.send(:include, mod)
8
+ end
9
+ end
10
+
11
+ class ControllerMock
12
+ attr_accessor :params, :request, :current_user
13
+ def self.before_filter(*); end
14
+
15
+ def initialize(params = {}, headers = {})
16
+ self.params = params.with_indifferent_access
17
+ self.request = OpenStruct.new(headers: headers.with_indifferent_access)
18
+ end
19
+ end