tng 0.2.8 → 0.2.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.
- checksums.yaml +4 -4
- data/bin/tng +2 -2
- data/binaries/tng.bundle +0 -0
- data/binaries/tng.so +0 -0
- data/lib/tng/analyzers/model.rb +60 -23
- data/lib/tng/analyzers/service.rb +1 -1
- data/lib/tng/services/test_generator.rb +4 -4
- data/lib/tng/services/user_app_config.rb +25 -0
- data/lib/tng/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 5f6e0805bc5c9a7dbb6d83b8298fcbc099a14c70e118c02743545a29dfbc8182
|
|
4
|
+
data.tar.gz: 070b0bbc5c54a91b01a2e38113588e7838bb0ff1d8217e0f666695d953a7ea6a
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: f46c75ee3f7fa1cc0e22bdb46687c600184e9f2435f8321be71bc1623c33c7a1494326d0cfcd2030017bbe94244f39869870a16f245d9abc3e2ee8b307dedf98
|
|
7
|
+
data.tar.gz: 1c971f70e79e8cf9d9bd99ce779e3d244bee955908aec4ab679fd7a72c491e68a624b2fa56ee8195ecef2e42ba2b3df2828bb7dcc8278caa5374692c68522807
|
data/bin/tng
CHANGED
|
@@ -827,7 +827,7 @@ class CLI
|
|
|
827
827
|
|
|
828
828
|
info_msg = [
|
|
829
829
|
@pastel.decorate("#{Tng::UI::Theme.icon(:config)} File: #{result[:file_path]}", Tng::UI::Theme.color(:info)),
|
|
830
|
-
@pastel.decorate("#{Tng::UI::Theme.icon(:marker)} Run: #{result[:run_command]}", Tng::UI::Theme.color(:
|
|
830
|
+
@pastel.decorate("#{Tng::UI::Theme.icon(:marker)} Run: #{result[:run_command]}", Tng::UI::Theme.color(:secondary))
|
|
831
831
|
].join("\n")
|
|
832
832
|
puts center_text(info_msg)
|
|
833
833
|
puts
|
|
@@ -845,7 +845,7 @@ class CLI
|
|
|
845
845
|
|
|
846
846
|
info_msg = [
|
|
847
847
|
@pastel.decorate("#{Tng::UI::Theme.icon(:config)} File: #{result[:file_path]}", Tng::UI::Theme.color(:info)),
|
|
848
|
-
@pastel.decorate("#{Tng::UI::Theme.icon(:marker)} Run: #{result[:run_command]}", Tng::UI::Theme.color(:
|
|
848
|
+
@pastel.decorate("#{Tng::UI::Theme.icon(:marker)} Run: #{result[:run_command]}", Tng::UI::Theme.color(:secondary))
|
|
849
849
|
].join("\n")
|
|
850
850
|
puts center_text(info_msg)
|
|
851
851
|
puts
|
data/binaries/tng.bundle
CHANGED
|
Binary file
|
data/binaries/tng.so
CHANGED
|
Binary file
|
data/lib/tng/analyzers/model.rb
CHANGED
|
@@ -33,32 +33,50 @@ module Tng
|
|
|
33
33
|
instance_methods = model_class.public_instance_methods(false)
|
|
34
34
|
class_methods = model_class.public_methods(false) - Class.public_methods
|
|
35
35
|
|
|
36
|
-
model_file =
|
|
36
|
+
model_file = Object.const_source_location(model_class.name)&.first
|
|
37
37
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
38
|
+
if model_file && File.exist?(model_file)
|
|
39
|
+
source_code = File.read(model_file)
|
|
40
|
+
result = Prism.parse(source_code)
|
|
41
41
|
|
|
42
|
-
|
|
43
|
-
|
|
42
|
+
defined_methods = []
|
|
43
|
+
synthetic_methods = []
|
|
44
|
+
scopes = []
|
|
45
|
+
validations = []
|
|
46
|
+
extract_method_names(result.value, defined_methods, synthetic_methods, scopes, validations)
|
|
44
47
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
+
filtered_instance_methods = instance_methods.select do |method_name|
|
|
49
|
+
method = model_class.instance_method(method_name)
|
|
50
|
+
next false unless method.owner == model_class
|
|
48
51
|
|
|
49
|
-
|
|
50
|
-
|
|
52
|
+
defined_methods.include?(method_name.to_s)
|
|
53
|
+
end
|
|
51
54
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
+
filtered_class_methods = class_methods.select do |method_name|
|
|
56
|
+
defined_methods.include?(method_name.to_s)
|
|
57
|
+
end
|
|
55
58
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
59
|
+
# Return array of hashes with type information
|
|
60
|
+
instance_method_hashes = filtered_instance_methods.map do |name|
|
|
61
|
+
{ name: name.to_s, type: "instance_method" }
|
|
62
|
+
end
|
|
63
|
+
class_method_hashes = filtered_class_methods.map do |name|
|
|
64
|
+
{ name: name.to_s, type: "class_method" }
|
|
65
|
+
end
|
|
66
|
+
synthetic_method_hashes = synthetic_methods.map do |name|
|
|
67
|
+
{ name: name.to_s, type: "synthetic" }
|
|
68
|
+
end
|
|
69
|
+
scope_hashes = scopes.map do |name|
|
|
70
|
+
{ name: name.to_s, type: "scope" }
|
|
71
|
+
end
|
|
72
|
+
validation_hashes = validations.map do |name|
|
|
73
|
+
{ name: name.to_s, type: "validation" }
|
|
74
|
+
end
|
|
60
75
|
|
|
61
|
-
|
|
76
|
+
instance_method_hashes + class_method_hashes + synthetic_method_hashes + scope_hashes + validation_hashes
|
|
77
|
+
else
|
|
78
|
+
[]
|
|
79
|
+
end
|
|
62
80
|
rescue NameError => e
|
|
63
81
|
puts "❌ Could not load model class #{model_name}: #{e.message}"
|
|
64
82
|
[]
|
|
@@ -68,7 +86,7 @@ module Tng
|
|
|
68
86
|
end
|
|
69
87
|
end
|
|
70
88
|
|
|
71
|
-
def self.extract_method_names(node, methods)
|
|
89
|
+
def self.extract_method_names(node, methods, synthetic_methods = [], scopes = [], validations = [])
|
|
72
90
|
return unless node.is_a?(Prism::Node)
|
|
73
91
|
|
|
74
92
|
case node
|
|
@@ -81,18 +99,37 @@ module Tng
|
|
|
81
99
|
first_arg = node.arguments.arguments.first
|
|
82
100
|
if first_arg.is_a?(Prism::SymbolNode)
|
|
83
101
|
scope_name = first_arg.value
|
|
84
|
-
|
|
102
|
+
scopes << scope_name if scope_name
|
|
103
|
+
end
|
|
104
|
+
# Handle validation macros
|
|
105
|
+
elsif node.name.to_s.start_with?("validate") && node.arguments&.arguments&.any?
|
|
106
|
+
if node.name == :validate
|
|
107
|
+
# Custom validation: validate :method_name -> extract actual method
|
|
108
|
+
node.arguments.arguments.each do |arg|
|
|
109
|
+
if arg.is_a?(Prism::SymbolNode)
|
|
110
|
+
validation_method = arg.value
|
|
111
|
+
validations << validation_method if validation_method
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
else
|
|
115
|
+
# Built-in validations: validates :attr, :attr2, ... -> add to validations
|
|
116
|
+
node.arguments.arguments.each do |arg|
|
|
117
|
+
if arg.is_a?(Prism::SymbolNode)
|
|
118
|
+
attr_name = arg.value
|
|
119
|
+
validations << attr_name if attr_name
|
|
120
|
+
end
|
|
121
|
+
end
|
|
85
122
|
end
|
|
86
123
|
end
|
|
87
124
|
when Prism::SingletonClassNode
|
|
88
125
|
# Methods inside class << self blocks
|
|
89
126
|
node.body&.child_nodes&.each do |child|
|
|
90
|
-
extract_method_names(child, methods)
|
|
127
|
+
extract_method_names(child, methods, synthetic_methods, scopes, validations)
|
|
91
128
|
end
|
|
92
129
|
end
|
|
93
130
|
|
|
94
131
|
node.child_nodes.each do |child|
|
|
95
|
-
extract_method_names(child, methods)
|
|
132
|
+
extract_method_names(child, methods, synthetic_methods, scopes, validations)
|
|
96
133
|
end
|
|
97
134
|
end
|
|
98
135
|
end
|
|
@@ -47,7 +47,7 @@ module Tng
|
|
|
47
47
|
end
|
|
48
48
|
|
|
49
49
|
# Fallback to const_source_location if no method source found
|
|
50
|
-
service_file ||=
|
|
50
|
+
service_file ||= Object.const_source_location(service_class.name)&.first
|
|
51
51
|
|
|
52
52
|
service_methods = if service_file && File.exist?(service_file)
|
|
53
53
|
source_code = File.read(service_file)
|
|
@@ -87,13 +87,13 @@ module Tng
|
|
|
87
87
|
|
|
88
88
|
case type
|
|
89
89
|
when :controller
|
|
90
|
-
Tng.send_request_for_controller(name, file_object[:path], method_info
|
|
90
|
+
Tng.send_request_for_controller(name, file_object[:path], method_info, *config)
|
|
91
91
|
when :model
|
|
92
|
-
Tng.send_request_for_model(name, file_object[:path], method_info
|
|
92
|
+
Tng.send_request_for_model(name, file_object[:path], method_info, *config)
|
|
93
93
|
when :service
|
|
94
|
-
Tng.send_request_for_service(name, file_object[:path], method_info
|
|
94
|
+
Tng.send_request_for_service(name, file_object[:path], method_info, *config)
|
|
95
95
|
when :other
|
|
96
|
-
Tng.send_request_for_other(name, file_object[:path], method_info
|
|
96
|
+
Tng.send_request_for_other(name, file_object[:path], method_info, *config)
|
|
97
97
|
end
|
|
98
98
|
end
|
|
99
99
|
|
|
@@ -34,6 +34,18 @@ module Tng
|
|
|
34
34
|
}
|
|
35
35
|
end
|
|
36
36
|
Tng.config[:authentication_entry_points_with_source] = auth_entry_points_with_source
|
|
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
|
+
|
|
37
49
|
Tng.config
|
|
38
50
|
end
|
|
39
51
|
|
|
@@ -71,6 +83,19 @@ module Tng
|
|
|
71
83
|
rescue StandardError
|
|
72
84
|
nil
|
|
73
85
|
end
|
|
86
|
+
|
|
87
|
+
def self.read_test_file_content(relative_path)
|
|
88
|
+
return nil unless relative_path
|
|
89
|
+
|
|
90
|
+
rails_root = defined?(Rails) && Rails.root ? Rails.root.to_s : Dir.pwd
|
|
91
|
+
full_path = File.join(rails_root, relative_path)
|
|
92
|
+
|
|
93
|
+
return nil unless File.exist?(full_path) && File.readable?(full_path)
|
|
94
|
+
|
|
95
|
+
File.read(full_path)
|
|
96
|
+
rescue StandardError
|
|
97
|
+
nil
|
|
98
|
+
end
|
|
74
99
|
end
|
|
75
100
|
end
|
|
76
101
|
end
|
data/lib/tng/version.rb
CHANGED
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.2.
|
|
4
|
+
version: 0.2.9
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- ralucab
|
|
@@ -287,7 +287,7 @@ post_install_message: "┌ TNG ────────────────
|
|
|
287
287
|
\ │\n│ • bundle exec
|
|
288
288
|
tng --help - Show help information │\n│ │\n│
|
|
289
289
|
\ \U0001F4A1 Generate tests for individual methods with precision │\n└────────────────────────────────────────────────────────────
|
|
290
|
-
v0.2.
|
|
290
|
+
v0.2.9 ┘\n"
|
|
291
291
|
rdoc_options: []
|
|
292
292
|
require_paths:
|
|
293
293
|
- lib
|