spec_scout 0.1.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 (49) hide show
  1. checksums.yaml +7 -0
  2. data/.idea/.gitignore +10 -0
  3. data/.idea/Projects.iml +41 -0
  4. data/.idea/copilot.data.migration.ask2agent.xml +6 -0
  5. data/.idea/modules.xml +8 -0
  6. data/.idea/vcs.xml +6 -0
  7. data/.rspec_status +236 -0
  8. data/Gemfile +11 -0
  9. data/Gemfile.lock +72 -0
  10. data/LICENSE +21 -0
  11. data/README.md +433 -0
  12. data/Rakefile +12 -0
  13. data/examples/README.md +321 -0
  14. data/examples/best_practices.md +401 -0
  15. data/examples/configurations/basic_config.rb +24 -0
  16. data/examples/configurations/ci_config.rb +35 -0
  17. data/examples/configurations/conservative_config.rb +32 -0
  18. data/examples/configurations/development_config.rb +37 -0
  19. data/examples/configurations/performance_focused_config.rb +38 -0
  20. data/examples/output_formatter_demo.rb +67 -0
  21. data/examples/sample_outputs/console_output_high_confidence.txt +27 -0
  22. data/examples/sample_outputs/console_output_medium_confidence.txt +27 -0
  23. data/examples/sample_outputs/console_output_no_action.txt +27 -0
  24. data/examples/sample_outputs/console_output_risk_detected.txt +27 -0
  25. data/examples/sample_outputs/json_output_high_confidence.json +108 -0
  26. data/examples/sample_outputs/json_output_no_action.json +108 -0
  27. data/examples/workflows/basic_workflow.md +159 -0
  28. data/examples/workflows/ci_integration.md +372 -0
  29. data/exe/spec_scout +7 -0
  30. data/lib/spec_scout/agent_result.rb +44 -0
  31. data/lib/spec_scout/agents/database_agent.rb +113 -0
  32. data/lib/spec_scout/agents/factory_agent.rb +179 -0
  33. data/lib/spec_scout/agents/intent_agent.rb +223 -0
  34. data/lib/spec_scout/agents/risk_agent.rb +290 -0
  35. data/lib/spec_scout/base_agent.rb +72 -0
  36. data/lib/spec_scout/cli.rb +158 -0
  37. data/lib/spec_scout/configuration.rb +162 -0
  38. data/lib/spec_scout/consensus_engine.rb +535 -0
  39. data/lib/spec_scout/enforcement_handler.rb +182 -0
  40. data/lib/spec_scout/output_formatter.rb +307 -0
  41. data/lib/spec_scout/profile_data.rb +37 -0
  42. data/lib/spec_scout/profile_normalizer.rb +238 -0
  43. data/lib/spec_scout/recommendation.rb +62 -0
  44. data/lib/spec_scout/safety_validator.rb +127 -0
  45. data/lib/spec_scout/spec_scout.rb +519 -0
  46. data/lib/spec_scout/testprof_integration.rb +206 -0
  47. data/lib/spec_scout/version.rb +5 -0
  48. data/lib/spec_scout.rb +43 -0
  49. metadata +166 -0
@@ -0,0 +1,206 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'test_prof'
4
+
5
+ module SpecScout
6
+ # Handles TestProf integration, execution, and data extraction
7
+ class TestProfIntegration
8
+ class TestProfError < StandardError; end
9
+
10
+ def initialize(config = nil)
11
+ @config = config || ::SpecScout.configuration
12
+ @enabled = false
13
+ end
14
+
15
+ # Enable and execute TestProf profiling
16
+ def execute_profiling(_spec_location = nil)
17
+ return nil unless @config.use_test_prof
18
+
19
+ begin
20
+ enable_testprof_profilers
21
+ @enabled = true
22
+
23
+ # Extract profile data immediately after enabling
24
+ extract_profile_data
25
+ rescue StandardError => e
26
+ raise TestProfError, "Failed to enable TestProf: #{e.message}"
27
+ end
28
+ end
29
+
30
+ # Extract structured profile data from TestProf results
31
+ def extract_profile_data
32
+ return {} unless @enabled
33
+
34
+ begin
35
+ {
36
+ factory_prof: extract_factory_prof_data,
37
+ event_prof: extract_event_prof_data,
38
+ db_queries: extract_db_query_data
39
+ }
40
+ rescue StandardError => e
41
+ raise TestProfError, "Failed to extract TestProf data: #{e.message}"
42
+ end
43
+ end
44
+
45
+ # Check if TestProf is available and properly configured
46
+ def testprof_available?
47
+ defined?(TestProf) && TestProf.respond_to?(:config)
48
+ end
49
+
50
+ private
51
+
52
+ def enable_testprof_profilers
53
+ raise TestProfError, 'TestProf not available' unless testprof_available?
54
+
55
+ # Enable FactoryProf
56
+ TestProf::FactoryProf.init if defined?(TestProf::FactoryProf)
57
+
58
+ # Configure TestProf settings
59
+ configure_testprof_settings
60
+ end
61
+
62
+ def configure_testprof_settings
63
+ return unless defined?(TestProf)
64
+
65
+ # Set up basic TestProf configuration
66
+ TestProf.configure do |config|
67
+ config.output_dir = 'tmp/test_prof'
68
+ config.timestamps = true
69
+ end
70
+ end
71
+
72
+ def extract_factory_prof_data
73
+ return {} unless defined?(TestProf::FactoryProf)
74
+
75
+ # Extract FactoryProf results if available
76
+ if TestProf::FactoryProf.respond_to?(:stats)
77
+ stats = TestProf::FactoryProf.stats
78
+ return {} unless stats
79
+
80
+ {
81
+ total_count: stats.values.sum { |s| s[:total] || 0 },
82
+ total_time: stats.values.sum { |s| s[:time] || 0.0 },
83
+ stats: extract_factory_stats_from_hash(stats)
84
+ }
85
+ else
86
+ {}
87
+ end
88
+ rescue StandardError => e
89
+ # Log error but don't fail the entire extraction
90
+ { error: "FactoryProf extraction failed: #{e.message}" }
91
+ end
92
+
93
+ def extract_factory_stats_from_hash(stats)
94
+ return {} unless stats.is_a?(Hash)
95
+
96
+ result = {}
97
+ stats.each do |factory_name, factory_data|
98
+ result[factory_name.to_sym] = {
99
+ count: factory_data[:total] || factory_data[:count] || 0,
100
+ time: factory_data[:time] || 0.0,
101
+ strategy: detect_factory_strategy(factory_data)
102
+ }
103
+ end
104
+ result
105
+ rescue StandardError
106
+ {}
107
+ end
108
+
109
+ def detect_factory_strategy(factory_data)
110
+ # Try to detect if factory used create, build, or build_stubbed
111
+ # This is a simplified detection - real implementation would need
112
+ # more sophisticated analysis of TestProf data
113
+ if factory_data[:create_count]&.positive?
114
+ :create
115
+ elsif factory_data[:build_count]&.positive?
116
+ :build
117
+ else
118
+ :unknown
119
+ end
120
+ end
121
+
122
+ def extract_event_prof_data
123
+ return {} unless defined?(TestProf::EventProf)
124
+
125
+ # EventProf doesn't have a simple results API like FactoryProf
126
+ # Return empty data for now - this would need to be implemented
127
+ # based on how EventProf actually stores its results
128
+ {}
129
+ rescue StandardError => e
130
+ # Log error but don't fail the entire extraction
131
+ { error: "EventProf extraction failed: #{e.message}" }
132
+ end
133
+
134
+ def extract_event_stats(results)
135
+ return {} unless results.respond_to?(:each)
136
+
137
+ events = {}
138
+ results.each do |event_name, event_data|
139
+ events[event_name.to_sym] = {
140
+ count: event_data[:count] || 0,
141
+ time: event_data[:time] || 0.0,
142
+ examples: event_data[:examples] || []
143
+ }
144
+ end
145
+ events
146
+ rescue StandardError
147
+ {}
148
+ end
149
+
150
+ def extract_db_query_data
151
+ # Extract database query information
152
+ # This would typically come from EventProf sql.active_record events
153
+ # or other TestProf database profiling features
154
+
155
+ db_data = {
156
+ total_queries: 0,
157
+ inserts: 0,
158
+ selects: 0,
159
+ updates: 0,
160
+ deletes: 0
161
+ }
162
+
163
+ # Try to get SQL event data from EventProf
164
+ event_data = extract_event_prof_data
165
+ if event_data[:events] && event_data[:events][:'sql.active_record']
166
+ sql_events = event_data[:events][:'sql.active_record']
167
+ db_data[:total_queries] = sql_events[:count] || 0
168
+
169
+ # Parse examples to categorize query types
170
+ categorize_sql_queries(sql_events[:examples], db_data) if sql_events[:examples]
171
+ end
172
+
173
+ db_data
174
+ rescue StandardError => e
175
+ # Return default structure on error
176
+ {
177
+ total_queries: 0,
178
+ inserts: 0,
179
+ selects: 0,
180
+ updates: 0,
181
+ deletes: 0,
182
+ error: "DB query extraction failed: #{e.message}"
183
+ }
184
+ end
185
+
186
+ def categorize_sql_queries(examples, db_data)
187
+ examples.each do |example|
188
+ next unless example.is_a?(Hash) && example[:sql]
189
+
190
+ sql = example[:sql].to_s.upcase
191
+ case sql
192
+ when /^INSERT/
193
+ db_data[:inserts] += 1
194
+ when /^SELECT/
195
+ db_data[:selects] += 1
196
+ when /^UPDATE/
197
+ db_data[:updates] += 1
198
+ when /^DELETE/
199
+ db_data[:deletes] += 1
200
+ end
201
+ end
202
+ rescue StandardError
203
+ # Ignore categorization errors
204
+ end
205
+ end
206
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SpecScout
4
+ VERSION = '0.1.0'
5
+ end
data/lib/spec_scout.rb ADDED
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'spec_scout/version'
4
+ require_relative 'spec_scout/profile_data'
5
+ require_relative 'spec_scout/agent_result'
6
+ require_relative 'spec_scout/recommendation'
7
+ require_relative 'spec_scout/base_agent'
8
+ require_relative 'spec_scout/configuration'
9
+ require_relative 'spec_scout/safety_validator'
10
+ require_relative 'spec_scout/enforcement_handler'
11
+ require_relative 'spec_scout/testprof_integration'
12
+ require_relative 'spec_scout/profile_normalizer'
13
+ require_relative 'spec_scout/consensus_engine'
14
+ require_relative 'spec_scout/output_formatter'
15
+
16
+ # Agents
17
+ require_relative 'spec_scout/agents/database_agent'
18
+ require_relative 'spec_scout/agents/factory_agent'
19
+ require_relative 'spec_scout/agents/intent_agent'
20
+ require_relative 'spec_scout/agents/risk_agent'
21
+
22
+ # Main orchestration class
23
+ require_relative 'spec_scout/spec_scout'
24
+ require_relative 'spec_scout/cli'
25
+
26
+ # Main module for Spec Scout, a tool for analyzing and optimizing test suite database and factory usage.
27
+ module SpecScout
28
+ class Error < StandardError; end
29
+
30
+ # Main entry point for Spec Scout functionality
31
+ def self.configure
32
+ yield(configuration) if block_given?
33
+ configuration
34
+ end
35
+
36
+ def self.configuration
37
+ @configuration ||= Configuration.new
38
+ end
39
+
40
+ def self.reset_configuration!
41
+ @configuration = nil
42
+ end
43
+ end
metadata ADDED
@@ -0,0 +1,166 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: spec_scout
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Ajeet Kumar
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2025-12-28 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: test-prof
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: prop_check
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '0.18'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '0.18'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '13.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '13.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '3.12'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '3.12'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rubocop
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '1.21'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '1.21'
83
+ description: Spec Scout transforms TestProf profiling data into actionable optimization
84
+ recommendations using specialized agents
85
+ email:
86
+ - akv1087@gmail.com
87
+ executables:
88
+ - spec_scout
89
+ extensions: []
90
+ extra_rdoc_files: []
91
+ files:
92
+ - ".idea/.gitignore"
93
+ - ".idea/Projects.iml"
94
+ - ".idea/copilot.data.migration.ask2agent.xml"
95
+ - ".idea/modules.xml"
96
+ - ".idea/vcs.xml"
97
+ - ".rspec_status"
98
+ - Gemfile
99
+ - Gemfile.lock
100
+ - LICENSE
101
+ - README.md
102
+ - Rakefile
103
+ - examples/README.md
104
+ - examples/best_practices.md
105
+ - examples/configurations/basic_config.rb
106
+ - examples/configurations/ci_config.rb
107
+ - examples/configurations/conservative_config.rb
108
+ - examples/configurations/development_config.rb
109
+ - examples/configurations/performance_focused_config.rb
110
+ - examples/output_formatter_demo.rb
111
+ - examples/sample_outputs/console_output_high_confidence.txt
112
+ - examples/sample_outputs/console_output_medium_confidence.txt
113
+ - examples/sample_outputs/console_output_no_action.txt
114
+ - examples/sample_outputs/console_output_risk_detected.txt
115
+ - examples/sample_outputs/json_output_high_confidence.json
116
+ - examples/sample_outputs/json_output_no_action.json
117
+ - examples/workflows/basic_workflow.md
118
+ - examples/workflows/ci_integration.md
119
+ - exe/spec_scout
120
+ - lib/spec_scout.rb
121
+ - lib/spec_scout/agent_result.rb
122
+ - lib/spec_scout/agents/database_agent.rb
123
+ - lib/spec_scout/agents/factory_agent.rb
124
+ - lib/spec_scout/agents/intent_agent.rb
125
+ - lib/spec_scout/agents/risk_agent.rb
126
+ - lib/spec_scout/base_agent.rb
127
+ - lib/spec_scout/cli.rb
128
+ - lib/spec_scout/configuration.rb
129
+ - lib/spec_scout/consensus_engine.rb
130
+ - lib/spec_scout/enforcement_handler.rb
131
+ - lib/spec_scout/output_formatter.rb
132
+ - lib/spec_scout/profile_data.rb
133
+ - lib/spec_scout/profile_normalizer.rb
134
+ - lib/spec_scout/recommendation.rb
135
+ - lib/spec_scout/safety_validator.rb
136
+ - lib/spec_scout/spec_scout.rb
137
+ - lib/spec_scout/testprof_integration.rb
138
+ - lib/spec_scout/version.rb
139
+ homepage: https://github.com/ajeet-g2/spec-scout
140
+ licenses:
141
+ - MIT
142
+ metadata:
143
+ allowed_push_host: https://rubygems.org
144
+ homepage_uri: https://github.com/ajeet-g2/spec-scout
145
+ source_code_uri: https://github.com/ajeet-g2/spec-scout
146
+ changelog_uri: https://github.com/ajeet-g2/spec-scout/blob/main/CHANGELOG.md
147
+ post_install_message:
148
+ rdoc_options: []
149
+ require_paths:
150
+ - lib
151
+ required_ruby_version: !ruby/object:Gem::Requirement
152
+ requirements:
153
+ - - ">="
154
+ - !ruby/object:Gem::Version
155
+ version: 2.7.0
156
+ required_rubygems_version: !ruby/object:Gem::Requirement
157
+ requirements:
158
+ - - ">="
159
+ - !ruby/object:Gem::Version
160
+ version: '0'
161
+ requirements: []
162
+ rubygems_version: 3.4.19
163
+ signing_key:
164
+ specification_version: 4
165
+ summary: Intelligent test optimization advisor built on TestProf
166
+ test_files: []