sitehub 0.5.0.alpha5 → 0.5.0.alpha6

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,129 @@
1
+ describe 'middleware' do
2
+ include_context :middleware_test
3
+
4
+ let(:downstream_url) { 'http://localhost:12345' }
5
+ let(:experiment1_url) { "#{downstream_url}/experiment1" }
6
+ let(:experiment2_url) { "#{downstream_url}/experiment2" }
7
+
8
+ def middleware(name)
9
+ create_middleware.tap do |clazz|
10
+ clazz.class_eval do
11
+ define_method :call do |env|
12
+ callback = env['async.callback'] || env['async.orig_callback']
13
+ env['async.orig_callback'] = env['async.callback'] = proc do |status, headers, body|
14
+ body = body.body.join if body.is_a?(Rack::BodyProxy)
15
+
16
+ callback.call(status, headers, "#{name}, #{body}")
17
+ end
18
+ @app.call(env)
19
+ end
20
+ end
21
+ end
22
+ end
23
+
24
+ let(:app) { Async::Middleware.new(builder) }
25
+
26
+ before do
27
+ WebMock.enable!
28
+ stub_request(:get, downstream_url).to_return(body: 'hello')
29
+ end
30
+
31
+ context 'middleware added to top level' do
32
+ let(:builder) do
33
+ middleware = middleware(:middleware1)
34
+ downstream_url = downstream_url()
35
+
36
+ SiteHub.build do
37
+ access_logger StringIO.new
38
+ use middleware
39
+ proxy '/1' => downstream_url
40
+ proxy '/2' => downstream_url
41
+ end
42
+ end
43
+
44
+ it 'adds it to each route' do
45
+ get('/1')
46
+ expect(app.last_response.body.join).to eq('middleware1, hello')
47
+ get('/2')
48
+ expect(app.last_response.body.join).to eq('middleware1, hello')
49
+ end
50
+ end
51
+
52
+ context 'middleware added to specific route' do
53
+ let(:builder) do
54
+ middleware = middleware(:middleware1)
55
+ downstream_url = downstream_url()
56
+
57
+ SiteHub.build do
58
+ access_logger StringIO.new
59
+ proxy '/1' do
60
+ use middleware
61
+ route label: :with_middleware, url: downstream_url
62
+ end
63
+ proxy '/2' => downstream_url
64
+ end
65
+ end
66
+
67
+ it 'adds it to that route only' do
68
+ get('/1')
69
+ expect(app.last_response.body.join).to eq('middleware1, hello')
70
+ get('/2')
71
+ expect(app.last_response.body.join).to eq('hello')
72
+ end
73
+ end
74
+
75
+ context 'base inherited middleware' do
76
+ let(:builder) do
77
+ middleware1 = middleware(:middleware1)
78
+ middleware2 = middleware(:middleware2)
79
+ downstream_url = downstream_url()
80
+
81
+ SiteHub.build do
82
+ access_logger StringIO.new
83
+ use middleware1
84
+ proxy '/1' do
85
+ use middleware2
86
+ route label: :with_middleware, url: downstream_url
87
+ end
88
+ proxy '/2' => downstream_url
89
+ end
90
+ end
91
+
92
+ it 'adds it to that route only' do
93
+ get('/1')
94
+ expect(app.last_response.body.join).to eq('middleware1, middleware2, hello')
95
+ get('/2')
96
+ expect(app.last_response.body.join).to eq('middleware1, hello')
97
+ end
98
+ end
99
+
100
+ context 'nested inherited middleware' do
101
+ let(:builder) do
102
+ middleware1 = middleware(:middleware1)
103
+ middleware2 = middleware(:middleware2)
104
+ downstream_url = downstream_url()
105
+
106
+ SiteHub.build do
107
+ access_logger StringIO.new
108
+
109
+ proxy '/1' do
110
+ split percentage: 100, label: :experiment1 do
111
+ use middleware1
112
+ split percentage: 100, label: :with_middleware do
113
+ use middleware2
114
+ split percentage: 100, label: :with_nested_middleware, url: downstream_url
115
+ end
116
+ end
117
+ end
118
+ proxy '/2' => downstream_url
119
+ end
120
+ end
121
+
122
+ it 'adds it to that route only' do
123
+ get('/1')
124
+ expect(app.last_response.body.join).to eq('middleware1, middleware2, hello')
125
+ get('/2')
126
+ expect(app.last_response.body.join).to eq('hello')
127
+ end
128
+ end
129
+ end
@@ -0,0 +1,55 @@
1
+ require 'stringio'
2
+
3
+ describe 'route affinity' do
4
+ let(:downstream_url) { 'http://localhost:12345' }
5
+ let(:experiment1_url) { "#{downstream_url}/experiment1" }
6
+ let(:experiment2_url) { "#{downstream_url}/experiment2" }
7
+
8
+ let(:experiment_body_1) { 'experiment1_body' }
9
+ let(:experiment_body_2) { 'experiment2_body' }
10
+
11
+ before do
12
+ WebMock.enable!
13
+ end
14
+
15
+ let(:app) do
16
+ experiment1_url = experiment1_url()
17
+ experiment2_url = experiment2_url()
18
+
19
+ sitehub = SiteHub.build do
20
+ access_logger StringIO.new
21
+ error_logger StringIO.new
22
+
23
+ proxy '/endpoint' do
24
+ split(label: :experiment1, percentage: 100) do
25
+ split percentage: 100, label: 'variant1', url: experiment1_url
26
+ end
27
+
28
+ split(label: :experiment2, percentage: 0) do
29
+ split percentage: 0, label: 'variant1', url: experiment2_url
30
+ split percentage: 100, label: 'variant2', url: :should_not_be_called
31
+ end
32
+ end
33
+ end
34
+ Async::Middleware.new(sitehub)
35
+ end
36
+
37
+ context 'requested route cookie not present' do
38
+ it 'drops a cookie to keep you on the same path' do
39
+ stub_request(:get, experiment1_url).to_return(body: experiment_body_1)
40
+ get('/endpoint')
41
+ expect(app.last_response.body).to eq([experiment_body_1])
42
+ expect(app.last_response.cookies[SiteHub::RECORDED_ROUTES_COOKIE][:value]).to eq('experiment1|variant1')
43
+ end
44
+ end
45
+
46
+ context 'requested route cookie present' do
47
+ it 'proxies to the preselected route' do
48
+ stub_request(:get, experiment2_url).to_return(body: experiment_body_2)
49
+
50
+ get('/endpoint', {}, 'HTTP_COOKIE' => "#{SiteHub::RECORDED_ROUTES_COOKIE}=experiment2|variant1")
51
+ expect(app.last_response.body).to eq([experiment_body_2])
52
+ expect(app.last_response.cookies[SiteHub::RECORDED_ROUTES_COOKIE][:value]).to eq('experiment2|variant1')
53
+ end
54
+ end
55
+ end
@@ -123,11 +123,11 @@ class SiteHub
123
123
  end
124
124
 
125
125
  it 'adds a forward proxies' do
126
- expect(subject.build).to be_using(Middleware::Routes)
126
+ expect(subject.build).to be_using(Middleware::CandidateRouteMappings)
127
127
  end
128
128
 
129
129
  it 'configures it with the sitehub_cookie_name' do
130
- forward_proxies = find_middleware(subject.build, Middleware::Routes)
130
+ forward_proxies = find_middleware(subject.build, Middleware::CandidateRouteMappings)
131
131
  expect(forward_proxies.sitehub_cookie_name).to eq(:custom_cookie_name)
132
132
  end
133
133
  end
@@ -173,7 +173,7 @@ class SiteHub
173
173
  Middleware::ErrorHandling,
174
174
  Middleware::TransactionId,
175
175
  Middleware::ReverseProxy,
176
- Middleware::Routes]
176
+ Middleware::CandidateRouteMappings]
177
177
 
178
178
  expect(middleware_stack).to eq(expected_middleware)
179
179
  end
@@ -1,18 +1,17 @@
1
1
  # rubocop:disable Metrics/ClassLength
2
- require 'sitehub/route_builder'
2
+ require 'sitehub/candidate_routes'
3
3
 
4
4
  class SiteHub
5
- describe RouteBuilder do
5
+ describe CandidateRoutes do
6
6
  include_context :middleware_test
7
7
 
8
8
  describe '::from_hash' do
9
9
  include_context :sitehub_json
10
10
 
11
- subject do
12
- described_class.from_hash(proxy_1, :expected).routes[Identifier.new(route_1[:label])]
13
- end
14
-
15
11
  context 'splits' do
12
+ subject do
13
+ described_class.from_hash(split_proxy, :expected)
14
+ end
16
15
  context 'sitehub_cookie_name' do
17
16
  it 'sets it' do
18
17
  expect(subject.sitehub_cookie_name).to eq(:expected)
@@ -21,26 +20,110 @@ class SiteHub
21
20
 
22
21
  context 'sitehub_cookie_path' do
23
22
  it 'sets it' do
24
- expect(subject.sitehub_cookie_path).to eq(proxy_1[:sitehub_cookie_path])
23
+ expect(subject.sitehub_cookie_path).to eq(split_proxy[:sitehub_cookie_path])
25
24
  end
26
25
  end
27
26
 
28
- pending 'returns core with splits'
27
+ it 'returns core with splits' do
28
+ split_1 = split_1()
29
+ split_2 = split_2()
30
+ expected = described_class.new(sitehub_cookie_name: :expected,
31
+ sitehub_cookie_path: subject.sitehub_cookie_path,
32
+ mapped_path: subject.mapped_path) do
33
+ split percentage: split_1[:percentage], label: split_1[:label], url: split_1[:url]
34
+ split percentage: split_2[:percentage], label: split_2[:label], url: split_2[:url]
35
+ end
36
+ expect(subject.candidates).to eq(expected.candidates)
37
+ end
38
+
39
+ context 'default' do
40
+ it 'sets it' do
41
+ expect(subject.default_route.app.mapped_url).to eq(split_proxy[:default])
42
+ end
43
+ end
29
44
  end
30
45
 
31
46
  context 'routes' do
47
+ subject do
48
+ described_class.from_hash(routes_proxy, :expected)
49
+ end
50
+
32
51
  context 'sitehub_cookie_name' do
33
- pending 'sets it'
52
+ it 'sets it' do
53
+ expect(subject.sitehub_cookie_name).to eq(:expected)
54
+ end
34
55
  end
35
56
 
36
57
  context 'sitehub_cookie_path' do
37
- pending 'sets it'
58
+ it 'sets it' do
59
+ expect(subject.sitehub_cookie_path).to eq(routes_proxy[:sitehub_cookie_path])
60
+ end
61
+ end
62
+
63
+ it 'returns core with routes' do
64
+ route_1 = route_1()
65
+ expected = described_class.new(sitehub_cookie_name: :expected,
66
+ sitehub_cookie_path: subject.sitehub_cookie_path,
67
+ mapped_path: subject.mapped_path) do
68
+ route label: route_1[:label], url: route_1[:url]
69
+ end
70
+ expect(subject.candidates).to eq(expected.candidates)
71
+ end
72
+
73
+ context 'default' do
74
+ it 'sets it' do
75
+ expect(subject.default_route.app.mapped_url).to eq(routes_proxy[:default])
76
+ end
77
+ end
78
+ end
79
+
80
+ context 'nested routes' do
81
+ context 'routes inside a split' do
82
+ subject do
83
+ described_class.from_hash(nested_route_proxy, :expected)
84
+ end
85
+
86
+ it 'creates them' do
87
+ route_1 = route_1()
88
+ nested_route = nested_route()
89
+
90
+ expected = described_class.new(sitehub_cookie_name: :expected,
91
+ sitehub_cookie_path: subject.sitehub_cookie_path,
92
+ mapped_path: subject.mapped_path) do
93
+ split(percentage: nested_route[:percentage], label: nested_route[:label]) do
94
+ route label: route_1[:label], url: route_1[:url]
95
+ end
96
+ end
97
+ expect(subject).to eq(expected)
98
+ end
99
+ end
100
+
101
+ context 'splits in a split' do
102
+ subject do
103
+ described_class.from_hash(nested_split_proxy, :expected)
104
+ end
105
+
106
+ it 'creates them' do
107
+ split_1 = split_1()
108
+ split_2 = split_2()
109
+ nested_split = nested_split()
110
+
111
+ expected = described_class.new(sitehub_cookie_name: :expected,
112
+ sitehub_cookie_path: subject.sitehub_cookie_path,
113
+ mapped_path: subject.mapped_path) do
114
+ split(percentage: nested_split[:percentage], label: nested_split[:label]) do
115
+ split percentage: split_1[:percentage], label: split_1[:label], url: split_1[:url]
116
+ split percentage: split_2[:percentage], label: split_2[:label], url: split_2[:url]
117
+ end
118
+ end
119
+ expect(subject).to eq(expected)
120
+ end
38
121
  end
39
- pending 'returns core with routes'
40
122
  end
41
123
 
42
124
  context 'default' do
43
- it 'sets the default'
125
+ it 'sets the default' do
126
+ end
44
127
  end
45
128
  end
46
129
 
@@ -51,26 +134,35 @@ class SiteHub
51
134
 
52
135
  describe '#routes' do
53
136
  it 'returns RouteCollection by default' do
54
- expect(subject.routes).to be_a(Collection::RouteCollection)
137
+ expect(subject.candidates).to be_a(Collection::RouteCollection)
55
138
  end
56
139
 
57
140
  it 'returns the same intance everytime' do
58
141
  collection = Collection::SplitRouteCollection.new
59
- subject.routes(collection)
60
- expect(subject.routes).to be(collection)
142
+ subject.candidates(collection)
143
+ expect(subject.candidates).to be(collection)
61
144
  end
62
145
 
63
146
  context 'endpoints already set' do
64
147
  context 'different object supplied' do
65
148
  it 'raises an error' do
66
- subject.routes(Collection::SplitRouteCollection.new)
67
- expect { subject.routes(Collection::RouteCollection.new) }
68
- .to raise_error(RouteBuilder::InvalidDefinitionException)
149
+ subject.candidates(Collection::SplitRouteCollection.new)
150
+ expect { subject.candidates(Collection::RouteCollection.new) }
151
+ .to raise_error(CandidateRoutes::InvalidDefinitionException)
69
152
  end
70
153
  end
71
154
  end
72
155
  end
73
156
 
157
+ describe '#[]' do
158
+ context 'id of existing route passed in' do
159
+ it 'returns it' do
160
+ subject.split(label: :current, percentage: 100, url: :url)
161
+ expect(subject[:current]).to eq(subject[:current])
162
+ end
163
+ end
164
+ end
165
+
74
166
  it 'supports middleware' do
75
167
  expect(described_class).to include(Middleware)
76
168
  end
@@ -141,20 +233,20 @@ class SiteHub
141
233
  describe '#split' do
142
234
  it 'setups up a splits collection' do
143
235
  subject.split percentage: 10, url: :url, label: :label
144
- expect(subject.routes).to be_a(Collection::SplitRouteCollection)
236
+ expect(subject.candidates).to be_a(Collection::SplitRouteCollection)
145
237
  end
146
238
  end
147
239
 
148
240
  describe '#route' do
149
241
  it 'sets up the routes collection' do
150
242
  subject.route url: :url, label: :current
151
- expect(subject.routes).to be_a(Collection::RouteCollection)
243
+ expect(subject.candidates).to be_a(Collection::RouteCollection)
152
244
  end
153
245
  end
154
246
 
155
- describe '#add_endpoint' do
247
+ describe '#add' do
156
248
  it 'stores the route against the given label' do
157
- subject.add_route url: :url, label: :current
249
+ subject.add url: :url, label: :current
158
250
 
159
251
  proxy = ForwardProxy.new(mapped_url: :url,
160
252
  mapped_path: subject.mapped_path)
@@ -164,17 +256,17 @@ class SiteHub
164
256
  sitehub_cookie_name: :cookie_name,
165
257
  sitehub_cookie_path: nil)
166
258
 
167
- expect(subject.routes[Identifier.new(:current)]).to eq(expected_route)
259
+ expect(subject[:current]).to eq(expected_route)
168
260
  end
169
261
 
170
262
  it 'accepts a rule' do
171
- endpoint = subject.add_route url: :url, label: :current, rule: :rule
263
+ endpoint = subject.add url: :url, label: :current, rule: :rule
172
264
  expect(endpoint.rule).to eq(:rule)
173
265
  end
174
266
 
175
267
  it 'accepts a percentage' do
176
- subject.routes(Collection::SplitRouteCollection.new)
177
- endpoint = subject.add_route url: :url, label: :current, percentage: 50
268
+ subject.candidates(Collection::SplitRouteCollection.new)
269
+ endpoint = subject.add url: :url, label: :current, percentage: 50
178
270
  expect(endpoint.upper).to eq(50)
179
271
  end
180
272
 
@@ -189,53 +281,65 @@ class SiteHub
189
281
 
190
282
  it 'stores the nested route_builder against the label' do
191
283
  rule = proc { true }
192
- subject.add_route(rule: rule, label: :label1, &block)
284
+ subject.add(rule: rule, label: :label1, &block)
193
285
  subject.use middleware
194
286
 
195
- expected_endpoints = RouteBuilder.new(rule: rule,
196
- id: :label1,
197
- sitehub_cookie_name: :cookie_name,
198
- mapped_path: '/path',
199
- &block).build
287
+ expected_endpoints = CandidateRoutes.new(rule: rule,
288
+ id: :label1,
289
+ sitehub_cookie_name: :cookie_name,
290
+ mapped_path: '/path',
291
+ &block).build
200
292
 
201
- expect(subject.routes[Identifier.new(:label1)]).to eq(expected_endpoints)
293
+ expect(subject[:label1]).to eq(expected_endpoints)
202
294
  subject.build
203
295
  end
204
296
 
205
297
  describe '#errors and warnings' do
206
298
  context 'precentage and rule not supplied' do
207
- it 'raise an error' do
208
- expected_message = described_class::RULE_NOT_SPECIFIED_MSG
209
- expect { subject.add_route(label: :label) {} }
210
- .to raise_exception described_class::InvalidDefinitionException, expected_message
299
+ context 'split required' do
300
+ it 'raise an error' do
301
+ subject.candidates(Collection::SplitRouteCollection.new)
302
+ expected_message = described_class::PERCENTAGE_NOT_SPECIFIED_MSG
303
+ expect { subject.add(label: :label) {} }
304
+ .to raise_exception described_class::InvalidDefinitionException, expected_message
305
+ end
306
+ end
307
+
308
+ context 'route required' do
309
+ it 'raise an error' do
310
+ subject.candidates(Collection::RouteCollection.new)
311
+ expected_message = described_class::RULE_NOT_SPECIFIED_MSG
312
+ expect { subject.add(label: :label) {} }
313
+ .to raise_exception described_class::InvalidDefinitionException, expected_message
314
+ end
211
315
  end
212
316
  end
213
317
 
214
318
  context 'url' do
215
319
  it 'gives a warning to say that the url will not be used' do
216
320
  expect(subject).to receive(:warn).with(described_class::IGNORING_URL_MSG)
217
- subject.add_route(rule: :rule, url: :url, label: :label, &block)
321
+ subject.add(rule: :rule, url: :url, label: :label, &block)
218
322
  end
219
323
  end
220
324
  end
221
325
 
222
326
  it 'stores a proxy builder' do
223
327
  rule = proc { true }
224
- subject.add_route(rule: rule, label: :label, &block)
328
+ subject.add(rule: rule, label: :label, &block)
225
329
 
226
330
  expected_endpoints = described_class.new(id: :label, sitehub_cookie_name: :cookie_name,
227
331
  rule: rule, mapped_path: subject.mapped_path, &block).tap do |builder|
228
332
  builder.sitehub_cookie_name subject.sitehub_cookie_name
229
333
  end.build
230
334
 
231
- expect(subject.routes.values).to eq([expected_endpoints])
335
+ expect(subject.candidates.values).to eq([expected_endpoints])
232
336
  end
233
337
 
234
338
  context 'invalid definitions inside block' do
235
339
  it 'raises an error' do
236
340
  rule = proc { true }
237
341
  expect do
238
- subject.add_route rule: rule, label: :label do
342
+ subject.add rule: rule, label: :label do
239
343
  split percentage: 20, url: :url, label: :label1
240
344
  end
241
345
  end.to raise_exception described_class::InvalidDefinitionException
@@ -250,9 +354,9 @@ class SiteHub
250
354
  context 'middleware not specified' do
251
355
  it 'leaves it the proxies alone' do
252
356
  subject.route url: :url, label: :current
253
- expect(subject.routes[Identifier.new(:current)]).to be_using_rack_stack(ForwardProxy)
357
+ expect(subject[:current]).to be_using_rack_stack(ForwardProxy)
254
358
  subject.build
255
- expect(subject.routes[Identifier.new(:current)]).to be_using_rack_stack(ForwardProxy)
359
+ expect(subject[:current]).to be_using_rack_stack(ForwardProxy)
256
360
  end
257
361
  end
258
362
 
@@ -264,7 +368,7 @@ class SiteHub
264
368
  it 'wraps the forward proxies in the middleware' do
265
369
  subject.route url: :url, label: :current
266
370
  subject.build
267
- expect(subject.routes[Identifier.new(:current)]).to be_using_rack_stack(middleware, ForwardProxy)
371
+ expect(subject[:current]).to be_using_rack_stack(middleware, ForwardProxy)
268
372
  end
269
373
 
270
374
  it 'wraps the default in the middleware' do
@@ -272,9 +376,18 @@ class SiteHub
272
376
  subject.build
273
377
  expect(subject.default_route).to be_using_rack_stack(middleware, ForwardProxy)
274
378
  end
379
+ end
275
380
 
276
- context 'nested routes' do
277
- pending 'what should it do?'
381
+ context 'middleware present on the parent route' do
382
+ it 'adds it to the list middleware to be added' do
383
+ middleware = middleware()
384
+ subject.split(percentage: 100, label: :parent) do
385
+ use middleware
386
+ split(percentage: 100, label: :child) do
387
+ default url: :url
388
+ end
389
+ end
390
+ expect(subject[:parent][:child].default_route).to be_using(middleware)
278
391
  end
279
392
  end
280
393
  end
@@ -284,11 +397,11 @@ class SiteHub
284
397
  context 'routes defined' do
285
398
  it 'returns that route' do
286
399
  subject.route url: :url, label: :current
287
- expect(subject.resolve(env: {})).to eq(subject.routes.values.first)
400
+ expect(subject.resolve(env: {})).to eq(subject.candidates.values.first)
288
401
  end
289
402
 
290
403
  it 'passes the env to the when resolving the correct route' do
291
- expect_any_instance_of(subject.routes.class).to receive(:resolve).with(env: :env).and_call_original
404
+ expect_any_instance_of(subject.candidates.class).to receive(:resolve).with(env: :env).and_call_original
292
405
  subject.resolve(env: :env)
293
406
  end
294
407
  end
@@ -343,15 +456,15 @@ class SiteHub
343
456
  context '#endpoints' do
344
457
  context 'called with a collection' do
345
458
  it 'sets endpoints to be that collection' do
346
- subject.routes(:collection)
347
- expect(subject.routes).to eq(:collection)
459
+ subject.candidates(:collection)
460
+ expect(subject.candidates).to eq(:collection)
348
461
  end
349
462
  end
350
463
 
351
464
  context 'already set with a different collection' do
352
465
  it 'raise an error' do
353
- subject.routes(:collection1)
354
- expect { subject.routes(:collection2) }.to raise_exception described_class::InvalidDefinitionException
466
+ subject.candidates(:collection1)
467
+ expect { subject.candidates(:collection2) }.to raise_exception described_class::InvalidDefinitionException
355
468
  end
356
469
  end
357
470
  end
@@ -374,5 +487,33 @@ class SiteHub
374
487
  expect(proxy.sitehub_cookie_name).to eq(:expected_cookie_name)
375
488
  end
376
489
  end
490
+
491
+ describe '#method_missing' do
492
+ context 'calling scope set' do
493
+ subject do
494
+ calling_scope = double(:calling_scope, parent_method: :called)
495
+ described_class.new(sitehub_cookie_name: :cookie_name,
496
+ mapped_path: '/path',
497
+ calling_scope: calling_scope)
498
+ end
499
+ context 'method on calling_context' do
500
+ it 'delegates to it' do
501
+ expect(subject.parent_method).to eq(:called)
502
+ end
503
+ end
504
+
505
+ context 'method on calling_context' do
506
+ it 'delegates to it' do
507
+ expect(subject.parent_method).to eq(:called)
508
+ end
509
+ end
510
+ end
511
+
512
+ context 'calling scope not set' do
513
+ it 'raises an error' do
514
+ expect { subject.parent_method }.to raise_error(NoMethodError)
515
+ end
516
+ end
517
+ end
377
518
  end
378
519
  end