mirror of
https://github.com/elicpeter/nyx.git
synced 2026-06-09 19:45:13 +02:00
refactor(dynamic): extend framework-specific fallbacks with Spring HandlerExecutionChain, Go gqlgen, Django handler/middleware chain, Celery task registry, and Sidekiq client handling; enhance coverage and test logic
This commit is contained in:
parent
fd39304eed
commit
71fade1d83
5 changed files with 356 additions and 24 deletions
|
|
@ -140,6 +140,18 @@ fn go_string_literal(s: &str) -> String {
|
|||
format!("\"{escaped}\"")
|
||||
}
|
||||
|
||||
fn go_identifier_expr(name: &str) -> Option<String> {
|
||||
let mut chars = name.chars();
|
||||
let first = chars.next()?;
|
||||
if !(first == '_' || first.is_ascii_alphabetic()) {
|
||||
return None;
|
||||
}
|
||||
if !chars.all(|c| c == '_' || c.is_ascii_alphanumeric()) {
|
||||
return None;
|
||||
}
|
||||
Some(format!("entry.{name}"))
|
||||
}
|
||||
|
||||
/// Sorted, deduped tab-prefixed import lines covering the driver's
|
||||
/// `fmt` + `os` plus everything in [`SHIM_IMPORTS`].
|
||||
fn chain_step_imports() -> String {
|
||||
|
|
@ -2613,6 +2625,96 @@ fn emit_graphql_resolver_harness(
|
|||
) -> HarnessSource {
|
||||
let shim = probe_shim();
|
||||
let go_mod = generate_go_mod_for_spec(GoShape::Generic, spec);
|
||||
let handler_expr = go_identifier_expr(handler).unwrap_or_else(|| "nil".to_owned());
|
||||
let use_gqlgen_runtime = spec
|
||||
.framework
|
||||
.as_ref()
|
||||
.map(|binding| binding.adapter == "graphql-gqlgen")
|
||||
.unwrap_or(false);
|
||||
let runtime_imports = if use_gqlgen_runtime {
|
||||
r#" "bytes"
|
||||
"encoding/json"
|
||||
"net/http/httptest"
|
||||
|
||||
"github.com/99designs/gqlgen/graphql"
|
||||
gqlhandler "github.com/99designs/gqlgen/graphql/handler"
|
||||
"github.com/vektah/gqlparser/v2"
|
||||
"github.com/vektah/gqlparser/v2/ast"
|
||||
"github.com/vektah/gqlparser/v2/gqlerror"
|
||||
"#
|
||||
} else {
|
||||
""
|
||||
};
|
||||
let runtime_call = if use_gqlgen_runtime {
|
||||
"\tif nyxTryGqlgenHandler(cb, payload) {\n\t\treturn\n\t}\n"
|
||||
} else {
|
||||
""
|
||||
};
|
||||
let runtime_helpers = if use_gqlgen_runtime {
|
||||
format!(
|
||||
r##"
|
||||
type nyxExecutableSchema struct {{
|
||||
schema *ast.Schema
|
||||
resolver reflect.Value
|
||||
payload string
|
||||
field string
|
||||
}}
|
||||
|
||||
func (s *nyxExecutableSchema) Schema() *ast.Schema {{
|
||||
return s.schema
|
||||
}}
|
||||
|
||||
func (s *nyxExecutableSchema) Complexity(typeName, fieldName string, childComplexity int, args map[string]interface{{}}) (int, bool) {{
|
||||
return 1, true
|
||||
}}
|
||||
|
||||
func (s *nyxExecutableSchema) Exec(ctx context.Context) graphql.ResponseHandler {{
|
||||
return func(ctx context.Context) *graphql.Response {{
|
||||
value, err := nyxInvokeResolverValue(s.resolver, s.payload)
|
||||
if err != nil {{
|
||||
return &graphql.Response{{Errors: gqlerror.List{{gqlerror.Errorf(err.Error())}}}}
|
||||
}}
|
||||
data, err := json.Marshal(map[string]interface{{}}{{s.field: fmt.Sprint(value)}})
|
||||
if err != nil {{
|
||||
return &graphql.Response{{Errors: gqlerror.List{{gqlerror.Errorf(err.Error())}}}}
|
||||
}}
|
||||
return &graphql.Response{{Data: json.RawMessage(data)}}
|
||||
}}
|
||||
}}
|
||||
|
||||
func nyxTryGqlgenHandler(cb reflect.Value, payload string) bool {{
|
||||
schema, err := gqlparser.LoadSchema(&ast.Source{{
|
||||
Name: "nyx.graphql",
|
||||
Input: "schema {{ query: Query }}\ntype Query {{ {field}(id: String, input: String): String }}",
|
||||
}})
|
||||
if err != nil {{
|
||||
fmt.Fprintf(os.Stderr, "NYX_GQLGEN_SCHEMA_FALLBACK: %v\n", err)
|
||||
return false
|
||||
}}
|
||||
server := gqlhandler.NewDefaultServer(&nyxExecutableSchema{{
|
||||
schema: schema, resolver: cb, payload: payload, field: "{field}",
|
||||
}})
|
||||
body, _ := json.Marshal(map[string]interface{{}}{{
|
||||
"query": "query($value: String) {{ {field}(id: $value, input: $value) }}",
|
||||
"variables": map[string]interface{{}}{{"value": payload}},
|
||||
}})
|
||||
req := httptest.NewRequest("POST", "/query", bytes.NewReader(body))
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
rec := httptest.NewRecorder()
|
||||
server.ServeHTTP(rec, req)
|
||||
if rec.Code < 200 || rec.Code >= 300 {{
|
||||
fmt.Fprintf(os.Stderr, "NYX_GQLGEN_HANDLER_FALLBACK: status=%d body=%s\n", rec.Code, rec.Body.String())
|
||||
return false
|
||||
}}
|
||||
fmt.Print(rec.Body.String())
|
||||
return true
|
||||
}}
|
||||
"##,
|
||||
field = field
|
||||
)
|
||||
} else {
|
||||
String::new()
|
||||
};
|
||||
let source = format!(
|
||||
r##"// Nyx dynamic harness — GraphQL resolver (Phase 21 / Track M.3).
|
||||
package main
|
||||
|
|
@ -2622,6 +2724,7 @@ import (
|
|||
"fmt"
|
||||
"os"
|
||||
"reflect"
|
||||
{runtime_imports}
|
||||
|
||||
"nyx-harness/entry"
|
||||
)
|
||||
|
|
@ -2640,37 +2743,67 @@ func main() {{
|
|||
payload := nyxPayload()
|
||||
fmt.Println("__NYX_GRAPHQL_RESOLVER__: " + "{type_name}" + "." + "{field}")
|
||||
fmt.Println("__NYX_SINK_HIT__")
|
||||
cb, ok := entry.NyxResolvers["{handler}"]
|
||||
if !ok {{
|
||||
cb := reflect.ValueOf({handler_expr})
|
||||
if !cb.IsValid() || cb.Kind() != reflect.Func {{
|
||||
fmt.Fprintln(os.Stderr, "NYX_RESOLVER_NOT_FOUND: " + "{handler}")
|
||||
os.Exit(78)
|
||||
}}
|
||||
v := reflect.ValueOf(cb)
|
||||
args := make([]reflect.Value, v.Type().NumIn())
|
||||
for i := 0; i < v.Type().NumIn(); i++ {{
|
||||
want := v.Type().In(i)
|
||||
if want.Kind() == reflect.String {{
|
||||
args[i] = reflect.ValueOf(payload)
|
||||
}} else if want.String() == "context.Context" {{
|
||||
args[i] = reflect.ValueOf(context.Background())
|
||||
}} else {{
|
||||
args[i] = reflect.Zero(want)
|
||||
}}
|
||||
}}
|
||||
{runtime_call}
|
||||
defer func() {{
|
||||
if r := recover(); r != nil {{
|
||||
fmt.Fprintf(os.Stderr, "NYX_EXCEPTION: panic: %v\n", r)
|
||||
}}
|
||||
}}()
|
||||
out := v.Call(args)
|
||||
if len(out) > 0 {{
|
||||
fmt.Println(out[0].Interface())
|
||||
value, err := nyxInvokeResolverValue(cb, payload)
|
||||
if err != nil {{
|
||||
fmt.Fprintf(os.Stderr, "NYX_EXCEPTION: %v\n", err)
|
||||
return
|
||||
}}
|
||||
if value != nil {{
|
||||
fmt.Println(value)
|
||||
}}
|
||||
}}
|
||||
|
||||
func nyxInvokeResolverValue(v reflect.Value, payload string) (interface{{}}, error) {{
|
||||
contextType := reflect.TypeOf((*context.Context)(nil)).Elem()
|
||||
errorType := reflect.TypeOf((*error)(nil)).Elem()
|
||||
args := make([]reflect.Value, v.Type().NumIn())
|
||||
for i := 0; i < v.Type().NumIn(); i++ {{
|
||||
want := v.Type().In(i)
|
||||
if want.Kind() == reflect.String {{
|
||||
args[i] = reflect.ValueOf(payload)
|
||||
}} else if want.Implements(contextType) {{
|
||||
args[i] = reflect.ValueOf(context.Background())
|
||||
}} else if contextType.AssignableTo(want) {{
|
||||
args[i] = reflect.ValueOf(context.Background())
|
||||
}} else {{
|
||||
args[i] = reflect.Zero(want)
|
||||
}}
|
||||
}}
|
||||
out := v.Call(args)
|
||||
var value interface{{}}
|
||||
for _, item := range out {{
|
||||
if item.Type().Implements(errorType) {{
|
||||
if (item.Kind() == reflect.Interface || item.Kind() == reflect.Pointer) && !item.IsNil() {{
|
||||
return nil, item.Interface().(error)
|
||||
}}
|
||||
continue
|
||||
}}
|
||||
if value == nil && item.IsValid() {{
|
||||
value = item.Interface()
|
||||
}}
|
||||
}}
|
||||
return value, nil
|
||||
}}
|
||||
{runtime_helpers}
|
||||
"##,
|
||||
handler = handler,
|
||||
handler_expr = handler_expr,
|
||||
type_name = type_name,
|
||||
field = field,
|
||||
runtime_imports = runtime_imports,
|
||||
runtime_call = runtime_call,
|
||||
runtime_helpers = runtime_helpers,
|
||||
);
|
||||
HarnessSource {
|
||||
source,
|
||||
|
|
|
|||
|
|
@ -4687,9 +4687,6 @@ public class NyxHarness {{
|
|||
System.exit(78);
|
||||
}}
|
||||
m.setAccessible(true);
|
||||
if (nyxTrySpringHandlerInterceptor(instance, m, payload)) {{
|
||||
return;
|
||||
}}
|
||||
Class<?>[] params = m.getParameterTypes();
|
||||
Object[] mArgs = new Object[params.length];
|
||||
for (int i = 0; i < params.length; i++) {{
|
||||
|
|
@ -4810,6 +4807,9 @@ public class NyxHarness {{
|
|||
System.exit(78);
|
||||
}}
|
||||
m.setAccessible(true);
|
||||
if (nyxTrySpringHandlerExecutionChain(instance, m, payload)) {{
|
||||
return;
|
||||
}}
|
||||
Class<?>[] params = m.getParameterTypes();
|
||||
Object[] mArgs = new Object[params.length];
|
||||
for (int i = 0; i < params.length; i++) {{
|
||||
|
|
@ -4835,6 +4835,57 @@ public class NyxHarness {{
|
|||
return "";
|
||||
}}
|
||||
|
||||
static boolean nyxTrySpringHandlerExecutionChain(Object instance, Method m, String payload) {{
|
||||
if (!m.getName().equals("preHandle") || m.getParameterTypes().length < 3) {{
|
||||
return false;
|
||||
}}
|
||||
try {{
|
||||
Class<?> chainClass = Class.forName("org.springframework.web.servlet.HandlerExecutionChain");
|
||||
Class<?> interceptorClass = Class.forName("org.springframework.web.servlet.HandlerInterceptor");
|
||||
if (!interceptorClass.isAssignableFrom(instance.getClass())) {{
|
||||
return false;
|
||||
}}
|
||||
Object interceptors = java.lang.reflect.Array.newInstance(interceptorClass, 1);
|
||||
java.lang.reflect.Array.set(interceptors, 0, instance);
|
||||
Object chain = chainClass
|
||||
.getConstructor(Object.class, interceptors.getClass())
|
||||
.newInstance(new Object(), interceptors);
|
||||
Method getInterceptors = chainClass.getMethod("getInterceptors");
|
||||
Object chainInterceptors = getInterceptors.invoke(chain);
|
||||
int count = chainInterceptors == null ? 0 : java.lang.reflect.Array.getLength(chainInterceptors);
|
||||
if (count == 0) {{
|
||||
return false;
|
||||
}}
|
||||
Object request = null;
|
||||
Object response = null;
|
||||
for (Class<?> p : m.getParameterTypes()) {{
|
||||
String name = p.getName();
|
||||
if (request == null && name.endsWith("HttpServletRequest")) {{
|
||||
request = nyxServletProxy(p, payload);
|
||||
}} else if (response == null && name.endsWith("HttpServletResponse")) {{
|
||||
response = nyxServletProxy(p, payload);
|
||||
}}
|
||||
}}
|
||||
if (request == null || response == null) {{
|
||||
return false;
|
||||
}}
|
||||
Object interceptor = java.lang.reflect.Array.get(chainInterceptors, 0);
|
||||
Method preHandle = interceptor.getClass().getMethod(
|
||||
"preHandle",
|
||||
m.getParameterTypes()[0],
|
||||
m.getParameterTypes()[1],
|
||||
m.getParameterTypes()[2]
|
||||
);
|
||||
preHandle.invoke(interceptor, request, response, new Object());
|
||||
return true;
|
||||
}} catch (ClassNotFoundException missingSpring) {{
|
||||
return false;
|
||||
}} catch (Throwable e) {{
|
||||
System.err.println("NYX_SPRING_CHAIN_FALLBACK: " + e.getClass().getName() + ": " + e.getMessage());
|
||||
return false;
|
||||
}}
|
||||
}}
|
||||
|
||||
static boolean nyxTrySpringHandlerInterceptor(Object instance, Method m, String payload) {{
|
||||
Class<?>[] params = m.getParameterTypes();
|
||||
if (params.length < 3 || !m.getName().equals("preHandle")) {{
|
||||
|
|
|
|||
|
|
@ -1536,8 +1536,64 @@ def _nyx_try_celery_eager(task, body):
|
|||
print(f"NYX_CELERY_EAGER_FALLBACK: {{type(_e).__name__}}: {{_e}}", file=sys.stderr, flush=True)
|
||||
return False
|
||||
|
||||
def _nyx_try_celery_registered_task(handler_name, task, body):
|
||||
try:
|
||||
from celery import current_app
|
||||
except Exception:
|
||||
return False
|
||||
try:
|
||||
app = getattr(task, "app", None) or current_app
|
||||
if app is None or not hasattr(app, "tasks"):
|
||||
return False
|
||||
if hasattr(app, "conf"):
|
||||
try:
|
||||
app.conf.task_always_eager = True
|
||||
app.conf.task_eager_propagates = False
|
||||
except Exception:
|
||||
pass
|
||||
candidates = [
|
||||
handler_name,
|
||||
getattr(task, "name", None),
|
||||
getattr(_entry_mod, "__name__", "") + "." + handler_name,
|
||||
]
|
||||
registered = None
|
||||
for name in candidates:
|
||||
if not name:
|
||||
continue
|
||||
try:
|
||||
registered = app.tasks.get(name)
|
||||
except Exception:
|
||||
registered = None
|
||||
if registered is not None:
|
||||
break
|
||||
if registered is None:
|
||||
suffix = "." + handler_name
|
||||
try:
|
||||
for task_name, candidate in app.tasks.items():
|
||||
if str(task_name).endswith(suffix):
|
||||
registered = candidate
|
||||
break
|
||||
except Exception:
|
||||
registered = None
|
||||
if registered is None:
|
||||
return False
|
||||
if hasattr(registered, "signature"):
|
||||
sig = registered.signature(args=(body,))
|
||||
result = sig.apply(throw=False)
|
||||
else:
|
||||
result = registered.apply(args=(body,), throw=False)
|
||||
value = getattr(result, "result", None)
|
||||
if value is not None:
|
||||
print(str(value), flush=True)
|
||||
return True
|
||||
except SystemExit:
|
||||
raise
|
||||
except Exception as _e:
|
||||
print(f"NYX_CELERY_REGISTRY_FALLBACK: {{type(_e).__name__}}: {{_e}}", file=sys.stderr, flush=True)
|
||||
return False
|
||||
|
||||
try:
|
||||
if not _nyx_try_celery_eager(_h, payload):
|
||||
if not _nyx_try_celery_registered_task({handler:?}, _h, payload) and not _nyx_try_celery_eager(_h, payload):
|
||||
_result = _h(payload)
|
||||
if _result is not None:
|
||||
try:
|
||||
|
|
@ -1842,7 +1898,54 @@ try:
|
|||
print(f"NYX_DJANGO_MIDDLEWARE_FALLBACK: {{type(_e).__name__}}: {{_e}}", file=sys.stderr, flush=True)
|
||||
return False
|
||||
|
||||
if not _nyx_try_django_middleware(_h, payload):
|
||||
def _nyx_try_django_handler_chain(factory, body):
|
||||
try:
|
||||
import types
|
||||
from django.conf import settings
|
||||
module_name = "_nyx_phase21_middleware"
|
||||
module = types.ModuleType(module_name)
|
||||
setattr(module, "NyxMiddleware", factory)
|
||||
module.urlpatterns = []
|
||||
sys.modules[module_name] = module
|
||||
middleware_path = module_name + ".NyxMiddleware"
|
||||
if not settings.configured:
|
||||
settings.configure(
|
||||
DEFAULT_CHARSET="utf-8",
|
||||
SECRET_KEY="nyx-dynamic-harness",
|
||||
ROOT_URLCONF=module_name,
|
||||
ALLOWED_HOSTS=["testserver", "localhost", "127.0.0.1"],
|
||||
INSTALLED_APPS=[],
|
||||
MIDDLEWARE=[middleware_path],
|
||||
)
|
||||
else:
|
||||
try:
|
||||
settings.MIDDLEWARE = [middleware_path]
|
||||
settings.ROOT_URLCONF = module_name
|
||||
except Exception:
|
||||
return False
|
||||
import django
|
||||
django.setup()
|
||||
from django.core.handlers.base import BaseHandler
|
||||
from django.test import RequestFactory
|
||||
request = RequestFactory().post("/nyx", data={{"q": body}})
|
||||
request._body = str(body).encode("utf-8", "replace")
|
||||
handler = BaseHandler()
|
||||
handler.load_middleware()
|
||||
response = handler.get_response(request)
|
||||
body_bytes = getattr(response, "content", None)
|
||||
if body_bytes:
|
||||
try:
|
||||
print(body_bytes.decode("utf-8", "replace"), flush=True)
|
||||
except Exception:
|
||||
print(str(body_bytes), flush=True)
|
||||
return True
|
||||
except SystemExit:
|
||||
raise
|
||||
except Exception as _e:
|
||||
print(f"NYX_DJANGO_HANDLER_CHAIN_FALLBACK: {{type(_e).__name__}}: {{_e}}", file=sys.stderr, flush=True)
|
||||
return False
|
||||
|
||||
if not _nyx_try_django_handler_chain(_h, payload) and not _nyx_try_django_middleware(_h, payload):
|
||||
_req = _NyxRequest(payload)
|
||||
# Try class-shaped middleware (instantiate with a get_response stub).
|
||||
try:
|
||||
|
|
|
|||
|
|
@ -765,7 +765,12 @@ if Object.const_defined?({handler:?})
|
|||
require 'sidekiq/testing'
|
||||
if cls.respond_to?(:perform_async)
|
||||
Sidekiq::Testing.inline! do
|
||||
cls.perform_async($nyx_payload)
|
||||
begin
|
||||
require 'sidekiq/client'
|
||||
Sidekiq::Client.push('class' => cls, 'args' => [$nyx_payload])
|
||||
rescue LoadError, StandardError
|
||||
cls.perform_async($nyx_payload)
|
||||
end
|
||||
end
|
||||
exit 0
|
||||
end
|
||||
|
|
|
|||
|
|
@ -680,6 +680,9 @@ fn scheduled_job_python_harness_carries_sentinel_and_handler() {
|
|||
assert!(h.source.contains("__NYX_SCHEDULED_JOB__"));
|
||||
assert!(h.source.contains("\"tick\""));
|
||||
assert!(h.source.contains("*/5 * * * *"));
|
||||
assert!(h.source.contains("_nyx_try_celery_registered_task"));
|
||||
assert!(h.source.contains("current_app"));
|
||||
assert!(h.source.contains("app.tasks"));
|
||||
assert!(h.source.contains("_nyx_try_celery_eager"));
|
||||
assert!(h.source.contains("task.apply"));
|
||||
}
|
||||
|
|
@ -714,6 +717,10 @@ fn scheduled_job_java_harness_carries_sentinel_and_handler() {
|
|||
assert!(h.source.contains("\"execute\""));
|
||||
assert!(h.source.contains("nyxTryQuartz"));
|
||||
assert!(h.source.contains("org.quartz.JobBuilder"));
|
||||
assert!(
|
||||
!h.source
|
||||
.contains("nyxTrySpringHandlerInterceptor(instance, m, payload)")
|
||||
);
|
||||
assert_eq!(h.command, vec!["java", "-cp", ".:lib/*", "NyxHarness"]);
|
||||
}
|
||||
|
||||
|
|
@ -729,6 +736,7 @@ fn scheduled_job_ruby_harness_carries_sentinel_and_handler() {
|
|||
assert!(h.source.contains("__NYX_SCHEDULED_JOB__"));
|
||||
assert!(h.source.contains("TickWorker"));
|
||||
assert!(h.source.contains("sidekiq/testing"));
|
||||
assert!(h.source.contains("Sidekiq::Client.push"));
|
||||
assert!(h.source.contains("perform_async"));
|
||||
}
|
||||
|
||||
|
|
@ -873,7 +881,34 @@ fn graphql_resolver_go_harness_carries_sentinel_and_field() {
|
|||
let h = lang::emit(&spec).expect("emit ok");
|
||||
assert!(h.source.contains("__NYX_GRAPHQL_RESOLVER__"));
|
||||
assert!(h.source.contains("ResolveUser"));
|
||||
assert!(h.source.contains("entry.NyxResolvers"));
|
||||
assert!(h.source.contains("reflect.ValueOf(entry.ResolveUser)"));
|
||||
assert!(!h.source.contains("entry.NyxResolvers"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn graphql_resolver_go_gqlgen_harness_uses_handler_runtime() {
|
||||
let spec = framework_bound_spec(
|
||||
Lang::Go,
|
||||
EvEntryKind::GraphQLResolver {
|
||||
type_name: "Query".into(),
|
||||
field: "user".into(),
|
||||
},
|
||||
"ResolveUser",
|
||||
"tests/dynamic_fixtures/graphql_resolver/gqlgen/vuln.go",
|
||||
"graphql-gqlgen",
|
||||
);
|
||||
let h = lang::emit(&spec).expect("emit ok");
|
||||
assert!(
|
||||
h.source
|
||||
.contains("github.com/99designs/gqlgen/graphql/handler")
|
||||
);
|
||||
assert!(h.source.contains("gqlhandler.NewDefaultServer"));
|
||||
assert!(h.source.contains("httptest.NewRecorder"));
|
||||
assert!(h.source.contains("nyxExecutableSchema"));
|
||||
assert!(h.source.contains(
|
||||
"Complexity(typeName, fieldName string, childComplexity int, args map[string]interface{})"
|
||||
));
|
||||
assert!(!h.source.contains("entry.NyxResolvers"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
@ -945,6 +980,9 @@ fn middleware_python_harness_carries_sentinel_and_handler() {
|
|||
let h = lang::emit(&spec).expect("emit ok");
|
||||
assert!(h.source.contains("__NYX_MIDDLEWARE__"));
|
||||
assert!(h.source.contains("\"audit\""));
|
||||
assert!(h.source.contains("_nyx_try_django_handler_chain"));
|
||||
assert!(h.source.contains("BaseHandler"));
|
||||
assert!(h.source.contains("handler.load_middleware"));
|
||||
assert!(h.source.contains("_nyx_try_django_middleware"));
|
||||
assert!(h.source.contains("RequestFactory"));
|
||||
}
|
||||
|
|
@ -979,6 +1017,8 @@ fn middleware_java_harness_carries_sentinel_and_handler() {
|
|||
let h = lang::emit(&spec).expect("emit ok");
|
||||
assert!(h.source.contains("__NYX_MIDDLEWARE__"));
|
||||
assert!(h.source.contains("\"preHandle\""));
|
||||
assert!(h.source.contains("nyxTrySpringHandlerExecutionChain"));
|
||||
assert!(h.source.contains("HandlerExecutionChain"));
|
||||
assert!(h.source.contains("nyxTrySpringHandlerInterceptor"));
|
||||
assert!(h.source.contains("HttpServletRequest"));
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue