appmap 0.41.1 → 0.44.0

Sign up to get free protection for your applications and to get access to all the features.
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