/*
* Copyright 2010-2023 Amazon.com, Inc. or its affiliates. All Rights
* Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file 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 com.amazonaws.util;
import static com.amazonaws.util.XpathUtils.asBoolean;
import static com.amazonaws.util.XpathUtils.asByte;
import static com.amazonaws.util.XpathUtils.asByteBuffer;
import static com.amazonaws.util.XpathUtils.asDate;
import static com.amazonaws.util.XpathUtils.asDouble;
import static com.amazonaws.util.XpathUtils.asFloat;
import static com.amazonaws.util.XpathUtils.asInteger;
import static com.amazonaws.util.XpathUtils.asLong;
import static com.amazonaws.util.XpathUtils.asNode;
import static com.amazonaws.util.XpathUtils.asString;
import static com.amazonaws.util.XpathUtils.documentFrom;
import static com.amazonaws.util.XpathUtils.isEmpty;
import static com.amazonaws.util.XpathUtils.xpath;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.nio.ByteBuffer;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.SimpleTimeZone;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.XPath;
import org.junit.Assert;
import org.junit.Test;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
import utils.TestHttpServer;
/**
* Unit tests for the XpathUtils class.
*
* @author fulghum@amazon.com
*/
public class XpathUtilsTest {
/** Test data for all tests to share */
private static final String DOCUMENT =
"" +
" Boo" +
" 1" +
" true" +
" 0.0000071759" +
" 2008-10-07T11:51:50.000Z" +
" - A
" +
" - B
" +
" - C
" +
" " +
" aGVsbG8gd29ybGQ=" +
" 123" +
" -99" +
"";
/** Test XML document with a namespace */
private static final String DOCUMENT_WITH_NAMESPACE =
" \n" +
" \n" +
" a074658d-7624-433e-b4e9-9271f6f5264b \n" +
" 174.129.242.223 \n" +
" \n";
@Test
public void testXmlDocumentWithNamespace() throws Exception {
Document document = documentFrom(DOCUMENT_WITH_NAMESPACE);
XPath xpath = xpath();
Node root = asNode("/", document, xpath);
assertNotNull(root);
Node node = asNode("//AllocateAddressResponse", document, xpath);
assertNotNull(node);
}
@Test
public void testAsString() throws Exception {
Document document = documentFrom(DOCUMENT);
XPath xpath = xpath();
assertEquals("Boo", asString("Foo/Title", document, xpath));
assertEquals("", XpathUtils.asString("Foo/Empty", document, xpath));
assertEquals("Bar", XpathUtils.asString("Foo/Count/@Foo", document, xpath));
}
@Test
public void testAsInteger() throws Exception {
Document document = documentFrom(DOCUMENT);
XPath xpath = xpath();
assertEquals((Integer)1, (Integer)asInteger("Foo/Count", document, xpath));
assertEquals(null, asInteger("Foo/Empty", document, xpath));
}
@Test
public void testAsBoolean() throws Exception {
Document document = XpathUtils.documentFrom(DOCUMENT);
XPath xpath = xpath();
assertEquals(true, asBoolean("Foo/Enabled", document, xpath));
assertEquals(null, asBoolean("Foo/Empty", document, xpath));
}
@Test
public void testAsFloat() throws Exception {
Document document = XpathUtils.documentFrom(DOCUMENT);
XPath xpath = xpath();
assertEquals((Float)0.0000071759f, (Float)asFloat("Foo/Usage", document, xpath));
assertEquals(null, asFloat("Foo/Empty", document, xpath));
}
/**
* Tests that we can correctly pull a Byte out of an XML document.
*/
@Test
public void testAsByte() throws Exception {
Document document = XpathUtils.documentFrom(DOCUMENT);
XPath xpath = xpath();
assertEquals(new Byte((byte)123), asByte("Foo/PositiveByte", document, xpath));
assertEquals(new Byte((byte)-99), asByte("Foo/NegativeByte", document, xpath));
assertEquals(null, XpathUtils.asByte("Foo/Empty", document));
}
/**
* Tests that we can correctly parse out a Date from an XML document.
*/
@Test
public void testAsDate() throws Exception {
/*
* The example date in our test XML document is:
* 2008-10-07T11:51:50.000Z
*
* So we construct that same date and verify that it matches
* what we parsed out of the XML.
*/
Calendar expectedDate = new GregorianCalendar();
expectedDate.set(Calendar.YEAR, 2008);
expectedDate.set(Calendar.MONTH, Calendar.OCTOBER);
expectedDate.set(Calendar.DAY_OF_MONTH, 7);
expectedDate.set(Calendar.AM_PM, Calendar.AM);
expectedDate.set(Calendar.HOUR, 11);
expectedDate.set(Calendar.MINUTE, 51);
expectedDate.set(Calendar.SECOND, 50);
expectedDate.set(Calendar.MILLISECOND, 0);
expectedDate.setTimeZone(new SimpleTimeZone(0, "UTC"));
Document document = documentFrom(DOCUMENT);
XPath xpath = xpath();
Date date = XpathUtils.asDate("Foo/Since", document, xpath);
assertNotNull(date);
assertEquals(expectedDate.getTimeInMillis(), date.getTime());
assertEquals(null, asDate("Foo/Empty", document, xpath));
}
@Test
public void testIsEmpty() throws Exception {
Document document = XpathUtils.documentFrom(DOCUMENT);
XPath xpath = xpath();
Node emptyNode = asNode("Foo/Fake", document, xpath);
Node realNode = asNode("Foo/Count", document, xpath);
assertTrue(isEmpty(emptyNode));
assertFalse(isEmpty(realNode));
}
@Test
public void testAsNode() throws Exception {
Document document = documentFrom(DOCUMENT);
XPath xpath = xpath();
Node node = asNode("Foo/Title", document, xpath);
assertNotNull(node);
assertEquals("Title", node.getNodeName());
}
/**
* Tests that we return null when a specified expression doesn't
* evaluate anything (instead of passing that null/empty value to
* a parser and getting an error in the parser).
*/
@Test
public void testMissingNodes() throws Exception {
Document document = documentFrom(DOCUMENT);
XPath xpath = XpathUtils.xpath();
assertNull(asDouble("non-existant-node/name", document, xpath));
assertNull(asLong("non-existant-node/name", document, xpath));
assertNull(asInteger("non-existant-node/name", document, xpath));
assertNull(asDate("non-existant-node/name", document, xpath));
assertNull(asFloat("non-existant-node/name", document, xpath));
assertNull(asString("non-existant-node/name", document, xpath));
}
/**
* Tests that {@link XpathUtils#asByteBuffer(String, Node)} correctly base64
* decodes the XML text data and transforms it into a ByteBuffer.
*/
@Test
public void testAsByteBuffer() throws Exception {
String expectedData = "hello world";
Document document = XpathUtils.documentFrom(DOCUMENT);
XPath xpath = XpathUtils.xpath();
ByteBuffer byteBuffer = asByteBuffer("Foo/Blob", document, xpath);
assertEquals(expectedData.length(), byteBuffer.limit());
String data = new String(byteBuffer.array());
assertEquals(expectedData, data);
assertEquals(null, asByteBuffer("Foo/Empty", document, xpath));
}
@Test
public void testFromDocumentDoesNotWriteToStderrWhenXmlInvalid() throws SAXException, IOException, ParserConfigurationException {
PrintStream err = System.err;
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
try {
PrintStream err2 = new PrintStream(bytes);
System.setErr(err2);
// invalid xml
XpathUtils.documentFrom("a");
Assert.fail();
} catch (SAXParseException e) {
//ensure nothing written to stderr
assertEquals(0, bytes.toByteArray().length);
} finally {
System.setErr(err);
}
}
@Test
public void testExternalEntity() throws Exception {
TestHttpServer server = new TestHttpServer("secret");
try {
String xml = String.format(
"\n"
+ "\n"
+ "]>\n"
+ "&foo;",
server.url());
parse(xml);
} catch (SAXParseException e) {
// okay
} finally {
server.stop();
}
if (server.accepted()) {
fail("Oops! The server has been reached!");
}
}
@Test
public void testExternalSchema() throws Exception {
TestHttpServer server = new TestHttpServer("secret");
try {
String xml = String.format(
"\n"
+ "\n"
+ "",
server.url());
parse(xml);
} catch (SAXParseException e) {
// okay
} finally {
server.stop();
}
if (server.accepted()) {
fail("Oops! The server has been reached!");
}
}
@Test
public void testExternalEntityParameter() throws Exception {
TestHttpServer server = new TestHttpServer("secret");
try {
String xml = String.format(
"\n"
+ "\n"
+ "%%sp;"
+ "]>\n"
+ "",
server.url());
parse(xml);
} catch (SAXParseException e) {
// okay
} finally {
server.stop();
}
if (server.accepted()) {
fail("Oops! The server has been reached!");
}
}
@Test(expected = SAXParseException.class)
public void billionLaughs() throws Exception {
String xml =
"\n" +
"\n" +
" \n" +
" \n" +
" \n" +
" \n" +
" \n" +
" \n" +
" \n" +
" \n" +
" \n" +
" \n" +
"]>\n" +
"&lol9;";
parse(xml);
}
private static void parse(String xml) throws Exception {
try {
XpathUtils.documentFrom(xml);
} catch (Exception e) {
if (e instanceof ParserConfigurationException
|| e.getCause() instanceof ParserConfigurationException) {
e.printStackTrace(System.out);
fail("Looks like parser configuration failed");
}
throw e;
}
}
}