tng 0.4.7 → 0.4.9

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.
data/Rakefile CHANGED
@@ -75,9 +75,17 @@ task :redo do
75
75
  # Code sign the bundles on macOS
76
76
  if RUBY_PLATFORM.include?("darwin")
77
77
  puts "🔐 Signing bundles..."
78
+ system("xattr -dr com.apple.quarantine binaries/ || true")
79
+
80
+ # Sign tng bundle
78
81
  system("codesign --force --sign - #{source_file}")
79
82
  system("codesign --force --sign - #{File.join(destination_dir, binary_name)}")
80
83
 
84
+ # Sign go-ui binaries if present
85
+ Dir.glob("binaries/go-ui-darwin-*").each do |binary|
86
+ system("codesign --force --sign - #{binary}")
87
+ end
88
+
81
89
  # Also sign the gem installation if it exists
82
90
  ruby_version = RUBY_VERSION.split(".")[0..1].join(".")
83
91
  gem_bundle = File.expand_path("~/.local/share/mise/installs/ruby/#{RUBY_VERSION}/lib/ruby/gems/#{ruby_version}.0/gems/tng-*/binaries/tng.bundle")
data/bin/tng CHANGED
@@ -48,6 +48,14 @@ class CLI
48
48
  example "Duplicate Detection:", ""
49
49
  example " bundle exec tng app/services/pricing_service.rb --clones", ""
50
50
  example " bundle exec tng --file=users_controller.rb --clones --level=2", ""
51
+ example ""
52
+ example "Dead Code Detection:", ""
53
+ example " bundle exec tng app/models/user.rb --deadcode", ""
54
+ example " bundle exec tng --file=app/services/auth_service.rb -d", ""
55
+ example ""
56
+ example "X-Ray Visualization:", ""
57
+ example " bundle exec tng app/services/payment_processor.rb process_payment --xray", ""
58
+ example " bundle exec tng --file=users_controller.rb --method=create --xray --json", ""
51
59
  end
52
60
 
53
61
  option :file do
@@ -68,10 +76,16 @@ class CLI
68
76
  desc "Run in clone detection mode (find code duplication)"
69
77
  end
70
78
 
79
+ flag :deadcode do
80
+ short "-d"
81
+ long "--deadcode"
82
+ desc "Run in dead code detection mode (unreachable code, unused variables)"
83
+ end
84
+
71
85
  option :level do
72
86
  short "-l"
73
87
  long "--level=LEVEL"
74
- desc "Analysis level: 1 (Token), 2 (Structural), all (Both)"
88
+ desc "Analysis level: 1 (Token), 2 (Structural), 3 (Fuzzy), all (All levels)"
75
89
  end
76
90
 
77
91
  flag :audit do
@@ -85,6 +99,11 @@ class CLI
85
99
  desc "Generate and visualize a symbolic trace"
86
100
  end
87
101
 
102
+ flag :xray do
103
+ long "--xray"
104
+ desc "Generate X-Ray visualization (Mermaid flowchart)"
105
+ end
106
+
88
107
  flag :json do
89
108
  short "-j"
90
109
  long "--json"
@@ -140,6 +159,10 @@ class CLI
140
159
  handle_fix_command
141
160
  elsif params[:trace] && params[:file]
142
161
  run_direct_trace
162
+ elsif params[:deadcode] && params[:file]
163
+ run_dead_code_detection
164
+ elsif params[:xray] && params[:file]
165
+ run_direct_xray
143
166
  elsif params[:file]
144
167
  run_direct_generation
145
168
  else
@@ -153,7 +176,10 @@ class CLI
153
176
  return unless check_configuration
154
177
 
155
178
  check_system_status
179
+ # puts "DEBUG: Verifying authentication..."
156
180
  return unless check_authentication
181
+ # puts "DEBUG: Authentication verified."
182
+
157
183
 
158
184
  clear_screen
159
185
  @go_ui.show_banner(Tng::VERSION)
@@ -175,12 +201,16 @@ class CLI
175
201
  normalized << "--audit"
176
202
  when /^(?:--)?(trace)$/
177
203
  normalized << "--trace"
204
+ when /^(?:--)?(xray)$/
205
+ normalized << "--xray"
178
206
  when /^(?:--)?(json|j)$/
179
207
  normalized << "--json"
180
208
  when /^(?:--)?(fix|x)$/
181
209
  normalized << "--fix"
182
210
  when /^(?:--)?(clones|c)$/
183
211
  normalized << "--clones"
212
+ when /^(?:--)?(deadcode|d)$/
213
+ normalized << "--deadcode"
184
214
  when /^(?:--)?(level|l)=(.+)$/
185
215
  normalized << "--level=#{::Regexp.last_match(2)}"
186
216
  when /^(help|h)=(.+)$/
@@ -240,8 +270,12 @@ class CLI
240
270
  handle_audit_method
241
271
  when "trace"
242
272
  handle_trace_method
273
+ when "xray"
274
+ handle_xray_method
243
275
  when "clones"
244
276
  handle_clone_detection
277
+ when "deadcode"
278
+ handle_dead_code_detection_menu
245
279
  when "stats"
246
280
  user_stats
247
281
  when "about"
@@ -506,6 +540,30 @@ class CLI
506
540
  @go_ui.show_audit_results(audit_data, "issues")
507
541
  end
508
542
 
543
+ def run_dead_code_detection
544
+ file_path = params[:file]
545
+ unless File.exist?(file_path)
546
+ puts @pastel.red("Error: File '#{file_path}' not found.")
547
+ return
548
+ end
549
+
550
+ absolute_path = File.expand_path(file_path)
551
+
552
+ begin
553
+ # Always get JSON from Rust for flexibility
554
+ result = Tng.detect_dead_code(absolute_path, true)
555
+
556
+ if @go_ui
557
+ @go_ui.show_dead_code_results(absolute_path, result)
558
+ else
559
+ # Fallback to text output
560
+ puts Tng.detect_dead_code(absolute_path, false)
561
+ end
562
+ rescue => e
563
+ puts @pastel.red("Error detecting dead code: #{e.message}")
564
+ end
565
+ end
566
+
509
567
  # Placeholder methods for other component types
510
568
  def audit_model_method
511
569
  models = nil
@@ -1169,13 +1227,18 @@ class CLI
1169
1227
  end
1170
1228
 
1171
1229
  def show_post_generation_menu(result)
1230
+ if @go_ui.is_a?(Tng::UI::JsonSession)
1231
+ @go_ui.show_generation_result(result)
1232
+ return
1233
+ end
1234
+
1172
1235
  is_direct_mode = params[:file]
1173
1236
 
1174
1237
  # Direct mode - just print info and exit
1175
1238
  if is_direct_mode
1176
1239
  puts "\n🚀 Test Generated Successfully!"
1177
1240
  puts "📁 File: #{result[:file_path]}"
1178
- puts "▶️ Run: #{result[:run_command]}"
1241
+ puts "▶\u{fe0f} Run: #{result[:run_command]}"
1179
1242
  puts "✅ Test generation completed successfully!"
1180
1243
  return
1181
1244
  end
@@ -1196,19 +1259,19 @@ class CLI
1196
1259
  end
1197
1260
  end
1198
1261
 
1199
- def copy_to_clipboard(command)
1262
+ def copy_to_clipboard(command, silent: false)
1200
1263
  if RUBY_PLATFORM.include?("darwin")
1201
1264
  IO.popen("pbcopy", "w") { |io| io.print command }
1202
- @go_ui.show_clipboard_success(command)
1265
+ @go_ui.show_clipboard_success(command) unless silent
1203
1266
  elsif RUBY_PLATFORM.include?("linux")
1204
1267
  begin
1205
1268
  IO.popen("xclip -selection clipboard", "w") { |io| io.print command }
1206
- @go_ui.show_clipboard_success(command)
1269
+ @go_ui.show_clipboard_success(command) unless silent
1207
1270
  rescue Errno::ENOENT
1208
- puts "\n📋 Copy this command:\n#{command}\n"
1271
+ puts "\n📋 Copy this command:\n#{command}\n" unless silent
1209
1272
  end
1210
1273
  else
1211
- puts "\n📋 Copy this command:\n#{command}\n"
1274
+ puts "\n📋 Copy this command:\n#{command}\n" unless silent
1212
1275
  end
1213
1276
  end
1214
1277
 
@@ -1255,6 +1318,21 @@ class CLI
1255
1318
  end
1256
1319
  end
1257
1320
 
1321
+ def handle_xray_method
1322
+ choice = @go_ui.show_test_type_menu("xray")
1323
+
1324
+ case choice
1325
+ when "controller"
1326
+ xray_controller_method
1327
+ when "model"
1328
+ xray_model_method
1329
+ when "service"
1330
+ xray_service_method
1331
+ when "other"
1332
+ xray_other_method
1333
+ end
1334
+ end
1335
+
1258
1336
  def trace_controller_method
1259
1337
  select_controller_and_method("Trace") do |controller, method_info|
1260
1338
  run_trace_for_method(controller, method_info)
@@ -1279,6 +1357,30 @@ class CLI
1279
1357
  end
1280
1358
  end
1281
1359
 
1360
+ def xray_controller_method
1361
+ select_controller_and_method("X-Ray") do |controller, method_info|
1362
+ run_xray_for_method(controller, method_info)
1363
+ end
1364
+ end
1365
+
1366
+ def xray_model_method
1367
+ select_model_and_method("X-Ray") do |model, method_info|
1368
+ run_xray_for_method(model, method_info)
1369
+ end
1370
+ end
1371
+
1372
+ def xray_service_method
1373
+ select_service_and_method("X-Ray") do |service, method_info|
1374
+ run_xray_for_method(service, method_info)
1375
+ end
1376
+ end
1377
+
1378
+ def xray_other_method
1379
+ select_other_and_method("X-Ray") do |file, method_info|
1380
+ run_xray_for_method(file, method_info)
1381
+ end
1382
+ end
1383
+
1282
1384
  def run_trace_for_method(file_info, method_info)
1283
1385
  result_path = nil
1284
1386
 
@@ -1332,6 +1434,68 @@ class CLI
1332
1434
  run_trace_for_method(file_info, method_info)
1333
1435
  end
1334
1436
 
1437
+ def run_direct_xray
1438
+ return unless check_configuration
1439
+
1440
+ return unless check_authentication_configuration
1441
+
1442
+ file_path = params[:file]
1443
+ method_name = params[:method]
1444
+
1445
+ unless method_name
1446
+ puts @pastel.decorate("Please specify a method name with --method or -m", Tng::UI::Theme.color(:error))
1447
+ return
1448
+ end
1449
+
1450
+ full_path = File.expand_path(file_path)
1451
+ unless File.exist?(full_path)
1452
+ puts @pastel.decorate("File not found: #{full_path}", Tng::UI::Theme.color(:error))
1453
+ return
1454
+ end
1455
+
1456
+ file_info = { path: full_path, name: File.basename(full_path) }
1457
+ method_info = { name: method_name }
1458
+
1459
+ run_xray_for_method(file_info, method_info)
1460
+ end
1461
+
1462
+ def run_xray_for_method(file_info, method_info)
1463
+ xray_result = nil
1464
+
1465
+ @go_ui.show_spinner("Generating X-Ray for #{method_info[:name]}...") do
1466
+ begin
1467
+ type = Tng::Services::FileTypeDetector.detect_type(file_info[:path])
1468
+ file_object = Tng::Services::FileTypeDetector.build_file_object(file_info[:path], type)
1469
+ generator = Tng::Services::TestGenerator.new(@http_client)
1470
+
1471
+ xray_result = case type
1472
+ when "controller"
1473
+ generator.run_xray_for_controller_method(file_object, method_info)
1474
+ when "model"
1475
+ generator.run_xray_for_model_method(file_object, method_info)
1476
+ when "service"
1477
+ generator.run_xray_for_service_method(file_object, method_info)
1478
+ else
1479
+ generator.run_xray_for_other_method(file_object, method_info)
1480
+ end
1481
+
1482
+ if xray_result&.dig(:error)
1483
+ { success: false, message: xray_result[:message] || "X-Ray failed" }
1484
+ else
1485
+ { success: true, message: "X-Ray generated" }
1486
+ end
1487
+ rescue => e
1488
+ { success: false, message: e.message }
1489
+ end
1490
+ end
1491
+
1492
+ return @go_ui.show_auth_error(xray_result[:message] || "X-Ray failed") if xray_result&.dig(:error)
1493
+
1494
+ if xray_result && xray_result[:mermaid_code]
1495
+ @go_ui.show_xray_results(xray_result, method_info)
1496
+ end
1497
+ end
1498
+
1335
1499
  def select_controller_and_method(action_name)
1336
1500
  controllers = nil
1337
1501
  @go_ui.show_spinner("Analyzing controllers...") do
@@ -1470,18 +1634,129 @@ class CLI
1470
1634
  choice = files.find { |f| f[:name] == selected }
1471
1635
  next unless choice
1472
1636
 
1473
- methods = extract_other_methods(choice)
1474
- if methods.empty?
1475
- @go_ui.show_no_items("methods in #{choice[:name]}")
1476
- next
1477
- end
1637
+ # For dead code, we don't need to select a method, just the file.
1638
+ # But this helper yields (file_choice, method_choice).
1639
+ # We might need a simpler selector for dead code if it's file-based only.
1640
+ # Re-using method selector for now if we want per-method, but dead code is usually per-file.
1478
1641
 
1479
- m_items = methods.map { |m| { name: m[:name], path: choice[:name] } }
1480
- m_selected = @go_ui.show_list_view("Select Method to #{action_name}", m_items)
1481
- next if m_selected == "back"
1642
+ # Actually, dead code analysis is whole-file.
1643
+ # So we should probably just return the file choice.
1482
1644
 
1483
- m_choice = methods.find { |m| m[:name] == m_selected }
1484
- yield(choice, m_choice)
1645
+ # Let's implement specific helpers for dead code that just pick files.
1646
+ end
1647
+ end
1648
+
1649
+ def handle_dead_code_detection_menu
1650
+ # Pick a file type first to narrow down
1651
+ choice = @go_ui.show_test_type_menu("deadcode")
1652
+ return if choice == "back"
1653
+
1654
+ case choice
1655
+ when "controller"
1656
+ detect_dead_code_for_type("app/controllers", Tng::Analyzers::Controller)
1657
+ when "model"
1658
+ detect_dead_code_for_type("app/models", Tng::Analyzers::Model)
1659
+ when "service"
1660
+ # Service analyzer is a bit different, it scans app/services usually
1661
+ detect_dead_code_for_service
1662
+ when "other"
1663
+ detect_dead_code_for_other
1664
+ end
1665
+ end
1666
+
1667
+ def detect_dead_code_for_type(dir, analyzer_class)
1668
+ files = nil
1669
+ @go_ui.show_spinner("Analyzing files in #{dir}...") do
1670
+ files = analyzer_class.files_in_dir(dir).map do |file|
1671
+ relative_path = file[:path].gsub(%r{^.*#{dir}/}, "").gsub(".rb", "")
1672
+ namespaced_name = relative_path.split("/").map(&:camelize).join("::")
1673
+ { name: namespaced_name, path: file[:path] }
1674
+ end
1675
+ { success: true, message: "Found #{files.length} files" }
1676
+ end
1677
+
1678
+ return @go_ui.show_no_items(dir) if files.empty?
1679
+
1680
+ items = files.map { |f| { name: f[:name], path: f[:path] } }
1681
+
1682
+ loop do
1683
+ selected = @go_ui.show_list_view("Select File to Analyze", items)
1684
+ return if selected == "back"
1685
+
1686
+ choice = files.find { |f| f[:name] == selected }
1687
+ next unless choice
1688
+
1689
+ # Run detection on the file
1690
+ run_dead_code_detection_interactive(choice[:path])
1691
+ end
1692
+ end
1693
+
1694
+ def detect_dead_code_for_service
1695
+ services = nil
1696
+ @go_ui.show_spinner("Analyzing services...") do
1697
+ services = Tng::Analyzers::Service.files_in_dir.map do |file|
1698
+ relative_path = file[:path].gsub(%r{^.*app/services?/}, "").gsub(".rb", "")
1699
+ namespaced_name = relative_path.split("/").map(&:camelize).join("::")
1700
+ { name: namespaced_name, path: file[:path] }
1701
+ end
1702
+ { success: true, message: "Found #{services.length} services" }
1703
+ end
1704
+
1705
+ return @go_ui.show_no_items("services") if services.empty?
1706
+
1707
+ items = services.map { |s| { name: s[:name], path: s[:path] } }
1708
+
1709
+ loop do
1710
+ selected = @go_ui.show_list_view("Select Service to Analyze", items)
1711
+ return if selected == "back"
1712
+
1713
+ choice = services.find { |s| s[:name] == selected }
1714
+ next unless choice
1715
+
1716
+ run_dead_code_detection_interactive(choice[:path])
1717
+ end
1718
+ end
1719
+
1720
+ def detect_dead_code_for_other
1721
+ files = nil
1722
+ @go_ui.show_spinner("Analyzing other files...") do
1723
+ files = Tng::Analyzers::Other.files_in_dir.map do |file|
1724
+ relative_path = file[:relative_path].gsub(".rb", "")
1725
+ namespaced_name = relative_path.split("/").map(&:camelize).join("::")
1726
+ { name: namespaced_name, path: file[:path], type: file[:type] }
1727
+ end
1728
+ { success: true, message: "Found #{files.length} other files" }
1729
+ end
1730
+
1731
+ return @go_ui.show_no_items("other files") if files.empty?
1732
+
1733
+ items = files.map { |f| { name: f[:name], path: f[:path] } }
1734
+
1735
+ loop do
1736
+ selected = @go_ui.show_list_view("Select File to Analyze", items)
1737
+ return if selected == "back"
1738
+
1739
+ choice = files.find { |f| f[:name] == selected }
1740
+ next unless choice
1741
+
1742
+ run_dead_code_detection_interactive(choice[:path])
1743
+ end
1744
+ end
1745
+
1746
+ def run_dead_code_detection_interactive(path)
1747
+ @go_ui.show_spinner("Detecting dead code...") do
1748
+ # Just a visual delay or setup if needed
1749
+ sleep(0.5)
1750
+ { success: true, message: "Analysis complete" }
1751
+ end
1752
+
1753
+ # Reuse the CLI logic but bypass params
1754
+ absolute_path = File.expand_path(path)
1755
+ begin
1756
+ result = Tng.detect_dead_code(absolute_path, true)
1757
+ @go_ui.show_dead_code_results(absolute_path, result)
1758
+ rescue => e
1759
+ @go_ui.display_error("Error detecting dead code: #{e.message}")
1485
1760
  end
1486
1761
  end
1487
1762
  end
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
data/binaries/tng.bundle CHANGED
Binary file
@@ -34,6 +34,7 @@ module Tng
34
34
  return unless Rails.env.development?
35
35
 
36
36
  Tng.configure do |config|
37
+ # Get your API key from https://app.tng.sh/
37
38
  config.api_key = ENV["TNG_API_KEY"]
38
39
  config.base_url = ENV["API_BASE_URI"] || "https://app.tng.sh/"
39
40
 
@@ -96,19 +96,41 @@ module Tng
96
96
  return
97
97
  end
98
98
 
99
- @go_ui.display_info(@pastel.bright_white("🎯 #{@params[:audit] ? "Auditing" : "Generating test for"} #{file_object[:name]}##{method_info[:name]}..."))
99
+ action =
100
+ if @params[:trace]
101
+ "Tracing"
102
+ elsif @params[:xray]
103
+ "Generating X-Ray for"
104
+ elsif @params[:audit]
105
+ "Auditing"
106
+ else
107
+ "Generating test for"
108
+ end
109
+ @go_ui.display_info(@pastel.bright_white("🎯 #{action} #{file_object[:name]}##{method_info[:name]}..."))
100
110
 
101
111
  result = @go_ui.show_progress("Processing...") do |progress|
102
112
  generate_test_result(file_object, method_info, type, progress)
103
113
  end
104
114
 
105
115
  if result&.dig(:error)
106
- @go_ui.display_error(@pastel.red("❌ #{@params[:audit] ? "Audit" : "Test generation"} failed: #{result[:message]}"))
116
+ label =
117
+ if @params[:trace]
118
+ "Trace"
119
+ elsif @params[:xray]
120
+ "X-Ray"
121
+ elsif @params[:audit]
122
+ "Audit"
123
+ else
124
+ "Test generation"
125
+ end
126
+ @go_ui.display_error(@pastel.red("❌ #{label} failed: #{result[:message]}"))
107
127
  elsif result&.dig(:trace_generated)
108
128
  if result[:file_path]
109
129
  @go_ui.show_trace_results(result[:file_path])
110
130
  File.unlink(result[:file_path]) if File.exist?(result[:file_path])
111
131
  end
132
+ elsif @params[:xray]
133
+ @go_ui.show_xray_results(result, method_info) if result
112
134
  elsif @params[:audit]
113
135
  display_audit_results(result)
114
136
  elsif result && result[:file_path]
@@ -147,6 +169,13 @@ module Tng
147
169
 
148
170
  if @params[:trace]
149
171
  perform_trace_analysis(file_object, method_info, progress)
172
+ elsif @params[:xray]
173
+ case type
174
+ when "controller" then generator.run_xray_for_controller_method(file_object, method_info, progress: progress)
175
+ when "model" then generator.run_xray_for_model_method(file_object, method_info, progress: progress)
176
+ when "service" then generator.run_xray_for_service_method(file_object, method_info, progress: progress)
177
+ else generator.run_xray_for_other_method(file_object, method_info, progress: progress)
178
+ end
150
179
  elsif @params[:audit]
151
180
  case type
152
181
  when "controller" then generator.run_audit_for_controller_method(file_object, method_info, progress: progress)
@@ -36,6 +36,7 @@ module Tng
36
36
  generate_test_for_type(other_file, method_info, :other, progress: progress)
37
37
  end
38
38
 
39
+
39
40
  # Audit mode - async, polls for completion
40
41
  def run_audit_for_controller_method(controller, method_info, progress: nil)
41
42
  run_audit_for_type(controller, method_info, :controller, progress: progress)
@@ -53,6 +54,23 @@ module Tng
53
54
  run_audit_for_type(other_file, method_info, :other, progress: progress)
54
55
  end
55
56
 
57
+ # X-Ray mode - async, polls for completion
58
+ def run_xray_for_controller_method(controller, method_info, progress: nil)
59
+ run_xray_for_type(controller, method_info, :controller, progress: progress)
60
+ end
61
+
62
+ def run_xray_for_model_method(model, method_info, progress: nil)
63
+ run_xray_for_type(model, method_info, :model, progress: progress)
64
+ end
65
+
66
+ def run_xray_for_service_method(service, method_info, progress: nil)
67
+ run_xray_for_type(service, method_info, :service, progress: progress)
68
+ end
69
+
70
+ def run_xray_for_other_method(other_file, method_info, progress: nil)
71
+ run_xray_for_type(other_file, method_info, :other, progress: progress)
72
+ end
73
+
56
74
  def run_audit_for_type(file_object, method_info, type, progress: nil)
57
75
  response = send_audit_request_for_type(file_object, method_info, type)
58
76
  return { error: :network_error, message: "Network error" } unless response
@@ -90,13 +108,59 @@ module Tng
90
108
  # Pass audit_mode: true as 8th parameter
91
109
  case type
92
110
  when :controller
93
- Tng.send_request_for_controller(name, file_object[:path], method_info, *config, true)
111
+ Tng.send_request_for_controller(name, file_object[:path], method_info, *config, true, false)
112
+ when :model
113
+ Tng.send_request_for_model(name, file_object[:path], method_info, *config, true, false)
114
+ when :service
115
+ Tng.send_request_for_service(name, file_object[:path], method_info, *config, true, false)
116
+ when :other
117
+ Tng.send_request_for_other(name, file_object[:path], method_info, *config, true, false)
118
+ end
119
+ end
120
+
121
+ def run_xray_for_type(file_object, method_info, type, progress: nil)
122
+ response = send_xray_request_for_type(file_object, method_info, type)
123
+ return { error: :network_error, message: "Network error" } unless response
124
+
125
+ if response.is_a?(HTTPX::ErrorResponse)
126
+ return { error: :network_error, message: response.error&.message || "Network error" }
127
+ end
128
+
129
+ return { error: :auth_failed, message: "Invalid or missing API key" } if response.status == 401
130
+ return { error: :auth_failed, message: "API key expired" } if response.status == 403
131
+
132
+ begin
133
+ job_data = JSON.parse(response.body)
134
+ return { error: :server_error, message: job_data["error"] } if job_data["error"]
135
+
136
+ job_id = job_data["job_id"]
137
+ return { error: :server_error, message: "No job_id returned" } unless job_id
138
+
139
+ # Poll for completion (similar to audit)
140
+ result = poll_for_completion(job_id, progress: progress)
141
+ return { error: :timeout, message: "X-Ray generation timed out" } unless result
142
+
143
+ # X-Ray results should contain mermaid_code and explanation
144
+ result
145
+ rescue JSON::ParserError => e
146
+ { error: :parse_error, message: "Failed to parse X-Ray response: #{e.message}" }
147
+ end
148
+ end
149
+
150
+ def send_xray_request_for_type(file_object, method_info, type)
151
+ config = request_config
152
+ name = file_object[:name] || File.basename(file_object[:path], ".rb")
153
+
154
+ # Pass xray_mode: true as 9th parameter (audit_mode: false, xray_mode: true)
155
+ case type
156
+ when :controller
157
+ Tng.send_request_for_controller(name, file_object[:path], method_info, *config, false, true)
94
158
  when :model
95
- Tng.send_request_for_model(name, file_object[:path], method_info, *config, true)
159
+ Tng.send_request_for_model(name, file_object[:path], method_info, *config, false, true)
96
160
  when :service
97
- Tng.send_request_for_service(name, file_object[:path], method_info, *config, true)
161
+ Tng.send_request_for_service(name, file_object[:path], method_info, *config, false, true)
98
162
  when :other
99
- Tng.send_request_for_other(name, file_object[:path], method_info, *config, true)
163
+ Tng.send_request_for_other(name, file_object[:path], method_info, *config, false, true)
100
164
  end
101
165
  end
102
166
 
@@ -144,13 +208,13 @@ module Tng
144
208
  # Pass audit_mode: false as 8th parameter for normal test generation
145
209
  case type
146
210
  when :controller
147
- Tng.send_request_for_controller(name, file_object[:path], method_info, *config, false)
211
+ Tng.send_request_for_controller(name, file_object[:path], method_info, *config, false, false)
148
212
  when :model
149
- Tng.send_request_for_model(name, file_object[:path], method_info, *config, false)
213
+ Tng.send_request_for_model(name, file_object[:path], method_info, *config, false, false)
150
214
  when :service
151
- Tng.send_request_for_service(name, file_object[:path], method_info, *config, false)
215
+ Tng.send_request_for_service(name, file_object[:path], method_info, *config, false, false)
152
216
  when :other
153
- Tng.send_request_for_other(name, file_object[:path], method_info, *config, false)
217
+ Tng.send_request_for_other(name, file_object[:path], method_info, *config, false, false)
154
218
  end
155
219
  end
156
220