WW-3427 Add regression test for conversion errors on aliased properties (#1814)

* WW-3427 test(core): cover conversion errors on aliased properties

Reproduce the WW-3427 scenario: an aliased property whose custom
TypeConverter throws TypeConversionException. AliasInterceptor already
reports such errors (setReportingConversionErrors on the secure child
stack, then copies conversion errors back to the original ActionContext),
but nothing exercised the alias + conversion-error path.

The test drives an action through params -> alias -> conversionError and
asserts the failure surfaces both in ActionContext.getConversionErrors()
and as a field error, confirming WW-3427 is fixed. Removing the copy-back
in AliasInterceptor makes it fail with "swallowed", proving it guards the
behavior.

Test-only; no production changes.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* WW-3427 test(core): add Apache license header to conversion.properties

RAT flagged the new test resource as having an unapproved license.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Lukasz Lenart
2026-07-26 10:33:09 +02:00
committed by GitHub
parent 12015d0bf5
commit 0b2bc2be14
5 changed files with 173 additions and 0 deletions
@@ -0,0 +1,50 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.struts2.interceptor;
import org.apache.struts2.ActionSupport;
import java.math.BigDecimal;
/**
* Action fixture for WW-3427. The {@code aliasDest} property is bound through a custom
* {@link ThrowingTypeConverter} (registered via {@code AliasConversionAction-conversion.properties}),
* so aliasing a value onto it triggers a {@code TypeConversionException}.
*/
public class AliasConversionAction extends ActionSupport {
private String aliasSource;
private BigDecimal aliasDest;
public String getAliasSource() {
return aliasSource;
}
public void setAliasSource(String aliasSource) {
this.aliasSource = aliasSource;
}
public BigDecimal getAliasDest() {
return aliasDest;
}
public void setAliasDest(BigDecimal aliasDest) {
this.aliasDest = aliasDest;
}
}
@@ -77,6 +77,30 @@ public class AliasInterceptorTest extends XWorkTestCase {
assertNull(actionOne.getBlah()); // WW-5087
}
// WW-3427: a conversion error thrown while binding an aliased property must not be swallowed;
// it must be reported through the ConversionErrorInterceptor / conversion errors, exactly as it
// would be for a non-aliased property.
public void testConversionErrorOnAliasedPropertyIsReported() throws Exception {
Map<String, Object> params = new HashMap<>();
params.put("aliasSource", "not a number");
XmlConfigurationProvider provider = new StrutsXmlConfigurationProvider("xwork-alias-conversion.xml");
container.inject(provider);
loadConfigurationProviders(provider);
ActionProxy proxy = actionProxyFactory.createActionProxy("", "aliasConversionTest", null, params);
AliasConversionAction action = (AliasConversionAction) proxy.getAction();
proxy.execute();
// The custom converter throws TypeConversionException while setting the aliased 'aliasDest'.
assertTrue("conversion error for aliased property was swallowed",
proxy.getInvocation().getInvocationContext().getConversionErrors().containsKey("aliasDest"));
assertTrue("ConversionErrorInterceptor did not register a field error for the aliased property",
action.getFieldErrors().containsKey("aliasDest"));
assertNull("aliasDest must remain unset after a failed conversion", action.getAliasDest());
}
public void testNameNotAccepted() throws Exception {
Map<String, Object> params = new HashMap<>();
params.put("aliasSource", "source here");
@@ -0,0 +1,36 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.struts2.interceptor;
import org.apache.struts2.conversion.TypeConversionException;
import org.apache.struts2.conversion.impl.DefaultTypeConverter;
import java.util.Map;
/**
* Test converter that always fails, used to reproduce WW-3427 (conversion errors on aliased
* properties must still be reported).
*/
public class ThrowingTypeConverter extends DefaultTypeConverter {
@Override
public Object convertValue(Map<String, Object> context, Object value, Class toType) {
throw new TypeConversionException("intentional conversion failure for value: " + value);
}
}
@@ -0,0 +1,19 @@
#
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
#
aliasDest=org.apache.struts2.interceptor.ThrowingTypeConverter
@@ -0,0 +1,44 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
-->
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 6.0//EN"
"https://struts.apache.org/dtds/struts-6.0.dtd">
<struts>
<include file="xwork-test-default.xml"/>
<package name="alias-conversion" extends="xwork-test-default">
<interceptors>
<interceptor name="conversionError" class="org.apache.struts2.interceptor.StrutsConversionErrorInterceptor"/>
</interceptors>
<!-- WW-3427: a conversion error on the aliased 'aliasDest' property must be reported -->
<action name="aliasConversionTest" class="org.apache.struts2.interceptor.AliasConversionAction">
<param name="aliases">#{ "aliasSource" : "aliasDest" }</param>
<interceptor-ref name="params"/>
<interceptor-ref name="alias"/>
<interceptor-ref name="conversionError"/>
<result name="success" type="mock"/>
<result name="input" type="mock"/>
</action>
</package>
</struts>