startback-websocket 0.14.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.
- 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
|