appmap 0.41.1 → 0.44.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.
Files changed (57) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +17 -2
  3. data/CHANGELOG.md +31 -0
  4. data/README.md +54 -22
  5. data/appmap.gemspec +0 -2
  6. data/lib/appmap.rb +3 -2
  7. data/lib/appmap/class_map.rb +7 -10
  8. data/lib/appmap/config.rb +94 -34
  9. data/lib/appmap/cucumber.rb +1 -1
  10. data/lib/appmap/event.rb +18 -0
  11. data/lib/appmap/hook.rb +42 -22
  12. data/lib/appmap/hook/method.rb +1 -1
  13. data/lib/appmap/minitest.rb +35 -30
  14. data/lib/appmap/rails/request_handler.rb +41 -10
  15. data/lib/appmap/record.rb +1 -1
  16. data/lib/appmap/rspec.rb +32 -96
  17. data/lib/appmap/util.rb +16 -0
  18. data/lib/appmap/version.rb +1 -1
  19. data/patch +1447 -0
  20. data/spec/abstract_controller_base_spec.rb +69 -26
  21. data/spec/class_map_spec.rb +3 -11
  22. data/spec/config_spec.rb +31 -1
  23. data/spec/fixtures/hook/custom_instance_method.rb +11 -0
  24. data/spec/fixtures/hook/method_named_call.rb +11 -0
  25. data/spec/fixtures/rails5_users_app/Gemfile +7 -3
  26. data/spec/fixtures/rails5_users_app/app/controllers/api/users_controller.rb +2 -0
  27. data/spec/fixtures/rails5_users_app/app/controllers/users_controller.rb +9 -1
  28. data/spec/fixtures/rails5_users_app/config/application.rb +2 -0
  29. data/spec/fixtures/rails5_users_app/create_app +8 -2
  30. data/spec/fixtures/rails5_users_app/docker-compose.yml +3 -0
  31. data/spec/fixtures/rails5_users_app/spec/controllers/users_controller_api_spec.rb +16 -3
  32. data/spec/fixtures/rails5_users_app/spec/controllers/users_controller_spec.rb +2 -2
  33. data/spec/fixtures/rails5_users_app/spec/models/user_spec.rb +2 -12
  34. data/spec/fixtures/rails5_users_app/spec/rails_helper.rb +3 -9
  35. data/spec/fixtures/rails6_users_app/Gemfile +5 -4
  36. data/spec/fixtures/rails6_users_app/app/controllers/api/users_controller.rb +1 -0
  37. data/spec/fixtures/rails6_users_app/app/controllers/users_controller.rb +9 -1
  38. data/spec/fixtures/rails6_users_app/config/application.rb +2 -0
  39. data/spec/fixtures/rails6_users_app/create_app +8 -2
  40. data/spec/fixtures/rails6_users_app/docker-compose.yml +3 -0
  41. data/spec/fixtures/rails6_users_app/spec/controllers/users_controller_api_spec.rb +16 -3
  42. data/spec/fixtures/rails6_users_app/spec/controllers/users_controller_spec.rb +2 -2
  43. data/spec/fixtures/rails6_users_app/spec/models/user_spec.rb +2 -12
  44. data/spec/fixtures/rails6_users_app/spec/rails_helper.rb +3 -9
  45. data/spec/hook_spec.rb +134 -10
  46. data/spec/record_sql_rails_pg_spec.rb +1 -1
  47. data/spec/spec_helper.rb +6 -0
  48. data/test/expectations/openssl_test_key_sign1.json +2 -4
  49. data/test/fixtures/rspec_recorder/spec/decorated_hello_spec.rb +3 -3
  50. data/test/fixtures/rspec_recorder/spec/plain_hello_spec.rb +1 -1
  51. data/test/gem_test.rb +1 -1
  52. data/test/minitest_test.rb +1 -2
  53. data/test/rspec_test.rb +1 -20
  54. metadata +7 -8
  55. data/exe/appmap +0 -154
  56. data/spec/rspec_feature_metadata_spec.rb +0 -31
  57. data/test/cli_test.rb +0 -116
@@ -1,7 +1,6 @@
1
1
  source 'https://rubygems.org'
2
2
  git_source(:github) { |repo| "https://github.com/#{repo}.git" }
3
3
 
4
- # Bundle edge Rails instead: gem 'rails', github: 'rails/rails'
5
4
  gem 'rails', '~> 6'
6
5
 
7
6
  gem 'haml-rails'
@@ -40,12 +39,14 @@ group :development, :test do
40
39
  gem 'appmap', appmap_options
41
40
  gem 'cucumber-rails', require: false
42
41
  gem 'rspec-rails'
43
- # Required for Sequel, since without ActiveRecord, the Rails transactional fixture support
44
- # isn't activated.
45
- gem 'database_cleaner'
46
42
  # Call 'byebug' anywhere in the code to stop execution and get a debugger console
47
43
  gem 'pry-byebug'
48
44
  end
49
45
 
46
+ group :test do
47
+ gem 'database_cleaner-active_record', require: false
48
+ gem 'database_cleaner-sequel', require: false
49
+ end
50
+
50
51
  group :development do
51
52
  end
@@ -6,6 +6,7 @@ module Api
6
6
  end
7
7
 
8
8
  def create
9
+ params = self.params.key?(:user) ? self.params[:user] : self.params
9
10
  @user = build_user(params.slice(:login).to_unsafe_h)
10
11
  unless @user.valid?
11
12
  error = {
@@ -4,7 +4,15 @@ class UsersController < ApplicationController
4
4
  end
5
5
 
6
6
  def show
7
- if (@user = User[login: params[:id]])
7
+ find_user = lambda do |id|
8
+ if User.respond_to?(:[])
9
+ User[login: id]
10
+ else
11
+ User.find_by_login!(id)
12
+ end
13
+ end
14
+
15
+ if (@user = find_user.(params[:id]))
8
16
  render plain: @user
9
17
  else
10
18
  render plain: 'Not found', status: 404
@@ -15,8 +15,10 @@ case orm_module
15
15
  when 'sequel'
16
16
  require 'sequel-rails'
17
17
  require 'sequel_secure_password'
18
+ require 'database_cleaner-sequel' if Rails.env.test?
18
19
  when 'activerecord'
19
20
  require 'active_record/railtie'
21
+ require 'database_cleaner-active_record' if Rails.env.test?
20
22
  end
21
23
 
22
24
  require 'appmap/railtie' if defined?(AppMap)
@@ -16,11 +16,17 @@ if [[ $? != 0 ]]; then
16
16
  exit 1
17
17
  fi
18
18
 
19
- psql -h pg -U postgres -c "create database app_development"
20
- psql -h pg -U postgres -c "create database app_test"
19
+ # Required for migrations
20
+ export ORM_MODULE=sequel
21
21
 
22
+ set +e
23
+ psql -h pg -U postgres -c "drop database if exists app_development"
24
+ psql -h pg -U postgres -c "drop database if exists app_test"
22
25
  set -e
23
26
 
27
+ psql -h pg -U postgres -c "create database app_development"
28
+ psql -h pg -U postgres -c "create database app_test"
29
+
24
30
  RAILS_ENV=development ./bin/rake db:migrate
25
31
  RAILS_ENV=test ./bin/rake db:migrate
26
32
 
@@ -19,6 +19,9 @@ services:
19
19
  environment:
20
20
  RAILS_ENV:
21
21
  ORM_MODULE:
22
+ PGHOST: pg
23
+ PGPORT: '5432'
24
+ DATABASE_URL: postgres://postgres@pg
22
25
  APPMAP:
23
26
  volumes:
24
27
  - .:/src/app
@@ -1,13 +1,19 @@
1
1
  require 'rails_helper'
2
2
  require 'rack/test'
3
3
 
4
- RSpec.describe Api::UsersController, feature_group: 'Users', type: :controller, appmap: true do
5
- describe 'POST /api/users', feature: 'Create a user' do
4
+ RSpec.describe Api::UsersController, type: :controller do
5
+ describe 'POST /api/users' do
6
6
  describe 'with required parameters' do
7
7
  it 'creates a user' do
8
8
  post :create, params: { login: 'alice', password: 'foobar' }
9
9
  expect(response.status).to eq(201)
10
10
  end
11
+ describe 'with object-style parameters' do
12
+ it 'creates a user' do
13
+ post :create, params: { user: { login: 'alice', password: 'foobar' } }
14
+ expect(response.status).to eq(201)
15
+ end
16
+ end
11
17
  end
12
18
  describe 'with a missing parameter' do
13
19
  it 'reports error 422' do
@@ -16,7 +22,7 @@ RSpec.describe Api::UsersController, feature_group: 'Users', type: :controller,
16
22
  end
17
23
  end
18
24
  end
19
- describe 'GET /api/users', feature: 'List users' do
25
+ describe 'GET /api/users' do
20
26
  before do
21
27
  post :create, params: { login: 'alice' }
22
28
  end
@@ -25,5 +31,12 @@ RSpec.describe Api::UsersController, feature_group: 'Users', type: :controller,
25
31
  users = JSON.parse(response.body)
26
32
  expect(users.map { |r| r['login'] }).to include('alice')
27
33
  end
34
+ describe 'with a custom header' do
35
+ it 'lists the users' do
36
+ request.headers['X-Sandwich'] = 'turkey'
37
+ get :index, params: {}
38
+ expect(response.status).to eq(200)
39
+ end
40
+ end
28
41
  end
29
42
  end
@@ -4,7 +4,7 @@ require 'rack/test'
4
4
  RSpec.describe UsersController, type: :controller do
5
5
  render_views
6
6
 
7
- describe 'GET /users', feature: 'Show all users' do
7
+ describe 'GET /users' do
8
8
  before do
9
9
  User.create login: 'alice'
10
10
  end
@@ -14,7 +14,7 @@ RSpec.describe UsersController, type: :controller do
14
14
  end
15
15
  end
16
16
 
17
- describe 'GET /users/:login', feature: 'Show a user' do
17
+ describe 'GET /users/:login' do
18
18
  before do
19
19
  User.create login: 'alice'
20
20
  end
@@ -1,6 +1,6 @@
1
1
  require 'rails_helper'
2
2
 
3
- describe User, feature_group: 'User', appmap: true do
3
+ describe User do
4
4
  # TODO: appmap/rspec doesn't handle shared_examples_for 100% correctly yet.
5
5
  # In my tests, only one of these two tests will be emitted as an appmap.
6
6
  shared_examples_for 'creates the user' do |username|
@@ -11,17 +11,7 @@ describe User, feature_group: 'User', appmap: true do
11
11
  end
12
12
  end
13
13
 
14
- describe 'creation', feature: 'Create a user' do
15
- context 'using shared_examples_for' do
16
- # AppMap.
17
- # context "with username 'alice'" do
18
- # it_should_behave_like 'creates the user', 'alice'
19
- # end
20
- # context "with username 'bob'" do
21
- # it_should_behave_like 'creates the user', 'bob'
22
- # end
23
- end
24
-
14
+ describe 'creation' do
25
15
  # So, instead of shared_examples_for, let's go with a simple method
26
16
  # containing the assertions. The method can be called from within an example.
27
17
  def save_and_verify
@@ -45,7 +45,6 @@ RSpec.configure do |config|
45
45
  # arbitrary gems may also be filtered via:
46
46
  # config.filter_gems_from_backtrace("gem name")
47
47
 
48
-
49
48
  DatabaseCleaner.allow_remote_database_url = true
50
49
 
51
50
  config.before(:suite) do
@@ -54,13 +53,8 @@ RSpec.configure do |config|
54
53
  end
55
54
 
56
55
  config.around :each do |example|
57
- # Enable the use of 'return' from a guard
58
- -> {
59
- return example.run unless %i[model controller].member?(example.metadata[:type])
60
-
61
- DatabaseCleaner.cleaning do
62
- example.run
63
- end
64
- }.call
56
+ DatabaseCleaner.cleaning do
57
+ example.run
58
+ end
65
59
  end
66
60
  end
data/spec/hook_spec.rb CHANGED
@@ -64,13 +64,144 @@ describe 'AppMap class Hooking', docker: false do
64
64
  it 'excludes named classes and methods' do
65
65
  load 'spec/fixtures/hook/exclude.rb'
66
66
  package = AppMap::Config::Package.build_from_path('spec/fixtures/hook/exclude.rb')
67
- config = AppMap::Config.new('hook_spec', [ package ], %w[ExcludeTest])
67
+ config = AppMap::Config.new('hook_spec', [ package ], exclude: %w[ExcludeTest])
68
68
  AppMap.configuration = config
69
69
 
70
70
  expect(config.never_hook?(ExcludeTest.new.method(:instance_method))).to be_truthy
71
71
  expect(config.never_hook?(ExcludeTest.method(:cls_method))).to be_truthy
72
72
  end
73
73
 
74
+ it "handles an instance method named 'call' without issues" do
75
+ events_yaml = <<~YAML
76
+ ---
77
+ - :id: 1
78
+ :event: :call
79
+ :defined_class: MethodNamedCall
80
+ :method_id: call
81
+ :path: spec/fixtures/hook/method_named_call.rb
82
+ :lineno: 8
83
+ :static: false
84
+ :parameters:
85
+ - :name: :a
86
+ :class: Integer
87
+ :value: '1'
88
+ :kind: :req
89
+ - :name: :b
90
+ :class: Integer
91
+ :value: '2'
92
+ :kind: :req
93
+ - :name: :c
94
+ :class: Integer
95
+ :value: '3'
96
+ :kind: :req
97
+ - :name: :d
98
+ :class: Integer
99
+ :value: '4'
100
+ :kind: :req
101
+ - :name: :e
102
+ :class: Integer
103
+ :value: '5'
104
+ :kind: :req
105
+ :receiver:
106
+ :class: MethodNamedCall
107
+ :value: MethodNamedCall
108
+ - :id: 2
109
+ :event: :return
110
+ :parent_id: 1
111
+ :return_value:
112
+ :class: String
113
+ :value: 1 2 3 4 5
114
+ YAML
115
+
116
+ _, tracer = test_hook_behavior 'spec/fixtures/hook/method_named_call.rb', events_yaml do
117
+ expect(MethodNamedCall.new.call(1, 2, 3, 4, 5)).to eq('1 2 3 4 5')
118
+ end
119
+ class_map = AppMap.class_map(tracer.event_methods)
120
+ expect(Diffy::Diff.new(<<~CLASSMAP, YAML.dump(class_map)).to_s).to eq('')
121
+ ---
122
+ - :name: spec/fixtures/hook/method_named_call.rb
123
+ :type: package
124
+ :children:
125
+ - :name: MethodNamedCall
126
+ :type: class
127
+ :children:
128
+ - :name: call
129
+ :type: function
130
+ :location: spec/fixtures/hook/method_named_call.rb:8
131
+ :static: false
132
+ CLASSMAP
133
+ end
134
+
135
+ it 'can custom hook and label a function' do
136
+ events_yaml = <<~YAML
137
+ ---
138
+ - :id: 1
139
+ :event: :call
140
+ :defined_class: CustomInstanceMethod
141
+ :method_id: say_default
142
+ :path: spec/fixtures/hook/custom_instance_method.rb
143
+ :lineno: 8
144
+ :static: false
145
+ :parameters: []
146
+ :receiver:
147
+ :class: CustomInstanceMethod
148
+ :value: CustomInstance Method fixture
149
+ - :id: 2
150
+ :event: :return
151
+ :parent_id: 1
152
+ :return_value:
153
+ :class: String
154
+ :value: default
155
+ YAML
156
+
157
+ config = AppMap::Config.load({
158
+ functions: [
159
+ {
160
+ package: 'hook_spec',
161
+ class: 'CustomInstanceMethod',
162
+ functions: [ :say_default ],
163
+ labels: ['cowsay']
164
+ }
165
+ ]
166
+ }.deep_stringify_keys)
167
+
168
+ load 'spec/fixtures/hook/custom_instance_method.rb'
169
+ hook_cls = CustomInstanceMethod
170
+ method = hook_cls.instance_method(:say_default)
171
+
172
+ require 'appmap/hook/method'
173
+ hook_method = AppMap::Hook::Method.new(config.package_for_method(method), hook_cls, method)
174
+ hook_method.activate
175
+
176
+ tracer = AppMap.tracing.trace
177
+ AppMap::Event.reset_id_counter
178
+ begin
179
+ expect(CustomInstanceMethod.new.say_default).to eq('default')
180
+ ensure
181
+ AppMap.tracing.delete(tracer)
182
+ end
183
+
184
+ events = collect_events(tracer).to_yaml
185
+
186
+ expect(Diffy::Diff.new(events_yaml, events).to_s).to eq('')
187
+ class_map = AppMap.class_map(tracer.event_methods)
188
+ expect(Diffy::Diff.new(<<~CLASSMAP, YAML.dump(class_map)).to_s).to eq('')
189
+ ---
190
+ - :name: hook_spec
191
+ :type: package
192
+ :children:
193
+ - :name: CustomInstanceMethod
194
+ :type: class
195
+ :children:
196
+ - :name: say_default
197
+ :type: function
198
+ :location: spec/fixtures/hook/custom_instance_method.rb:8
199
+ :static: false
200
+ :labels:
201
+ - cowsay
202
+ CLASSMAP
203
+ end
204
+
74
205
  it 'parses labels from comments' do
75
206
  _, tracer = invoke_test_file 'spec/fixtures/hook/labels.rb' do
76
207
  ClassWithLabel.new.fn_with_label
@@ -91,9 +222,6 @@ describe 'AppMap class Hooking', docker: false do
91
222
  :labels:
92
223
  - has-fn-label
93
224
  :comment: "# @label has-fn-label\\n"
94
- :source: |2
95
- def fn_with_label
96
- end
97
225
  YAML
98
226
  end
99
227
 
@@ -148,10 +276,6 @@ describe 'AppMap class Hooking', docker: false do
148
276
  :type: function
149
277
  :location: spec/fixtures/hook/instance_method.rb:8
150
278
  :static: false
151
- :source: |2
152
- def say_default
153
- 'default'
154
- end
155
279
  YAML
156
280
  end
157
281
 
@@ -746,6 +870,7 @@ describe 'AppMap class Hooking', docker: false do
746
870
  end
747
871
  secure_compare_event = YAML.load(events).find { |evt| evt[:defined_class] == 'ActiveSupport::SecurityUtils' }
748
872
  secure_compare_event.delete(:lineno)
873
+ secure_compare_event.delete(:path)
749
874
 
750
875
  expect(Diffy::Diff.new(<<~YAML, secure_compare_event.to_yaml).to_s).to eq('')
751
876
  ---
@@ -753,7 +878,6 @@ describe 'AppMap class Hooking', docker: false do
753
878
  :event: :call
754
879
  :defined_class: ActiveSupport::SecurityUtils
755
880
  :method_id: secure_compare
756
- :path: lib/active_support/security_utils.rb
757
881
  :static: true
758
882
  :parameters:
759
883
  - :name: :a
@@ -837,7 +961,7 @@ describe 'AppMap class Hooking', docker: false do
837
961
  entry = cm[1][:children][0][:children][0][:children][0]
838
962
  # Sanity check, make sure we got the right one
839
963
  expect(entry[:name]).to eq('secure_compare')
840
- expect(entry[:labels]).to eq(%w[provider.secure_compare])
964
+ expect(entry[:labels]).to eq(%w[crypto.secure_compare])
841
965
  end
842
966
  end
843
967
 
@@ -61,7 +61,7 @@ describe 'SQL events' do
61
61
  end
62
62
 
63
63
  context 'while listing records' do
64
- let(:test_line_number) { 23 }
64
+ let(:test_line_number) { 29 }
65
65
  let(:appmap_json) { File.join(tmpdir, 'appmap/rspec/Api_UsersController_GET_api_users_lists_the_users.appmap.json') }
66
66
 
67
67
  context 'using Sequel ORM' do
data/spec/spec_helper.rb CHANGED
@@ -14,3 +14,9 @@ require 'appmap'
14
14
  RSpec.configure do |config|
15
15
  config.example_status_persistence_file_path = "tmp/rspec_failed_examples.txt"
16
16
  end
17
+
18
+ # Re-run the Rails specs without re-generating the data. This is useful for efficiently enhancing and
19
+ # debugging the test itself.
20
+ def use_existing_data?
21
+ ENV['USE_EXISTING_DATA'] == 'true'
22
+ end
@@ -11,8 +11,7 @@
11
11
  "name": "sign",
12
12
  "type": "function",
13
13
  "location": "lib/openssl_key_sign.rb:10",
14
- "static": true,
15
- "source": " def Example.sign\n key = OpenSSL::PKey::RSA.new 2048\n\n document = 'the document'\n\n digest = OpenSSL::Digest::SHA256.new\n key.sign digest, document\n end\n"
14
+ "static": true
16
15
  }
17
16
  ]
18
17
  }
@@ -40,8 +39,7 @@
40
39
  "location": "OpenSSL::PKey::PKey#sign",
41
40
  "static": false,
42
41
  "labels": [
43
- "security",
44
- "crypto"
42
+ "crypto.pkey"
45
43
  ]
46
44
  }
47
45
  ]
@@ -2,7 +2,7 @@ require 'rspec'
2
2
  require 'appmap/rspec'
3
3
  require 'hello'
4
4
 
5
- describe Hello, feature_group: 'Saying hello' do
5
+ describe Hello do
6
6
  before do
7
7
  # Trick appmap-ruby into thinking we're a Rails app.
8
8
  stub_const('Rails', double('rails', version: 'fake.0'))
@@ -11,11 +11,11 @@ describe Hello, feature_group: 'Saying hello' do
11
11
  # The order of these examples is important. The tests check the
12
12
  # appmap for 'says hello', and we want another example to get run
13
13
  # before it.
14
- it 'does not say goodbye', feature: 'Speak hello', appmap: true do
14
+ it 'does not say goodbye' do
15
15
  expect(Hello.new.say_hello).not_to eq('Goodbye!')
16
16
  end
17
17
 
18
- it 'says hello', feature: 'Speak hello', appmap: true do
18
+ it 'says hello' do
19
19
  expect(Hello.new.say_hello).to eq('Hello!')
20
20
  end
21
21
  end