tng 0.3.8 → 0.4.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 +4 -4
- data/bin/tng +404 -19
- data/binaries/go-ui-darwin-amd64 +0 -0
- data/binaries/go-ui-darwin-arm64 +0 -0
- data/binaries/go-ui-linux-amd64 +0 -0
- data/binaries/go-ui-linux-arm64 +0 -0
- data/binaries/tng-darwin-arm64.bundle +0 -0
- data/binaries/tng-darwin-x86_64.bundle +0 -0
- data/binaries/tng-linux-arm64.so +0 -0
- data/binaries/tng-linux-x86_64.so +0 -0
- data/binaries/tng.bundle +0 -0
- data/lib/tng/analyzers/controller.rb +0 -6
- data/lib/tng/analyzers/model.rb +4 -9
- data/lib/tng/analyzers/service.rb +1 -7
- data/lib/tng/services/direct_generation.rb +57 -27
- data/lib/tng/services/fix_orchestrator.rb +92 -0
- data/lib/tng/services/repair_service.rb +48 -0
- data/lib/tng/services/test_generator.rb +87 -5
- data/lib/tng/ui/go_ui_session.rb +27 -4
- data/lib/tng/ui/json_session.rb +198 -0
- data/lib/tng/utils.rb +25 -2
- data/lib/tng/version.rb +1 -1
- data/lib/tng.rb +7 -3
- metadata +7 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 4090a9996518027b0e5d49534360ce2269a735c407dcdb5544e4842a1431b590
|
|
4
|
+
data.tar.gz: 1171ff1e89fe751cb2a4de7a8cb688db9c65b22babdb7c1164f5d24032cc1092
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: af6285fd48d5c93a1fad7698b1636aa03fef7f8f6f2b4705ed68390d1b0ee78a2aa118836952c7b2aac63dfaec417f23d9eaf99134e604ce43bed85576b3ddb6
|
|
7
|
+
data.tar.gz: 24f76ab0623a752f753b26c84b22ec8b971970de6e8493ea0f52c024eb94ee5fcd3f8459d4845782d01dcc23e92f8ac551136fec3a533be7b0ebac09812c973f
|
data/bin/tng
CHANGED
|
@@ -10,6 +10,7 @@ require "tng"
|
|
|
10
10
|
require "tng/services/direct_generation"
|
|
11
11
|
require "tng/services/extract_methods"
|
|
12
12
|
require "tng/services/file_type_detector"
|
|
13
|
+
require "tng/services/fix_orchestrator"
|
|
13
14
|
|
|
14
15
|
require "tty-screen"
|
|
15
16
|
require "tty-option"
|
|
@@ -57,6 +58,23 @@ class CLI
|
|
|
57
58
|
desc "Method name (for per-method tests)"
|
|
58
59
|
end
|
|
59
60
|
|
|
61
|
+
flag :audit do
|
|
62
|
+
short "-a"
|
|
63
|
+
long "--audit"
|
|
64
|
+
desc "Run in audit mode (find issues and behaviours instead of generating tests)"
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
flag :json do
|
|
68
|
+
long "--json"
|
|
69
|
+
desc "Output results in JSON format"
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
flag :fix do
|
|
73
|
+
short "-x"
|
|
74
|
+
long "--fix"
|
|
75
|
+
desc "Run in fix mode (automatically repair syntax, lint, and test errors)"
|
|
76
|
+
end
|
|
77
|
+
|
|
60
78
|
flag :help do
|
|
61
79
|
short "-h"
|
|
62
80
|
long "--help"
|
|
@@ -65,7 +83,13 @@ class CLI
|
|
|
65
83
|
|
|
66
84
|
def initialize
|
|
67
85
|
@pastel = Pastel.new
|
|
68
|
-
|
|
86
|
+
# Check for --json flag raw before parsing
|
|
87
|
+
if ARGV.any? { |arg| arg == "--json" }
|
|
88
|
+
require "tng/ui/json_session"
|
|
89
|
+
@go_ui = Tng::UI::JsonSession.new
|
|
90
|
+
else
|
|
91
|
+
@go_ui = Tng::UI::GoUISession.new
|
|
92
|
+
end
|
|
69
93
|
@terminal_width = begin
|
|
70
94
|
TTY::Screen.width
|
|
71
95
|
rescue StandardError
|
|
@@ -90,7 +114,9 @@ class CLI
|
|
|
90
114
|
|
|
91
115
|
initialize_config_and_clients if rails_loaded
|
|
92
116
|
|
|
93
|
-
if params[:
|
|
117
|
+
if params[:fix]
|
|
118
|
+
handle_fix_command
|
|
119
|
+
elsif params[:file]
|
|
94
120
|
run_direct_generation
|
|
95
121
|
else
|
|
96
122
|
if find_rails_root && !rails_loaded && !defined?(Rails)
|
|
@@ -121,6 +147,12 @@ class CLI
|
|
|
121
147
|
normalized << "--file=#{::Regexp.last_match(2)}"
|
|
122
148
|
when /^(?:--)?(method|m)=(.+)$/
|
|
123
149
|
normalized << "--method=#{::Regexp.last_match(2)}"
|
|
150
|
+
when /^(?:--)?(audit|a)$/
|
|
151
|
+
normalized << "--audit"
|
|
152
|
+
when /^(?:--)?(json)$/
|
|
153
|
+
normalized << "--json"
|
|
154
|
+
when /^(?:--)?(fix|x)$/
|
|
155
|
+
normalized << "--fix"
|
|
124
156
|
when /^(help|h)=(.+)$/
|
|
125
157
|
normalized << "--help=#{::Regexp.last_match(2)}"
|
|
126
158
|
when /^--file$/, /^-f$/
|
|
@@ -151,8 +183,11 @@ class CLI
|
|
|
151
183
|
arg = positional_args[0]
|
|
152
184
|
has_file = normalized.any? { |a| a.match?(/^--file/) }
|
|
153
185
|
has_method = normalized.any? { |a| a.match?(/^--method/) }
|
|
186
|
+
has_fix = normalized.any? { |a| a.match?(/^--fix/) }
|
|
154
187
|
|
|
155
|
-
if
|
|
188
|
+
if has_fix && !has_file
|
|
189
|
+
normalized << "--file=#{arg}"
|
|
190
|
+
elsif !has_file && (arg.end_with?(".rb") || arg.include?("/"))
|
|
156
191
|
normalized << "--file=#{arg}"
|
|
157
192
|
elsif !has_method && !arg.include?("/")
|
|
158
193
|
normalized << "--method=#{arg}"
|
|
@@ -171,6 +206,8 @@ class CLI
|
|
|
171
206
|
case choice
|
|
172
207
|
when "tests"
|
|
173
208
|
handle_test_generation
|
|
209
|
+
when "audit"
|
|
210
|
+
handle_audit_method
|
|
174
211
|
when "stats"
|
|
175
212
|
user_stats
|
|
176
213
|
when "about"
|
|
@@ -201,6 +238,332 @@ class CLI
|
|
|
201
238
|
end
|
|
202
239
|
end
|
|
203
240
|
|
|
241
|
+
def handle_audit_method
|
|
242
|
+
choice = @go_ui.show_test_type_menu("audit")
|
|
243
|
+
|
|
244
|
+
case choice
|
|
245
|
+
when "controller"
|
|
246
|
+
audit_controller_method
|
|
247
|
+
when "model"
|
|
248
|
+
audit_model_method
|
|
249
|
+
when "service"
|
|
250
|
+
audit_service_method
|
|
251
|
+
when "other"
|
|
252
|
+
audit_other_method
|
|
253
|
+
end
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
def audit_controller_method
|
|
257
|
+
controllers = nil
|
|
258
|
+
|
|
259
|
+
@go_ui.show_spinner("Analyzing controllers...") do
|
|
260
|
+
controllers = Tng::Analyzers::Controller.files_in_dir("app/controllers").map do |file|
|
|
261
|
+
relative_path = file[:path].gsub(%r{^.*app/controllers/}, "").gsub(".rb", "")
|
|
262
|
+
namespaced_name = relative_path.split("/").map(&:camelize).join("::")
|
|
263
|
+
{ name: namespaced_name, path: file[:path] }
|
|
264
|
+
end
|
|
265
|
+
{ success: true, message: "Found #{controllers.length} controllers" }
|
|
266
|
+
end
|
|
267
|
+
|
|
268
|
+
if controllers.empty?
|
|
269
|
+
@go_ui.show_no_items("controllers")
|
|
270
|
+
return
|
|
271
|
+
end
|
|
272
|
+
|
|
273
|
+
items = controllers.map { |c| { name: c[:name], path: c[:path] } }
|
|
274
|
+
|
|
275
|
+
loop do
|
|
276
|
+
selected_name = @go_ui.show_list_view("Select Controller to Audit", items)
|
|
277
|
+
return if selected_name == "back"
|
|
278
|
+
|
|
279
|
+
controller_choice = controllers.find { |c| c[:name] == selected_name }
|
|
280
|
+
next unless controller_choice
|
|
281
|
+
|
|
282
|
+
show_controller_audit_method_selection(controller_choice)
|
|
283
|
+
# If we return here, it means user pressed back from method selection
|
|
284
|
+
# Loop will show controller list again
|
|
285
|
+
end
|
|
286
|
+
end
|
|
287
|
+
|
|
288
|
+
def show_controller_audit_method_selection(controller)
|
|
289
|
+
methods = extract_controller_methods(controller)
|
|
290
|
+
|
|
291
|
+
if methods.empty?
|
|
292
|
+
@go_ui.show_no_items("methods in #{controller[:name]}")
|
|
293
|
+
return
|
|
294
|
+
end
|
|
295
|
+
|
|
296
|
+
items = methods.map { |m| { name: m[:name], path: controller[:name] } }
|
|
297
|
+
selected_name = @go_ui.show_list_view("Select Method to Audit", items)
|
|
298
|
+
return if selected_name == "back"
|
|
299
|
+
|
|
300
|
+
method_choice = methods.find { |m| m[:name] == selected_name }
|
|
301
|
+
return unless method_choice
|
|
302
|
+
|
|
303
|
+
run_audit_for_controller_method(controller, method_choice)
|
|
304
|
+
end
|
|
305
|
+
|
|
306
|
+
def run_audit_for_controller_method(controller, method_info)
|
|
307
|
+
result = nil
|
|
308
|
+
|
|
309
|
+
@go_ui.show_progress("Auditing #{controller[:name]}##{method_info[:name]}") do |progress|
|
|
310
|
+
progress.update("Analyzing method context...", 25)
|
|
311
|
+
progress.update("Running logical analysis...", 50)
|
|
312
|
+
progress.update("Detecting issues and behaviours...", 75)
|
|
313
|
+
|
|
314
|
+
result = Tng::Services::TestGenerator.new(@http_client).run_audit_for_controller_method(
|
|
315
|
+
controller, method_info, progress: progress
|
|
316
|
+
)
|
|
317
|
+
|
|
318
|
+
progress.update("Processing results...", 100)
|
|
319
|
+
|
|
320
|
+
if result&.dig(:error)
|
|
321
|
+
{ message: result[:message] || "Audit failed", error: result[:error] }
|
|
322
|
+
else
|
|
323
|
+
{ message: "Audit complete!" }
|
|
324
|
+
end
|
|
325
|
+
end
|
|
326
|
+
|
|
327
|
+
return @go_ui.show_auth_error(result[:message] || "Audit failed") if result&.dig(:error)
|
|
328
|
+
|
|
329
|
+
display_audit_results(result)
|
|
330
|
+
end
|
|
331
|
+
|
|
332
|
+
def display_audit_results(result)
|
|
333
|
+
audit_data = result[:audit_results]
|
|
334
|
+
|
|
335
|
+
# Save to JSON for persistence
|
|
336
|
+
File.write("audit.json", JSON.pretty_generate(audit_data))
|
|
337
|
+
puts @pastel.green("💾 Audit results saved to audit.json")
|
|
338
|
+
|
|
339
|
+
# Send entire audit_data to go-ui (includes issues, behaviours, method_name, class_name, method_source_with_lines)
|
|
340
|
+
@go_ui.show_audit_results(audit_data, "issues")
|
|
341
|
+
end
|
|
342
|
+
|
|
343
|
+
# Placeholder methods for other component types
|
|
344
|
+
def audit_model_method
|
|
345
|
+
models = nil
|
|
346
|
+
|
|
347
|
+
@go_ui.show_spinner("Analyzing models...") do
|
|
348
|
+
models = Tng::Analyzers::Model.files_in_dir("app/models").map do |file|
|
|
349
|
+
relative_path = file[:path].gsub(%r{^.*app/models/}, "").gsub(".rb", "")
|
|
350
|
+
namespaced_name = relative_path.split("/").map(&:camelize).join("::")
|
|
351
|
+
{ name: namespaced_name, path: file[:path] }
|
|
352
|
+
end
|
|
353
|
+
{ success: true, message: "Found #{models.length} models" }
|
|
354
|
+
end
|
|
355
|
+
|
|
356
|
+
if models.empty?
|
|
357
|
+
@go_ui.show_no_items("models")
|
|
358
|
+
return
|
|
359
|
+
end
|
|
360
|
+
|
|
361
|
+
items = models.map { |m| { name: m[:name], path: m[:path] } }
|
|
362
|
+
|
|
363
|
+
loop do
|
|
364
|
+
selected_name = @go_ui.show_list_view("Select Model to Audit", items)
|
|
365
|
+
return if selected_name == "back"
|
|
366
|
+
|
|
367
|
+
model_choice = models.find { |m| m[:name] == selected_name }
|
|
368
|
+
next unless model_choice
|
|
369
|
+
|
|
370
|
+
show_model_audit_method_selection(model_choice)
|
|
371
|
+
end
|
|
372
|
+
end
|
|
373
|
+
|
|
374
|
+
def show_model_audit_method_selection(model)
|
|
375
|
+
methods = extract_model_methods(model)
|
|
376
|
+
|
|
377
|
+
if methods.empty?
|
|
378
|
+
@go_ui.show_no_items("methods in #{model[:name]}")
|
|
379
|
+
return
|
|
380
|
+
end
|
|
381
|
+
|
|
382
|
+
items = methods.map { |m| { name: m[:name], path: model[:name] } }
|
|
383
|
+
selected_name = @go_ui.show_list_view("Select Method to Audit", items)
|
|
384
|
+
return if selected_name == "back"
|
|
385
|
+
|
|
386
|
+
method_choice = methods.find { |m| m[:name] == selected_name }
|
|
387
|
+
return unless method_choice
|
|
388
|
+
|
|
389
|
+
run_audit_for_model_method(model, method_choice)
|
|
390
|
+
end
|
|
391
|
+
|
|
392
|
+
def run_audit_for_model_method(model, method_info)
|
|
393
|
+
result = nil
|
|
394
|
+
|
|
395
|
+
@go_ui.show_progress("Auditing #{model[:name]}##{method_info[:name]}") do |progress|
|
|
396
|
+
progress.update("Parsing method details...", 25)
|
|
397
|
+
progress.update("Analyzing method context...", 25)
|
|
398
|
+
progress.update("Running logical analysis...", 50)
|
|
399
|
+
progress.update("Detecting issues and behaviours...", 75)
|
|
400
|
+
|
|
401
|
+
result = Tng::Services::TestGenerator.new(@http_client).run_audit_for_model_method(
|
|
402
|
+
model, method_info, progress: progress
|
|
403
|
+
)
|
|
404
|
+
|
|
405
|
+
progress.update("Processing results...", 100)
|
|
406
|
+
|
|
407
|
+
if result&.dig(:error)
|
|
408
|
+
{ message: result[:message] || "Audit failed", error: result[:error] }
|
|
409
|
+
else
|
|
410
|
+
{ message: "Audit complete!" }
|
|
411
|
+
end
|
|
412
|
+
end
|
|
413
|
+
|
|
414
|
+
return @go_ui.show_auth_error(result[:message] || "Audit failed") if result&.dig(:error)
|
|
415
|
+
|
|
416
|
+
display_audit_results(result)
|
|
417
|
+
end
|
|
418
|
+
|
|
419
|
+
def audit_service_method
|
|
420
|
+
services = nil
|
|
421
|
+
|
|
422
|
+
@go_ui.show_spinner("Analyzing services...") do
|
|
423
|
+
services = Tng::Analyzers::Service.files_in_dir.map do |file|
|
|
424
|
+
relative_path = file[:path].gsub(%r{^.*app/services?/}, "").gsub(".rb", "")
|
|
425
|
+
namespaced_name = relative_path.split("/").map(&:camelize).join("::")
|
|
426
|
+
{ name: namespaced_name, path: file[:path] }
|
|
427
|
+
end
|
|
428
|
+
{ success: true, message: "Found #{services.length} services" }
|
|
429
|
+
end
|
|
430
|
+
|
|
431
|
+
if services.empty?
|
|
432
|
+
@go_ui.show_no_items("services")
|
|
433
|
+
return
|
|
434
|
+
end
|
|
435
|
+
|
|
436
|
+
items = services.map { |s| { name: s[:name], path: s[:path] } }
|
|
437
|
+
|
|
438
|
+
loop do
|
|
439
|
+
selected_name = @go_ui.show_list_view("Select Service to Audit", items)
|
|
440
|
+
return if selected_name == "back"
|
|
441
|
+
|
|
442
|
+
service_choice = services.find { |s| s[:name] == selected_name }
|
|
443
|
+
next unless service_choice
|
|
444
|
+
|
|
445
|
+
show_service_audit_method_selection(service_choice)
|
|
446
|
+
end
|
|
447
|
+
end
|
|
448
|
+
|
|
449
|
+
def show_service_audit_method_selection(service)
|
|
450
|
+
methods = extract_service_methods(service)
|
|
451
|
+
|
|
452
|
+
if methods.empty?
|
|
453
|
+
@go_ui.show_no_items("methods in #{service[:name]}")
|
|
454
|
+
return
|
|
455
|
+
end
|
|
456
|
+
|
|
457
|
+
items = methods.map { |m| { name: m[:name], path: service[:name] } }
|
|
458
|
+
selected_name = @go_ui.show_list_view("Select Method to Audit", items)
|
|
459
|
+
return if selected_name == "back"
|
|
460
|
+
|
|
461
|
+
method_choice = methods.find { |m| m[:name] == selected_name }
|
|
462
|
+
return unless method_choice
|
|
463
|
+
|
|
464
|
+
run_audit_for_service_method(service, method_choice)
|
|
465
|
+
end
|
|
466
|
+
|
|
467
|
+
def run_audit_for_service_method(service, method_info)
|
|
468
|
+
result = nil
|
|
469
|
+
|
|
470
|
+
@go_ui.show_progress("Auditing #{service[:name]}##{method_info[:name]}") do |progress|
|
|
471
|
+
progress.update("Parsing method details...", 25)
|
|
472
|
+
progress.update("Analyzing method context...", 25)
|
|
473
|
+
progress.update("Running logical analysis...", 50)
|
|
474
|
+
progress.update("Detecting issues and behaviours...", 75)
|
|
475
|
+
|
|
476
|
+
result = Tng::Services::TestGenerator.new(@http_client).run_audit_for_service_method(
|
|
477
|
+
service, method_info, progress: progress
|
|
478
|
+
)
|
|
479
|
+
|
|
480
|
+
progress.update("Processing results...", 100)
|
|
481
|
+
|
|
482
|
+
if result&.dig(:error)
|
|
483
|
+
{ message: result[:message] || "Audit failed", error: result[:error] }
|
|
484
|
+
else
|
|
485
|
+
{ message: "Audit complete!" }
|
|
486
|
+
end
|
|
487
|
+
end
|
|
488
|
+
|
|
489
|
+
return @go_ui.show_auth_error(result[:message] || "Audit failed") if result&.dig(:error)
|
|
490
|
+
|
|
491
|
+
display_audit_results(result)
|
|
492
|
+
end
|
|
493
|
+
|
|
494
|
+
def audit_other_method
|
|
495
|
+
other_files = nil
|
|
496
|
+
|
|
497
|
+
@go_ui.show_spinner("Analyzing other files...") do
|
|
498
|
+
other_files = Tng::Analyzers::Other.files_in_dir.map do |file|
|
|
499
|
+
relative_path = file[:relative_path].gsub(".rb", "")
|
|
500
|
+
namespaced_name = relative_path.split("/").map(&:camelize).join("::")
|
|
501
|
+
|
|
502
|
+
{ name: namespaced_name, path: file[:path], type: file[:type] }
|
|
503
|
+
end
|
|
504
|
+
{ success: true, message: "Found #{other_files.length} other files" }
|
|
505
|
+
end
|
|
506
|
+
|
|
507
|
+
if other_files.empty?
|
|
508
|
+
@go_ui.show_no_items("other files")
|
|
509
|
+
return
|
|
510
|
+
end
|
|
511
|
+
|
|
512
|
+
items = other_files.map { |f| { name: f[:name], path: f[:path] } }
|
|
513
|
+
selected_name = @go_ui.show_list_view("Select File", items)
|
|
514
|
+
return if selected_name == "back"
|
|
515
|
+
|
|
516
|
+
other_choice = other_files.find { |f| f[:name] == selected_name }
|
|
517
|
+
return unless other_choice
|
|
518
|
+
|
|
519
|
+
show_audit_other_method_selection(other_choice)
|
|
520
|
+
end
|
|
521
|
+
|
|
522
|
+
def show_audit_other_method_selection(other_file)
|
|
523
|
+
methods = extract_other_methods(other_file)
|
|
524
|
+
|
|
525
|
+
if methods.empty?
|
|
526
|
+
@go_ui.show_no_items("methods in #{other_file[:name]}")
|
|
527
|
+
return
|
|
528
|
+
end
|
|
529
|
+
|
|
530
|
+
items = methods.map { |m| { name: m[:name], path: other_file[:name] } }
|
|
531
|
+
selected_name = @go_ui.show_list_view("Audit Method in #{other_file[:name]}", items)
|
|
532
|
+
return if selected_name == "back"
|
|
533
|
+
|
|
534
|
+
method_choice = methods.find { |m| m[:name] == selected_name }
|
|
535
|
+
return unless method_choice
|
|
536
|
+
|
|
537
|
+
run_audit_for_other_method(other_file, method_choice)
|
|
538
|
+
end
|
|
539
|
+
|
|
540
|
+
def run_audit_for_other_method(other_file, method_info)
|
|
541
|
+
result = nil
|
|
542
|
+
|
|
543
|
+
@go_ui.show_progress("Auditing #{other_file[:name]}##{method_info[:name]}") do |progress|
|
|
544
|
+
progress.update("Parsing method details...", 25)
|
|
545
|
+
progress.update("Analyzing method context...", 25)
|
|
546
|
+
progress.update("Running logical analysis...", 50)
|
|
547
|
+
progress.update("Detecting issues and behaviours...", 75)
|
|
548
|
+
|
|
549
|
+
result = Tng::Services::TestGenerator.new(@http_client).run_audit_for_other_method(
|
|
550
|
+
other_file, method_info, progress: progress
|
|
551
|
+
)
|
|
552
|
+
|
|
553
|
+
progress.update("Processing results...", 100)
|
|
554
|
+
|
|
555
|
+
if result&.dig(:error)
|
|
556
|
+
{ message: result[:message] || "Audit failed", error: result[:error] }
|
|
557
|
+
else
|
|
558
|
+
{ message: "Audit complete!" }
|
|
559
|
+
end
|
|
560
|
+
end
|
|
561
|
+
|
|
562
|
+
return @go_ui.show_auth_error(result[:message] || "Audit failed") if result&.dig(:error)
|
|
563
|
+
|
|
564
|
+
display_audit_results(result)
|
|
565
|
+
end
|
|
566
|
+
|
|
204
567
|
def generate_controller_tests
|
|
205
568
|
controllers = nil
|
|
206
569
|
|
|
@@ -220,13 +583,16 @@ class CLI
|
|
|
220
583
|
end
|
|
221
584
|
|
|
222
585
|
items = controllers.map { |c| { name: c[:name], path: c[:path] } }
|
|
223
|
-
selected_name = @go_ui.show_list_view("Select Controller", items)
|
|
224
|
-
return if selected_name == "back"
|
|
225
586
|
|
|
226
|
-
|
|
227
|
-
|
|
587
|
+
loop do
|
|
588
|
+
selected_name = @go_ui.show_list_view("Select Controller", items)
|
|
589
|
+
return if selected_name == "back"
|
|
228
590
|
|
|
229
|
-
|
|
591
|
+
controller_choice = controllers.find { |c| c[:name] == selected_name }
|
|
592
|
+
next unless controller_choice
|
|
593
|
+
|
|
594
|
+
show_controller_test_options(controller_choice)
|
|
595
|
+
end
|
|
230
596
|
end
|
|
231
597
|
|
|
232
598
|
def generate_model_tests
|
|
@@ -248,13 +614,16 @@ class CLI
|
|
|
248
614
|
end
|
|
249
615
|
|
|
250
616
|
items = models.map { |m| { name: m[:name], path: m[:path] } }
|
|
251
|
-
selected_name = @go_ui.show_list_view("Select Model", items)
|
|
252
|
-
return if selected_name == "back"
|
|
253
617
|
|
|
254
|
-
|
|
255
|
-
|
|
618
|
+
loop do
|
|
619
|
+
selected_name = @go_ui.show_list_view("Select Model", items)
|
|
620
|
+
return if selected_name == "back"
|
|
621
|
+
|
|
622
|
+
model_choice = models.find { |m| m[:name] == selected_name }
|
|
623
|
+
next unless model_choice
|
|
256
624
|
|
|
257
|
-
|
|
625
|
+
show_model_test_options(model_choice)
|
|
626
|
+
end
|
|
258
627
|
end
|
|
259
628
|
|
|
260
629
|
def generate_service_tests
|
|
@@ -276,13 +645,16 @@ class CLI
|
|
|
276
645
|
end
|
|
277
646
|
|
|
278
647
|
items = services.map { |s| { name: s[:name], path: s[:path] } }
|
|
279
|
-
selected_name = @go_ui.show_list_view("Select Service", items)
|
|
280
|
-
return if selected_name == "back"
|
|
281
648
|
|
|
282
|
-
|
|
283
|
-
|
|
649
|
+
loop do
|
|
650
|
+
selected_name = @go_ui.show_list_view("Select Service", items)
|
|
651
|
+
return if selected_name == "back"
|
|
284
652
|
|
|
285
|
-
|
|
653
|
+
service_choice = services.find { |s| s[:name] == selected_name }
|
|
654
|
+
next unless service_choice
|
|
655
|
+
|
|
656
|
+
show_service_test_options(service_choice)
|
|
657
|
+
end
|
|
286
658
|
end
|
|
287
659
|
|
|
288
660
|
def generate_other_tests
|
|
@@ -623,7 +995,8 @@ class CLI
|
|
|
623
995
|
@testng,
|
|
624
996
|
@http_client,
|
|
625
997
|
params,
|
|
626
|
-
method(:show_post_generation_menu)
|
|
998
|
+
method(:show_post_generation_menu),
|
|
999
|
+
@go_ui
|
|
627
1000
|
)
|
|
628
1001
|
|
|
629
1002
|
direct_generator.run
|
|
@@ -688,6 +1061,18 @@ class CLI
|
|
|
688
1061
|
)
|
|
689
1062
|
@testng = Services::Testng.new(@http_client)
|
|
690
1063
|
end
|
|
1064
|
+
|
|
1065
|
+
def handle_fix_command
|
|
1066
|
+
file_path = params[:file]
|
|
1067
|
+
unless file_path
|
|
1068
|
+
puts @pastel.red("❌ File parameter is required for fix mode")
|
|
1069
|
+
puts @pastel.yellow("Usage: bundle exec tng --fix --file=your_file.rb")
|
|
1070
|
+
return
|
|
1071
|
+
end
|
|
1072
|
+
|
|
1073
|
+
orchestrator = Tng::Services::FixOrchestrator.new(@pastel, @http_client)
|
|
1074
|
+
orchestrator.run(file_path)
|
|
1075
|
+
end
|
|
691
1076
|
end
|
|
692
1077
|
|
|
693
1078
|
cli = CLI.new
|
data/binaries/go-ui-darwin-amd64
CHANGED
|
Binary file
|
data/binaries/go-ui-darwin-arm64
CHANGED
|
Binary file
|
data/binaries/go-ui-linux-amd64
CHANGED
|
Binary file
|
data/binaries/go-ui-linux-arm64
CHANGED
|
Binary file
|
|
Binary file
|
|
Binary file
|
data/binaries/tng-linux-arm64.so
CHANGED
|
Binary file
|
|
Binary file
|
data/binaries/tng.bundle
CHANGED
|
Binary file
|
|
@@ -64,12 +64,6 @@ module Tng
|
|
|
64
64
|
name: method_name.to_s
|
|
65
65
|
}
|
|
66
66
|
end
|
|
67
|
-
rescue NameError => e
|
|
68
|
-
puts "❌ Could not load controller class #{controller_name}: #{e.message}"
|
|
69
|
-
[]
|
|
70
|
-
rescue StandardError => e
|
|
71
|
-
puts "❌ Error analyzing controller #{controller_name}: #{e.message}"
|
|
72
|
-
[]
|
|
73
67
|
end
|
|
74
68
|
end
|
|
75
69
|
end
|
data/lib/tng/analyzers/model.rb
CHANGED
|
@@ -31,7 +31,8 @@ module Tng
|
|
|
31
31
|
model_class = model_name.constantize
|
|
32
32
|
|
|
33
33
|
instance_methods = model_class.public_instance_methods(false) +
|
|
34
|
-
|
|
34
|
+
model_class.protected_instance_methods(false) +
|
|
35
|
+
model_class.private_instance_methods(false)
|
|
35
36
|
class_methods = model_class.public_methods(false) - Class.public_methods
|
|
36
37
|
|
|
37
38
|
model_file = Object.const_source_location(model_class.name)&.first
|
|
@@ -78,12 +79,6 @@ module Tng
|
|
|
78
79
|
else
|
|
79
80
|
[]
|
|
80
81
|
end
|
|
81
|
-
rescue NameError => e
|
|
82
|
-
puts "❌ Could not load model class #{model_name}: #{e.message}"
|
|
83
|
-
[]
|
|
84
|
-
rescue StandardError => e
|
|
85
|
-
puts "❌ Error analyzing model #{model_name}: #{e.message}"
|
|
86
|
-
[]
|
|
87
82
|
end
|
|
88
83
|
end
|
|
89
84
|
|
|
@@ -109,7 +104,7 @@ module Tng
|
|
|
109
104
|
node.arguments.arguments.each do |arg|
|
|
110
105
|
if arg.is_a?(Prism::SymbolNode)
|
|
111
106
|
validation_method = arg.value
|
|
112
|
-
validations << validation_method if validation_method
|
|
107
|
+
validations << "validate :#{validation_method}" if validation_method
|
|
113
108
|
end
|
|
114
109
|
end
|
|
115
110
|
else
|
|
@@ -117,7 +112,7 @@ module Tng
|
|
|
117
112
|
node.arguments.arguments.each do |arg|
|
|
118
113
|
if arg.is_a?(Prism::SymbolNode)
|
|
119
114
|
attr_name = arg.value
|
|
120
|
-
validations << attr_name if attr_name
|
|
115
|
+
validations << "validates :#{attr_name}" if attr_name
|
|
121
116
|
end
|
|
122
117
|
end
|
|
123
118
|
end
|
|
@@ -32,7 +32,7 @@ module Tng
|
|
|
32
32
|
service_class = service_name.constantize
|
|
33
33
|
|
|
34
34
|
instance_methods = service_class.public_instance_methods(false) +
|
|
35
|
-
|
|
35
|
+
service_class.private_instance_methods(false)
|
|
36
36
|
class_methods = service_class.public_methods(false) - Class.public_methods
|
|
37
37
|
|
|
38
38
|
# Try to get source file from any method, fallback to const_source_location
|
|
@@ -74,12 +74,6 @@ module Tng
|
|
|
74
74
|
end
|
|
75
75
|
|
|
76
76
|
service_methods.map { |method_name| { name: method_name.to_s } }
|
|
77
|
-
rescue NameError => e
|
|
78
|
-
puts "❌ Could not load service class #{service_name}: #{e.message}"
|
|
79
|
-
[]
|
|
80
|
-
rescue StandardError => e
|
|
81
|
-
puts "❌ Error analyzing service #{service_name}: #{e.message}"
|
|
82
|
-
[]
|
|
83
77
|
end
|
|
84
78
|
end
|
|
85
79
|
|