rails_proof 1.0.0 → 1.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1500c170053642438fbc0cbc5fa9ef30430ce37fe788b8478c18c7c62b20f603
4
- data.tar.gz: e28a65c12b6619ffe852da881e7420efc63da2ce5b84af05966d036234e46447
3
+ metadata.gz: f5bc052c6972058b263ddf1afd897bd78eabda2639d407fc3ec4f652abed5eb9
4
+ data.tar.gz: 65317ae8a4a383083becd070161200fedbe24f2d884e7c9e0da431b09572abec
5
5
  SHA512:
6
- metadata.gz: f1395a5300acda174dafd78f1f6486871e39d43a861b52db6c462563c0f208f235ed9488801d999e60adc5e420123bfc31a0fa4a0b658421af230efd8dbef117
7
- data.tar.gz: '081d3e8cac32707be3aadb419fd0d467c7f5a94b5bb31753072f3fbb57f391aa48ca137eb5b020df66f39368ab0e4148d690a27503d5ce4b93b93f72f3a2898f'
6
+ metadata.gz: 15cfa004f2adf092a455e0d86d249364d0ff1a25fbfbced29f0a7067c22e519993013209500c86deae31e190157030723dcdc2360d42860ddbe40b3790a72bce
7
+ data.tar.gz: b88cc75ef1f4bd2d6bd91ba54d3288484ca0604124874f92d51ce0d45473b7428febb482a41d3b00217bfffd9d9f77288da9b0e264c5b1f38f14a2c6d3b5abf2
@@ -11,6 +11,7 @@ require "rails_proof/controller_test_coverage_plan"
11
11
  require "rails_proof/controller_test_writer"
12
12
  require "rails_proof/ai_test_planner"
13
13
  require "rails_proof/ai_test_executor"
14
+ require "rails_proof/test_file_inserter"
14
15
 
15
16
  module RailsProof
16
17
  class TestGenerator < Rails::Generators::Base
@@ -170,10 +171,12 @@ module RailsProof
170
171
  @controller_inspector = RailsProof::ControllerInspector.new(
171
172
  @controller_class
172
173
  )
174
+
173
175
  @controller_test_plan = RailsProof::ControllerTestPlan.new(
174
176
  @controller_inspector
175
177
  )
176
- @controller_coverage_plan =
178
+
179
+ @controller_test_coverage_plan =
177
180
  RailsProof::ControllerTestCoveragePlan.new(
178
181
  @controller_test_plan,
179
182
  @test_inspector
@@ -181,7 +184,7 @@ module RailsProof
181
184
  else
182
185
  @controller_inspector = nil
183
186
  @controller_test_plan = nil
184
- @controller_coverage_plan = nil
187
+ @controller_test_coverage_plan = nil
185
188
  end
186
189
  end
187
190
 
@@ -334,10 +337,10 @@ module RailsProof
334
337
  end
335
338
 
336
339
  say "Coverage:"
337
- say " Covered: #{@controller_coverage_plan.covered_count}"
338
- say " Missing: #{@controller_coverage_plan.missing_count}"
340
+ say " Covered: #{@controller_test_coverage_plan.covered_count}"
341
+ say " Missing: #{@controller_test_coverage_plan.missing_count}"
339
342
 
340
- @controller_coverage_plan.missing_concerns.each do |concern|
343
+ @controller_test_coverage_plan.missing_concerns.each do |concern|
341
344
  say " #{concern[:description]}"
342
345
  end
343
346
  end
@@ -509,10 +512,10 @@ module RailsProof
509
512
  )
510
513
 
511
514
  if @absolute_test_file_path.file?
512
- inject_into_file(
513
- @test_file_path,
514
- "\n#{writer.render}\n",
515
- before: /^end\s*\z/
515
+ insert_rendered_tests(
516
+ writer.render,
517
+ test_class_name: "#{@current_target.class_name}Test",
518
+ superclass: "ActiveSupport::TestCase"
516
519
  )
517
520
  else
518
521
  create_file @test_file_path, writer.render_test_file
@@ -521,22 +524,38 @@ module RailsProof
521
524
 
522
525
  def write_missing_controller_tests
523
526
  return unless @controller_class
524
- return if @controller_coverage_plan.missing_count.zero?
527
+ return if @controller_test_coverage_plan.missing_count.zero?
525
528
 
526
529
  writer = RailsProof::ControllerTestWriter.new(
527
530
  controller_class_name: @current_target.class_name,
528
- concerns: @controller_coverage_plan.missing_concerns
531
+ concerns: @controller_test_coverage_plan.missing_concerns
529
532
  )
530
533
 
531
534
  if @absolute_test_file_path.file?
532
- inject_into_file(
533
- @test_file_path,
534
- "\n#{writer.render}\n",
535
- before: /^end\s*\z/
535
+ insert_rendered_tests(
536
+ writer.render,
537
+ test_class_name: "#{@current_target.class_name}Test",
538
+ superclass: "ActionDispatch::IntegrationTest"
536
539
  )
537
540
  else
538
541
  create_file @test_file_path, writer.render_test_file
539
542
  end
540
543
  end
544
+
545
+ def insert_rendered_tests(
546
+ rendered_test,
547
+ test_class_name:,
548
+ superclass:
549
+ )
550
+ source = @absolute_test_file_path.read
551
+
552
+ updated = RailsProof::TestFileInserter.new(
553
+ source: source,
554
+ test_class_name: test_class_name,
555
+ superclass: superclass
556
+ ).insert(rendered_test)
557
+
558
+ @absolute_test_file_path.write(updated)
559
+ end
541
560
  end
542
561
  end
@@ -4,6 +4,7 @@ require "rails_proof/ai_test_writer"
4
4
  require "rails_proof/ai_concern_filter"
5
5
  require "rails_proof/test_runner"
6
6
  require "rails_proof/review_store"
7
+ require "rails_proof/test_file_inserter"
7
8
 
8
9
  module RailsProof
9
10
  class AiTestExecutor
@@ -34,13 +35,13 @@ module RailsProof
34
35
  end
35
36
 
36
37
  attr_reader :root,
37
- :target_path,
38
- :test_file_path,
39
- :test_class_name,
40
- :superclass,
41
- :concerns,
42
- :runner_class,
43
- :review_store
38
+ :target_path,
39
+ :test_file_path,
40
+ :test_class_name,
41
+ :superclass,
42
+ :concerns,
43
+ :runner_class,
44
+ :review_store
44
45
 
45
46
  def initialize(
46
47
  root:,
@@ -124,7 +125,10 @@ module RailsProof
124
125
  test_output: test_result.output
125
126
  )
126
127
 
127
- errors = ["candidate test failed against application"]
128
+ errors = [
129
+ "candidate test failed against application"
130
+ ]
131
+
128
132
  errors << persistence_error if persistence_error
129
133
 
130
134
  Result.new(
@@ -199,7 +203,9 @@ module RailsProof
199
203
  return unless state
200
204
 
201
205
  if state[:existed]
202
- absolute_test_file_path.write(state[:content])
206
+ absolute_test_file_path.write(
207
+ state[:content]
208
+ )
203
209
  elsif absolute_test_file_path.exist?
204
210
  absolute_test_file_path.delete
205
211
  end
@@ -213,25 +219,27 @@ module RailsProof
213
219
  )
214
220
 
215
221
  if absolute_test_file_path.file?
216
- insert_into_existing_file(writer.render)
222
+ insert_into_existing_file(
223
+ writer.render
224
+ )
217
225
  else
218
226
  absolute_test_file_path.dirname.mkpath
219
- absolute_test_file_path.write(writer.render_test_file)
227
+
228
+ absolute_test_file_path.write(
229
+ writer.render_test_file
230
+ )
220
231
  end
221
232
  end
222
233
 
223
234
  def insert_into_existing_file(rendered_test)
224
235
  source = absolute_test_file_path.read
225
236
 
226
- unless source.match?(/^end\s*\z/)
227
- raise ArgumentError,
228
- "RailsProof could not find the end of #{test_file_path}"
229
- end
230
-
231
- updated = source.sub(
232
- /^end\s*\z/,
233
- "\n#{rendered_test}\nend"
234
- )
237
+ updated =
238
+ RailsProof::TestFileInserter.new(
239
+ source: source,
240
+ test_class_name: test_class_name,
241
+ superclass: superclass
242
+ ).insert(rendered_test)
235
243
 
236
244
  absolute_test_file_path.write(updated)
237
245
  end
@@ -245,7 +253,10 @@ module RailsProof
245
253
  test_class_name: test_class_name
246
254
  )
247
255
 
248
- [path, nil]
256
+ [
257
+ path,
258
+ nil
259
+ ]
249
260
  rescue StandardError => error
250
261
  [
251
262
  nil,
@@ -0,0 +1,142 @@
1
+ module RailsProof
2
+ class TestFileInserter
3
+ class Error < StandardError
4
+ end
5
+
6
+ attr_reader :source,
7
+ :test_class_name,
8
+ :superclass
9
+
10
+ def initialize(
11
+ source:,
12
+ test_class_name:,
13
+ superclass:
14
+ )
15
+ @source = source
16
+ @test_class_name = test_class_name
17
+ @superclass = superclass
18
+ end
19
+
20
+ def insert(rendered_test)
21
+ lines = source.lines
22
+
23
+ class_index, class_indent = find_test_class(lines)
24
+
25
+ closing_index = find_class_end(
26
+ lines,
27
+ class_index,
28
+ class_indent
29
+ )
30
+
31
+ indented_test = indent_rendered_test(
32
+ rendered_test,
33
+ class_indent
34
+ )
35
+
36
+ lines.insert(
37
+ closing_index,
38
+ "\n#{indented_test.rstrip}\n"
39
+ )
40
+
41
+ lines.join
42
+ end
43
+
44
+ private
45
+
46
+ def find_test_class(lines)
47
+ exact_name = test_class_name
48
+ short_name = test_class_name.split("::").last
49
+
50
+ exact_matches = class_matches(
51
+ lines,
52
+ exact_name
53
+ )
54
+
55
+ matches =
56
+ if exact_matches.any?
57
+ exact_matches
58
+ else
59
+ class_matches(
60
+ lines,
61
+ short_name
62
+ )
63
+ end
64
+
65
+ if matches.empty?
66
+ raise Error,
67
+ "RailsProof could not find " \
68
+ "#{test_class_name} < #{superclass}"
69
+ end
70
+
71
+ if matches.count > 1
72
+ raise Error,
73
+ "RailsProof found multiple candidate " \
74
+ "test classes for #{test_class_name}"
75
+ end
76
+
77
+ matches.first
78
+ end
79
+
80
+ def class_matches(lines, class_name)
81
+ pattern =
82
+ /\A(?<indent>[ \t]*)class\s+#{Regexp.escape(class_name)}\s*<\s*#{Regexp.escape(superclass)}\s*(?:#.*)?\r?\n?\z/
83
+
84
+ lines.each_with_index.filter_map do |line, index|
85
+ match = line.match(pattern)
86
+ next unless match
87
+
88
+ [
89
+ index,
90
+ match[:indent]
91
+ ]
92
+ end
93
+ end
94
+
95
+ def find_class_end(
96
+ lines,
97
+ class_index,
98
+ class_indent
99
+ )
100
+ pattern =
101
+ /\A#{Regexp.escape(class_indent)}end(?:\s*#.*)?\s*\z/
102
+
103
+ closing_index =
104
+ ((class_index + 1)...lines.length).find do |index|
105
+ lines[index].match?(pattern)
106
+ end
107
+
108
+ return closing_index if closing_index
109
+
110
+ raise Error,
111
+ "RailsProof could not find the end of #{test_class_name}"
112
+ end
113
+
114
+ def indent_rendered_test(
115
+ rendered_test,
116
+ class_indent
117
+ )
118
+ lines = rendered_test.rstrip.lines
119
+
120
+ nonblank_lines = lines.reject do |line|
121
+ line.strip.empty?
122
+ end
123
+
124
+ source_indent =
125
+ nonblank_lines
126
+ .map { |line| line[/\A[ \t]*/].length }
127
+ .min || 0
128
+
129
+ target_indent = "#{class_indent} "
130
+
131
+ lines.map do |line|
132
+ if line.strip.empty?
133
+ ""
134
+ else
135
+ content = line[source_indent..]
136
+
137
+ "#{target_indent}#{content.rstrip}"
138
+ end
139
+ end.join("\n")
140
+ end
141
+ end
142
+ end
@@ -1,3 +1,3 @@
1
1
  module RailsProof
2
- VERSION = "1.0.0"
2
+ VERSION = "1.1.0"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rails_proof
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Oak Harbor Ventures, LLC
@@ -63,6 +63,7 @@ files:
63
63
  - lib/rails_proof/review_store.rb
64
64
  - lib/rails_proof/target_discovery.rb
65
65
  - lib/rails_proof/test_coverage_plan.rb
66
+ - lib/rails_proof/test_file_inserter.rb
66
67
  - lib/rails_proof/test_inspector.rb
67
68
  - lib/rails_proof/test_runner.rb
68
69
  - lib/rails_proof/test_writer.rb