This is an automated email from the ASF dual-hosted git repository.

lukaszlenart pushed a commit to branch WW-5631-chaining-require-annotations
in repository https://gitbox.apache.org/repos/asf/struts.git

commit 6970a3b2a58b5bd59dc22514fe18cab983b28141
Author: Lukasz Lenart <[email protected]>
AuthorDate: Wed May 27 08:17:36 2026 +0200

    WW-5631 test(chaining): add failing @StrutsParameter enforcement tests
    
    Co-Authored-By: Claude Opus 4.7 <[email protected]>
---
 .../interceptor/ChainingInterceptorTest.java       | 91 ++++++++++++++++++++++
 1 file changed, 91 insertions(+)

diff --git 
a/core/src/test/java/org/apache/struts2/interceptor/ChainingInterceptorTest.java
 
b/core/src/test/java/org/apache/struts2/interceptor/ChainingInterceptorTest.java
index 85df164cc..e59780a93 100644
--- 
a/core/src/test/java/org/apache/struts2/interceptor/ChainingInterceptorTest.java
+++ 
b/core/src/test/java/org/apache/struts2/interceptor/ChainingInterceptorTest.java
@@ -27,6 +27,9 @@ import org.apache.struts2.SimpleAction;
 import org.apache.struts2.TestBean;
 import org.apache.struts2.XWorkTestCase;
 import org.apache.struts2.util.ValueStack;
+import org.apache.struts2.interceptor.parameter.StrutsParameterAuthorizer;
+import org.apache.struts2.ognl.OgnlUtil;
+import org.apache.struts2.util.ProxyService;
 
 import java.util.*;
 
@@ -149,6 +152,94 @@ public class ChainingInterceptorTest extends XWorkTestCase 
{
     }
 
 
+    private StrutsParameterAuthorizer buildAuthorizer(boolean 
requireAnnotations, boolean transitionMode) {
+        StrutsParameterAuthorizer authorizer = new StrutsParameterAuthorizer();
+        authorizer.setOgnlUtil(container.getInstance(OgnlUtil.class));
+        authorizer.setProxyService(container.getInstance(ProxyService.class));
+        authorizer.setRequireAnnotations(String.valueOf(requireAnnotations));
+        
authorizer.setRequireAnnotationsTransitionMode(String.valueOf(transitionMode));
+        return authorizer;
+    }
+
+    private void enableChainingEnforcement(boolean requireAnnotations, boolean 
transitionMode) {
+        interceptor.setParameterAuthorizer(buildAuthorizer(requireAnnotations, 
transitionMode));
+        interceptor.setRequireAnnotations("true");
+    }
+
+    public void testFlagOffCopiesUnannotatedProperty() throws Exception {
+        AnnotatedChainingAction source = new AnnotatedChainingAction();
+        source.setManagerApproved(true);
+        UnannotatedChainingAction target = new UnannotatedChainingAction();
+        mockInvocation.matchAndReturn("getAction", target);
+        stack.push(source);
+        stack.push(target);
+
+        interceptor.intercept(invocation);
+
+        assertTrue("legacy chaining should copy the property when flag is 
off", target.getManagerApproved());
+    }
+
+    public void testFlagOnSkipsUnannotatedProperty() throws Exception {
+        AnnotatedChainingAction source = new AnnotatedChainingAction();
+        source.setManagerApproved(true);
+        UnannotatedChainingAction target = new UnannotatedChainingAction();
+        mockInvocation.matchAndReturn("getAction", target);
+        stack.push(source);
+        stack.push(target);
+
+        enableChainingEnforcement(true, false);
+        interceptor.intercept(invocation);
+
+        assertFalse("unannotated target property must NOT be copied when 
enforcement is on",
+                target.getManagerApproved());
+    }
+
+    public void testFlagOnCopiesAnnotatedProperty() throws Exception {
+        AnnotatedChainingAction source = new AnnotatedChainingAction();
+        source.setManagerApproved(true);
+        AnnotatedChainingAction target = new AnnotatedChainingAction();
+        mockInvocation.matchAndReturn("getAction", target);
+        stack.push(source);
+        stack.push(target);
+
+        enableChainingEnforcement(true, false);
+        interceptor.intercept(invocation);
+
+        assertTrue("annotated target property should be copied when 
enforcement is on",
+                target.getManagerApproved());
+    }
+
+    public void testTransitionModeCopiesNonNestedUnannotatedProperty() throws 
Exception {
+        AnnotatedChainingAction source = new AnnotatedChainingAction();
+        source.setManagerApproved(true);
+        UnannotatedChainingAction target = new UnannotatedChainingAction();
+        mockInvocation.matchAndReturn("getAction", target);
+        stack.push(source);
+        stack.push(target);
+
+        enableChainingEnforcement(true, true);
+        interceptor.intercept(invocation);
+
+        assertTrue("transition mode should copy depth-0 property without 
annotation",
+                target.getManagerApproved());
+    }
+
+    public void testRequireAnnotationsFalseIsNoOp() throws Exception {
+        AnnotatedChainingAction source = new AnnotatedChainingAction();
+        source.setManagerApproved(true);
+        UnannotatedChainingAction target = new UnannotatedChainingAction();
+        mockInvocation.matchAndReturn("getAction", target);
+        stack.push(source);
+        stack.push(target);
+
+        interceptor.setParameterAuthorizer(buildAuthorizer(false, false));
+        interceptor.setRequireAnnotations("true");
+        interceptor.intercept(invocation);
+
+        assertTrue("when global requireAnnotations is off, enforcement is a 
no-op",
+                target.getManagerApproved());
+    }
+
     @Override
     protected void setUp() throws Exception {
         super.setUp();

Reply via email to