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

lukaszlenart pushed a commit to branch 
WW-2934-skip-validators-on-conversion-error
in repository https://gitbox.apache.org/repos/asf/struts.git

commit 5ba95dcd8245fc62f6e25c6f1ff0388744e914bc
Author: Lukasz Lenart <[email protected]>
AuthorDate: Thu Jul 23 21:44:08 2026 +0200

    WW-2934 test(core): cover annotation manager + document conversion-error 
skip exemption
    
    Co-Authored-By: Claude Opus 4.8 <[email protected]>
---
 .../validator/DefaultActionValidatorManager.java   |  7 +++++
 .../org/apache/struts2/default.properties          |  1 +
 .../DefaultActionValidatorManagerTest.java         | 33 ++++++++++++++++++++++
 3 files changed, 41 insertions(+)

diff --git 
a/core/src/main/java/org/apache/struts2/validator/DefaultActionValidatorManager.java
 
b/core/src/main/java/org/apache/struts2/validator/DefaultActionValidatorManager.java
index f5c40a1fb..9e1c5a7a6 100644
--- 
a/core/src/main/java/org/apache/struts2/validator/DefaultActionValidatorManager.java
+++ 
b/core/src/main/java/org/apache/struts2/validator/DefaultActionValidatorManager.java
@@ -99,6 +99,13 @@ public class DefaultActionValidatorManager implements 
ActionValidatorManager {
         this.reloadingConfigs = Boolean.parseBoolean(reloadingConfigs);
     }
 
+    /**
+     * When set to {@code true}, a field's remaining validators are skipped 
once that field has a
+     * type-conversion error. The field's own {@link 
ConversionErrorFieldValidator} still runs (so its
+     * message is still shown), and action-level validators are unaffected. 
Defaults to {@code false}.
+     *
+     * @param skipValidatorsOnConversionError whether to skip a field's 
remaining validators when it has a conversion error.
+     */
     @Inject(value = 
StrutsConstants.STRUTS_VALIDATORS_SKIP_VALIDATORS_ON_CONVERSION_ERROR, required 
= false)
     public void setSkipValidatorsOnConversionError(String 
skipValidatorsOnConversionError) {
         this.skipValidatorsOnConversionError = 
Boolean.parseBoolean(skipValidatorsOnConversionError);
diff --git a/core/src/main/resources/org/apache/struts2/default.properties 
b/core/src/main/resources/org/apache/struts2/default.properties
index 0dcb5ab80..76a1ff9a2 100644
--- a/core/src/main/resources/org/apache/struts2/default.properties
+++ b/core/src/main/resources/org/apache/struts2/default.properties
@@ -148,6 +148,7 @@ struts.devMode = false
 
 ### When set to true, a field's remaining validators are skipped once that 
field
 ### has a type conversion error, avoiding a duplicate error (WW-2934).
+### The field's own conversion validator still runs, so its message is still 
shown.
 ### valid values are: true, false (false is the default)
 struts.validators.skipValidatorsOnConversionError = false
 
diff --git 
a/core/src/test/java/org/apache/struts2/validator/DefaultActionValidatorManagerTest.java
 
b/core/src/test/java/org/apache/struts2/validator/DefaultActionValidatorManagerTest.java
index f2ad1550f..e868bb669 100644
--- 
a/core/src/test/java/org/apache/struts2/validator/DefaultActionValidatorManagerTest.java
+++ 
b/core/src/test/java/org/apache/struts2/validator/DefaultActionValidatorManagerTest.java
@@ -19,11 +19,14 @@
 package org.apache.struts2.validator;
 
 import org.apache.struts2.ActionContext;
+import org.apache.struts2.ActionInvocation;
+import org.apache.struts2.ActionProxy;
 import org.apache.struts2.FileManagerFactory;
 import org.apache.struts2.SimpleAction;
 import org.apache.struts2.TestBean;
 import org.apache.struts2.ValidationOrderAction;
 import org.apache.struts2.XWorkTestCase;
+import org.apache.struts2.config.entities.ActionConfig;
 import org.apache.struts2.conversion.impl.ConversionData;
 import org.apache.struts2.interceptor.ValidationAware;
 import org.apache.struts2.test.DataAware2;
@@ -39,6 +42,7 @@ import 
org.apache.struts2.validator.validators.RequiredStringValidator;
 import org.apache.struts2.validator.validators.ShortRangeFieldValidator;
 import org.apache.struts2.StrutsException;
 import org.assertj.core.api.Assertions;
+import org.easymock.EasyMock;
 import org.xml.sax.SAXParseException;
 
 import java.util.ArrayList;
@@ -432,4 +436,33 @@ public class DefaultActionValidatorManagerTest extends 
XWorkTestCase {
         assertTrue(action.getActionErrors().contains("Action level always 
fails"));
     }
 
+    public void testConversionError_skipFiresForAnnotationManager() throws 
Exception {
+        // AnnotationActionValidatorManager.buildValidatorKey() needs an 
ActionInvocation/ActionProxy
+        // on the ActionContext to resolve the package name/config for the 
validator cache key.
+        ActionConfig config = new ActionConfig.Builder("packageName", "name", 
"").build();
+        ActionInvocation invocation = 
EasyMock.createNiceMock(ActionInvocation.class);
+        ActionProxy proxy = EasyMock.createNiceMock(ActionProxy.class);
+        EasyMock.expect(invocation.getProxy()).andReturn(proxy).anyTimes();
+        EasyMock.expect(proxy.getMethod()).andReturn("execute").anyTimes();
+        EasyMock.expect(proxy.getConfig()).andReturn(config).anyTimes();
+        EasyMock.replay(invocation);
+        EasyMock.replay(proxy);
+        ActionContext.getContext().withActionInvocation(invocation);
+
+        AnnotationActionValidatorManager annMgr = 
container.inject(AnnotationActionValidatorManager.class);
+        annMgr.setSkipValidatorsOnConversionError("true");
+
+        ConversionErrorSkipAction action = new ConversionErrorSkipAction();
+        ActionContext.getContext().getConversionErrors()
+                .put("age", new ConversionData(new String[]{"one"}, 
Integer.class));
+
+        annMgr.validate(action, null);
+
+        List<String> ageErrors = action.getFieldErrors().get("age");
+        assertNotNull(ageErrors);
+        // required is skipped; the conversion validator itself still runs
+        assertEquals(1, ageErrors.size());
+        assertEquals("Age must be a valid number", ageErrors.get(0));
+    }
+
 }

Reply via email to