appmap 0.59.2 → 0.62.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 (56) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +6 -16
  3. data/ARCHITECTURE.md +68 -0
  4. data/CHANGELOG.md +37 -0
  5. data/exe/appmap-agent-validate +19 -0
  6. data/exe/appmap-index +7 -0
  7. data/lib/appmap.rb +2 -0
  8. data/lib/appmap/agent.rb +0 -11
  9. data/lib/appmap/command/agent_setup/status.rb +1 -1
  10. data/lib/appmap/command/agent_setup/validate.rb +24 -0
  11. data/lib/appmap/command/index.rb +25 -0
  12. data/lib/appmap/command/inspect.rb +0 -1
  13. data/lib/appmap/config.rb +8 -1
  14. data/lib/appmap/depends.rb +2 -0
  15. data/lib/appmap/depends/api.rb +84 -0
  16. data/lib/appmap/depends/configuration.rb +59 -0
  17. data/lib/appmap/depends/node_cli.rb +44 -0
  18. data/lib/appmap/depends/rake_tasks.rb +58 -0
  19. data/lib/appmap/depends/test_file_inspector.rb +89 -0
  20. data/lib/appmap/depends/test_runner.rb +106 -0
  21. data/lib/appmap/depends/util.rb +34 -0
  22. data/lib/appmap/service/config_analyzer.rb +7 -8
  23. data/lib/appmap/service/integration_test_path_finder.rb +29 -25
  24. data/lib/appmap/service/test_command_provider.rb +5 -7
  25. data/lib/appmap/service/validator/config_validator.rb +89 -0
  26. data/lib/appmap/service/validator/violation.rb +50 -0
  27. data/lib/appmap/version.rb +4 -1
  28. data/package.json +1 -1
  29. data/spec/depends/api_spec.rb +184 -0
  30. data/spec/depends/spec_helper.rb +27 -0
  31. data/spec/fixtures/config/invalid_config.yml +2 -2
  32. data/spec/fixtures/config/invalid_yaml_config.yml +3 -0
  33. data/spec/fixtures/depends/.gitignore +2 -0
  34. data/spec/fixtures/depends/app/controllers/api/api_keys_controller.rb +2 -0
  35. data/spec/fixtures/depends/app/controllers/organizations_controller.rb +2 -0
  36. data/spec/fixtures/depends/app/models/api_key.rb +2 -0
  37. data/spec/fixtures/depends/app/models/configuration.rb +2 -0
  38. data/spec/fixtures/depends/app/models/show.rb +2 -0
  39. data/spec/fixtures/depends/app/models/user.rb +2 -0
  40. data/spec/fixtures/depends/revoke_api_key.appmap.json +901 -0
  41. data/spec/fixtures/depends/spec/actual_rspec_test.rb +7 -0
  42. data/spec/fixtures/depends/spec/api_spec.rb +2 -0
  43. data/spec/fixtures/depends/spec/user_spec.rb +2 -0
  44. data/spec/fixtures/depends/test/actual_minitest_test.rb +5 -0
  45. data/spec/fixtures/depends/user_page_scenario.appmap.json +1776 -0
  46. data/spec/fixtures/rails5_users_app/create_app +3 -3
  47. data/spec/fixtures/rails6_users_app/create_app +3 -3
  48. data/spec/fixtures/rails6_users_app/lib/tasks/appmap.rake +11 -1
  49. data/spec/service/config_analyzer_spec.rb +4 -4
  50. data/spec/service/integration_test_path_finder_spec.rb +24 -0
  51. data/spec/service/validator/violation_spec.rb +68 -0
  52. data/test/agent_setup_status_test.rb +8 -5
  53. data/test/agent_setup_validate_test.rb +75 -0
  54. data/test/test_helper.rb +3 -0
  55. data/yarn.lock +23 -9
  56. metadata +38 -2
@@ -16,9 +16,7 @@ module AppMap
16
16
  command: {
17
17
  program: 'bundle',
18
18
  args: %w[exec rspec] + integration_test_paths[:rspec].map { |path| "./#{path}" },
19
- environment: {
20
- APPMAP: 'true'
21
- }
19
+ environment: { APPMAP: 'true', DISABLE_SPRING: 'true' }
22
20
  }
23
21
  }
24
22
  end
@@ -32,7 +30,7 @@ module AppMap
32
30
  command: {
33
31
  program: 'bundle',
34
32
  args: %w[exec cucumber],
35
- environment: { APPMAP: 'true' }
33
+ environment: { APPMAP: 'true', DISABLE_SPRING: 'true' }
36
34
  }
37
35
  }
38
36
  end
@@ -50,7 +48,7 @@ module AppMap
50
48
  command: {
51
49
  program: 'bundle',
52
50
  args: %w[exec rails test] + integration_test_paths[:minitest].map { |path| "./#{path}" },
53
- environment: { APPMAP: 'true' }
51
+ environment: { APPMAP: 'true', DISABLE_SPRING: 'true' }
54
52
  }
55
53
  }
56
54
  ]
@@ -61,7 +59,7 @@ module AppMap
61
59
  command: {
62
60
  program: 'bundle',
63
61
  args: ['exec', 'ruby', "./#{path}"],
64
- environment: { APPMAP: 'true' }
62
+ environment: { APPMAP: 'true', DISABLE_SPRING: 'true' }
65
63
  }
66
64
  }
67
65
  end
@@ -69,7 +67,7 @@ module AppMap
69
67
  end
70
68
 
71
69
  def integration_test_paths
72
- @paths ||= Service::IntegrationTestPathFinder.find
70
+ @paths ||= Service::IntegrationTestPathFinder.new.find
73
71
  end
74
72
  end
75
73
  end
@@ -0,0 +1,89 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'appmap/service/validator/violation'
4
+
5
+ module AppMap
6
+ module Service
7
+ module Validator
8
+ class ConfigValidator
9
+ attr_reader :violations
10
+
11
+ def initialize(config_file)
12
+ @config_file = config_file
13
+ @violations = []
14
+ end
15
+
16
+ def config
17
+ parse_config
18
+ end
19
+
20
+ def valid?
21
+ validate_ruby_version
22
+ validate_rails_presence
23
+ validate_config_presence
24
+ parse_config
25
+ validate_config_load
26
+ @violations.empty?
27
+ end
28
+
29
+ private
30
+
31
+ def present?
32
+ File.exist?(@config_file)
33
+ end
34
+
35
+ def parse_config
36
+ return unless present?
37
+
38
+ @config_data ||= YAML.load_file(@config_file)
39
+ rescue Psych::SyntaxError => e
40
+ @violations << Violation.error(
41
+ filename: @config_file,
42
+ message: 'AppMap configuration is not valid YAML',
43
+ detailed_message: e.message
44
+ )
45
+ nil
46
+ end
47
+
48
+ def validate_config_load
49
+ return unless @config_data
50
+
51
+ AppMap::Config.load(@config_data)
52
+ rescue StandardError => e
53
+ @violations << Violation.error(
54
+ filename: @config_file,
55
+ message: 'AppMap configuration could not be loaded',
56
+ detailed_message: e.message
57
+ )
58
+ nil
59
+ end
60
+
61
+ def validate_config_presence
62
+ unless present?
63
+ @violations << Violation.error(
64
+ filename: @config_file,
65
+ message: 'AppMap configuration file does not exist'
66
+ )
67
+ end
68
+ end
69
+
70
+ def validate_rails_presence
71
+ unless Gem.loaded_specs.has_key?('rails')
72
+ @violations << Violation.error(
73
+ message: 'AppMap auto-configuration is currently not available for non Rails projects'
74
+ )
75
+ end
76
+ end
77
+
78
+ def validate_ruby_version
79
+ unless RUBY_VERSION =~ AppMap::SUPPORTED_RUBY_VERSIONS_REGEX
80
+ @violations << Violation.error(
81
+ message: "AppMap does not support Ruby #{RUBY_VERSION}. " \
82
+ "Supported versions are: #{AppMap::SUPPORTED_RUBY_VERSIONS.join(', ')}."
83
+ )
84
+ end
85
+ end
86
+ end
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AppMap
4
+ module Service
5
+ module Validator
6
+ class Violation
7
+ attr_reader :level, :setting, :filename, :message, :detailed_message, :help_urls
8
+
9
+ class << self
10
+ def error(message:, setting: nil, filename: nil, detailed_message: nil, help_urls: nil)
11
+ self.new(
12
+ level: :error,
13
+ message: message,
14
+ setting: setting,
15
+ filename: filename,
16
+ detailed_message: detailed_message,
17
+ help_urls: help_urls
18
+ )
19
+ end
20
+
21
+ def warning(message:, setting: nil, filename: nil, detailed_message: nil, help_urls: nil)
22
+ self.new(
23
+ level: :warning,
24
+ message: message,
25
+ setting: setting,
26
+ filename: filename,
27
+ detailed_message: detailed_message,
28
+ help_urls: help_urls
29
+ )
30
+ end
31
+ end
32
+
33
+ def initialize(level:, message:, setting:, filename:, detailed_message:, help_urls:)
34
+ @level = level
35
+ @setting = setting
36
+ @filename = filename
37
+ @message = message
38
+ @detailed_message = detailed_message
39
+ @help_urls = help_urls
40
+ end
41
+
42
+ def to_h
43
+ instance_variables.each_with_object({}) do |var, hash|
44
+ hash[var.to_s.delete("@")] = self.instance_variable_get(var)
45
+ end.compact
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -3,10 +3,13 @@
3
3
  module AppMap
4
4
  URL = 'https://github.com/applandinc/appmap-ruby'
5
5
 
6
- VERSION = '0.59.2'
6
+ VERSION = '0.62.0'
7
7
 
8
8
  APPMAP_FORMAT_VERSION = '1.5.1'
9
9
 
10
+ SUPPORTED_RUBY_VERSIONS_REGEX = /^2\.[567]\./.freeze
11
+ SUPPORTED_RUBY_VERSIONS = %w[2.5 2.6 2.7].freeze
12
+
10
13
  DEFAULT_APPMAP_DIR = 'tmp/appmap'.freeze
11
14
  DEFAULT_CONFIG_FILE_PATH = 'appmap.yml'.freeze
12
15
  end
data/package.json CHANGED
@@ -17,6 +17,6 @@
17
17
  },
18
18
  "homepage": "https://github.com/applandinc/appmap-ruby#readme",
19
19
  "dependencies": {
20
- "@appland/cli": "^1.1.0"
20
+ "@appland/cli": "^1.3.2"
21
21
  }
22
22
  }
@@ -0,0 +1,184 @@
1
+ require_relative './spec_helper'
2
+ require 'appmap/depends/api'
3
+
4
+ module AppMap
5
+ module Depends
6
+ module APISpec
7
+ class << self
8
+ def minitest_environment_method
9
+ AppMap::Depends.test_env
10
+ end
11
+
12
+ def rspec_environment_method
13
+ AppMap::Depends.test_env
14
+ end
15
+
16
+ def minitest_test_command(test_files)
17
+ "time bundle exec ruby -rminitest -Itest #{test_files}"
18
+ end
19
+
20
+ alias minitest_test_command_method minitest_test_command
21
+
22
+ def rspec_test_command_method(test_files)
23
+ "time bundle exec rspec #{test_files}"
24
+ end
25
+
26
+ def rspec_select_tests_method(test_files)
27
+ AppMap::Depends.select_rspec_tests(test_files)
28
+ end
29
+
30
+ def minitest_select_tests_method(test_files)
31
+ AppMap::Depends.select_minitest_tests(test_files)
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
37
+
38
+ describe 'Depends API' do
39
+ let(:api) { AppMap::Depends::API.new(ENV['DEBUG'] == 'true') }
40
+ let(:fixture_dir) { DEPENDS_TEST_DIR }
41
+
42
+ describe '.modified' do
43
+ it 'is empty by default' do
44
+ test_list = api.modified(appmap_dir: DEPENDS_TEST_DIR, base_dir: DEPENDS_BASE_DIR)
45
+ expect(test_list).to be_empty
46
+ end
47
+
48
+ it 'detects modification of a dependent file' do
49
+ FileUtils.touch 'spec/fixtures/depends/app/models/user.rb'
50
+ test_list = api.modified(appmap_dir: DEPENDS_TEST_DIR, base_dir: DEPENDS_BASE_DIR)
51
+ expect(test_list.to_a).to eq(%w[spec/fixtures/depends/spec/user_spec.rb])
52
+ end
53
+ end
54
+
55
+ describe '.inspect_test_files' do
56
+ it 'reports metadata, added, removed, changed, failed' do
57
+ test_report = api.inspect_test_files(appmap_dir: DEPENDS_TEST_DIR, test_file_patterns: %w[spec/fixtures/depends/spec/*_spec.rb])
58
+ expect(test_report.metadata_files).to eq(%w[spec/fixtures/depends/user_page_scenario/metadata.json spec/fixtures/depends/revoke_api_key/metadata.json])
59
+ expect(test_report.added).to be_empty
60
+ expect(test_report.removed).to be_empty
61
+ expect(test_report.changed).to be_empty
62
+ expect(test_report.failed.to_a).to eq(%w[spec/fixtures/depends/spec/user_spec.rb])
63
+ end
64
+ it 'detects an added test' do
65
+ FileUtils.touch 'spec/tmp/new_spec.rb'
66
+ test_report = api.inspect_test_files(appmap_dir: DEPENDS_TEST_DIR, test_file_patterns: %w[spec/fixtures/depends/spec/*_spec.rb spec/tmp/*_spec.rb])
67
+ expect(test_report.added.to_a).to eq(%w[spec/tmp/new_spec.rb])
68
+ end
69
+ it 'detects a removed test' do
70
+ FileUtils.mv 'spec/fixtures/depends/spec/user_spec.rb', 'spec/tmp/'
71
+ begin
72
+ test_report = api.inspect_test_files(appmap_dir: DEPENDS_TEST_DIR, test_file_patterns: %w[spec/fixtures/depends/spec/*_spec.rb spec/tmp/*_spec.rb])
73
+ expect(test_report.removed.to_a).to eq(%w[spec/fixtures/depends/spec/user_spec.rb])
74
+ ensure
75
+ FileUtils.mv 'spec/tmp/user_spec.rb', 'spec/fixtures/depends/spec/'
76
+ end
77
+ end
78
+ it 'detects a changed test' do
79
+ FileUtils.touch 'spec/fixtures/depends/spec/user_spec.rb'
80
+ test_report = api.inspect_test_files(appmap_dir: DEPENDS_TEST_DIR, test_file_patterns: %w[spec/fixtures/depends/spec/*_spec.rb])
81
+ expect(test_report.changed.to_a).to eq(%w[spec/fixtures/depends/spec/user_spec.rb])
82
+ end
83
+ it 'removes AppMaps whose source file has been removed' do
84
+ appmap = JSON.parse(File.read('spec/fixtures/depends/revoke_api_key.appmap.json'))
85
+ appmap['metadata']['source_location'] = 'spec/tmp/new_spec.rb'
86
+ new_spec_file = 'spec/fixtures/depends/revoke_api_key_2.appmap.json'
87
+ File.write new_spec_file, JSON.pretty_generate(appmap)
88
+
89
+ begin
90
+ update_appmap_index
91
+ test_report = api.inspect_test_files(appmap_dir: DEPENDS_TEST_DIR, test_file_patterns: %w[spec/fixtures/depends/spec/*_spec.rb])
92
+ expect(test_report.removed.to_a).to eq(%w[spec/tmp/new_spec.rb])
93
+
94
+ test_report.clean_appmaps
95
+
96
+ expect(File.exists?(new_spec_file)).to be_falsey
97
+ ensure
98
+ FileUtils.rm_f new_spec_file if File.exists?(new_spec_file)
99
+ FileUtils.rm_rf new_spec_file.split('.')[0]
100
+ end
101
+ end
102
+ end
103
+
104
+ describe '.run_tests' do
105
+ def run_tests
106
+ Dir.chdir 'spec/fixtures/depends' do
107
+ api.run_tests([ 'spec/actual_rspec_test.rb', 'test/actual_minitest_test.rb' ], appmap_dir: Pathname.new(DEPENDS_TEST_DIR).expand_path.to_s)
108
+ end
109
+ end
110
+
111
+ describe 'smoke test' do
112
+ around do |test|
113
+ @minitest_test_command_method = AppMap.configuration.depends_config.minitest_test_command_method
114
+ AppMap.configuration.depends_config.minitest_test_command_method = 'AppMap::Depends::APISpec.minitest_test_command'
115
+
116
+ test.call
117
+ ensure
118
+ AppMap.configuration.depends_config.minitest_test_command_method = @minitest_test_command
119
+ end
120
+
121
+ it 'passes a smoke test' do
122
+ run_tests
123
+ end
124
+ end
125
+
126
+ describe 'configuration settings' do
127
+ it 'can all be modified' do
128
+ defaults = {}
129
+
130
+ %i[rspec minitest].each do |framework|
131
+ %i[environment_method select_tests_method test_command_method].each do |setting|
132
+ full_setting = [ framework, setting ].join('_').to_sym
133
+ defaults[full_setting] = AppMap.configuration.depends_config.send(full_setting)
134
+ AppMap.configuration.depends_config.send("#{full_setting}=", "AppMap::Depends::APISpec.#{full_setting}")
135
+ end
136
+ end
137
+
138
+ run_tests
139
+ ensure
140
+ defaults.keys.each do |setting|
141
+ AppMap.configuration.depends_config.send("#{setting}=", defaults[setting])
142
+ end
143
+ end
144
+ end
145
+ end
146
+
147
+ describe '.remove_out_of_date_appmaps' do
148
+ it 'is a nop in normal circumstances' do
149
+ since = Time.now
150
+ removed = api.remove_out_of_date_appmaps(since, appmap_dir: DEPENDS_TEST_DIR, base_dir: DEPENDS_BASE_DIR)
151
+ expect(removed).to be_empty
152
+ end
153
+
154
+ it "removes an out-of-date AppMap that hasn't been brought up to date" do
155
+ # This AppMap will be modified before the 'since' time
156
+ appmap_path = "spec/fixtures/depends/user_page_scenario.appmap.json"
157
+ appmap = File.read(appmap_path)
158
+
159
+ sleep 0.01
160
+ since = Time.now
161
+ sleep 0.01
162
+
163
+ # Touch the rest of the AppMaps so that they are modified after +since+
164
+ Dir.glob('spec/fixtures/depends/*.appmap.json').each do |path|
165
+ next if path == appmap_path
166
+ FileUtils.touch path
167
+ end
168
+
169
+ sleep 0.01
170
+ # Make the AppMaps out of date
171
+ FileUtils.touch 'spec/fixtures/depends/app/models/user.rb'
172
+ sleep 0.01
173
+
174
+ begin
175
+ # At this point, we would run tests to bring the AppMaps up to date
176
+ # Then once the tests have finished, remove any AppMaps that weren't refreshed
177
+ removed = api.remove_out_of_date_appmaps(since, appmap_dir: DEPENDS_TEST_DIR, base_dir: DEPENDS_BASE_DIR)
178
+ expect(removed).to eq([ appmap_path.split('.')[0] ])
179
+ ensure
180
+ File.write(appmap_path, appmap)
181
+ end
182
+ end
183
+ end
184
+ end
@@ -0,0 +1,27 @@
1
+ require_relative '../spec_helper'
2
+
3
+ DEPENDS_TEST_DIR = 'spec/fixtures/depends'
4
+ DEPENDS_BASE_DIR = DEPENDS_TEST_DIR
5
+
6
+ def update_appmap_index
7
+ cmd = [
8
+ './exe/appmap-index',
9
+ '--appmap-dir',
10
+ DEPENDS_TEST_DIR
11
+ ]
12
+ if ENV['DEBUG'] == 'true'
13
+ cmd << '--verbose'
14
+ end
15
+
16
+ system cmd.join(' ') or raise "Failed to update AppMap index in #{DEPENDS_TEST_DIR}"
17
+ end
18
+
19
+ RSpec.configure do |rspec|
20
+ rspec.before do
21
+ Dir.glob("#{DEPENDS_TEST_DIR}/*.appmap.json").each { |fname| FileUtils.touch fname }
22
+ update_appmap_index
23
+
24
+ FileUtils.rm_rf 'spec/tmp'
25
+ FileUtils.mkdir_p 'spec/tmp'
26
+ end
27
+ end
@@ -1,2 +1,2 @@
1
- name: -123- %
2
- packages: ~{}~ ''
1
+ name: name
2
+ packages: [1,2,3]
@@ -0,0 +1,3 @@
1
+ name:
2
+ -
3
+ packages: "[]" asd
@@ -0,0 +1,2 @@
1
+ user_page_scenario
2
+ revoke_api_key
@@ -0,0 +1,2 @@
1
+ # dummy
2
+ # This file is just here so it can be touched and trigger re-test.
@@ -0,0 +1,2 @@
1
+ # dummy
2
+ # This file is just here so it can be touched and trigger re-test.