appmap 0.77.3 → 0.79.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 +4 -4
- data/.rubocop.yml +1 -0
- data/.travis.yml +4 -23
- data/CHANGELOG.md +21 -0
- data/{spec/fixtures/rails5_users_app/Dockerfile.pg → Dockerfile.pg} +0 -0
- data/README.md +14 -44
- data/README_CI.md +0 -7
- data/Rakefile +12 -150
- data/appmap.gemspec +3 -2
- data/docker-compose.yml +10 -0
- data/ext/appmap/appmap.c +21 -2
- data/lib/appmap/builtin_hooks/ruby.yml +6 -3
- data/lib/appmap/handler/eval.rb +41 -0
- data/lib/appmap/handler/function.rb +8 -8
- data/lib/appmap/handler/net_http.rb +19 -18
- data/lib/appmap/handler/rails/request_handler.rb +3 -4
- data/lib/appmap/handler/rails/template.rb +68 -62
- data/lib/appmap/hook/method/ruby2.rb +56 -0
- data/lib/appmap/hook/method/ruby3.rb +56 -0
- data/lib/appmap/hook/method.rb +42 -98
- data/lib/appmap/hook.rb +2 -2
- data/lib/appmap/version.rb +1 -1
- data/spec/config_spec.rb +1 -1
- data/spec/depends/api_spec.rb +13 -5
- data/spec/depends/spec_helper.rb +0 -9
- data/spec/fixtures/database.yml +11 -0
- data/spec/fixtures/rails5_users_app/config/database.yml +1 -0
- data/spec/fixtures/rails6_users_app/Gemfile +1 -25
- data/spec/fixtures/rails6_users_app/config/database.yml +1 -0
- data/spec/fixtures/rails7_users_app/Gemfile +1 -25
- data/spec/fixtures/rails7_users_app/config/database.yml +1 -0
- data/spec/handler/eval_spec.rb +66 -0
- data/spec/hook_spec.rb +3 -3
- data/spec/rails_recording_spec.rb +4 -20
- data/spec/rails_spec_helper.rb +76 -63
- data/spec/rails_test_spec.rb +7 -17
- data/spec/railtie_spec.rb +4 -18
- data/spec/record_sql_rails_pg_spec.rb +44 -75
- data/spec/remote_recording_spec.rb +18 -30
- data/spec/spec_helper.rb +1 -0
- data/spec/swagger/swagger_spec.rb +1 -16
- data/spec/util_spec.rb +1 -1
- metadata +25 -21
- data/Dockerfile.appmap +0 -5
- data/spec/fixtures/rack_users_app/Dockerfile +0 -32
- data/spec/fixtures/rack_users_app/docker-compose.yml +0 -9
- data/spec/fixtures/rails5_users_app/Dockerfile +0 -29
- data/spec/fixtures/rails5_users_app/config/database.yml +0 -18
- data/spec/fixtures/rails5_users_app/create_app +0 -33
- data/spec/fixtures/rails5_users_app/docker-compose.yml +0 -31
- data/spec/fixtures/rails6_users_app/.ruby-version +0 -1
- data/spec/fixtures/rails6_users_app/Dockerfile +0 -44
- data/spec/fixtures/rails6_users_app/Dockerfile.pg +0 -3
- data/spec/fixtures/rails6_users_app/config/database.yml +0 -18
- data/spec/fixtures/rails6_users_app/create_app +0 -33
- data/spec/fixtures/rails6_users_app/docker-compose.yml +0 -31
- data/spec/fixtures/rails7_users_app/.ruby-version +0 -1
- data/spec/fixtures/rails7_users_app/Dockerfile +0 -30
- data/spec/fixtures/rails7_users_app/Dockerfile.pg +0 -3
- data/spec/fixtures/rails7_users_app/config/database.yml +0 -86
- data/spec/fixtures/rails7_users_app/create_app +0 -31
- data/spec/fixtures/rails7_users_app/docker-compose.yml +0 -31
@@ -69,31 +69,7 @@ group :test do
|
|
69
69
|
gem "webdrivers"
|
70
70
|
end
|
71
71
|
|
72
|
-
appmap_path = \
|
73
|
-
# Support debugging inside the container with volume-mounted source
|
74
|
-
if File.directory?('/src/appmap-ruby')
|
75
|
-
'/src/appmap-ruby'
|
76
|
-
elsif File.exist?('../../../appmap.gemspec')
|
77
|
-
'../../..'
|
78
|
-
end
|
79
|
-
|
80
|
-
if appmap_path
|
81
|
-
# Set the branch parameter, so that 'bundle config local.appmap' will work
|
82
|
-
appmap_branch = Dir.chdir appmap_path do
|
83
|
-
`git rev-parse --abbrev-ref HEAD`.strip
|
84
|
-
end
|
85
|
-
end
|
86
|
-
|
87
|
-
appmap_options = \
|
88
|
-
if appmap_path && appmap_branch
|
89
|
-
{ git: appmap_path, branch: appmap_branch }
|
90
|
-
elsif appmap_path
|
91
|
-
{ path: appmap_path }
|
92
|
-
else
|
93
|
-
{}
|
94
|
-
end.merge(require: %w[appmap])
|
95
|
-
|
96
72
|
group :development, :test do
|
97
|
-
gem 'appmap',
|
73
|
+
gem 'appmap', path: '../../..'
|
98
74
|
gem 'pry-byebug', '>=0', '< 99'
|
99
75
|
end
|
@@ -0,0 +1 @@
|
|
1
|
+
../../database.yml
|
@@ -0,0 +1,66 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# rubocop:disable Security/Eval, Style/EvalWithLocation
|
4
|
+
|
5
|
+
require 'spec_helper'
|
6
|
+
require 'appmap/config'
|
7
|
+
|
8
|
+
describe 'AppMap::Handler::Eval' do
|
9
|
+
include_context 'collect events'
|
10
|
+
let!(:config) { AppMap::Config.new('hook_spec') }
|
11
|
+
before { AppMap.configuration = config }
|
12
|
+
after { AppMap.configuration = nil }
|
13
|
+
|
14
|
+
def record_block
|
15
|
+
AppMap::Hook.new(config).enable do
|
16
|
+
tracer = AppMap.tracing.trace
|
17
|
+
AppMap::Event.reset_id_counter
|
18
|
+
begin
|
19
|
+
yield
|
20
|
+
ensure
|
21
|
+
AppMap.tracing.delete(tracer)
|
22
|
+
end
|
23
|
+
tracer
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'produces a simple result' do
|
28
|
+
tracer = record_block do
|
29
|
+
expect(eval('12')).to eq(12)
|
30
|
+
end
|
31
|
+
events = collect_events(tracer)
|
32
|
+
expect(events[0]).to match hash_including \
|
33
|
+
defined_class: 'Kernel',
|
34
|
+
method_id: 'eval',
|
35
|
+
parameters: [{ class: 'Array', kind: :rest, name: 'arg', value: '[12]' }]
|
36
|
+
end
|
37
|
+
|
38
|
+
# a la Ruby 2.6.3 ruby-token.rb
|
39
|
+
# token_c = eval("class #{token_n} < #{super_token}; end; #{token_n}")
|
40
|
+
it 'can define a new class' do
|
41
|
+
num = (Random.random_number * 10_000).to_i
|
42
|
+
class_name = "Cls_#{num}"
|
43
|
+
m = ClassMaker
|
44
|
+
cls = nil
|
45
|
+
record_block do
|
46
|
+
cls = m.make_class class_name
|
47
|
+
end
|
48
|
+
expect(cls).to be_instance_of(Class)
|
49
|
+
# If execution context wasn't substituted, the class would be defined as
|
50
|
+
# eg. AppMap::Handler::Eval::Cls_7566
|
51
|
+
expect { AppMap::Handler::Eval.const_get(class_name) }.to raise_error(NameError)
|
52
|
+
# This would be the right behavior
|
53
|
+
expect(m.const_get(class_name)).to be_instance_of(Class)
|
54
|
+
expect(m.const_get(class_name)).to eq(cls)
|
55
|
+
new_cls = Class.new do
|
56
|
+
include m
|
57
|
+
end
|
58
|
+
expect(new_cls.const_get(class_name)).to eq(cls)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
module ClassMaker
|
63
|
+
def self.make_class(class_name)
|
64
|
+
eval "class #{class_name}; end; #{class_name}"
|
65
|
+
end
|
66
|
+
end
|
data/spec/hook_spec.rb
CHANGED
@@ -16,7 +16,7 @@ module ShowYamlNulls
|
|
16
16
|
end
|
17
17
|
Psych::Visitors::YAMLTree.prepend(ShowYamlNulls)
|
18
18
|
|
19
|
-
describe 'AppMap class Hooking'
|
19
|
+
describe 'AppMap class Hooking' do
|
20
20
|
include_context 'collect events'
|
21
21
|
|
22
22
|
def invoke_test_file(file, setup: nil, packages: nil)
|
@@ -115,7 +115,7 @@ describe 'AppMap class Hooking', docker: false do
|
|
115
115
|
require 'appmap/hook/method'
|
116
116
|
package = config.lookup_package(hook_cls, method)
|
117
117
|
expect(package).to be
|
118
|
-
hook_method = AppMap::
|
118
|
+
hook_method = AppMap::Handler::Function.new(package, hook_cls, method)
|
119
119
|
hook_method.activate
|
120
120
|
|
121
121
|
tracer = AppMap.tracing.trace
|
@@ -1188,7 +1188,7 @@ describe 'AppMap class Hooking', docker: false do
|
|
1188
1188
|
require 'appmap/hook/method'
|
1189
1189
|
|
1190
1190
|
pkg = AppMap::Config::Package.new('fixtures/hook/prependend_override')
|
1191
|
-
AppMap::
|
1191
|
+
AppMap::Handler::Function.new(pkg, PrependedClass, PrependedClass.public_instance_method(:say_hello)).activate
|
1192
1192
|
|
1193
1193
|
tracer = AppMap.tracing.trace
|
1194
1194
|
AppMap::Event.reset_id_counter
|
@@ -15,14 +15,6 @@ describe 'Rails' do
|
|
15
15
|
include_context 'Rails app pg database', "spec/fixtures/rails#{rails_major_version}_users_app" unless use_existing_data?
|
16
16
|
include_context 'rails integration test setup'
|
17
17
|
|
18
|
-
def run_spec(spec_name)
|
19
|
-
cmd = <<~CMD.gsub "\n", ' '
|
20
|
-
docker-compose run --rm -e RAILS_ENV=test -e APPMAP=true
|
21
|
-
-v #{File.absolute_path tmpdir}:/app/tmp app ./bin/rspec #{spec_name}
|
22
|
-
CMD
|
23
|
-
run_cmd cmd, chdir: fixture_dir
|
24
|
-
end
|
25
|
-
|
26
18
|
describe 'an API route' do
|
27
19
|
describe 'creating an object' do
|
28
20
|
let(:appmap_json_file) do
|
@@ -123,7 +115,7 @@ describe 'Rails' do
|
|
123
115
|
)
|
124
116
|
)
|
125
117
|
)
|
126
|
-
)
|
118
|
+
)
|
127
119
|
end
|
128
120
|
end
|
129
121
|
end
|
@@ -201,7 +193,7 @@ describe 'Rails' do
|
|
201
193
|
'path_info' => '/users/alice',
|
202
194
|
'normalized_path_info' => '/users/{id}',
|
203
195
|
'headers' => {
|
204
|
-
'Host' => 'test.host',
|
196
|
+
'Host' => 'test.host',
|
205
197
|
'User-Agent' => 'Rails Testing'
|
206
198
|
}
|
207
199
|
}
|
@@ -236,7 +228,7 @@ describe 'Rails' do
|
|
236
228
|
'defined_class' => 'inline_template',
|
237
229
|
'method_id' => 'render'
|
238
230
|
)
|
239
|
-
|
231
|
+
|
240
232
|
expect(appmap['classMap']).to include hash_including(
|
241
233
|
'name' => 'actionview',
|
242
234
|
'children' => include(hash_including(
|
@@ -251,7 +243,7 @@ describe 'Rails' do
|
|
251
243
|
))
|
252
244
|
))
|
253
245
|
)
|
254
|
-
end
|
246
|
+
end
|
255
247
|
end
|
256
248
|
end
|
257
249
|
end
|
@@ -261,14 +253,6 @@ describe 'Rails' do
|
|
261
253
|
include_context 'Rails app pg database', "spec/fixtures/rails6_users_app" unless use_existing_data?
|
262
254
|
include_context 'rails integration test setup'
|
263
255
|
|
264
|
-
def run_spec(spec_name)
|
265
|
-
cmd = <<~CMD.gsub "\n", ' '
|
266
|
-
docker-compose run --rm -e RAILS_ENV=test -e APPMAP=true -e APPMAP_CONFIG_FILE=no/such/file
|
267
|
-
-v #{File.absolute_path tmpdir}:/app/tmp app ./bin/rspec #{spec_name}
|
268
|
-
CMD
|
269
|
-
run_cmd cmd, chdir: fixture_dir
|
270
|
-
end
|
271
|
-
|
272
256
|
let(:appmap_json_file) do
|
273
257
|
'Api_UsersController_POST_api_users_with_required_parameters_creates_a_user.appmap.json'
|
274
258
|
end
|
data/spec/rails_spec_helper.rb
CHANGED
@@ -1,93 +1,106 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'open3'
|
4
|
+
|
3
5
|
require 'spec_helper'
|
4
6
|
require 'active_support'
|
5
7
|
require 'active_support/core_ext'
|
6
|
-
require 'open3'
|
7
8
|
|
8
|
-
# The RUBY_VERSION global variable indicates the version of the
|
9
|
-
# runtime. The RUBY_VERSION environment variable is the version we're
|
10
|
-
# testing, so it needs to be used to pick a fixture directory.
|
11
9
|
def testing_ruby_2?
|
12
|
-
|
10
|
+
RUBY_VERSION.split('.')[0].to_i == 2
|
13
11
|
end
|
14
12
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
13
|
+
class TestRailsApp
|
14
|
+
def initialize(fixture_dir)
|
15
|
+
@fixture_dir = fixture_dir
|
16
|
+
end
|
19
17
|
|
20
|
-
|
21
|
-
start_time = Time.now
|
22
|
-
until `#{PS_CMD} #{app_name}`.strip != ''
|
23
|
-
elapsed = Time.now - start_time
|
24
|
-
raise "Timeout waiting for container #{app_name} to be ready" if elapsed > 10
|
18
|
+
attr_reader :fixture_dir
|
25
19
|
|
26
|
-
|
27
|
-
|
20
|
+
def run_cmd(cmd, env = {})
|
21
|
+
run_process method(:system), cmd, env, exception: true
|
28
22
|
end
|
29
|
-
end
|
30
23
|
|
31
|
-
def
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
warn <<~WARNING
|
36
|
-
Command failed:
|
37
|
-
#{cmd}
|
38
|
-
<<< Output:
|
39
|
-
#{out}
|
40
|
-
>>> End of output
|
41
|
-
WARNING
|
42
|
-
failed&.call
|
43
|
-
raise 'Command failed'
|
44
|
-
end
|
24
|
+
def spawn_cmd(cmd, env = {})
|
25
|
+
puts "Spawning `#{cmd}` in #{fixture_dir}..."
|
26
|
+
run_process Process.method(:spawn), cmd, env
|
27
|
+
end
|
45
28
|
|
46
|
-
|
47
|
-
|
29
|
+
def capture_cmd(cmd, env = {})
|
30
|
+
puts "Capturing `#{cmd}` in #{fixture_dir}..."
|
31
|
+
run_process(Open3.method(:capture2), cmd, env).first
|
32
|
+
end
|
48
33
|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
puts
|
54
|
-
puts logs
|
55
|
-
end
|
34
|
+
def database_name
|
35
|
+
# This is used locally too, so make the name nice and unique.
|
36
|
+
@database_name ||= "appland-rails-test-#{Random.bytes(8).unpack1('h*')}"
|
37
|
+
end
|
56
38
|
|
57
|
-
|
58
|
-
|
59
|
-
cmd = 'docker-compose up -d pg'
|
60
|
-
run_cmd cmd
|
61
|
-
wait_for_container 'pg'
|
39
|
+
def bundle
|
40
|
+
return if @bundled
|
62
41
|
|
63
|
-
|
64
|
-
|
65
|
-
end
|
42
|
+
run_cmd 'bundle'
|
43
|
+
@bundled = true
|
66
44
|
end
|
67
45
|
|
68
|
-
|
69
|
-
if
|
70
|
-
|
71
|
-
|
72
|
-
|
46
|
+
def prepare_db
|
47
|
+
return if @db_prepared
|
48
|
+
|
49
|
+
bundle
|
50
|
+
run_cmd './bin/rake db:create db:schema:load'
|
51
|
+
@db_prepared = true
|
52
|
+
at_exit { drop_db }
|
53
|
+
end
|
54
|
+
|
55
|
+
def drop_db
|
56
|
+
return unless @db_prepared
|
57
|
+
|
58
|
+
run_cmd './bin/rake db:drop'
|
59
|
+
@db_prepared = false
|
73
60
|
end
|
74
|
-
end
|
75
61
|
|
76
|
-
shared_context 'rails integration test setup' do
|
77
62
|
def tmpdir
|
78
|
-
'tmp
|
63
|
+
@tmpdir ||= File.join(fixture_dir, 'tmp')
|
64
|
+
end
|
65
|
+
|
66
|
+
def run_specs
|
67
|
+
return if @specs_ran or use_existing_data?
|
68
|
+
|
69
|
+
prepare_db
|
70
|
+
FileUtils.rm_rf tmpdir
|
71
|
+
run_cmd \
|
72
|
+
'./bin/rspec spec/controllers/users_controller_spec.rb spec/controllers/users_controller_api_spec.rb',
|
73
|
+
'APPMAP' => 'true'
|
74
|
+
@specs_ran = true
|
75
|
+
end
|
76
|
+
|
77
|
+
def self.for_fixture(fixture_dir)
|
78
|
+
@apps ||= {}
|
79
|
+
@apps[fixture_dir] ||= TestRailsApp.new fixture_dir
|
79
80
|
end
|
80
81
|
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
82
|
+
protected
|
83
|
+
|
84
|
+
def run_process(method, cmd, env, options = {})
|
85
|
+
Bundler.with_clean_env do
|
86
|
+
method.call \
|
87
|
+
env.merge('TEST_DATABASE' => database_name),
|
88
|
+
cmd,
|
89
|
+
options.merge(chdir: fixture_dir)
|
87
90
|
end
|
88
91
|
end
|
92
|
+
end
|
93
|
+
|
94
|
+
shared_context 'Rails app pg database' do |dir|
|
95
|
+
before(:all) { @app = TestRailsApp.for_fixture dir }
|
96
|
+
let(:app) { @app }
|
97
|
+
end
|
98
|
+
|
99
|
+
shared_context 'rails integration test setup' do
|
100
|
+
let(:tmpdir) { app.tmpdir }
|
101
|
+
|
102
|
+
before(:all) { @app.run_specs } unless use_existing_data?
|
89
103
|
|
90
|
-
let(:appmap) { JSON.parse File.read File.join tmpdir, 'appmap/rspec', appmap_json_file }
|
91
104
|
let(:appmap_json_path) { File.join(tmpdir, 'appmap/rspec', appmap_json_file) }
|
92
105
|
let(:appmap) { JSON.parse File.read(appmap_json_path) }
|
93
106
|
let(:events) { appmap['events'] }
|
data/spec/rails_test_spec.rb
CHANGED
@@ -3,7 +3,7 @@ require 'rails_spec_helper'
|
|
3
3
|
# Rails5 doesn't work with Ruby 3.x, Rails 7 doesn't work with Ruby < 2.7.
|
4
4
|
def default_rails_versions
|
5
5
|
if testing_ruby_2?
|
6
|
-
if Gem::Requirement.create('>= 2.7') =~ Gem::Version.new(
|
6
|
+
if Gem::Requirement.create('>= 2.7') =~ Gem::Version.new(RUBY_VERSION)
|
7
7
|
[ 5, 6, 7 ]
|
8
8
|
else
|
9
9
|
[ 5, 6 ]
|
@@ -21,24 +21,14 @@ describe 'Rails' do
|
|
21
21
|
rails_versions.each do |rails_major_version| # rubocop:disable Metrics/BlockLength
|
22
22
|
context "#{rails_major_version}" do
|
23
23
|
include_context 'Rails app pg database', "spec/fixtures/rails#{rails_major_version}_users_app" unless use_existing_data?
|
24
|
-
def tmpdir
|
25
|
-
'tmp/spec/rails_test_spec'
|
26
|
-
end
|
27
|
-
before(:all) do
|
28
|
-
FileUtils.rm_rf tmpdir
|
29
|
-
FileUtils.mkdir_p tmpdir
|
30
|
-
end
|
31
|
-
|
32
|
-
def run_tests
|
33
|
-
cmd = <<~CMD.gsub "\n", ' '
|
34
|
-
docker-compose run --rm -e RAILS_ENV=test -e APPMAP=true -e TEST_OPTS=--verbose
|
35
|
-
-v #{File.absolute_path tmpdir}:/app/tmp app bundle exec rake
|
36
|
-
CMD
|
37
|
-
run_cmd cmd, chdir: fixture_dir
|
38
|
-
end
|
39
24
|
|
40
25
|
it 'runs tests with APPMAP=true' do
|
41
|
-
|
26
|
+
app.prepare_db
|
27
|
+
app.run_cmd \
|
28
|
+
'bundle exec rake',
|
29
|
+
'RAILS_ENV' => 'test',
|
30
|
+
'APPMAP' => 'true',
|
31
|
+
'TEST_OPTS' => '--verbose'
|
42
32
|
end
|
43
33
|
end
|
44
34
|
end
|
data/spec/railtie_spec.rb
CHANGED
@@ -1,27 +1,13 @@
|
|
1
1
|
require 'rails_spec_helper'
|
2
2
|
|
3
3
|
describe 'AppMap tracer via Railtie' do
|
4
|
-
include_context 'Rails app pg database', 'spec/fixtures/rails6_users_app' do
|
4
|
+
include_context 'Rails app pg database', 'spec/fixtures/rails6_users_app' do
|
5
5
|
let(:env) { {} }
|
6
6
|
|
7
|
-
let(:
|
8
|
-
|
9
|
-
|
10
|
-
Open3.capture3(env, cmd, chdir: fixture_dir).tap do |result|
|
11
|
-
unless result[2] == 0
|
12
|
-
warn <<~STDERR
|
13
|
-
Failed to run rails6_users_app container
|
14
|
-
<<< Output:
|
15
|
-
#{result[0]}
|
16
|
-
#{result[1]}
|
17
|
-
>>> End of output
|
18
|
-
STDERR
|
19
|
-
raise 'Failed to run rails6_users_app container'
|
20
|
-
end
|
21
|
-
end
|
7
|
+
let(:command_output) do
|
8
|
+
app.prepare_db
|
9
|
+
app.capture_cmd(%{./bin/rails r "puts AppMap.instance_variable_get('@configuration').nil?"}, env).strip
|
22
10
|
end
|
23
|
-
let(:command_output) { command_capture2[0].strip }
|
24
|
-
let(:command_result) { command_capture2[2] }
|
25
11
|
|
26
12
|
describe 'with APPMAP=false' do
|
27
13
|
let(:env) { { 'APPMAP' => 'false' } }
|
@@ -2,89 +2,58 @@ require 'rails_spec_helper'
|
|
2
2
|
|
3
3
|
describe 'SQL events' do
|
4
4
|
include_context 'Rails app pg database', 'spec/fixtures/rails6_users_app' do
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
example.run
|
12
|
-
end
|
13
|
-
|
14
|
-
let(:tmpdir) { "tmp/spec/record_sql_rails_pg_spec" }
|
15
|
-
|
16
|
-
describe 'fields' do
|
17
|
-
let(:test_line_number) { 8 }
|
18
|
-
let(:appmap_json) { File.join(tmpdir, 'appmap/rspec/Api_UsersController_POST_api_users_with_required_parameters_creates_a_user.appmap.json') }
|
19
|
-
let(:orm_module) { 'sequel' }
|
20
|
-
let(:appmap) { JSON.parse(File.read(appmap_json)) }
|
21
|
-
describe 'on a call event' do
|
22
|
-
let(:event) do
|
23
|
-
appmap['events'].find do |event|
|
24
|
-
event['event'] == 'call' &&
|
25
|
-
event.keys.include?('sql_query')
|
5
|
+
def self.check_queries(cases)
|
6
|
+
cases.each do |test_case, query|
|
7
|
+
context "in #{test_case}" do
|
8
|
+
let(:test_case) { test_case }
|
9
|
+
it "captures #{query}" do
|
10
|
+
expect(sql_events).to include sql_query query
|
26
11
|
end
|
27
12
|
end
|
28
|
-
it 'do not include function-only fields' do
|
29
|
-
expect(event.keys).to_not include('defined_class')
|
30
|
-
expect(event.keys).to_not include('method_id')
|
31
|
-
expect(event.keys).to_not include('path')
|
32
|
-
expect(event.keys).to_not include('lineno')
|
33
|
-
end
|
34
13
|
end
|
35
14
|
end
|
36
15
|
|
37
|
-
|
38
|
-
|
39
|
-
context 'while creating a new record' do
|
40
|
-
let(:test_line_number) { 8 }
|
41
|
-
let(:appmap_json) { File.join(tmpdir, 'appmap/rspec/Api_UsersController_POST_api_users_with_required_parameters_creates_a_user.appmap.json') }
|
16
|
+
context 'with Sequel' do
|
17
|
+
before(:context) { run_specs 'sequel' }
|
42
18
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
SQL_QUERY
|
50
|
-
end
|
51
|
-
end
|
52
|
-
context 'using ActiveRecord ORM' do
|
53
|
-
let(:orm_module) { 'activerecord' }
|
54
|
-
it 'detects the sql INSERT' do
|
55
|
-
expect(appmap).to include(<<-SQL_QUERY.strip)
|
56
|
-
sql_query:
|
57
|
-
sql: INSERT INTO "users" ("login") VALUES ($1) RETURNING "id"
|
58
|
-
SQL_QUERY
|
59
|
-
end
|
60
|
-
end
|
61
|
-
end
|
19
|
+
check_queries(
|
20
|
+
'Api_UsersController_POST_api_users_with_required_parameters_creates_a_user' =>
|
21
|
+
%(INSERT INTO "users" ("login") VALUES ('alice') RETURNING *),
|
22
|
+
'Api_UsersController_GET_api_users_lists_the_users' => %(SELECT * FROM "users")
|
23
|
+
)
|
24
|
+
end
|
62
25
|
|
63
|
-
|
64
|
-
|
65
|
-
let(:appmap_json) { File.join(tmpdir, 'appmap/rspec/Api_UsersController_GET_api_users_lists_the_users.appmap.json') }
|
26
|
+
context 'with ActiveRecord' do
|
27
|
+
before(:context) { run_specs 'activerecord' }
|
66
28
|
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
29
|
+
check_queries(
|
30
|
+
'Api_UsersController_POST_api_users_with_required_parameters_creates_a_user' =>
|
31
|
+
%(INSERT INTO "users" ("login") VALUES ($1) RETURNING "id"),
|
32
|
+
'Api_UsersController_GET_api_users_lists_the_users' => %(SELECT "users".* FROM "users")
|
33
|
+
)
|
34
|
+
end
|
35
|
+
|
36
|
+
def run_specs(orm_module)
|
37
|
+
@app.prepare_db
|
38
|
+
@app.run_cmd \
|
39
|
+
'./bin/rspec spec/controllers/users_controller_api_spec.rb:8 spec/controllers/users_controller_api_spec.rb:29',
|
40
|
+
'ORM_MODULE' => orm_module,
|
41
|
+
'RAILS_ENV' => 'test',
|
42
|
+
'APPMAP' => 'true'
|
43
|
+
end
|
44
|
+
|
45
|
+
let(:appmap_json) { File.join tmpdir, "appmap/rspec/#{test_case}.appmap.json" }
|
46
|
+
let(:appmap) { JSON.parse(File.read(appmap_json)) }
|
47
|
+
let(:tmpdir) { app.tmpdir }
|
48
|
+
let(:sql_events) { appmap['events'].select { |ev| ev.include? 'sql_query' } }
|
49
|
+
|
50
|
+
RSpec::Matchers.define_negated_matcher :not_include, :include
|
51
|
+
def sql_query(query)
|
52
|
+
(include('sql_query' => (include 'sql' => query)))
|
53
|
+
.and(not_include('defined_class'))
|
54
|
+
.and(not_include('method_id'))
|
55
|
+
.and(not_include('path'))
|
56
|
+
.and(not_include('lineno'))
|
88
57
|
end
|
89
58
|
end
|
90
59
|
end
|
@@ -1,41 +1,27 @@
|
|
1
1
|
require 'rails_spec_helper'
|
2
|
+
|
3
|
+
require 'random-port'
|
4
|
+
|
2
5
|
require 'net/http'
|
3
6
|
require 'socket'
|
4
7
|
|
5
8
|
describe 'remote recording', :order => :defined do
|
6
9
|
include_context 'Rails app pg database', 'spec/fixtures/rails6_users_app' do
|
7
10
|
before(:all) do
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
11
|
+
@service_port = RandomPort::Pool::SINGLETON.acquire
|
12
|
+
@app.prepare_db
|
13
|
+
@server = @app.spawn_cmd \
|
14
|
+
"./bin/rails server -p #{@service_port}",
|
15
|
+
'ORM_MODULE' => 'sequel',
|
16
|
+
'APPMAP' => 'true'
|
14
17
|
|
15
|
-
port_cmd = 'docker-compose port app 3000'
|
16
|
-
port_out, = run_cmd port_cmd, chdir: fixture_dir
|
17
|
-
@service_port = port_out.strip.split(':')[1]
|
18
|
-
|
19
|
-
service_running = false
|
20
|
-
retry_count = 0
|
21
18
|
uri = URI("http://localhost:#{@service_port}/health")
|
22
19
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
end
|
29
|
-
|
30
|
-
status = res.response.code.to_i
|
31
|
-
service_running = true if status >= 200 && status < 300
|
32
|
-
|
33
|
-
# give up after a certain error threshold is met
|
34
|
-
# we don't want to wait forever if there's an unrecoverable issue
|
35
|
-
raise 'gave up waiting on fixture service' if (retry_count += 1) == 10
|
36
|
-
rescue Errno::ETIMEDOUT, Errno::ECONNRESET, EOFError
|
37
|
-
$stderr.print('.')
|
38
|
-
end
|
20
|
+
100.times do
|
21
|
+
Net::HTTP.get(uri)
|
22
|
+
break
|
23
|
+
rescue Errno::ECONNREFUSED
|
24
|
+
sleep 0.1
|
39
25
|
end
|
40
26
|
end
|
41
27
|
|
@@ -44,8 +30,10 @@ describe 'remote recording', :order => :defined do
|
|
44
30
|
end
|
45
31
|
|
46
32
|
after(:all) do
|
47
|
-
|
48
|
-
|
33
|
+
if @server
|
34
|
+
Process.kill 'INT', @server
|
35
|
+
Process.wait @server
|
36
|
+
end
|
49
37
|
end
|
50
38
|
|
51
39
|
let(:service_address) { URI("http://localhost:#{@service_port}") }
|
data/spec/spec_helper.rb
CHANGED
@@ -13,6 +13,7 @@ require 'appmap'
|
|
13
13
|
|
14
14
|
RSpec.configure do |config|
|
15
15
|
config.example_status_persistence_file_path = "tmp/rspec_failed_examples.txt"
|
16
|
+
config.profile_examples = true
|
16
17
|
end
|
17
18
|
|
18
19
|
# Re-run the Rails specs without re-generating the data. This is useful for efficiently enhancing and
|