tng 0.3.3 โ 0.3.5
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/Rakefile +20 -0
- data/bin/tng +8 -8
- 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-linux-arm64.so +0 -0
- data/binaries/tng-linux-x86_64.so +0 -0
- data/binaries/tng.bundle +0 -0
- data/lib/generators/tng/install_generator.rb +41 -370
- data/lib/tng/analyzers/model.rb +2 -1
- data/lib/tng/analyzers/other.rb +4 -2
- data/lib/tng/analyzers/service.rb +2 -1
- data/lib/tng/services/test_generator.rb +36 -40
- data/lib/tng/services/user_app_config.rb +0 -11
- data/lib/tng/ui/go_ui_session.rb +3 -2
- data/lib/tng/utils.rb +46 -23
- data/lib/tng/version.rb +1 -1
- data/lib/tng.rb +6 -213
- metadata +26 -28
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: ca324d4cd07a9428327d1d75b4d75bfa1aaaf79595b9b308f49b230c7741f50c
|
|
4
|
+
data.tar.gz: f2e6aab56415c983e235a0f4fe6c9ca20f8bdea6d1441941896bf21fbb1941a1
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 05d121c10f312e0fb33d781e312b7f5385001bb6886ee023df6c0e8ff22037501f0e06850e95fe9ca6c83bf6579ea2ff166ab324ac84313aa8c39e6b952609a1
|
|
7
|
+
data.tar.gz: bf91663c5390fa85787eeaf5b9e290ce4b32c53cd8a29e02c7d82e0d0bc765c5155a8ced68e4d50e5b35eb273896ba3e504a52fd07b87e0f1bb4cb3aea0e76a4
|
data/Rakefile
CHANGED
|
@@ -114,6 +114,26 @@ task :build_both do
|
|
|
114
114
|
system("./build_both.sh") || (puts "โ Build failed"; exit 1)
|
|
115
115
|
end
|
|
116
116
|
|
|
117
|
+
task :verify_binaries do
|
|
118
|
+
puts "๐ Verifying binary artifacts..."
|
|
119
|
+
required_go = %w[go-ui-darwin-amd64 go-ui-darwin-arm64 go-ui-linux-amd64 go-ui-linux-arm64]
|
|
120
|
+
missing = required_go.reject { |f| File.exist?("binaries/#{f}") }
|
|
121
|
+
|
|
122
|
+
if missing.any?
|
|
123
|
+
abort "โ Cannot release: Missing binaries in binaries/: #{missing.join(', ')}\nRun 'rake build_both' or './build_both.sh' first!"
|
|
124
|
+
end
|
|
125
|
+
puts "โ
All required binaries present."
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
# Add this task to suppress the native artifact warning
|
|
129
|
+
task release: :build_both do
|
|
130
|
+
# Temporarily rename the binary to avoid the warning during gem build
|
|
131
|
+
puts "๐ฆ Building gem with pre-compiled binaries..." if File.exist?("binaries/tng.bundle")
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
# Ensure verify_binaries runs before release
|
|
135
|
+
task release: :verify_binaries
|
|
136
|
+
|
|
117
137
|
desc "Development build: compile for current OS and build gem"
|
|
118
138
|
task :dev do
|
|
119
139
|
puts "๐งน Cleaning Rust binaries (preserving go-ui)..."
|
data/bin/tng
CHANGED
|
@@ -340,7 +340,8 @@ class CLI
|
|
|
340
340
|
progress.update("Extracting method context...", 60)
|
|
341
341
|
progress.update("Generating method test code...", 80)
|
|
342
342
|
|
|
343
|
-
result = Tng::Services::TestGenerator.new(@http_client).run_for_controller_method(controller, method_info,
|
|
343
|
+
result = Tng::Services::TestGenerator.new(@http_client).run_for_controller_method(controller, method_info,
|
|
344
|
+
progress: progress)
|
|
344
345
|
|
|
345
346
|
progress.update("Writing test file...", 100)
|
|
346
347
|
|
|
@@ -396,7 +397,8 @@ class CLI
|
|
|
396
397
|
progress.update("Analyzing method dependencies...", 50)
|
|
397
398
|
progress.update("Generating method test code...", 75)
|
|
398
399
|
|
|
399
|
-
result = Tng::Services::TestGenerator.new(@http_client).run_for_model_method(model, method_info,
|
|
400
|
+
result = Tng::Services::TestGenerator.new(@http_client).run_for_model_method(model, method_info,
|
|
401
|
+
progress: progress)
|
|
400
402
|
|
|
401
403
|
progress.update("Writing test file...", 100)
|
|
402
404
|
|
|
@@ -416,7 +418,8 @@ class CLI
|
|
|
416
418
|
progress.update("Analyzing method dependencies...", 50)
|
|
417
419
|
progress.update("Generating method test code...", 75)
|
|
418
420
|
|
|
419
|
-
result = Tng::Services::TestGenerator.new(@http_client).run_for_service_method(service, method_info,
|
|
421
|
+
result = Tng::Services::TestGenerator.new(@http_client).run_for_service_method(service, method_info,
|
|
422
|
+
progress: progress)
|
|
420
423
|
|
|
421
424
|
progress.update("Writing test file...", 100)
|
|
422
425
|
|
|
@@ -478,7 +481,8 @@ class CLI
|
|
|
478
481
|
progress.update("Analyzing method dependencies...", 50)
|
|
479
482
|
progress.update("Generating method test code...", 75)
|
|
480
483
|
|
|
481
|
-
result = Tng::Services::TestGenerator.new(@http_client).run_for_other_method(other_file, method_info,
|
|
484
|
+
result = Tng::Services::TestGenerator.new(@http_client).run_for_other_method(other_file, method_info,
|
|
485
|
+
progress: progress)
|
|
482
486
|
|
|
483
487
|
progress.update("Writing test file...", 100)
|
|
484
488
|
|
|
@@ -653,8 +657,6 @@ class CLI
|
|
|
653
657
|
end
|
|
654
658
|
end
|
|
655
659
|
|
|
656
|
-
|
|
657
|
-
|
|
658
660
|
def copy_to_clipboard(command)
|
|
659
661
|
if RUBY_PLATFORM.include?("darwin")
|
|
660
662
|
IO.popen("pbcopy", "w") { |io| io.print command }
|
|
@@ -671,8 +673,6 @@ class CLI
|
|
|
671
673
|
end
|
|
672
674
|
end
|
|
673
675
|
|
|
674
|
-
|
|
675
|
-
|
|
676
676
|
def initialize_config_and_clients
|
|
677
677
|
@config_initialized = true
|
|
678
678
|
|
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
|
data/binaries/tng-linux-arm64.so
CHANGED
|
Binary file
|
|
Binary file
|
data/binaries/tng.bundle
ADDED
|
File without changes
|
|
@@ -1,399 +1,70 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require "rails/generators"
|
|
4
|
-
require "yaml"
|
|
5
|
-
require "tng"
|
|
6
|
-
require "pathname"
|
|
7
4
|
|
|
8
5
|
module Tng
|
|
9
6
|
module Generators
|
|
10
7
|
class InstallGenerator < Rails::Generators::Base
|
|
11
|
-
desc "Creates a
|
|
12
|
-
|
|
13
|
-
def self.source_root
|
|
14
|
-
@source_root ||= File.expand_path("templates", __dir__)
|
|
15
|
-
end
|
|
8
|
+
desc "Creates a TNG configuration file"
|
|
16
9
|
|
|
17
10
|
def create_tng_configuration
|
|
18
|
-
@test_framework = detect_test_framework
|
|
19
|
-
@authentication_library = detect_authentication_library
|
|
20
|
-
@authz_library = detect_authorization_library
|
|
21
|
-
@factory_library = detect_factory_library
|
|
22
|
-
@test_examples = detect_test_examples
|
|
23
|
-
|
|
24
11
|
initializer_path = "config/initializers/tng.rb"
|
|
25
12
|
|
|
26
13
|
if File.exist?(initializer_path)
|
|
27
14
|
say "Configuration file already exists at #{initializer_path}", :yellow
|
|
28
15
|
if yes?("Do you want to overwrite it? (y/n)")
|
|
29
16
|
remove_file initializer_path
|
|
30
|
-
create_file initializer_path,
|
|
17
|
+
create_file initializer_path, configuration_template
|
|
31
18
|
say "TNG configuration file updated!", :green
|
|
32
19
|
else
|
|
33
20
|
say "Skipping configuration file creation.", :blue
|
|
34
|
-
nil
|
|
35
21
|
end
|
|
36
22
|
else
|
|
37
|
-
create_file initializer_path,
|
|
38
|
-
say "
|
|
23
|
+
create_file initializer_path, configuration_template
|
|
24
|
+
say "TNG configuration file created at #{initializer_path}", :green
|
|
39
25
|
end
|
|
40
26
|
end
|
|
41
27
|
|
|
42
|
-
def ruby_configuration_template
|
|
43
|
-
framework_config = generate_framework_config(@test_framework)
|
|
44
|
-
|
|
45
|
-
if @authentication_library && @authentication_library != "none"
|
|
46
|
-
auth_enabled = "config.authentication_enabled = true"
|
|
47
|
-
auth_comment = " (detected: #{@authentication_library})"
|
|
48
|
-
auth_lib = "config.authentication_library = \"#{@authentication_library}\""
|
|
49
|
-
else
|
|
50
|
-
auth_enabled = "config.authentication_enabled = true"
|
|
51
|
-
auth_comment = ""
|
|
52
|
-
auth_lib = "config.authentication_library = nil"
|
|
53
|
-
end
|
|
54
|
-
|
|
55
|
-
if @authz_library && @authz_library != "none"
|
|
56
|
-
authz_lib = "config.authorization_library = \"#{@authz_library}\""
|
|
57
|
-
authz_comment = " (detected: #{@authz_library})"
|
|
58
|
-
else
|
|
59
|
-
authz_lib = "config.authorization_library = nil"
|
|
60
|
-
authz_comment = ""
|
|
61
|
-
end
|
|
62
|
-
|
|
63
|
-
factory_lib = "config.factory_library = \"#{@factory_library}\""
|
|
64
|
-
|
|
65
|
-
framework_specific = generate_framework_specific_config(@test_framework, framework_config)
|
|
66
|
-
|
|
67
|
-
test_examples_config = @test_examples.any? ? @test_examples.inspect : "[]"
|
|
68
|
-
[
|
|
69
|
-
"# frozen_string_literal: true",
|
|
70
|
-
"",
|
|
71
|
-
"return unless Rails.env.development?",
|
|
72
|
-
"",
|
|
73
|
-
"Tng.configure do |config|",
|
|
74
|
-
" config.api_key = nil",
|
|
75
|
-
" # You dont need to change this url, unless you will instructed by the CLI.",
|
|
76
|
-
" config.base_url = \"https://app.tng.sh/\"",
|
|
77
|
-
"",
|
|
78
|
-
" # Testing Framework",
|
|
79
|
-
" config.testing_framework = \"#{@test_framework}\" # Options: minitest, rspec",
|
|
80
|
-
framework_specific,
|
|
81
|
-
" config.mock_library = \"#{framework_config["mock_library"]}\" # Options: mocha, minitest/mock, rspec-mocks, nil",
|
|
82
|
-
" config.http_mock_library = \"#{framework_config["http_mock_library"]}\" # Options: webmock, vcr, httparty, nil",
|
|
83
|
-
" #{factory_lib} # Options: factory_bot, factory_girl, fabrication, fabricator, fixtures, active_record",
|
|
84
|
-
"",
|
|
85
|
-
" # Test Examples",
|
|
86
|
-
" # Example test files for LLM to learn patterns and reduce hallucinations",
|
|
87
|
-
" # Format: [{{\"name\" => \"test_name\", \"path\" => \"spec/models/user_spec.rb\"}}]",
|
|
88
|
-
" # Leave empty [] to auto-detect from project",
|
|
89
|
-
" config.test_examples = #{test_examples_config}",
|
|
90
|
-
"",
|
|
91
|
-
" # Source Code Reading Configuration",
|
|
92
|
-
" # When enabled (true), TNG will only read the file where the method is located",
|
|
93
|
-
" # and will not analyze other files in the project. This may increase the accuracy of the tests,",
|
|
94
|
-
" # but it may also increase the time it takes to generate the tests.",
|
|
95
|
-
" config.read_file_source_code = false # Options: true, false",
|
|
96
|
-
"",
|
|
97
|
-
" # Authentication#{auth_comment}",
|
|
98
|
-
" #{auth_enabled} # Options: true, false",
|
|
99
|
-
" #{auth_lib} # Options: devise, clearance, sorcery, nil",
|
|
100
|
-
"",
|
|
101
|
-
"",
|
|
102
|
-
" # โ ๏ธ IMPORTANT: AUTHENTICATION CONFIGURATION REQUIRED โ ๏ธ",
|
|
103
|
-
" # You MUST configure your authentication methods below for TNG to work properly.",
|
|
104
|
-
" # Uncomment and modify the authentication_methods configuration:",
|
|
105
|
-
"",
|
|
106
|
-
" # Authentication Methods (multiple methods supported)",
|
|
107
|
-
" # Supported authentication types: session, devise, jwt, token_auth, basic_auth, oauth, headers, custom, nil",
|
|
108
|
-
" # EXAMPLE: Uncomment and modify these examples to match your app's authentication:",
|
|
109
|
-
"",
|
|
110
|
-
" # config.authentication_methods = [",
|
|
111
|
-
" # {",
|
|
112
|
-
" # method: \"authenticate_user_via_session!\",",
|
|
113
|
-
" # file_location: \"app/controllers/application_controller.rb\",",
|
|
114
|
-
" # auth_type: \"session\"",
|
|
115
|
-
" # },",
|
|
116
|
-
" # {",
|
|
117
|
-
" # method: \"authenticate_user_via_api_key!\",",
|
|
118
|
-
" # file_location: \"app/controllers/application_controller.rb\",",
|
|
119
|
-
" # auth_type: \"headers\"",
|
|
120
|
-
" # }",
|
|
121
|
-
" # ]",
|
|
122
|
-
" # โ ๏ธ Remember to configure your authentication methods above! โ ๏ธ",
|
|
123
|
-
"",
|
|
124
|
-
" # Authorization#{authz_comment}",
|
|
125
|
-
" #{authz_lib} # Options: cancancan, pundit, rolify, nil",
|
|
126
|
-
"end"
|
|
127
|
-
].join("\n") + "\n"
|
|
128
|
-
end
|
|
129
|
-
|
|
130
28
|
private
|
|
131
29
|
|
|
132
|
-
def
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
nil
|
|
169
|
-
end
|
|
170
|
-
|
|
171
|
-
def detect_authorization_library
|
|
172
|
-
return unless defined?(Bundler)
|
|
173
|
-
|
|
174
|
-
gemfile_specs = Bundler.load.specs
|
|
175
|
-
return "cancancan" if gemfile_specs.any? { |spec| spec.name == "cancancan" }
|
|
176
|
-
return "pundit" if gemfile_specs.any? { |spec| spec.name == "pundit" }
|
|
177
|
-
return "rolify" if gemfile_specs.any? { |spec| spec.name == "rolify" }
|
|
178
|
-
|
|
179
|
-
nil
|
|
180
|
-
end
|
|
181
|
-
|
|
182
|
-
def detect_factory_library
|
|
183
|
-
return "active_record" unless defined?(Bundler)
|
|
184
|
-
|
|
185
|
-
rails_root = defined?(Rails) && Rails.root ? Rails.root.to_s : Dir.pwd
|
|
186
|
-
|
|
187
|
-
# Check for fixtures in test/fixtures or spec/fixtures
|
|
188
|
-
test_fixtures_path = File.join(rails_root, "test", "fixtures")
|
|
189
|
-
spec_fixtures_path = File.join(rails_root, "spec", "fixtures")
|
|
190
|
-
|
|
191
|
-
return "fixtures" if Dir.exist?(test_fixtures_path) && !Dir.empty?(test_fixtures_path)
|
|
192
|
-
|
|
193
|
-
return "fixtures" if Dir.exist?(spec_fixtures_path) && !Dir.empty?(spec_fixtures_path)
|
|
194
|
-
|
|
195
|
-
gemfile_specs = Bundler.load.specs
|
|
196
|
-
return "factory_bot" if gemfile_specs.any? { |spec| spec.name.match(/factory_bot/) }
|
|
197
|
-
return "factory_girl" if gemfile_specs.any? { |spec| spec.name.match(/factory_girl/) }
|
|
198
|
-
return "fabrication" if gemfile_specs.any? { |spec| spec.name.match(/fabrication/) }
|
|
199
|
-
return "fabricator" if gemfile_specs.any? { |spec| spec.name.match(/fabricator/) }
|
|
200
|
-
|
|
201
|
-
"active_record"
|
|
202
|
-
rescue StandardError
|
|
203
|
-
# Fallback if Bundler isn't available
|
|
204
|
-
"active_record"
|
|
205
|
-
end
|
|
206
|
-
|
|
207
|
-
def detect_test_examples
|
|
208
|
-
rails_root = defined?(Rails) && Rails.root ? Rails.root.to_s : Dir.pwd
|
|
209
|
-
examples = []
|
|
210
|
-
|
|
211
|
-
# Determine test directory based on framework
|
|
212
|
-
test_dir = @test_framework == "rspec" ? "spec" : "test"
|
|
213
|
-
|
|
214
|
-
# Comprehensive exclusion list for build artifacts, caches, dependencies
|
|
215
|
-
exclude_dirs = [
|
|
216
|
-
".git", "log", "tmp", "vendor", "node_modules",
|
|
217
|
-
"coverage", "public", "storage", "db", "config",
|
|
218
|
-
"lib/tasks", "bin", ".bundle", ".vscode", ".idea"
|
|
219
|
-
]
|
|
220
|
-
|
|
221
|
-
# Also exclude files larger than 500KB (probably not good examples)
|
|
222
|
-
max_file_size = 500 * 1024 # 500KB
|
|
223
|
-
|
|
224
|
-
# Find all test directories and subdirectories
|
|
225
|
-
test_directories = find_test_directories(rails_root, test_dir, exclude_dirs)
|
|
226
|
-
|
|
227
|
-
# Determine collection strategy based on folder structure
|
|
228
|
-
main_test_dir = File.join(rails_root, test_dir)
|
|
229
|
-
has_subfolders = test_directories.size > 1
|
|
230
|
-
|
|
231
|
-
if has_subfolders
|
|
232
|
-
# If we have subfolders, take 2 files per directory
|
|
233
|
-
collected_files = []
|
|
234
|
-
test_directories.each do |dir_path|
|
|
235
|
-
dir_files = find_valid_test_files_in_directory(dir_path, max_file_size)
|
|
236
|
-
# Take up to 2 files from this directory
|
|
237
|
-
collected_files.concat(dir_files.first(2))
|
|
30
|
+
def configuration_template
|
|
31
|
+
<<~RUBY
|
|
32
|
+
# frozen_string_literal: true
|
|
33
|
+
|
|
34
|
+
return unless Rails.env.development?
|
|
35
|
+
|
|
36
|
+
Tng.configure do |config|
|
|
37
|
+
config.api_key = ENV["TNG_API_KEY"]
|
|
38
|
+
config.base_url = ENV["API_BASE_URI"] || "https://app.tng.sh/"
|
|
39
|
+
|
|
40
|
+
# Test Helper File (optional - auto-detected if not set)
|
|
41
|
+
# Uncomment to override auto-detection with a custom path
|
|
42
|
+
# Default auto-detection checks: spec/rails_helper.rb, spec/spec_helper.rb, test/test_helper.rb
|
|
43
|
+
# config.test_helper_path = "spec/rails_helper.rb"
|
|
44
|
+
|
|
45
|
+
# โ ๏ธ IMPORTANT: AUTHENTICATION CONFIGURATION REQUIRED โ ๏ธ
|
|
46
|
+
# You MUST configure your authentication methods below for TNG to work properly.
|
|
47
|
+
# Uncomment and modify the authentication_methods configuration:
|
|
48
|
+
|
|
49
|
+
# Authentication Methods (multiple methods supported)
|
|
50
|
+
# Supported authentication types: session, devise, jwt, token_auth, basic_auth, oauth, headers, custom, nil
|
|
51
|
+
# EXAMPLE: Uncomment and modify these examples to match your app's authentication:
|
|
52
|
+
|
|
53
|
+
# config.authentication_methods = [
|
|
54
|
+
# {
|
|
55
|
+
# method: "authenticate_user_via_session!",
|
|
56
|
+
# file_location: "app/controllers/application_controller.rb",
|
|
57
|
+
# auth_type: "session"
|
|
58
|
+
# },
|
|
59
|
+
# {
|
|
60
|
+
# method: "authenticate_user_via_api_key!",
|
|
61
|
+
# file_location: "app/controllers/application_controller.rb",
|
|
62
|
+
# auth_type: "headers"
|
|
63
|
+
# }
|
|
64
|
+
# ]
|
|
65
|
+
# โ ๏ธ Remember to configure your authentication methods above! โ ๏ธ
|
|
238
66
|
end
|
|
239
|
-
|
|
240
|
-
# If no subfolders, take up to 5 files from main test directory
|
|
241
|
-
collected_files = find_valid_test_files_in_directory(main_test_dir, max_file_size).first(5)
|
|
242
|
-
end
|
|
243
|
-
|
|
244
|
-
# Sort all collected files by modification time (recent first), then by size (smaller files first)
|
|
245
|
-
collected_files.sort_by! { |f| [-File.mtime(f).to_i, File.size(f)] }
|
|
246
|
-
|
|
247
|
-
# Convert to examples format
|
|
248
|
-
collected_files.first(5).each do |test_file|
|
|
249
|
-
examples << {
|
|
250
|
-
"name" => File.basename(test_file),
|
|
251
|
-
"path" => Pathname.new(test_file).relative_path_from(Pathname.new(rails_root)).to_s
|
|
252
|
-
}
|
|
253
|
-
rescue StandardError
|
|
254
|
-
next
|
|
255
|
-
end
|
|
256
|
-
|
|
257
|
-
examples
|
|
258
|
-
rescue StandardError
|
|
259
|
-
# Return empty array if anything goes wrong
|
|
260
|
-
[]
|
|
261
|
-
end
|
|
262
|
-
|
|
263
|
-
def find_test_directories(rails_root, test_dir, exclude_dirs)
|
|
264
|
-
directories = []
|
|
265
|
-
main_test_path = File.join(rails_root, test_dir)
|
|
266
|
-
|
|
267
|
-
return [] unless Dir.exist?(main_test_path)
|
|
268
|
-
|
|
269
|
-
# Add main test directory
|
|
270
|
-
directories << main_test_path
|
|
271
|
-
|
|
272
|
-
# Find all subdirectories recursively
|
|
273
|
-
Dir.glob(File.join(main_test_path, "**/*/")).each do |dir|
|
|
274
|
-
# Skip excluded directories
|
|
275
|
-
next if exclude_dirs.any? { |excluded| dir.include?("/#{excluded}/") }
|
|
276
|
-
|
|
277
|
-
# Skip if it's not a directory with actual test files
|
|
278
|
-
next unless test_files?(dir)
|
|
279
|
-
|
|
280
|
-
directories << dir
|
|
281
|
-
end
|
|
282
|
-
|
|
283
|
-
directories.uniq
|
|
284
|
-
end
|
|
285
|
-
|
|
286
|
-
def test_files?(dir_path)
|
|
287
|
-
return false unless Dir.exist?(dir_path)
|
|
288
|
-
|
|
289
|
-
pattern = @test_framework == "rspec" ? "*_spec.rb" : "*_test.rb"
|
|
290
|
-
Dir.glob(File.join(dir_path, pattern)).any?
|
|
291
|
-
end
|
|
292
|
-
|
|
293
|
-
def find_valid_test_files_in_directory(dir_path, max_file_size)
|
|
294
|
-
return [] unless Dir.exist?(dir_path)
|
|
295
|
-
|
|
296
|
-
pattern = @test_framework == "rspec" ? "*_spec.rb" : "*_test.rb"
|
|
297
|
-
files = []
|
|
298
|
-
|
|
299
|
-
Dir.glob(File.join(dir_path, pattern)).each do |test_file|
|
|
300
|
-
next if File.size(test_file) > max_file_size
|
|
301
|
-
next unless valid_test_file?(test_file)
|
|
302
|
-
|
|
303
|
-
files << test_file
|
|
304
|
-
rescue StandardError
|
|
305
|
-
next
|
|
306
|
-
end
|
|
307
|
-
|
|
308
|
-
files
|
|
309
|
-
end
|
|
310
|
-
|
|
311
|
-
def valid_test_file?(file_path)
|
|
312
|
-
return false unless File.exist?(file_path) && File.readable?(file_path)
|
|
313
|
-
return false if File.size(file_path) == 0
|
|
314
|
-
|
|
315
|
-
begin
|
|
316
|
-
# Read first 1KB of the file
|
|
317
|
-
content = File.read(file_path, 1024)
|
|
318
|
-
|
|
319
|
-
# Check for Ruby test indicators based on framework
|
|
320
|
-
test_indicators = if @test_framework == "rspec"
|
|
321
|
-
[
|
|
322
|
-
"describe ", # RSpec describe blocks
|
|
323
|
-
"context ", # RSpec context blocks
|
|
324
|
-
"it ", # RSpec examples
|
|
325
|
-
"specify ", # RSpec specify
|
|
326
|
-
"expect(", # RSpec expectations
|
|
327
|
-
"before(", # RSpec hooks
|
|
328
|
-
"let(", # RSpec let
|
|
329
|
-
"subject ", # RSpec subject
|
|
330
|
-
'require "spec_helper"', # RSpec spec helper
|
|
331
|
-
'require "rails_helper"' # RSpec rails helper
|
|
332
|
-
]
|
|
333
|
-
else # minitest
|
|
334
|
-
[
|
|
335
|
-
"def test_", # Minitest test methods
|
|
336
|
-
"class.*Test", # Test classes
|
|
337
|
-
"assert ", # Assertions
|
|
338
|
-
"refute ", # Refutations
|
|
339
|
-
'require "test_helper"', # Minitest helper
|
|
340
|
-
'require "minitest"', # Minitest require
|
|
341
|
-
"MiniTest::Test" # Minitest base class
|
|
342
|
-
]
|
|
343
|
-
end
|
|
344
|
-
|
|
345
|
-
# Must contain at least one test indicator
|
|
346
|
-
return false unless test_indicators.any? { |indicator| content.include?(indicator) }
|
|
347
|
-
|
|
348
|
-
# Basic Ruby syntax check - should not have obvious non-Ruby content
|
|
349
|
-
return false if content.scan(/#!/).size > 5 || content.include?("<?xml")
|
|
350
|
-
|
|
351
|
-
true
|
|
352
|
-
rescue StandardError
|
|
353
|
-
false
|
|
354
|
-
end
|
|
355
|
-
end
|
|
356
|
-
|
|
357
|
-
def generate_framework_config(framework)
|
|
358
|
-
if framework == "minitest"
|
|
359
|
-
{
|
|
360
|
-
"test_style" => "test_block", # Options: spec, unit, test_block
|
|
361
|
-
"setup_style" => true, # Options: true, false
|
|
362
|
-
"assertion_style" => "assert/refute", # Options: assert/refute, assert/assert_not, must/wont
|
|
363
|
-
"teardown_style" => false, # Options: true, false
|
|
364
|
-
"http_mock_library" => "webmock", # Options: webmock, vcr, httparty, none
|
|
365
|
-
"mock_library" => "minitest/mock" # Options: mocha, minitest/mock, rspec-mocks, none
|
|
366
|
-
}
|
|
367
|
-
else # rspec
|
|
368
|
-
{
|
|
369
|
-
"describe_style" => true, # Options: true, false
|
|
370
|
-
"context_style" => "context", # Options: context, describe
|
|
371
|
-
"it_style" => "it", # Options: it, specify
|
|
372
|
-
"before_style" => "before", # Options: before, setup
|
|
373
|
-
"after_style" => "after", # Options: after, teardown
|
|
374
|
-
"let_style" => true, # Options: true, false
|
|
375
|
-
"subject_style" => true, # Options: true, false
|
|
376
|
-
"mock_library" => "rspec-mocks", # Options: rspec-mocks, mocha, flexmock, none
|
|
377
|
-
"http_mock_library" => "webmock" # Options: webmock, vcr, httparty, none
|
|
378
|
-
}
|
|
379
|
-
end
|
|
380
|
-
end
|
|
381
|
-
|
|
382
|
-
def generate_framework_specific_config(framework, framework_config)
|
|
383
|
-
if framework == "minitest"
|
|
384
|
-
" config.test_style = \"#{framework_config["test_style"]}\" # Options: spec, unit, test_block\n" +
|
|
385
|
-
" config.setup_style = #{framework_config["setup_style"]} # Options: true, false\n" +
|
|
386
|
-
" config.assertion_style = \"#{framework_config["assertion_style"]}\" # Options: assert/refute, assert/assert_not, must/wont\n" +
|
|
387
|
-
" config.teardown_style = #{framework_config["teardown_style"]} # Options: true, false"
|
|
388
|
-
else # rspec
|
|
389
|
-
" config.describe_style = #{framework_config["describe_style"]} # Options: true, false\n" +
|
|
390
|
-
" config.context_style = \"#{framework_config["context_style"]}\" # Options: context, describe\n" +
|
|
391
|
-
" config.it_style = \"#{framework_config["it_style"]}\" # Options: it, specify\n" +
|
|
392
|
-
" config.before_style = \"#{framework_config["before_style"]}\" # Options: before, setup\n" +
|
|
393
|
-
" config.after_style = \"#{framework_config["after_style"]}\" # Options: after, teardown\n" +
|
|
394
|
-
" config.let_style = #{framework_config["let_style"]} # Options: true, false\n" +
|
|
395
|
-
" config.subject_style = #{framework_config["subject_style"]} # Options: true, false"
|
|
396
|
-
end
|
|
67
|
+
RUBY
|
|
397
68
|
end
|
|
398
69
|
end
|
|
399
70
|
end
|
data/lib/tng/analyzers/model.rb
CHANGED
|
@@ -30,7 +30,8 @@ module Tng
|
|
|
30
30
|
# Load the model class
|
|
31
31
|
model_class = model_name.constantize
|
|
32
32
|
|
|
33
|
-
instance_methods = model_class.public_instance_methods(false)
|
|
33
|
+
instance_methods = model_class.public_instance_methods(false) +
|
|
34
|
+
model_class.private_instance_methods(false)
|
|
34
35
|
class_methods = model_class.public_methods(false) - Class.public_methods
|
|
35
36
|
|
|
36
37
|
model_file = Object.const_source_location(model_class.name)&.first
|
data/lib/tng/analyzers/other.rb
CHANGED
|
@@ -79,8 +79,10 @@ module Tng
|
|
|
79
79
|
|
|
80
80
|
case node
|
|
81
81
|
when Prism::DefNode
|
|
82
|
-
#
|
|
83
|
-
|
|
82
|
+
# Add methods that are public, OR specifically 'initialize' (which is conventionally private but often needs testing)
|
|
83
|
+
if current_visibility == :public || node.name.to_s == "initialize"
|
|
84
|
+
methods << node.name.to_s
|
|
85
|
+
end
|
|
84
86
|
when Prism::CallNode
|
|
85
87
|
# Handle visibility modifiers (private, protected, public)
|
|
86
88
|
if node.receiver.nil? && node.arguments.nil?
|
|
@@ -31,7 +31,8 @@ module Tng
|
|
|
31
31
|
# Load the service class
|
|
32
32
|
service_class = service_name.constantize
|
|
33
33
|
|
|
34
|
-
instance_methods = service_class.public_instance_methods(false)
|
|
34
|
+
instance_methods = service_class.public_instance_methods(false) +
|
|
35
|
+
service_class.private_instance_methods(false)
|
|
35
36
|
class_methods = service_class.public_methods(false) - Class.public_methods
|
|
36
37
|
|
|
37
38
|
# Try to get source file from any method, fallback to const_source_location
|
|
@@ -36,7 +36,6 @@ module Tng
|
|
|
36
36
|
generate_test_for_type(other_file, method_info, :other, progress: progress)
|
|
37
37
|
end
|
|
38
38
|
|
|
39
|
-
|
|
40
39
|
def generate_test_for_type(file_object, method_info, type, progress: nil)
|
|
41
40
|
start_time = Time.now
|
|
42
41
|
|
|
@@ -50,14 +49,11 @@ module Tng
|
|
|
50
49
|
job_data = JSON.parse(response.body)
|
|
51
50
|
|
|
52
51
|
# Check for authentication errors
|
|
53
|
-
if response.status == 401
|
|
54
|
-
return { error: :auth_failed, message: "Invalid or missing API key" }
|
|
55
|
-
end
|
|
52
|
+
return { error: :auth_failed, message: "Invalid or missing API key" } if response.status == 401
|
|
56
53
|
|
|
57
54
|
# Check for forbidden responses (usage limits or invalid keys)
|
|
58
|
-
if response.status == 403
|
|
59
|
-
|
|
60
|
-
end
|
|
55
|
+
return { error: :auth_failed, message: "API key expired or usage limit reached" } if response.status == 403
|
|
56
|
+
|
|
61
57
|
job_id = job_data["job_id"]
|
|
62
58
|
|
|
63
59
|
return unless job_id
|
|
@@ -114,15 +110,15 @@ module Tng
|
|
|
114
110
|
# We create 4 generic steps for the agents
|
|
115
111
|
# Note: indices are relative to the current session, so we just append them.
|
|
116
112
|
# But we need their indices to update them later.
|
|
117
|
-
# Since we can't ask progress for current index easily without hacking,
|
|
113
|
+
# Since we can't ask progress for current index easily without hacking,
|
|
118
114
|
# we rely on the fact that we call update 4 times.
|
|
119
|
-
|
|
115
|
+
|
|
120
116
|
# Use a base offset if we could know it, but we can't reliably.
|
|
121
117
|
# Actually, if we use explicit_step, we need absolute indices.
|
|
122
118
|
# Let's assume the previous steps were 0, 1, 2, 3 based on bin/tng.
|
|
123
119
|
# So we start at 4.
|
|
124
|
-
base_idx = 4
|
|
125
|
-
|
|
120
|
+
base_idx = 4
|
|
121
|
+
|
|
126
122
|
progress.update("Context Builder: Pending...", nil, step_increment: true)
|
|
127
123
|
agent_step_indices["context_agent_status"] = base_idx
|
|
128
124
|
|
|
@@ -146,10 +142,10 @@ module Tng
|
|
|
146
142
|
|
|
147
143
|
# Calculate pseudo-percentage
|
|
148
144
|
if progress
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
145
|
+
pct = ((attempts.to_f / max_attempts.to_f) * 95.0).to_i
|
|
146
|
+
# Update global progress bar without adding a new step (explicit_step=4 updates Context Builder line, but we want to just update percentage?)
|
|
147
|
+
# The progress bar component takes the percent from the *last* update message.
|
|
148
|
+
# So we can pass specific percent when we update any agent step.
|
|
153
149
|
else
|
|
154
150
|
pct = 0
|
|
155
151
|
end
|
|
@@ -166,32 +162,32 @@ module Tng
|
|
|
166
162
|
begin
|
|
167
163
|
status_data = JSON.parse(status_response.body.to_s)
|
|
168
164
|
status = status_data["status"]
|
|
169
|
-
|
|
165
|
+
|
|
170
166
|
# Update UI with granular info
|
|
171
167
|
if progress && status_data["info"].is_a?(Hash)
|
|
172
168
|
info = status_data["info"]
|
|
173
|
-
|
|
169
|
+
|
|
174
170
|
agent_step_indices.each do |key, step_idx|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
171
|
+
item_data = info[key]
|
|
172
|
+
next unless item_data
|
|
173
|
+
|
|
174
|
+
agent_status = "pending"
|
|
175
|
+
values = []
|
|
176
|
+
|
|
177
|
+
if item_data.is_a?(Hash)
|
|
178
|
+
agent_status = item_data["status"] || "pending"
|
|
179
|
+
values = item_data["values"] || []
|
|
180
|
+
else
|
|
181
|
+
agent_status = item_data.to_s
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
label = case key
|
|
185
|
+
when "context_agent_status" then "Context Builder"
|
|
186
|
+
when "style_agent_status" then "Style Analyzer"
|
|
187
|
+
when "logical_issue_status" then "Logic Analyzer"
|
|
188
|
+
when "behavior_expert_status" then "Logic Generator"
|
|
189
|
+
else key
|
|
190
|
+
end
|
|
195
191
|
|
|
196
192
|
msg = if agent_status == "processing"
|
|
197
193
|
"#{label}: Processing..."
|
|
@@ -213,8 +209,8 @@ module Tng
|
|
|
213
209
|
|
|
214
210
|
# Pass percentage only on the last step (Logic Generator) to keep main bar moving?
|
|
215
211
|
# Or pass it on all updates.
|
|
216
|
-
p =
|
|
217
|
-
|
|
212
|
+
p = step_idx == agent_step_indices["behavior_expert_status"] ? pct : nil
|
|
213
|
+
|
|
218
214
|
progress.update(msg, p, step_increment: false, explicit_step: step_idx)
|
|
219
215
|
end
|
|
220
216
|
end
|
|
@@ -222,7 +218,7 @@ module Tng
|
|
|
222
218
|
case status
|
|
223
219
|
when "completed"
|
|
224
220
|
debug_log("Test generation completed!") if debug_enabled?
|
|
225
|
-
return status_data["result"]
|
|
221
|
+
return status_data["result"].merge("analysis" => status_data["analysis"])
|
|
226
222
|
when "failed"
|
|
227
223
|
debug_log("Test generation failed: #{status_data["error"]}") if debug_enabled?
|
|
228
224
|
return { error: :generation_failed, message: status_data["error"] }
|
|
@@ -35,17 +35,6 @@ module Tng
|
|
|
35
35
|
end
|
|
36
36
|
Tng.config[:authentication_entry_points_with_source] = auth_entry_points_with_source
|
|
37
37
|
|
|
38
|
-
# Add source content to test examples for API requests
|
|
39
|
-
if Tng.config[:test_examples]&.any?
|
|
40
|
-
Tng.config[:test_examples] = Tng.config[:test_examples].map do |example|
|
|
41
|
-
next example unless example.is_a?(Hash) && example["path"]
|
|
42
|
-
|
|
43
|
-
example.merge("source" => read_test_file_content(example["path"]))
|
|
44
|
-
rescue StandardError
|
|
45
|
-
example
|
|
46
|
-
end
|
|
47
|
-
end
|
|
48
|
-
|
|
49
38
|
Tng.config
|
|
50
39
|
end
|
|
51
40
|
|
data/lib/tng/ui/go_ui_session.rb
CHANGED
|
@@ -381,8 +381,10 @@ module Tng
|
|
|
381
381
|
def update(message, percent = nil, step_increment: true, explicit_step: nil)
|
|
382
382
|
step_idx = if explicit_step
|
|
383
383
|
explicit_step
|
|
384
|
+
elsif step_increment
|
|
385
|
+
@step
|
|
384
386
|
else
|
|
385
|
-
|
|
387
|
+
(@step > 0 ? @step - 1 : 0)
|
|
386
388
|
end
|
|
387
389
|
|
|
388
390
|
data = { type: "step", step: step_idx, message: message }
|
|
@@ -403,4 +405,3 @@ module Tng
|
|
|
403
405
|
end
|
|
404
406
|
end
|
|
405
407
|
end
|
|
406
|
-
|
data/lib/tng/utils.rb
CHANGED
|
@@ -80,9 +80,7 @@ module Tng
|
|
|
80
80
|
puts "๐ Raw API response: #{test_content[0..200]}..." if ENV["DEBUG"]
|
|
81
81
|
parsed_response = JSON.parse(test_content)
|
|
82
82
|
|
|
83
|
-
if parsed_response["error"]
|
|
84
|
-
return { error: :generation_failed, message: parsed_response["error"] }
|
|
85
|
-
end
|
|
83
|
+
return { error: :generation_failed, message: parsed_response["error"] } if parsed_response["error"]
|
|
86
84
|
# Validate required fields
|
|
87
85
|
unless parsed_response["file_content"]
|
|
88
86
|
return { error: :invalid_response, message: "API response missing file_content" }
|
|
@@ -90,16 +88,16 @@ module Tng
|
|
|
90
88
|
|
|
91
89
|
# Handle both possible field names for file path
|
|
92
90
|
file_path = parsed_response["test_file_path"] || parsed_response["file_path"] || parsed_response["file_name"] || parsed_response["file"]
|
|
93
|
-
unless file_path
|
|
94
|
-
return { error: :invalid_response, message: "API response missing file path" }
|
|
95
|
-
end
|
|
91
|
+
return { error: :invalid_response, message: "API response missing file path" } unless file_path
|
|
96
92
|
|
|
97
93
|
begin
|
|
98
|
-
|
|
94
|
+
clean_content = cleanup_file_content(parsed_response["file_content"])
|
|
95
|
+
File.write(file_path, clean_content)
|
|
99
96
|
rescue Errno::ENOENT
|
|
100
97
|
# Create directory if it doesn't exist
|
|
101
98
|
FileUtils.mkdir_p(File.dirname(file_path))
|
|
102
|
-
|
|
99
|
+
clean_content = cleanup_file_content(parsed_response["file_content"])
|
|
100
|
+
File.write(file_path, clean_content)
|
|
103
101
|
end
|
|
104
102
|
absolute_path = File.expand_path(file_path)
|
|
105
103
|
|
|
@@ -123,22 +121,47 @@ module Tng
|
|
|
123
121
|
}
|
|
124
122
|
end
|
|
125
123
|
|
|
126
|
-
def self.
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
124
|
+
def self.cleanup_file_content(content)
|
|
125
|
+
return content unless content.is_a?(String)
|
|
126
|
+
|
|
127
|
+
cleaned = content.dup
|
|
128
|
+
|
|
129
|
+
# Strip trailing whitespace first
|
|
130
|
+
cleaned.rstrip!
|
|
131
|
+
|
|
132
|
+
# Remove trailing JSON artifacts that shouldn't be in Ruby code
|
|
133
|
+
# Common patterns: ends with `end"` or `end"\n}` or just `}`
|
|
134
|
+
loop do
|
|
135
|
+
original = cleaned.dup
|
|
136
|
+
|
|
137
|
+
# Remove trailing lone } or " that shouldn't be there
|
|
138
|
+
cleaned.sub!(/\n\s*\}\s*\z/, "\n")
|
|
139
|
+
cleaned.sub!(/"\s*\z/, "")
|
|
140
|
+
cleaned.sub!(/\n\s*"\s*\z/, "\n")
|
|
141
|
+
cleaned.sub!(/end"\s*\z/, "end")
|
|
142
|
+
|
|
143
|
+
break if cleaned == original
|
|
141
144
|
end
|
|
145
|
+
|
|
146
|
+
# Ensure file ends with single newline
|
|
147
|
+
cleaned.rstrip!
|
|
148
|
+
cleaned << "\n" unless cleaned.empty?
|
|
149
|
+
|
|
150
|
+
cleaned
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
def self.fixture_content
|
|
154
|
+
# Auto-detect: try fixtures first, then factory_bot, then fabricator
|
|
155
|
+
fixtures = load_all_fixtures_data
|
|
156
|
+
return fixtures if fixtures.any?
|
|
157
|
+
|
|
158
|
+
factories = load_all_factory_data
|
|
159
|
+
return factories if factories.any?
|
|
160
|
+
|
|
161
|
+
fabricators = load_all_fabricator_data
|
|
162
|
+
return fabricators if fabricators.any?
|
|
163
|
+
|
|
164
|
+
{}
|
|
142
165
|
end
|
|
143
166
|
|
|
144
167
|
def self.load_all_fixtures_data
|
data/lib/tng/version.rb
CHANGED
data/lib/tng.rb
CHANGED
|
@@ -66,16 +66,8 @@ module Tng
|
|
|
66
66
|
@config = {
|
|
67
67
|
api_key: nil,
|
|
68
68
|
base_url: "https://app.tng.sh/",
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
authorization_library: nil,
|
|
72
|
-
authentication_library: nil,
|
|
73
|
-
authentication_methods: [],
|
|
74
|
-
mock_library: "minitest/mock",
|
|
75
|
-
http_mock_library: "webmock",
|
|
76
|
-
factory_library: "active_record",
|
|
77
|
-
test_examples: [],
|
|
78
|
-
read_file_source_code: false
|
|
69
|
+
test_helper_path: nil,
|
|
70
|
+
authentication_methods: []
|
|
79
71
|
}
|
|
80
72
|
|
|
81
73
|
def self.configure
|
|
@@ -102,37 +94,12 @@ module Tng
|
|
|
102
94
|
@config[:base_url]
|
|
103
95
|
end
|
|
104
96
|
|
|
105
|
-
def self.
|
|
106
|
-
@config[:
|
|
107
|
-
initialize_framework_defaults(value)
|
|
97
|
+
def self.test_helper_path=(value)
|
|
98
|
+
@config[:test_helper_path] = value
|
|
108
99
|
end
|
|
109
100
|
|
|
110
|
-
def self.
|
|
111
|
-
@config[:
|
|
112
|
-
end
|
|
113
|
-
|
|
114
|
-
def self.authentication_enabled=(value)
|
|
115
|
-
@config[:authentication_enabled] = value
|
|
116
|
-
end
|
|
117
|
-
|
|
118
|
-
def self.authentication_enabled
|
|
119
|
-
@config[:authentication_enabled]
|
|
120
|
-
end
|
|
121
|
-
|
|
122
|
-
def self.authentication_library=(value)
|
|
123
|
-
@config[:authentication_library] = value
|
|
124
|
-
end
|
|
125
|
-
|
|
126
|
-
def self.authentication_library
|
|
127
|
-
@config[:authentication_library]
|
|
128
|
-
end
|
|
129
|
-
|
|
130
|
-
def self.authorization_library=(value)
|
|
131
|
-
@config[:authorization_library] = value
|
|
132
|
-
end
|
|
133
|
-
|
|
134
|
-
def self.authorization_library
|
|
135
|
-
@config[:authorization_library]
|
|
101
|
+
def self.test_helper_path
|
|
102
|
+
@config[:test_helper_path]
|
|
136
103
|
end
|
|
137
104
|
|
|
138
105
|
def self.authentication_methods=(value)
|
|
@@ -142,178 +109,4 @@ module Tng
|
|
|
142
109
|
def self.authentication_methods
|
|
143
110
|
@config[:authentication_methods]
|
|
144
111
|
end
|
|
145
|
-
|
|
146
|
-
def self.mock_library=(value)
|
|
147
|
-
@config[:mock_library] = value
|
|
148
|
-
end
|
|
149
|
-
|
|
150
|
-
def self.mock_library
|
|
151
|
-
@config[:mock_library]
|
|
152
|
-
end
|
|
153
|
-
|
|
154
|
-
def self.http_mock_library=(value)
|
|
155
|
-
@config[:http_mock_library] = value
|
|
156
|
-
end
|
|
157
|
-
|
|
158
|
-
def self.http_mock_library
|
|
159
|
-
@config[:http_mock_library]
|
|
160
|
-
end
|
|
161
|
-
|
|
162
|
-
def self.factory_library=(value)
|
|
163
|
-
@config[:factory_library] = value
|
|
164
|
-
end
|
|
165
|
-
|
|
166
|
-
def self.factory_library
|
|
167
|
-
@config[:factory_library]
|
|
168
|
-
end
|
|
169
|
-
|
|
170
|
-
def self.test_style=(value)
|
|
171
|
-
@config[:test_style] = value
|
|
172
|
-
end
|
|
173
|
-
|
|
174
|
-
def self.test_style
|
|
175
|
-
@config[:test_style]
|
|
176
|
-
end
|
|
177
|
-
|
|
178
|
-
def self.setup_style=(value)
|
|
179
|
-
@config[:setup_style] = value
|
|
180
|
-
end
|
|
181
|
-
|
|
182
|
-
def self.setup_style
|
|
183
|
-
@config[:setup_style]
|
|
184
|
-
end
|
|
185
|
-
|
|
186
|
-
def self.assertion_style=(value)
|
|
187
|
-
@config[:assertion_style] = value
|
|
188
|
-
end
|
|
189
|
-
|
|
190
|
-
def self.assertion_style
|
|
191
|
-
@config[:assertion_style]
|
|
192
|
-
end
|
|
193
|
-
|
|
194
|
-
def self.teardown_style=(value)
|
|
195
|
-
@config[:teardown_style] = value
|
|
196
|
-
end
|
|
197
|
-
|
|
198
|
-
def self.teardown_style
|
|
199
|
-
@config[:teardown_style]
|
|
200
|
-
end
|
|
201
|
-
|
|
202
|
-
def self.describe_style=(value)
|
|
203
|
-
@config[:describe_style] = value
|
|
204
|
-
end
|
|
205
|
-
|
|
206
|
-
def self.describe_style
|
|
207
|
-
@config[:describe_style]
|
|
208
|
-
end
|
|
209
|
-
|
|
210
|
-
def self.context_style=(value)
|
|
211
|
-
@config[:context_style] = value
|
|
212
|
-
end
|
|
213
|
-
|
|
214
|
-
def self.context_style
|
|
215
|
-
@config[:context_style]
|
|
216
|
-
end
|
|
217
|
-
|
|
218
|
-
def self.it_style=(value)
|
|
219
|
-
@config[:it_style] = value
|
|
220
|
-
end
|
|
221
|
-
|
|
222
|
-
def self.it_style
|
|
223
|
-
@config[:it_style]
|
|
224
|
-
end
|
|
225
|
-
|
|
226
|
-
def self.before_style=(value)
|
|
227
|
-
@config[:before_style] = value
|
|
228
|
-
end
|
|
229
|
-
|
|
230
|
-
def self.before_style
|
|
231
|
-
@config[:before_style]
|
|
232
|
-
end
|
|
233
|
-
|
|
234
|
-
def self.after_style=(value)
|
|
235
|
-
@config[:after_style] = value
|
|
236
|
-
end
|
|
237
|
-
|
|
238
|
-
def self.after_style
|
|
239
|
-
@config[:after_style]
|
|
240
|
-
end
|
|
241
|
-
|
|
242
|
-
def self.let_style=(value)
|
|
243
|
-
@config[:let_style] = value
|
|
244
|
-
end
|
|
245
|
-
|
|
246
|
-
def self.let_style
|
|
247
|
-
@config[:let_style]
|
|
248
|
-
end
|
|
249
|
-
|
|
250
|
-
def self.subject_style=(value)
|
|
251
|
-
@config[:subject_style] = value
|
|
252
|
-
end
|
|
253
|
-
|
|
254
|
-
def self.subject_style
|
|
255
|
-
@config[:subject_style]
|
|
256
|
-
end
|
|
257
|
-
|
|
258
|
-
def self.test_examples=(value)
|
|
259
|
-
@config[:test_examples] = value
|
|
260
|
-
end
|
|
261
|
-
|
|
262
|
-
def self.test_examples
|
|
263
|
-
@config[:test_examples]
|
|
264
|
-
end
|
|
265
|
-
|
|
266
|
-
def self.read_file_source_code=(value)
|
|
267
|
-
@config[:read_file_source_code] = value
|
|
268
|
-
end
|
|
269
|
-
|
|
270
|
-
def self.read_file_source_code
|
|
271
|
-
@config[:read_file_source_code]
|
|
272
|
-
end
|
|
273
|
-
|
|
274
|
-
def self.authentication_configured?
|
|
275
|
-
return false unless authentication_enabled
|
|
276
|
-
return false if authentication_methods.nil? || authentication_methods.empty?
|
|
277
|
-
|
|
278
|
-
authentication_methods.all? do |method|
|
|
279
|
-
method.is_a?(Hash) &&
|
|
280
|
-
method.key?(:method) && !method[:method].to_s.strip.empty? &&
|
|
281
|
-
method.key?(:file_location) && !method[:file_location].to_s.strip.empty? &&
|
|
282
|
-
method.key?(:auth_type) && !method[:auth_type].to_s.strip.empty?
|
|
283
|
-
end
|
|
284
|
-
end
|
|
285
|
-
|
|
286
|
-
def self.initialize_framework_defaults(framework)
|
|
287
|
-
if framework == "minitest"
|
|
288
|
-
@config.merge!({
|
|
289
|
-
test_style: "spec",
|
|
290
|
-
setup_style: false,
|
|
291
|
-
assertion_style: "assert",
|
|
292
|
-
teardown_style: false
|
|
293
|
-
})
|
|
294
|
-
@config.delete(:describe_style)
|
|
295
|
-
@config.delete(:context_style)
|
|
296
|
-
@config.delete(:it_style)
|
|
297
|
-
@config.delete(:before_style)
|
|
298
|
-
@config.delete(:after_style)
|
|
299
|
-
@config.delete(:let_style)
|
|
300
|
-
@config.delete(:subject_style)
|
|
301
|
-
elsif framework == "rspec"
|
|
302
|
-
@config.merge!({
|
|
303
|
-
describe_style: true,
|
|
304
|
-
context_style: "context",
|
|
305
|
-
it_style: "it",
|
|
306
|
-
before_style: "before",
|
|
307
|
-
after_style: "after",
|
|
308
|
-
let_style: true,
|
|
309
|
-
subject_style: true
|
|
310
|
-
})
|
|
311
|
-
@config.delete(:test_style)
|
|
312
|
-
@config.delete(:setup_style)
|
|
313
|
-
@config.delete(:assertion_style)
|
|
314
|
-
@config.delete(:teardown_style)
|
|
315
|
-
end
|
|
316
|
-
end
|
|
317
|
-
|
|
318
|
-
initialize_framework_defaults(@config[:testing_framework])
|
|
319
112
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: tng
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.3.
|
|
4
|
+
version: 0.3.5
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- ralucab
|
|
@@ -165,6 +165,7 @@ files:
|
|
|
165
165
|
- binaries/tng-darwin-arm64.bundle
|
|
166
166
|
- binaries/tng-linux-arm64.so
|
|
167
167
|
- binaries/tng-linux-x86_64.so
|
|
168
|
+
- binaries/tng.bundle
|
|
168
169
|
- lib/generators/tng/install_generator.rb
|
|
169
170
|
- lib/tng.rb
|
|
170
171
|
- lib/tng/analyzers/controller.rb
|
|
@@ -194,33 +195,30 @@ metadata:
|
|
|
194
195
|
source_code_uri: https://github.com/tng-sh/tng-rails-public
|
|
195
196
|
license_uri: https://github.com/tng-sh/tng-rails-public/blob/main/LICENSE.md
|
|
196
197
|
post_install_message: "โ TNG โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ\nโ
|
|
197
|
-
\ โ\nโ
|
|
198
|
-
|
|
199
|
-
\ \
|
|
200
|
-
\ โ\nโ
|
|
201
|
-
|
|
202
|
-
\
|
|
203
|
-
\
|
|
204
|
-
\
|
|
205
|
-
\
|
|
206
|
-
|
|
207
|
-
\
|
|
208
|
-
\
|
|
209
|
-
|
|
210
|
-
\
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
\
|
|
216
|
-
\
|
|
217
|
-
\
|
|
218
|
-
|
|
219
|
-
\
|
|
220
|
-
|
|
221
|
-
exec tng --help\e[0m\e[37m - Show help information\e[0m โ\nโ โ\nโ
|
|
222
|
-
\ \e[37m\U0001F4A1 Generate tests for individual methods with precision\e[0m โ\nโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
223
|
-
v0.3.3 โ\n"
|
|
198
|
+
\ โ\nโ โ
Tng
|
|
199
|
+
installed successfully! โ\nโ โ\nโ
|
|
200
|
+
\ \U0001F4CB SETUP REQUIRED โ\nโ
|
|
201
|
+
\ โ\nโ 1. Generate
|
|
202
|
+
configuration: โ\nโ rails g tng:install
|
|
203
|
+
\ โ\nโ โ\nโ
|
|
204
|
+
\ 2. Edit configuration file: โ\nโ config/initializers/tng.rb
|
|
205
|
+
\ โ\nโ โ\nโ
|
|
206
|
+
\ 3. Add your license key: โ\nโ config.api_key
|
|
207
|
+
= 'your-license-key-here' โ\nโ โ\nโ
|
|
208
|
+
\ \U0001F4CB Check documentation for the correct authentication setup โ\nโ
|
|
209
|
+
\ โ\nโ \U0001F680
|
|
210
|
+
Usage: โ\nโ โ\nโ
|
|
211
|
+
\ Interactive mode: โ\nโ โข bundle
|
|
212
|
+
exec tng - Interactive CLI with method selection โ\nโ โ\nโ
|
|
213
|
+
\ Features: โ\nโ โข Test
|
|
214
|
+
20+ file types: Controllers, Models, Services + Jobs, โ\nโ Helpers, Lib, Policies,
|
|
215
|
+
Presenters, Mailers, GraphQL, and more โ\nโ โข Select specific methods to test
|
|
216
|
+
\ โ\nโ โข Search and filter through methods โ\nโ
|
|
217
|
+
\ โ\nโ Help:
|
|
218
|
+
\ โ\nโ โข bundle exec
|
|
219
|
+
tng --help - Show help information โ\nโ โ\nโ
|
|
220
|
+
\ \U0001F4A1 Generate tests for individual methods with precision โ\nโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
221
|
+
v0.3.5 โ\n"
|
|
224
222
|
rdoc_options: []
|
|
225
223
|
require_paths:
|
|
226
224
|
- lib
|