appsignal 0.4.7 → 0.5.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.
- data/.ruby-version +1 -0
- data/README.md +20 -19
- data/appsignal.gemspec +2 -2
- data/lib/appsignal.rb +41 -18
- data/lib/appsignal/agent.rb +28 -54
- data/lib/appsignal/aggregator.rb +65 -0
- data/lib/appsignal/aggregator/post_processor.rb +27 -0
- data/lib/appsignal/config.rb +9 -4
- data/lib/appsignal/listener.rb +30 -0
- data/lib/appsignal/middleware.rb +4 -30
- data/lib/appsignal/middleware/action_view_sanitizer.rb +21 -0
- data/lib/appsignal/middleware/active_record_sanitizer.rb +60 -0
- data/lib/appsignal/middleware/chain.rb +99 -0
- data/lib/appsignal/middleware/delete_blanks.rb +12 -0
- data/lib/appsignal/railtie.rb +9 -1
- data/lib/appsignal/to_appsignal_hash.rb +23 -0
- data/lib/appsignal/transaction.rb +72 -16
- data/lib/appsignal/transaction/params_sanitizer.rb +91 -13
- data/lib/appsignal/transaction/transaction_formatter.rb +32 -68
- data/lib/appsignal/version.rb +1 -1
- data/spec/appsignal/agent_spec.rb +46 -156
- data/spec/appsignal/aggregator/post_processor_spec.rb +84 -0
- data/spec/appsignal/aggregator_spec.rb +182 -0
- data/spec/appsignal/inactive_railtie_spec.rb +2 -1
- data/spec/appsignal/{middleware_spec.rb → listener_spec.rb} +2 -2
- data/spec/appsignal/middleware/action_view_sanitizer_spec.rb +27 -0
- data/spec/appsignal/middleware/active_record_sanitizer_spec.rb +201 -0
- data/spec/appsignal/middleware/chain_spec.rb +168 -0
- data/spec/appsignal/middleware/delete_blanks_spec.rb +37 -0
- data/spec/appsignal/railtie_spec.rb +47 -34
- data/spec/appsignal/to_appsignal_hash_spec.rb +29 -0
- data/spec/appsignal/transaction/params_sanitizer_spec.rb +141 -36
- data/spec/appsignal/transaction/transaction_formatter_spec.rb +60 -155
- data/spec/appsignal/transaction_spec.rb +186 -53
- data/spec/appsignal/transmitter_spec.rb +11 -6
- data/spec/appsignal_spec.rb +33 -0
- data/spec/spec_helper.rb +9 -62
- data/spec/support/helpers/notification_helpers.rb +30 -0
- data/spec/support/helpers/transaction_helpers.rb +64 -0
- metadata +74 -63
- data/.rvmrc +0 -1
- data/lib/appsignal/transaction/faulty_request_formatter.rb +0 -30
- data/lib/appsignal/transaction/regular_request_formatter.rb +0 -11
- data/lib/appsignal/transaction/slow_request_formatter.rb +0 -34
- data/spec/appsignal/transaction/faulty_request_formatter_spec.rb +0 -49
- data/spec/appsignal/transaction/regular_request_formatter_spec.rb +0 -14
- data/spec/appsignal/transaction/slow_request_formatter_spec.rb +0 -76
@@ -0,0 +1,182 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Appsignal::Aggregator do
|
4
|
+
let(:aggregator) { Appsignal::Aggregator.new }
|
5
|
+
|
6
|
+
describe "#add" do
|
7
|
+
subject { aggregator.add(transaction) }
|
8
|
+
|
9
|
+
context "adding a regular transaction" do
|
10
|
+
let(:transaction) { regular_transaction }
|
11
|
+
|
12
|
+
specify { transaction.should_receive(:truncate!) }
|
13
|
+
end
|
14
|
+
|
15
|
+
context "adding a slow transaction" do
|
16
|
+
let(:transaction) { slow_transaction }
|
17
|
+
|
18
|
+
specify {
|
19
|
+
aggregator.should_receive(:pre_process_slowness!).with(transaction)
|
20
|
+
}
|
21
|
+
end
|
22
|
+
|
23
|
+
context "adding a transaction with an exception" do
|
24
|
+
let(:transaction) { transaction_with_exception }
|
25
|
+
|
26
|
+
specify { transaction.should_receive(:convert_values_to_primitives!) }
|
27
|
+
end
|
28
|
+
|
29
|
+
after { subject }
|
30
|
+
end
|
31
|
+
|
32
|
+
describe "#has_transactions?" do
|
33
|
+
subject { aggregator.has_transactions? }
|
34
|
+
|
35
|
+
context "with an empty queue" do
|
36
|
+
it { should be_false }
|
37
|
+
end
|
38
|
+
|
39
|
+
context "with an non empty queue" do
|
40
|
+
before { aggregator.add(regular_transaction) }
|
41
|
+
|
42
|
+
it { should be_true }
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
describe "#post_processed_queue!" do
|
47
|
+
let(:transaction) { slow_transaction }
|
48
|
+
let(:other_transaction) { regular_transaction }
|
49
|
+
subject { aggregator.post_processed_queue! }
|
50
|
+
before do
|
51
|
+
aggregator.add(transaction)
|
52
|
+
aggregator.add(other_transaction)
|
53
|
+
end
|
54
|
+
|
55
|
+
it { should be_a Array }
|
56
|
+
|
57
|
+
it "calls to_hash on each transaction in the queue" do
|
58
|
+
transaction.should_receive(:to_hash)
|
59
|
+
other_transaction.should_receive(:to_hash)
|
60
|
+
subject
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
# protected
|
65
|
+
|
66
|
+
describe "#similar_slowest" do
|
67
|
+
subject { aggregator.send(:similar_slowest, transaction) }
|
68
|
+
before { aggregator.add(other_transaction) }
|
69
|
+
|
70
|
+
context "passing a transaction concerning a different action" do
|
71
|
+
let(:transaction) { slow_transaction }
|
72
|
+
let(:other_transaction) do
|
73
|
+
time = Time.parse('01-01-2001 10:01:00')
|
74
|
+
appsignal_transaction(
|
75
|
+
:process_action_event => notification_event(
|
76
|
+
:payload => create_payload(
|
77
|
+
:action => 'show',
|
78
|
+
:controller => 'SomeOtherController'
|
79
|
+
)
|
80
|
+
)
|
81
|
+
)
|
82
|
+
end
|
83
|
+
|
84
|
+
it { should be_nil }
|
85
|
+
end
|
86
|
+
|
87
|
+
context "passing concerning the same action" do
|
88
|
+
let(:transaction) { slow_transaction }
|
89
|
+
let(:other_transaction) { slower_transaction }
|
90
|
+
|
91
|
+
it { should == other_transaction }
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
describe "#pre_process_slowness!" do
|
96
|
+
subject { aggregator.send(:pre_process_slowness!, transaction) }
|
97
|
+
|
98
|
+
context "without a similar slow transaction" do
|
99
|
+
let(:transaction) { slow_transaction }
|
100
|
+
|
101
|
+
it "calls convert_values_to_primitives on transaction" do
|
102
|
+
transaction.should_receive(:convert_values_to_primitives!)
|
103
|
+
subject
|
104
|
+
end
|
105
|
+
|
106
|
+
it "indexes the slow transaction" do
|
107
|
+
expect { subject }.to change(aggregator, :slowness_index).
|
108
|
+
to({transaction.action => transaction})
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
context "with a non similar slow transaction" do
|
113
|
+
let(:transaction) { slow_transaction }
|
114
|
+
let(:other_transaction) do
|
115
|
+
time = Time.parse('01-01-2001 10:01:00')
|
116
|
+
appsignal_transaction(
|
117
|
+
:process_action_event => notification_event(
|
118
|
+
:action => 'foooo',
|
119
|
+
:start => time,
|
120
|
+
:ending => time + Appsignal.config[:slow_request_threshold] / 500.0
|
121
|
+
)
|
122
|
+
)
|
123
|
+
end
|
124
|
+
|
125
|
+
it "calls convert_values_to_primitives on transaction" do
|
126
|
+
transaction.should_receive(:convert_values_to_primitives!)
|
127
|
+
subject
|
128
|
+
end
|
129
|
+
|
130
|
+
it "indexes the slow transaction" do
|
131
|
+
expect { subject }.to change(aggregator, :slowness_index).to({
|
132
|
+
other_transaction.action => other_transaction,
|
133
|
+
transaction.action => transaction
|
134
|
+
})
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
context "with a similar but slower transaction" do
|
139
|
+
let(:transaction) { slow_transaction }
|
140
|
+
let(:slower) { slower_transaction }
|
141
|
+
before { aggregator.add(slower) }
|
142
|
+
|
143
|
+
it "calls truncate on the transaction" do
|
144
|
+
transaction.should_receive(:truncate!)
|
145
|
+
transaction.should_not_receive(:convert_values_to_primitives!)
|
146
|
+
subject
|
147
|
+
end
|
148
|
+
|
149
|
+
it "does not index the slow transaction" do
|
150
|
+
expect { subject }.to_not change(aggregator, :slowness_index)
|
151
|
+
end
|
152
|
+
|
153
|
+
it "does not modify the slow transaction" do
|
154
|
+
slower.should_not_receive(:truncate!)
|
155
|
+
slower.should_not_receive(:convert_values_to_primitives!)
|
156
|
+
subject
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
context "with a similar but faster transaction" do
|
161
|
+
let(:transaction) { slower_transaction }
|
162
|
+
let(:faster_transaction) { slow_transaction }
|
163
|
+
before { aggregator.add(faster_transaction) }
|
164
|
+
|
165
|
+
it "calls truncate on the faster transaction" do
|
166
|
+
faster_transaction.should_receive(:truncate!)
|
167
|
+
subject
|
168
|
+
end
|
169
|
+
|
170
|
+
it "calls convert_values_to_primitives on the (slower) transaction" do
|
171
|
+
transaction.should_receive(:convert_values_to_primitives!)
|
172
|
+
subject
|
173
|
+
end
|
174
|
+
|
175
|
+
it "indexes the slow transaction" do
|
176
|
+
expect { subject }.to change(aggregator, :slowness_index).
|
177
|
+
to({faster_transaction.action => transaction})
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
end
|
@@ -21,7 +21,8 @@ describe "Inactive Appsignal::Railtie" do
|
|
21
21
|
end
|
22
22
|
MyTempApp::Application.initialize!
|
23
23
|
|
24
|
-
MyTempApp::Application.middleware.to_a.
|
24
|
+
MyTempApp::Application.middleware.to_a.
|
25
|
+
should_not include Appsignal::Listener
|
25
26
|
end
|
26
27
|
Process.wait(pid)
|
27
28
|
raise 'Example failed' unless $?.exitstatus == 0
|
@@ -10,11 +10,11 @@ class AppWithError
|
|
10
10
|
end
|
11
11
|
end
|
12
12
|
|
13
|
-
describe Appsignal::
|
13
|
+
describe Appsignal::Listener do
|
14
14
|
describe '#call' do
|
15
15
|
let(:app) { stub(:call => true) }
|
16
16
|
let(:env) { {'action_dispatch.request_id' => '1'} }
|
17
|
-
let(:middleware) { Appsignal::
|
17
|
+
let(:middleware) { Appsignal::Listener.new(app, {})}
|
18
18
|
let(:current) { stub(:complete! => true, :add_exception => true) }
|
19
19
|
before { Appsignal::Transaction.stub(:current => current) }
|
20
20
|
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Appsignal::Middleware::ActionViewSanitizer do
|
4
|
+
let(:klass) { Appsignal::Middleware::ActionViewSanitizer }
|
5
|
+
let(:sanitizer) { klass.new }
|
6
|
+
|
7
|
+
describe "#call" do
|
8
|
+
before { Rails.root.stub(:to_s => '/var/www/app/20130101') }
|
9
|
+
let(:event) do
|
10
|
+
notification_event(
|
11
|
+
:name => 'render_partial.action_view',
|
12
|
+
:payload => create_payload(payload)
|
13
|
+
)
|
14
|
+
end
|
15
|
+
let(:payload) do
|
16
|
+
{
|
17
|
+
:identifier => '/var/www/app/20130101/app/views/home/index/html.erb'
|
18
|
+
}
|
19
|
+
end
|
20
|
+
subject { event.payload }
|
21
|
+
before { sanitizer.call(event) { } }
|
22
|
+
|
23
|
+
it "should strip Rails root from the path" do
|
24
|
+
payload[:identifier].should == 'app/views/home/index/html.erb'
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,201 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Appsignal::Middleware::ActiveRecordSanitizer do
|
4
|
+
let(:klass) { Appsignal::Middleware::ActiveRecordSanitizer }
|
5
|
+
let(:sql_event_sanitizer) { klass.new }
|
6
|
+
let(:connection_config) { {} }
|
7
|
+
before do
|
8
|
+
ActiveRecord::Base.stub(
|
9
|
+
:connection_config => connection_config
|
10
|
+
)
|
11
|
+
end
|
12
|
+
|
13
|
+
describe "#call" do
|
14
|
+
let(:name) { 'Model load' }
|
15
|
+
let(:binds) { [] }
|
16
|
+
let(:event) do
|
17
|
+
notification_event(
|
18
|
+
:name => 'sql.active_record',
|
19
|
+
:payload => create_payload(
|
20
|
+
:name => name,
|
21
|
+
:sql => sql,
|
22
|
+
:binds => binds,
|
23
|
+
:connection_id => 1111
|
24
|
+
)
|
25
|
+
)
|
26
|
+
end
|
27
|
+
subject { event.payload[:sql] }
|
28
|
+
before { sql_event_sanitizer.call(event) { } }
|
29
|
+
|
30
|
+
context "connection id and bindings" do
|
31
|
+
let(:sql) { '' }
|
32
|
+
subject { event.payload }
|
33
|
+
|
34
|
+
it { should_not have_key(:connection_id) }
|
35
|
+
it { should_not have_key(:binds) }
|
36
|
+
end
|
37
|
+
|
38
|
+
context "with backtick table names" do
|
39
|
+
before { sql_event_sanitizer.stub(:adapter_uses_double_quoted_table_names? => false) }
|
40
|
+
|
41
|
+
context "single quoted data value" do
|
42
|
+
let(:sql) { "SELECT `table`.* FROM `table` WHERE `id` = 'secret'" }
|
43
|
+
|
44
|
+
it { should == "SELECT `table`.* FROM `table` WHERE `id` = ?" }
|
45
|
+
|
46
|
+
context "with an escaped single quote" do
|
47
|
+
let(:sql) { "`id` = '\\'big\\' secret'" }
|
48
|
+
|
49
|
+
it { should == "`id` = ?" }
|
50
|
+
end
|
51
|
+
|
52
|
+
context "with an escaped double quote" do
|
53
|
+
let(:sql) { "`id` = '\\\"big\\\" secret'" }
|
54
|
+
|
55
|
+
it { should == "`id` = ?" }
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
context "double quoted data value" do
|
60
|
+
let(:sql) { 'SELECT `table`.* FROM `table` WHERE `id` = "secret"' }
|
61
|
+
|
62
|
+
it { should == 'SELECT `table`.* FROM `table` WHERE `id` = ?' }
|
63
|
+
|
64
|
+
context "with an escaped single quote" do
|
65
|
+
let(:sql) { '`id` = "\\\'big\\\' secret"' }
|
66
|
+
|
67
|
+
it { should == "`id` = ?" }
|
68
|
+
end
|
69
|
+
|
70
|
+
context "with an escaped double quote" do
|
71
|
+
let(:sql) { '`id` = "\\"big\\" secret"' }
|
72
|
+
|
73
|
+
it { should == "`id` = ?" }
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
context "numeric parameter" do
|
78
|
+
let(:sql) { 'SELECT `table`.* FROM `table` WHERE `id` = 1' }
|
79
|
+
|
80
|
+
it { should == 'SELECT `table`.* FROM `table` WHERE `id` = ?' }
|
81
|
+
end
|
82
|
+
|
83
|
+
context "parameter array" do
|
84
|
+
let(:sql) { 'SELECT `table`.* FROM `table` WHERE `id` IN (1, 2)' }
|
85
|
+
|
86
|
+
it { should == 'SELECT `table`.* FROM `table` WHERE `id` IN (?)' }
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
context "with double quote style table names and no prepared statements" do
|
91
|
+
let(:connection_config) { {:adapter => 'postgresql', :prepared_statements => false} }
|
92
|
+
|
93
|
+
context "single quoted data value" do
|
94
|
+
let(:sql) { "SELECT \"table\".* FROM \"table\" WHERE \"id\" = 'secret'" }
|
95
|
+
|
96
|
+
it { should == "SELECT \"table\".* FROM \"table\" WHERE \"id\" = ?" }
|
97
|
+
|
98
|
+
context "with an escaped single quote" do
|
99
|
+
let(:sql) { "\"id\" = '\\'big\\' secret'" }
|
100
|
+
|
101
|
+
it { should == "\"id\" = ?" }
|
102
|
+
end
|
103
|
+
|
104
|
+
context "with an escaped double quote" do
|
105
|
+
let(:sql) { "\"id\" = '\\\"big\\\" secret'" }
|
106
|
+
|
107
|
+
it { should == "\"id\" = ?" }
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
context "numeric parameter" do
|
112
|
+
let(:sql) { 'SELECT "table".* FROM "table" WHERE "id"=1' }
|
113
|
+
|
114
|
+
it { should == 'SELECT "table".* FROM "table" WHERE "id"=?' }
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
context "skip sanitization for prepared statements" do
|
119
|
+
let(:connection_config) { {:adapter => 'postgresql'} }
|
120
|
+
|
121
|
+
let(:sql) { 'SELECT "table".* FROM "table" WHERE "id"=$1' }
|
122
|
+
|
123
|
+
it { should == 'SELECT "table".* FROM "table" WHERE "id"=$1' }
|
124
|
+
end
|
125
|
+
|
126
|
+
context "skip sanitization for schema queries" do
|
127
|
+
let(:name) { 'SCHEMA' }
|
128
|
+
let(:sql) { 'SET client_min_messages TO 22' }
|
129
|
+
|
130
|
+
it { should == 'SET client_min_messages TO 22' }
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
describe "#schema_query?" do
|
135
|
+
let(:payload) { {} }
|
136
|
+
let(:event) { notification_event(:payload => payload) }
|
137
|
+
subject { sql_event_sanitizer.schema_query?(event) }
|
138
|
+
|
139
|
+
it { should be_false }
|
140
|
+
|
141
|
+
context "when name is schema" do
|
142
|
+
let(:payload) { {:name => 'SCHEMA'} }
|
143
|
+
|
144
|
+
it { should be_true }
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
context "connection config" do
|
149
|
+
describe "#connection_config" do
|
150
|
+
let(:connection_config) { {:adapter => 'adapter'} }
|
151
|
+
|
152
|
+
subject { sql_event_sanitizer.connection_config }
|
153
|
+
|
154
|
+
it { should == {:adapter => 'adapter'} }
|
155
|
+
end
|
156
|
+
|
157
|
+
describe "#adapter_uses_double_quoted_table_names?" do
|
158
|
+
subject { sql_event_sanitizer.adapter_uses_double_quoted_table_names? }
|
159
|
+
|
160
|
+
context "when using mysql" do
|
161
|
+
let(:connection_config) { {:adapter => 'mysql'} }
|
162
|
+
|
163
|
+
it { should be_false }
|
164
|
+
end
|
165
|
+
|
166
|
+
context "when using postgresql" do
|
167
|
+
let(:connection_config) { {:adapter => 'postgresql'} }
|
168
|
+
|
169
|
+
it { should be_true }
|
170
|
+
end
|
171
|
+
|
172
|
+
context "when using sqlite" do
|
173
|
+
let(:connection_config) { {:adapter => 'sqlite'} }
|
174
|
+
|
175
|
+
it { should be_true }
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
describe "adapter_uses_prepared_statements?" do
|
180
|
+
subject { sql_event_sanitizer.adapter_uses_prepared_statements? }
|
181
|
+
|
182
|
+
context "when using mysql" do
|
183
|
+
let(:connection_config) { {:adapter => 'mysql'} }
|
184
|
+
|
185
|
+
it { should be_false }
|
186
|
+
end
|
187
|
+
|
188
|
+
context "when using postgresql" do
|
189
|
+
let(:connection_config) { {:adapter => 'postgresql'} }
|
190
|
+
|
191
|
+
it { should be_true }
|
192
|
+
end
|
193
|
+
|
194
|
+
context "when using postgresql and prepared statements is disabled" do
|
195
|
+
let(:connection_config) { {:adapter => 'postgresql', :prepared_statements => false} }
|
196
|
+
|
197
|
+
it { should be_false }
|
198
|
+
end
|
199
|
+
end
|
200
|
+
end
|
201
|
+
end
|
@@ -0,0 +1,168 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Appsignal::Middleware do
|
4
|
+
let(:chain_klass) { Appsignal::Middleware::Chain }
|
5
|
+
let(:entry_klass) { Appsignal::Middleware::Entry }
|
6
|
+
let(:chain) { chain_klass.new }
|
7
|
+
let(:entry) { entry_klass.new(object, {:foo => :bar}) }
|
8
|
+
|
9
|
+
describe Appsignal::Middleware::Chain do
|
10
|
+
let(:object) { mock(:object) }
|
11
|
+
let(:other_object) { mock(:other_object) }
|
12
|
+
|
13
|
+
describe "#initialize" do
|
14
|
+
it "yields itself when passing a block" do
|
15
|
+
chain_klass.new { |o| o.should be_instance_of chain_klass }
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
describe "#remove" do
|
20
|
+
subject { chain.remove(object) }
|
21
|
+
before { chain.add(other_object) }
|
22
|
+
|
23
|
+
context "when it doesn't find the object" do
|
24
|
+
specify { expect { subject }.to_not raise_error }
|
25
|
+
end
|
26
|
+
|
27
|
+
context "when it finds the object" do
|
28
|
+
before { chain.add(object) }
|
29
|
+
|
30
|
+
specify { expect { subject }.to change(chain.entries, :count).by(-1) }
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
describe "#add" do
|
35
|
+
subject { chain.add(object) }
|
36
|
+
|
37
|
+
context "adding a new object" do
|
38
|
+
specify { expect { subject }.to change(chain.entries, :count).by(1) }
|
39
|
+
end
|
40
|
+
|
41
|
+
context "trying to add a duplicate (even with different arguments)" do
|
42
|
+
before { chain.add(object, :some_arguments) }
|
43
|
+
|
44
|
+
specify { expect { subject }.to_not change(chain.entries, :count).by(1) }
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
context "with a chain holding three items" do
|
49
|
+
subject { chain.entries.map(&:klass) }
|
50
|
+
before { [:first, :second, :third].each { |o| chain.add(o) } }
|
51
|
+
|
52
|
+
describe "#insert_before" do
|
53
|
+
|
54
|
+
context "before the second" do
|
55
|
+
before { chain.insert_before(:second, :object) }
|
56
|
+
|
57
|
+
it { should == [:first, :object, :second, :third] }
|
58
|
+
end
|
59
|
+
|
60
|
+
context "before the first" do
|
61
|
+
before { chain.insert_before(:first, :object) }
|
62
|
+
|
63
|
+
it { should == [:object, :first, :second, :third] }
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
describe "#insert_after" do
|
68
|
+
|
69
|
+
context "after the second" do
|
70
|
+
before { chain.insert_after(:second, :object) }
|
71
|
+
|
72
|
+
it { should == [:first, :second, :object, :third] }
|
73
|
+
end
|
74
|
+
|
75
|
+
context "after the third" do
|
76
|
+
before { chain.insert_after(:third, :object) }
|
77
|
+
|
78
|
+
it { should == [:first, :second, :third, :object] }
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
describe "#exists?" do
|
83
|
+
subject { chain.exists?(object) }
|
84
|
+
|
85
|
+
context "when it is in the chain" do
|
86
|
+
let(:object) { :first }
|
87
|
+
|
88
|
+
it { should be_true }
|
89
|
+
end
|
90
|
+
|
91
|
+
context "when it is not" do
|
92
|
+
let(:object) { :unknown }
|
93
|
+
|
94
|
+
it { should be_false }
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
describe "#retrieve" do
|
99
|
+
specify { chain.entries.each { |o| o.should_receive(:make_new) } }
|
100
|
+
|
101
|
+
after { chain.retrieve }
|
102
|
+
end
|
103
|
+
|
104
|
+
describe "#clear" do
|
105
|
+
before { chain.clear }
|
106
|
+
subject { chain.entries }
|
107
|
+
|
108
|
+
it { should be_empty }
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
describe "#invoke" do
|
113
|
+
let(:recorder) { [] }
|
114
|
+
subject { chain.invoke(:unsused) }
|
115
|
+
before(:all) do
|
116
|
+
TestInvoker1 = Struct.new(:id, :recorder) do
|
117
|
+
def call(event)
|
118
|
+
recorder << "Before#{id}"
|
119
|
+
yield
|
120
|
+
recorder << "After#{id}"
|
121
|
+
end
|
122
|
+
end
|
123
|
+
TestInvoker3 = Class.new(TestInvoker1)
|
124
|
+
end
|
125
|
+
|
126
|
+
context "all yielding entries" do
|
127
|
+
before do
|
128
|
+
TestInvoker2 = Class.new(TestInvoker1)
|
129
|
+
|
130
|
+
chain.add(TestInvoker1, 1, recorder)
|
131
|
+
chain.add(TestInvoker2, 2, recorder)
|
132
|
+
chain.add(TestInvoker3, 3, recorder)
|
133
|
+
end
|
134
|
+
|
135
|
+
it { should == %w(Before1 Before2 Before3 After3 After2 After1) }
|
136
|
+
end
|
137
|
+
|
138
|
+
context "a non yielding entry" do
|
139
|
+
before do
|
140
|
+
TestBlocker = Struct.new(:recorder) do
|
141
|
+
def call(event)
|
142
|
+
recorder << 'End-of-the-line!'
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
chain.add(TestInvoker1, 1, recorder)
|
147
|
+
chain.add(TestBlocker, recorder)
|
148
|
+
chain.add(TestInvoker3, 3, recorder)
|
149
|
+
end
|
150
|
+
|
151
|
+
it { should == %w(Before1 End-of-the-line! After1) }
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
describe Appsignal::Middleware::Entry do
|
157
|
+
|
158
|
+
describe "#make_new" do
|
159
|
+
let(:object) { mock }
|
160
|
+
subject { entry.make_new }
|
161
|
+
|
162
|
+
it "initializes the passed object" do
|
163
|
+
object.should_receive(:new)
|
164
|
+
subject
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|