mirror of
https://github.com/elicpeter/nyx.git
synced 2026-06-09 19:45:13 +02:00
refactor(dynamic): add recursive dependency resolution for Java, Go, and Ruby receivers, expand corresponding tests
This commit is contained in:
parent
0e8c900078
commit
acec041676
10 changed files with 366 additions and 10 deletions
|
|
@ -1876,11 +1876,78 @@ func nyxBuildReceiver(structName string) (reflect.Value, error) {{
|
|||
// the auto-generated file references the target type by name and
|
||||
// the Go compiler enforces the contract.
|
||||
if r, ok := entry.NyxAutoReceivers[structName]; ok {{
|
||||
return reflect.ValueOf(r), nil
|
||||
return nyxPopulateReceiver(reflect.ValueOf(r), 3), nil
|
||||
}}
|
||||
return reflect.Value{{}}, fmt.Errorf("class not found: %s", structName)
|
||||
}}
|
||||
|
||||
func nyxPopulateReceiver(v reflect.Value, depth int) reflect.Value {{
|
||||
seen := map[reflect.Type]bool{{}}
|
||||
return nyxPopulateValue(v, depth, seen)
|
||||
}}
|
||||
|
||||
func nyxPopulateValue(v reflect.Value, depth int, seen map[reflect.Type]bool) reflect.Value {{
|
||||
if !v.IsValid() || depth < 0 {{
|
||||
return v
|
||||
}}
|
||||
if v.Kind() == reflect.Pointer {{
|
||||
if v.IsNil() {{
|
||||
if v.Type().Elem().Kind() != reflect.Struct {{
|
||||
return v
|
||||
}}
|
||||
v = reflect.New(v.Type().Elem())
|
||||
}}
|
||||
nyxPopulateStruct(v.Elem(), depth, seen)
|
||||
return v
|
||||
}}
|
||||
if v.Kind() == reflect.Struct {{
|
||||
out := reflect.New(v.Type()).Elem()
|
||||
out.Set(v)
|
||||
nyxPopulateStruct(out, depth, seen)
|
||||
return out
|
||||
}}
|
||||
return v
|
||||
}}
|
||||
|
||||
func nyxPopulateStruct(v reflect.Value, depth int, seen map[reflect.Type]bool) {{
|
||||
if !v.IsValid() || v.Kind() != reflect.Struct || depth < 0 {{
|
||||
return
|
||||
}}
|
||||
t := v.Type()
|
||||
if seen[t] {{
|
||||
return
|
||||
}}
|
||||
seen[t] = true
|
||||
defer delete(seen, t)
|
||||
for i := 0; i < v.NumField(); i++ {{
|
||||
field := v.Field(i)
|
||||
if !field.CanSet() {{
|
||||
continue
|
||||
}}
|
||||
dep := nyxBuildValueForType(field.Type(), depth-1, seen)
|
||||
if dep.IsValid() && dep.Type().AssignableTo(field.Type()) {{
|
||||
field.Set(dep)
|
||||
}}
|
||||
}}
|
||||
}}
|
||||
|
||||
func nyxBuildValueForType(t reflect.Type, depth int, seen map[reflect.Type]bool) reflect.Value {{
|
||||
if depth < 0 {{
|
||||
return reflect.Value{{}}
|
||||
}}
|
||||
if t.Kind() == reflect.Pointer && t.Elem().Kind() == reflect.Struct {{
|
||||
ptr := reflect.New(t.Elem())
|
||||
nyxPopulateStruct(ptr.Elem(), depth, seen)
|
||||
return ptr
|
||||
}}
|
||||
if t.Kind() == reflect.Struct {{
|
||||
value := reflect.New(t).Elem()
|
||||
nyxPopulateStruct(value, depth, seen)
|
||||
return value
|
||||
}}
|
||||
return reflect.Value{{}}
|
||||
}}
|
||||
|
||||
func nyxPayload() string {{
|
||||
if v := os.Getenv("NYX_PAYLOAD"); v != "" {{
|
||||
return v
|
||||
|
|
|
|||
|
|
@ -3313,6 +3313,9 @@ fn emit_class_method_harness(
|
|||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
public class NyxHarness {{
|
||||
{probe}
|
||||
|
|
@ -3322,6 +3325,14 @@ public class NyxHarness {{
|
|||
{mock_log}
|
||||
|
||||
static Object nyxBuildReceiver(Class<?> cls) throws Exception {{
|
||||
return nyxBuildReceiver(cls, 3, new HashSet<Class<?>>());
|
||||
}}
|
||||
|
||||
static Object nyxBuildReceiver(Class<?> cls, int depth, Set<Class<?>> seen) throws Exception {{
|
||||
if (cls == null || seen.contains(cls)) {{
|
||||
return null;
|
||||
}}
|
||||
seen.add(cls);
|
||||
// Preferred path: zero-arg ctor.
|
||||
try {{
|
||||
Constructor<?> c = cls.getDeclaredConstructor();
|
||||
|
|
@ -3335,22 +3346,28 @@ public class NyxHarness {{
|
|||
Class<?>[] params = c.getParameterTypes();
|
||||
Object[] args = new Object[params.length];
|
||||
for (int i = 0; i < params.length; i++) {{
|
||||
args[i] = nyxStubForType(params[i]);
|
||||
args[i] = nyxValueForType(params[i], depth - 1, new HashSet<Class<?>>(seen));
|
||||
}}
|
||||
try {{ return c.newInstance(args); }} catch (Exception ignore) {{}}
|
||||
}}
|
||||
return null;
|
||||
}}
|
||||
|
||||
static Object nyxStubForType(Class<?> t) {{
|
||||
String n = t.getName().toLowerCase();
|
||||
if (n.contains("http") || n.contains("client")) return new MockHttpClient();
|
||||
if (n.contains("database") || n.contains("connection") || n.contains("session") || n.contains("repository")) return new MockDatabaseConnection();
|
||||
if (n.contains("logger") || n.contains("log")) return new MockLogger();
|
||||
static Object nyxValueForType(Class<?> t, int depth, Set<Class<?>> seen) {{
|
||||
if (t.equals(String.class)) return "";
|
||||
if (t.equals(int.class) || t.equals(Integer.class)) return 0;
|
||||
if (t.equals(long.class) || t.equals(Long.class)) return 0L;
|
||||
if (t.equals(boolean.class) || t.equals(Boolean.class)) return false;
|
||||
if (depth >= 0 && !t.isPrimitive() && !t.isInterface() && !Modifier.isAbstract(t.getModifiers())) {{
|
||||
try {{
|
||||
Object receiver = nyxBuildReceiver(t, depth, seen);
|
||||
if (receiver != null) return receiver;
|
||||
}} catch (Throwable ignore) {{}}
|
||||
}}
|
||||
String n = t.getName().toLowerCase();
|
||||
if (n.contains("http") || n.contains("client")) return new MockHttpClient();
|
||||
if (n.contains("database") || n.contains("connection") || n.contains("session") || n.contains("repository")) return new MockDatabaseConnection();
|
||||
if (n.contains("logger") || n.contains("log")) return new MockLogger();
|
||||
return null;
|
||||
}}
|
||||
|
||||
|
|
@ -3380,10 +3397,13 @@ public class NyxHarness {{
|
|||
Class<?>[] params = match.getParameterTypes();
|
||||
Object[] mArgs = new Object[params.length];
|
||||
for (int i = 0; i < params.length; i++) {{
|
||||
mArgs[i] = params[i].equals(String.class) ? payload : nyxStubForType(params[i]);
|
||||
mArgs[i] = params[i].equals(String.class) ? payload : nyxValueForType(params[i], 2, new HashSet<Class<?>>());
|
||||
}}
|
||||
match.invoke(instance, mArgs);
|
||||
Object result = match.invoke(instance, mArgs);
|
||||
System.out.println("__NYX_SINK_HIT__");
|
||||
if (result != null) {{
|
||||
System.out.println(result.toString());
|
||||
}}
|
||||
}} catch (InvocationTargetException ite) {{
|
||||
Throwable cause = ite.getCause() == null ? ite : ite.getCause();
|
||||
System.err.println("NYX_EXCEPTION: " + cause.getClass().getName() + ": " + cause.getMessage());
|
||||
|
|
|
|||
|
|
@ -578,11 +578,47 @@ unless Object.const_defined?(cls_name)
|
|||
end
|
||||
cls = Object.const_get(cls_name)
|
||||
|
||||
def _nyx_build_receiver(cls)
|
||||
def _nyx_known_mock_for(name)
|
||||
n = name.to_s.downcase
|
||||
return MockHttpClient.new if n.include?('http') || n.include?('client')
|
||||
return MockDatabaseConnection.new if n.include?('db') || n.include?('conn') || n.include?('repo') || n.include?('session')
|
||||
return MockLogger.new if n.include?('log')
|
||||
nil
|
||||
end
|
||||
|
||||
def _nyx_const_for_param(name)
|
||||
raw = name.to_s
|
||||
camel = raw.split('_').reject(&:empty?).map {{ |part| part[0].upcase + part[1..].to_s }}.join
|
||||
[camel, raw].each do |candidate|
|
||||
next if candidate.empty?
|
||||
if Object.const_defined?(candidate, false)
|
||||
value = Object.const_get(candidate)
|
||||
return value if value.is_a?(Class)
|
||||
end
|
||||
end
|
||||
nil
|
||||
end
|
||||
|
||||
def _nyx_build_receiver(cls, depth = 3, seen = {{}})
|
||||
return nil if seen[cls]
|
||||
seen = seen.merge(cls => true)
|
||||
begin
|
||||
return cls.new
|
||||
rescue ArgumentError
|
||||
end
|
||||
begin
|
||||
init = cls.instance_method(:initialize)
|
||||
deps = init.parameters.map do |_kind, name|
|
||||
dep = nil
|
||||
if depth > 0 && name
|
||||
dep_cls = _nyx_const_for_param(name)
|
||||
dep = _nyx_build_receiver(dep_cls, depth - 1, seen) if dep_cls && dep_cls != cls
|
||||
end
|
||||
dep || _nyx_known_mock_for(name)
|
||||
end
|
||||
return cls.new(*deps)
|
||||
rescue StandardError
|
||||
end
|
||||
begin
|
||||
return cls.new(MockHttpClient.new, MockDatabaseConnection.new, MockLogger.new)
|
||||
rescue StandardError
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue