startback-websocket 0.14.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/Gemfile +3 -0
- data/README.md +13 -0
- data/Rakefile +18 -0
- data/lib/startback/audit/prometheus.rb +87 -0
- data/lib/startback/audit/shared.rb +17 -0
- data/lib/startback/audit/trailer.rb +129 -0
- data/lib/startback/audit.rb +3 -0
- data/lib/startback/caching/entity_cache.rb +157 -0
- data/lib/startback/caching/no_store.rb +28 -0
- data/lib/startback/caching/store.rb +34 -0
- data/lib/startback/context/h_factory.rb +43 -0
- data/lib/startback/context/middleware.rb +53 -0
- data/lib/startback/context.rb +122 -0
- data/lib/startback/errors.rb +197 -0
- data/lib/startback/event/agent.rb +84 -0
- data/lib/startback/event/bus/bunny/async.rb +162 -0
- data/lib/startback/event/bus/bunny.rb +1 -0
- data/lib/startback/event/bus/memory/async.rb +45 -0
- data/lib/startback/event/bus/memory/sync.rb +35 -0
- data/lib/startback/event/bus/memory.rb +2 -0
- data/lib/startback/event/bus.rb +100 -0
- data/lib/startback/event/engine.rb +94 -0
- data/lib/startback/event/ext/context.rb +5 -0
- data/lib/startback/event/ext/operation.rb +13 -0
- data/lib/startback/event.rb +47 -0
- data/lib/startback/ext/date_time.rb +9 -0
- data/lib/startback/ext/time.rb +9 -0
- data/lib/startback/ext.rb +2 -0
- data/lib/startback/model.rb +6 -0
- data/lib/startback/operation/error_operation.rb +19 -0
- data/lib/startback/operation/multi_operation.rb +28 -0
- data/lib/startback/operation.rb +78 -0
- data/lib/startback/services.rb +11 -0
- data/lib/startback/support/data_object.rb +71 -0
- data/lib/startback/support/env.rb +41 -0
- data/lib/startback/support/fake_logger.rb +18 -0
- data/lib/startback/support/hooks.rb +48 -0
- data/lib/startback/support/log_formatter.rb +34 -0
- data/lib/startback/support/logger.rb +34 -0
- data/lib/startback/support/operation_runner.rb +150 -0
- data/lib/startback/support/robustness.rb +157 -0
- data/lib/startback/support/transaction_manager.rb +25 -0
- data/lib/startback/support/transaction_policy.rb +33 -0
- data/lib/startback/support/world.rb +54 -0
- data/lib/startback/support.rb +26 -0
- data/lib/startback/version.rb +8 -0
- data/lib/startback/web/api.rb +99 -0
- data/lib/startback/web/auto_caching.rb +85 -0
- data/lib/startback/web/catch_all.rb +52 -0
- data/lib/startback/web/cors_headers.rb +80 -0
- data/lib/startback/web/health_check.rb +49 -0
- data/lib/startback/web/magic_assets/ng_html_transformer.rb +80 -0
- data/lib/startback/web/magic_assets/rake_tasks.rb +64 -0
- data/lib/startback/web/magic_assets.rb +98 -0
- data/lib/startback/web/middleware.rb +13 -0
- data/lib/startback/web/prometheus.rb +16 -0
- data/lib/startback/web/shield.rb +58 -0
- data/lib/startback.rb +43 -0
- data/spec/spec_helper.rb +49 -0
- data/spec/unit/audit/test_prometheus.rb +72 -0
- data/spec/unit/audit/test_trailer.rb +105 -0
- data/spec/unit/caching/test_entity_cache.rb +136 -0
- data/spec/unit/context/test_abstraction_factory.rb +64 -0
- data/spec/unit/context/test_dup.rb +42 -0
- data/spec/unit/context/test_fork.rb +37 -0
- data/spec/unit/context/test_h_factory.rb +31 -0
- data/spec/unit/context/test_middleware.rb +45 -0
- data/spec/unit/context/test_with_world.rb +20 -0
- data/spec/unit/context/test_world.rb +17 -0
- data/spec/unit/event/bus/memory/test_async.rb +43 -0
- data/spec/unit/event/bus/memory/test_sync.rb +43 -0
- data/spec/unit/support/hooks/test_after_hook.rb +54 -0
- data/spec/unit/support/hooks/test_before_hook.rb +54 -0
- data/spec/unit/support/operation_runner/test_around_run.rb +156 -0
- data/spec/unit/support/operation_runner/test_before_after_call.rb +48 -0
- data/spec/unit/support/test_data_object.rb +156 -0
- data/spec/unit/support/test_env.rb +75 -0
- data/spec/unit/support/test_robusteness.rb +229 -0
- data/spec/unit/support/test_transaction_manager.rb +64 -0
- data/spec/unit/support/test_world.rb +72 -0
- data/spec/unit/test_event.rb +62 -0
- data/spec/unit/test_operation.rb +55 -0
- data/spec/unit/test_support.rb +40 -0
- data/spec/unit/web/fixtures/assets/app/hello.es6 +4 -0
- data/spec/unit/web/fixtures/assets/app/hello.html +1 -0
- data/spec/unit/web/fixtures/assets/index.es6 +1 -0
- data/spec/unit/web/test_api.rb +82 -0
- data/spec/unit/web/test_auto_caching.rb +81 -0
- data/spec/unit/web/test_catch_all.rb +77 -0
- data/spec/unit/web/test_cors_headers.rb +88 -0
- data/spec/unit/web/test_healthcheck.rb +59 -0
- data/spec/unit/web/test_magic_assets.rb +82 -0
- data/tasks/test.rake +14 -0
- metadata +237 -0
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
module Startback
|
3
|
+
module Support
|
4
|
+
describe TransactionManager do
|
5
|
+
subject do
|
6
|
+
TransactionManager.new(db).call(nil, op) do
|
7
|
+
op.call
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
class FakeDatabase
|
12
|
+
def initialize
|
13
|
+
@called = false
|
14
|
+
end
|
15
|
+
attr_reader :called
|
16
|
+
|
17
|
+
def transaction
|
18
|
+
@called = true
|
19
|
+
yield
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
let(:db) do
|
24
|
+
FakeDatabase.new
|
25
|
+
end
|
26
|
+
|
27
|
+
context 'when called with a default operation' do
|
28
|
+
class OperationNotManagingTransactions < Startback::Operation
|
29
|
+
def call
|
30
|
+
12
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
let(:op) do
|
35
|
+
OperationNotManagingTransactions.new
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'calls db.transaction' do
|
39
|
+
expect(subject).to eql(12)
|
40
|
+
expect(db.called).to eql(true)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
context 'when called with an operation that manages the transactions itself' do
|
45
|
+
class OperationManagingTransactions < Startback::Operation
|
46
|
+
self.transaction_policy = :within_call
|
47
|
+
|
48
|
+
def call
|
49
|
+
12
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
let(:op) do
|
54
|
+
OperationManagingTransactions.new
|
55
|
+
end
|
56
|
+
|
57
|
+
it 'calls db.transaction' do
|
58
|
+
expect(subject).to eql(12)
|
59
|
+
expect(db.called).to eql(false)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module Startback
|
4
|
+
module Support
|
5
|
+
describe World do
|
6
|
+
|
7
|
+
let(:world) do
|
8
|
+
World.new
|
9
|
+
end
|
10
|
+
|
11
|
+
describe 'with' do
|
12
|
+
subject do
|
13
|
+
world.with(foo: :bar, bar: :baz)
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'returns a new world instance' do
|
17
|
+
expect(subject).to be_a(World)
|
18
|
+
expect(subject).not_to be(world)
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'gives access to those variables' do
|
22
|
+
expect(subject.foo).to eql(:bar)
|
23
|
+
expect(subject[:bar]).to eql(:baz)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
describe 'dup' do
|
28
|
+
let(:world) do
|
29
|
+
World.new(foo: :bar)
|
30
|
+
end
|
31
|
+
|
32
|
+
subject do
|
33
|
+
world.dup
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'returns another instance' do
|
37
|
+
expect(subject).to be_a(World)
|
38
|
+
expect(subject).not_to be(world)
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'keeps variables' do
|
42
|
+
expect(subject.foo).to eql(:bar)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
describe 'class factory' do
|
47
|
+
subject do
|
48
|
+
world.factory(:foo) do
|
49
|
+
hello
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def hello
|
54
|
+
OpenStruct.new(bar: 12)
|
55
|
+
end
|
56
|
+
|
57
|
+
it 'returns a world instance' do
|
58
|
+
expect(subject).to be_a(World)
|
59
|
+
expect(subject).not_to be(world)
|
60
|
+
end
|
61
|
+
|
62
|
+
it 'installs the factory' do
|
63
|
+
bound = subject.with_scope(self)
|
64
|
+
got = bound.foo
|
65
|
+
expect(got).to be_a(OpenStruct)
|
66
|
+
expect(got.bar).to eql(12)
|
67
|
+
expect(bound.foo).to be(got)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module Startback
|
4
|
+
describe Event do
|
5
|
+
|
6
|
+
subject{
|
7
|
+
Event.new("User::Changed", { "foo" => "bar" })
|
8
|
+
}
|
9
|
+
|
10
|
+
it 'presents an ostruct on top of its data' do
|
11
|
+
expect(subject.data.foo).to eql("bar")
|
12
|
+
end
|
13
|
+
|
14
|
+
describe "the json information contract" do
|
15
|
+
|
16
|
+
JSON_SRC = <<-JSON.gsub(/\s+/, "")
|
17
|
+
{
|
18
|
+
"type": "User::Changed",
|
19
|
+
"data": {
|
20
|
+
"foo": "bar"
|
21
|
+
}
|
22
|
+
}
|
23
|
+
JSON
|
24
|
+
|
25
|
+
it 'has a to_json method that works as expected' do
|
26
|
+
expect(subject.to_json).to eql(JSON_SRC)
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'has a to_json that dumps the context if any' do
|
30
|
+
evt = Event.new("User::Changed", { "foo" => "bar" }, { "baz": "context" })
|
31
|
+
expect(evt.to_json).to eql(<<-JSON.gsub(/\s+/, ""))
|
32
|
+
{
|
33
|
+
"type": "User::Changed",
|
34
|
+
"data": {
|
35
|
+
"foo": "bar"
|
36
|
+
},
|
37
|
+
"context": {
|
38
|
+
"baz": "context"
|
39
|
+
}
|
40
|
+
}
|
41
|
+
JSON
|
42
|
+
end
|
43
|
+
|
44
|
+
|
45
|
+
it 'has a json class method that works as expected' do
|
46
|
+
evt = Event.json(JSON_SRC, nil)
|
47
|
+
expect(evt).to be_a(Event)
|
48
|
+
expect(evt.type).to eql("User::Changed")
|
49
|
+
expect(evt.data).to eql(subject.data)
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'accepts an explicit context as second argument' do
|
53
|
+
c = SubContext.new.tap{|x| x.foo = 'hello' }
|
54
|
+
evt = Event.json(JSON_SRC, c)
|
55
|
+
expect(evt.context).not_to be(c)
|
56
|
+
expect(evt.context).to be_a(SubContext)
|
57
|
+
expect(evt.context.foo).to eql('hello')
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
62
|
+
end # module Startback
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module Startback
|
4
|
+
describe Operation do
|
5
|
+
|
6
|
+
class FooOp < Operation
|
7
|
+
|
8
|
+
def initialize(foo = :bar)
|
9
|
+
@foo = foo
|
10
|
+
end
|
11
|
+
attr_accessor :foo
|
12
|
+
|
13
|
+
def call
|
14
|
+
@foo
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'can be bound, which returns a new operation' do
|
20
|
+
foo1 = FooOp.new
|
21
|
+
foo1.foo = :bar1
|
22
|
+
|
23
|
+
foo2 = foo1.bind({ db: :bar })
|
24
|
+
expect(foo2.foo).to eql(:bar1)
|
25
|
+
expect(foo2.db).to eql(:bar)
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
|
30
|
+
describe Operation::MultiOperation do
|
31
|
+
|
32
|
+
it 'lets chain with +' do
|
33
|
+
mop = Operation::MultiOperation.new
|
34
|
+
mop2 = (mop + FooOp.new)
|
35
|
+
|
36
|
+
expect(mop == mop2).to eql(false)
|
37
|
+
expect(mop.size).to eql(0)
|
38
|
+
expect(mop2.size).to eql(1)
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'calls and collects the result on call' do
|
42
|
+
mop = Operation::MultiOperation.new + FooOp.new(:hello) + FooOp.new(:world)
|
43
|
+
expect(mop.call).to eql([:hello, :world])
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'binds every sub operation recursively' do
|
47
|
+
mop = Operation::MultiOperation.new + FooOp.new(:hello) + FooOp.new(:world)
|
48
|
+
mop2 = mop.bind({requester: :bar})
|
49
|
+
|
50
|
+
expect(mop == mop2).to eql(false)
|
51
|
+
expect(mop2.ops.all?{|op| op.requester == :bar })
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
end # module Startback
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module Startback
|
4
|
+
describe Support do
|
5
|
+
include Support
|
6
|
+
|
7
|
+
describe "deep_merge" do
|
8
|
+
|
9
|
+
it 'works as expected' do
|
10
|
+
h1 = {
|
11
|
+
:foo => "bar",
|
12
|
+
:bar => "unchanged",
|
13
|
+
:baz => {
|
14
|
+
"hello" => "world",
|
15
|
+
"changed" => "yes"
|
16
|
+
}
|
17
|
+
}
|
18
|
+
h2 = {
|
19
|
+
:foo => "baz",
|
20
|
+
:baz => {
|
21
|
+
"eloy" => "tom",
|
22
|
+
"changed" => "no"
|
23
|
+
}
|
24
|
+
}
|
25
|
+
expected = {
|
26
|
+
:foo => "baz",
|
27
|
+
:bar => "unchanged",
|
28
|
+
:baz => {
|
29
|
+
"hello" => "world",
|
30
|
+
"eloy" => "tom",
|
31
|
+
"changed" => "no"
|
32
|
+
}
|
33
|
+
}
|
34
|
+
expect(deep_merge(h1, h2)).to eql(expected)
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
<p>Hello {{who}}</p>
|
@@ -0,0 +1 @@
|
|
1
|
+
//= require_tree ./app
|
@@ -0,0 +1,82 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'startback/web/api'
|
3
|
+
|
4
|
+
module Startback
|
5
|
+
module Web
|
6
|
+
describe Api do
|
7
|
+
include Rack::Test::Methods
|
8
|
+
|
9
|
+
class JsonDto
|
10
|
+
def to(accept, content_type)
|
11
|
+
["application/json", %Q{{"foo":"bar"}}]
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
class PathDto
|
16
|
+
def to(accept, content_type)
|
17
|
+
["text/plain", Path.file]
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
class DtoAble
|
22
|
+
def initialize(dto)
|
23
|
+
@dto = dto
|
24
|
+
end
|
25
|
+
def to_dto(context)
|
26
|
+
@dto
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
class TestedApi < ::Startback::Web::Api
|
31
|
+
get '/no-such-one' do
|
32
|
+
serve("Something", nil)
|
33
|
+
end
|
34
|
+
|
35
|
+
get '/entity' do
|
36
|
+
serve('Entity', {foo: "bar"})
|
37
|
+
end
|
38
|
+
|
39
|
+
get '/path' do
|
40
|
+
serve('Path', Path.file)
|
41
|
+
end
|
42
|
+
|
43
|
+
get '/dto-able' do
|
44
|
+
serve('DTO', DtoAble.new(JsonDto.new))
|
45
|
+
end
|
46
|
+
|
47
|
+
get '/dto-path' do
|
48
|
+
serve('DTO', DtoAble.new(PathDto.new))
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
let(:app) {
|
53
|
+
TestedApi
|
54
|
+
}
|
55
|
+
|
56
|
+
it 'convert nil to 404' do
|
57
|
+
get '/no-such-one'
|
58
|
+
expect(last_response.status).to eql(404)
|
59
|
+
end
|
60
|
+
|
61
|
+
it 'supports serving entities' do
|
62
|
+
get '/entity'
|
63
|
+
expect(last_response.body).to eql(%Q{{"foo":"bar"}})
|
64
|
+
end
|
65
|
+
|
66
|
+
it 'supports serving paths' do
|
67
|
+
get '/path'
|
68
|
+
expect(last_response.body).to eql(Path.file.read)
|
69
|
+
end
|
70
|
+
|
71
|
+
it 'supports serving DTO-able objects' do
|
72
|
+
get '/dto-able'
|
73
|
+
expect(last_response.body).to eql(%Q{{"foo":"bar"}})
|
74
|
+
end
|
75
|
+
|
76
|
+
it 'supports serving DTO-able objects eventually returning paths' do
|
77
|
+
get '/dto-path'
|
78
|
+
expect(last_response.body).to eql(Path.file.read)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end # module Web
|
82
|
+
end # module Startback
|
@@ -0,0 +1,81 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'startback/web/auto_caching'
|
3
|
+
|
4
|
+
module Startback
|
5
|
+
module Web
|
6
|
+
describe AutoCaching do
|
7
|
+
include Rack::Test::Methods
|
8
|
+
|
9
|
+
context 'when used without options' do
|
10
|
+
def app
|
11
|
+
Rack::Builder.new do
|
12
|
+
use AutoCaching
|
13
|
+
run ->(env){ [200, {}, ["Hello error"]] }
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'sets the development Cache-Control since this is a test' do
|
18
|
+
get '/'
|
19
|
+
expect(last_response['Cache-Control']). to eql("no-cache, no-store, max-age=0, must-revalidate")
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
context 'when forcing production' do
|
24
|
+
def app
|
25
|
+
Rack::Builder.new do
|
26
|
+
use AutoCaching, false
|
27
|
+
run ->(env){ [200, {}, ["Hello error"]] }
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'sets the production Cache-Control' do
|
32
|
+
get '/'
|
33
|
+
expect(last_response['Cache-Control']). to eql("public, must-revalidate, max-age=3600, s-max-age=3600")
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
context 'when forcing development headers' do
|
38
|
+
def app
|
39
|
+
Rack::Builder.new do
|
40
|
+
use AutoCaching, development: { "Cache-Control" => "no-cache" }
|
41
|
+
run ->(env){ [200, {}, ["Hello error"]] }
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'sets the production Cache-Control' do
|
46
|
+
get '/'
|
47
|
+
expect(last_response['Cache-Control']). to eql("no-cache")
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
context 'when setting the Cache-Control header only' do
|
52
|
+
def app
|
53
|
+
Rack::Builder.new do
|
54
|
+
use AutoCaching, development: "no-cache"
|
55
|
+
run ->(env){ [200, {}, ["Hello error"]] }
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
it 'sets the production Cache-Control' do
|
60
|
+
get '/'
|
61
|
+
expect(last_response['Cache-Control']). to eql("no-cache")
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
context 'when a Cache-Control header is already set by the app' do
|
66
|
+
def app
|
67
|
+
Rack::Builder.new do
|
68
|
+
use AutoCaching
|
69
|
+
run ->(env){ [200, {"Cache-Control" => "priority"}, ["Hello error"]] }
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
it 'sets the production Cache-Control' do
|
74
|
+
get '/'
|
75
|
+
expect(last_response['Cache-Control']). to eql("priority")
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
end # CatchAll
|
80
|
+
end # module Web
|
81
|
+
end # module Startback
|
@@ -0,0 +1,77 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'singleton'
|
3
|
+
require 'startback/web/catch_all'
|
4
|
+
|
5
|
+
module Startback
|
6
|
+
module Web
|
7
|
+
describe CatchAll do
|
8
|
+
include Rack::Test::Methods
|
9
|
+
|
10
|
+
context 'when used without context' do
|
11
|
+
def app
|
12
|
+
Rack::Builder.new do
|
13
|
+
use CatchAll
|
14
|
+
run ->(env){ raise "Hello error" }
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'returns a 500 with json explanation' do
|
19
|
+
get '/'
|
20
|
+
expect(last_response.status).to eql(500)
|
21
|
+
expect(last_response.content_type).to eql("application/json")
|
22
|
+
result = JSON.parse(last_response.body)
|
23
|
+
expect(result).to eql({
|
24
|
+
"code" => "Startback::Errors::InternalServerError",
|
25
|
+
"description" => "An error occured, sorry"
|
26
|
+
})
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
context 'when used with a context providing an error handler' do
|
31
|
+
|
32
|
+
class AnError < StandardError
|
33
|
+
end
|
34
|
+
|
35
|
+
class ErrorHandler
|
36
|
+
include Singleton
|
37
|
+
|
38
|
+
attr_reader :ex
|
39
|
+
|
40
|
+
def fatal(ex)
|
41
|
+
@ex = ex
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
|
46
|
+
class MyContextWithErrorHandler < Startback::Context
|
47
|
+
|
48
|
+
def error_handler
|
49
|
+
ErrorHandler.instance
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
|
54
|
+
def app
|
55
|
+
Rack::Builder.new do
|
56
|
+
use Context::Middleware, MyContextWithErrorHandler.new
|
57
|
+
use CatchAll
|
58
|
+
run ->(env){ raise AnError, "Hello error" }
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
it 'returns a 500 with json explanation' do
|
63
|
+
get '/'
|
64
|
+
expect(last_response.status).to eql(500)
|
65
|
+
expect(last_response.content_type).to eql("application/json")
|
66
|
+
result = JSON.parse(last_response.body)
|
67
|
+
expect(result).to eql({
|
68
|
+
"code" => "Startback::Errors::InternalServerError",
|
69
|
+
"description" => "An error occured, sorry"
|
70
|
+
})
|
71
|
+
expect(ErrorHandler.instance.ex).to be_a(AnError)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
end # CatchAll
|
76
|
+
end # module Web
|
77
|
+
end # module Startback
|
@@ -0,0 +1,88 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'startback/web/cors_headers'
|
3
|
+
|
4
|
+
module Startback
|
5
|
+
module Web
|
6
|
+
describe CorsHeaders do
|
7
|
+
include Rack::Test::Methods
|
8
|
+
|
9
|
+
context 'when used without options' do
|
10
|
+
def app
|
11
|
+
Rack::Builder.new do
|
12
|
+
use CorsHeaders
|
13
|
+
run ->(env){ [200, {}, ["Hello world"]] }
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'sets the CORS headers to default values' do
|
18
|
+
header('Origin', "https://test.com")
|
19
|
+
get '/'
|
20
|
+
expect(last_response['Access-Control-Allow-Origin']). to eql("*")
|
21
|
+
expect(last_response['Access-Control-Allow-Methods']). to eql("OPTIONS, HEAD, GET, POST, PUT, PATCH, DELETE")
|
22
|
+
expect(last_response.body).to eql("Hello world")
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'strips everything when option' do
|
26
|
+
header('Origin', "https://test.com")
|
27
|
+
options '/'
|
28
|
+
expect(last_response['Access-Control-Allow-Origin']). to eql("*")
|
29
|
+
expect(last_response['Access-Control-Allow-Methods']). to eql("OPTIONS, HEAD, GET, POST, PUT, PATCH, DELETE")
|
30
|
+
expect(last_response.status).to eql(204)
|
31
|
+
expect(last_response.body).to eql("")
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
context 'when used with the :bounce option' do
|
36
|
+
def app
|
37
|
+
Rack::Builder.new do
|
38
|
+
use CorsHeaders, bounce: true
|
39
|
+
run ->(env){ [200, {}, ["Hello world"]] }
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'sets the CORS Origin header to the caller' do
|
44
|
+
header('Origin', "https://test.com")
|
45
|
+
get '/'
|
46
|
+
expect(last_response['Access-Control-Allow-Origin']). to eql("https://test.com")
|
47
|
+
expect(last_response['Access-Control-Allow-Methods']). to eql("OPTIONS, HEAD, GET, POST, PUT, PATCH, DELETE")
|
48
|
+
expect(last_response.body).to eql("Hello world")
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
context 'when overriding a header' do
|
53
|
+
def app
|
54
|
+
Rack::Builder.new do
|
55
|
+
use CorsHeaders, headers: { 'Access-Control-Allow-Methods' => "POST" }
|
56
|
+
run ->(env){ [200, {}, ["Hello world"]] }
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
it 'sets the CORS Origin header to the caller' do
|
61
|
+
header('Origin', "https://test.com")
|
62
|
+
get '/'
|
63
|
+
expect(last_response['Access-Control-Allow-Origin']). to eql("*")
|
64
|
+
expect(last_response['Access-Control-Allow-Methods']). to eql("POST")
|
65
|
+
expect(last_response.body).to eql("Hello world")
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
context 'when the app sets specific headers' do
|
70
|
+
def app
|
71
|
+
Rack::Builder.new do
|
72
|
+
use CorsHeaders
|
73
|
+
run ->(env){ [200, {'Access-Control-Allow-Methods' => "POST"}, ["Hello world"]] }
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
it 'does not override them' do
|
78
|
+
header('Origin', "https://test.com")
|
79
|
+
get '/'
|
80
|
+
expect(last_response['Access-Control-Allow-Origin']). to eql("*")
|
81
|
+
expect(last_response['Access-Control-Allow-Methods']). to eql("POST")
|
82
|
+
expect(last_response.body).to eql("Hello world")
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
end # CatchAll
|
87
|
+
end # module Web
|
88
|
+
end # module Startback
|
@@ -0,0 +1,59 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'startback/web/health_check'
|
3
|
+
|
4
|
+
module Startback
|
5
|
+
module Web
|
6
|
+
describe HealthCheck do
|
7
|
+
include Rack::Test::Methods
|
8
|
+
|
9
|
+
context 'when used without a block and no failure' do
|
10
|
+
def app
|
11
|
+
HealthCheck.new
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'returns a 204 when ok' do
|
15
|
+
get '/'
|
16
|
+
expect(last_response.status).to eql(204)
|
17
|
+
expect(last_response.body).to be_empty
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
context 'when used without a block a failure' do
|
22
|
+
def app
|
23
|
+
app = HealthCheck.new
|
24
|
+
def app.check!(env)
|
25
|
+
raise "Hello error"
|
26
|
+
end
|
27
|
+
app
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'raises when ko' do
|
31
|
+
expect(->(){ get '/' }).to raise_error("Hello error")
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
context 'when used with a block returning a debug message' do
|
36
|
+
def app
|
37
|
+
HealthCheck.new{ "Hello world" }
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'returns a 200 with plain text message' do
|
41
|
+
get '/'
|
42
|
+
expect(last_response.status).to eql(200)
|
43
|
+
expect(last_response.body).to eql("Hello world")
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
context 'when used with a block raising an exception' do
|
48
|
+
def app
|
49
|
+
HealthCheck.new{ raise("Hello error") }
|
50
|
+
end
|
51
|
+
|
52
|
+
it 're-raises it' do
|
53
|
+
expect(->(){ get '/' }).to raise_error("Hello error")
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
end # module Web
|
59
|
+
end # module Startback
|