SWS-544 - Added XPath validation

This commit is contained in:
Arjen Poutsma
2010-07-27 09:20:56 +00:00
parent 0a08364dfc
commit 345181e271
5 changed files with 459 additions and 3 deletions

View File

@@ -0,0 +1,132 @@
/*
* Copyright 2005-2010 the original author or authors.
*
* Licensed 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.springframework.ws.mock.client;
import java.io.IOException;
import java.net.URI;
import java.util.Map;
import javax.xml.transform.TransformerException;
import javax.xml.transform.dom.DOMResult;
import org.springframework.util.Assert;
import org.springframework.ws.WebServiceMessage;
import org.springframework.xml.transform.TransformerObjectSupport;
import org.springframework.xml.xpath.XPathExpression;
import org.springframework.xml.xpath.XPathExpressionFactory;
import org.w3c.dom.Node;
import static org.springframework.ws.mock.client.Assert.assertEquals;
import static org.springframework.ws.mock.client.Assert.fail;
/**
* Default implementation of {@link XPathExpectations}.
*
* @author Lukas Krecan
* @author Arjen Poutsma
* @since 2.0
*/
class DefaultXPathExpectations extends TransformerObjectSupport implements XPathExpectations {
private final XPathExpression expression;
private final String expressionString;
DefaultXPathExpectations(String expression, Map<String, String> namespaces) {
Assert.hasLength(expression, "'expression' must not be empty");
this.expression = XPathExpressionFactory.createXPathExpression(expression, namespaces);
this.expressionString = expression;
}
public RequestMatcher exists() {
return new RequestMatcher() {
public void match(URI uri, WebServiceMessage request) throws IOException, AssertionError {
Node payload = transformToNode(request);
Node result = expression.evaluateAsNode(payload);
if (result == null) {
fail("No match for \"" + expressionString + "\" found");
}
}
};
}
public RequestMatcher doesNotExist() {
return new RequestMatcher() {
public void match(URI uri, WebServiceMessage request) throws IOException, AssertionError {
Node payload = transformToNode(request);
Node result = expression.evaluateAsNode(payload);
if (result != null) {
fail("Match for \"" + expressionString + "\" found");
}
}
};
}
public RequestMatcher evaluatesTo(final boolean expectedValue) {
return new RequestMatcher() {
public void match(URI uri, WebServiceMessage request) throws IOException, AssertionError {
Node payload = transformToNode(request);
boolean result = expression.evaluateAsBoolean(payload);
assertEquals("Evaluation of XPath expression \"" + expressionString + "\" failed.", expectedValue,
result);
}
};
}
public RequestMatcher evaluatesTo(int expectedValue) {
return evaluatesTo((double) expectedValue);
}
public RequestMatcher evaluatesTo(final double expectedValue) {
return new RequestMatcher() {
public void match(URI uri, WebServiceMessage request) throws IOException, AssertionError {
Node payload = transformToNode(request);
double result = expression.evaluateAsNumber(payload);
assertEquals("Evaluation of XPath expression \"" + expressionString + "\" failed.", expectedValue,
result);
}
};
}
public RequestMatcher evaluatesTo(final String expectedValue) {
Assert.notNull(expectedValue, "'expectedValue' must not be null");
return new RequestMatcher() {
public void match(URI uri, WebServiceMessage request) throws IOException, AssertionError {
Node payload = transformToNode(request);
String result = expression.evaluateAsString(payload);
assertEquals("Evaluation of XPath expression \"" + expressionString + "\" failed.", expectedValue,
result);
}
};
}
private Node transformToNode(WebServiceMessage request) {
DOMResult domResult = new DOMResult();
try {
transform(request.getPayloadSource(), domResult);
return domResult.getNode();
}
catch (TransformerException ex) {
fail("Could not transform request payload: " + ex.getMessage());
return null;
}
}
}

View File

@@ -19,6 +19,7 @@ package org.springframework.ws.mock.client;
import java.io.IOException;
import java.net.URI;
import java.util.Locale;
import java.util.Map;
import javax.xml.namespace.QName;
import javax.xml.transform.Source;
@@ -89,7 +90,7 @@ public abstract class WebServiceMock {
/**
* Expects the payload to validate against the given XSD schema(s).
*
* @param schema the schema
* @param schema the schema
* @param furtherSchemas further schemas, if necessary
* @return the request matcher
*/
@@ -107,6 +108,27 @@ public abstract class WebServiceMock {
}
}
/**
* Expects the given XPath expression to (not) exist or be evaluated to a value.
*
* @param xpathExpression the XPath expression
* @return the XPath expectations, to be further configured
*/
public static XPathExpectations xpath(String xpathExpression) {
return new DefaultXPathExpectations(xpathExpression, null);
}
/**
* Expects the given XPath expression to (not) exist or be evaluated to a value.
*
* @param xpathExpression the XPath expression
* @param namespaceMapping the namespaces
* @return the XPath expectations, to be further configured
*/
public static XPathExpectations xpath(String xpathExpression, Map<String, String> namespaceMapping) {
return new DefaultXPathExpectations(xpathExpression, namespaceMapping);
}
/**
* Expects the given SOAP header in the outgoing message.
*
@@ -210,6 +232,7 @@ public abstract class WebServiceMock {
Assert.hasLength(faultStringOrReason, "'faultStringOrReason' must not be empty");
return SoapFaultResponseCallback.createMustUnderstandFault(faultStringOrReason, locale);
}
/**
* Respond with a {@code Client} (SOAP 1.1) or {@code Sender} (SOAP 1.2) fault.
*
@@ -246,7 +269,6 @@ public abstract class WebServiceMock {
return SoapFaultResponseCallback.createVersionMismatchFault(faultStringOrReason, locale);
}
private static ResourceSource createResourceSource(Resource resource) {
try {
return new ResourceSource(resource);
@@ -256,5 +278,4 @@ public abstract class WebServiceMock {
}
}
}

View File

@@ -0,0 +1,75 @@
/*
* Copyright 2005-2010 the original author or authors.
*
* Licensed 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.springframework.ws.mock.client;
/**
* Allows for setting up expectations on XPath expressions. Implementations of this interface are returned by {@link
* WebServiceMock}.
*
* @author Lukas Krecan
* @author Arjen Poutsma
* @since 2.0
*/
public interface XPathExpectations {
/**
* Expects the XPath expression to exist.
*
* @return the request matcher
*/
RequestMatcher exists();
/**
* Expects the XPath expression to not exist.
*
* @return the request matcher
*/
RequestMatcher doesNotExist();
/**
* Expects the XPath expression to evaluate to the given boolean.
*
* @param expectedValue the expected value
* @return the request matcher
*/
RequestMatcher evaluatesTo(final boolean expectedValue);
/**
* Expects the XPath expression to evaluate to the given integer.
*
* @param expectedValue the expected value
* @return the request matcher
*/
RequestMatcher evaluatesTo(int expectedValue);
/**
* Expects the XPath expression to evaluate to the given double.
*
* @param expectedValue the expected value
* @return the request matcher
*/
RequestMatcher evaluatesTo(double expectedValue);
/**
* Expects the XPath expression to evaluate to the given string.
*
* @param expectedValue the expected value
* @return the request matcher
*/
RequestMatcher evaluatesTo(String expectedValue);
}

View File

@@ -0,0 +1,205 @@
/*
* Copyright 2005-2010 the original author or authors.
*
* Licensed 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.springframework.ws.mock.client;
import java.io.IOException;
import java.util.Collections;
import java.util.Map;
import org.springframework.ws.WebServiceMessage;
import org.springframework.xml.transform.StringSource;
import org.junit.Test;
import static org.easymock.EasyMock.*;
import static org.junit.Assert.assertNotNull;
public class DefaultXPathExpectationsTest {
@Test
public void existsMatch() throws IOException, AssertionError {
RequestMatcher requestMatcher = WebServiceMock.xpath("//b").exists();
assertNotNull(requestMatcher);
WebServiceMessage message = createMock(WebServiceMessage.class);
expect(message.getPayloadSource()).andReturn(new StringSource("<a><b/></a>"));
replay(message);
requestMatcher.match(null, message);
verify(message);
}
@Test(expected = AssertionError.class)
public void existsNonMatch() throws IOException, AssertionError {
RequestMatcher requestMatcher = WebServiceMock.xpath("//c").exists();
assertNotNull(requestMatcher);
WebServiceMessage message = createMock(WebServiceMessage.class);
expect(message.getPayloadSource()).andReturn(new StringSource("<a><b/></a>"));
replay(message);
requestMatcher.match(null, message);
}
@Test
public void notExistsMatch() throws IOException, AssertionError {
RequestMatcher requestMatcher = WebServiceMock.xpath("//c").doesNotExist();
assertNotNull(requestMatcher);
WebServiceMessage message = createMock(WebServiceMessage.class);
expect(message.getPayloadSource()).andReturn(new StringSource("<a><b/></a>"));
replay(message);
requestMatcher.match(null, message);
verify(message);
}
@Test(expected = AssertionError.class)
public void notExistsNonMatch() throws IOException, AssertionError {
RequestMatcher requestMatcher = WebServiceMock.xpath("//a").doesNotExist();
assertNotNull(requestMatcher);
WebServiceMessage message = createMock(WebServiceMessage.class);
expect(message.getPayloadSource()).andReturn(new StringSource("<a><b/></a>"));
replay(message);
requestMatcher.match(null, message);
}
@Test
public void evaluatesToTrueMatch() throws IOException, AssertionError {
RequestMatcher requestMatcher = WebServiceMock.xpath("//b=1").evaluatesTo(true);
assertNotNull(requestMatcher);
WebServiceMessage message = createMock(WebServiceMessage.class);
expect(message.getPayloadSource()).andReturn(new StringSource("<a><b>1</b></a>"));
replay(message);
requestMatcher.match(null, message);
verify(message);
}
@Test(expected = AssertionError.class)
public void evaluatesToTrueNonMatch() throws IOException, AssertionError {
RequestMatcher requestMatcher = WebServiceMock.xpath("//b=2").evaluatesTo(true);
assertNotNull(requestMatcher);
WebServiceMessage message = createMock(WebServiceMessage.class);
expect(message.getPayloadSource()).andReturn(new StringSource("<a><b>1</b></a>"));
replay(message);
requestMatcher.match(null, message);
}
@Test
public void evaluatesToFalseMatch() throws IOException, AssertionError {
RequestMatcher requestMatcher = WebServiceMock.xpath("//b!=1").evaluatesTo(false);
assertNotNull(requestMatcher);
WebServiceMessage message = createMock(WebServiceMessage.class);
expect(message.getPayloadSource()).andReturn(new StringSource("<a><b>1</b></a>"));
replay(message);
requestMatcher.match(null, message);
verify(message);
}
@Test(expected = AssertionError.class)
public void evaluatesToFalseNonMatch() throws IOException, AssertionError {
RequestMatcher requestMatcher = WebServiceMock.xpath("//b!=2").evaluatesTo(false);
assertNotNull(requestMatcher);
WebServiceMessage message = createMock(WebServiceMessage.class);
expect(message.getPayloadSource()).andReturn(new StringSource("<a><b>1</b></a>"));
replay(message);
requestMatcher.match(null, message);
}
@Test
public void existsWithNamespacesMatch() throws IOException, AssertionError {
Map<String, String> ns = Collections.singletonMap("x", "http://example.org");
RequestMatcher requestMatcher = WebServiceMock.xpath("//x:b", ns).exists();
assertNotNull(requestMatcher);
WebServiceMessage message = createMock(WebServiceMessage.class);
expect(message.getPayloadSource())
.andReturn(new StringSource("<a:a xmlns:a=\"http://example.org\"><a:b/></a:a>"));
replay(message);
requestMatcher.match(null, message);
verify(message);
}
@Test(expected = AssertionError.class)
public void existsWithNamespacesNonMatch() throws IOException, AssertionError {
Map<String, String> ns = Collections.singletonMap("x", "http://example.org");
RequestMatcher requestMatcher = WebServiceMock.xpath("//b", ns).exists();
assertNotNull(requestMatcher);
WebServiceMessage message = createMock(WebServiceMessage.class);
expect(message.getPayloadSource())
.andReturn(new StringSource("<a:a xmlns:a=\"http://example.org\"><a:b/></a:a>"));
replay(message);
requestMatcher.match(null, message);
}
@Test
public void evaluatesToIntegerMatch() throws IOException, AssertionError {
RequestMatcher requestMatcher = WebServiceMock.xpath("//b").evaluatesTo(1);
assertNotNull(requestMatcher);
WebServiceMessage message = createMock(WebServiceMessage.class);
expect(message.getPayloadSource()).andReturn(new StringSource("<a><b>1</b></a>"));
replay(message);
requestMatcher.match(null, message);
verify(message);
}
@Test(expected = AssertionError.class)
public void evaluatesToIntegerNonMatch() throws IOException, AssertionError {
RequestMatcher requestMatcher = WebServiceMock.xpath("//b").evaluatesTo(2);
assertNotNull(requestMatcher);
WebServiceMessage message = createMock(WebServiceMessage.class);
expect(message.getPayloadSource()).andReturn(new StringSource("<a><b>1</b></a>"));
replay(message);
requestMatcher.match(null, message);
}
}

View File

@@ -18,6 +18,8 @@ package org.springframework.ws.mock.client;
import java.io.IOException;
import java.net.URI;
import java.util.Collections;
import java.util.Map;
import javax.xml.namespace.QName;
import javax.xml.transform.Source;
import javax.xml.transform.TransformerException;
@@ -192,4 +194,25 @@ public class WebServiceMockTest {
template.sendSourceAndReceiveToResult(new StringSource(actual), result);
}
@Test
public void xpathExistsMatch() throws Exception {
final Map<String, String> ns = Collections.singletonMap("ns", "http://example.com");
expect(xpath("/ns:request", ns).exists());
template.sendSourceAndReceiveToResult(new StringSource("<request xmlns='http://example.com'/>"),
new StringResult());
}
@Test(expected = AssertionError.class)
public void xpathExistsNonMatch() throws Exception {
final Map<String, String> ns = Collections.singletonMap("ns", "http://example.com");
expect(xpath("/ns:foo", ns).exists());
template.sendSourceAndReceiveToResult(new StringSource("<request xmlns='http://example.com'/>"),
new StringResult());
}
}