View Javadoc
1   package it.com.atlassian.activeobjects;
2   
3   import com.google.common.base.Function;
4   import com.google.common.base.Predicate;
5   import com.google.common.collect.Collections2;
6   import com.google.common.collect.ImmutableMap;
7   import org.apache.commons.httpclient.HttpClient;
8   import org.apache.commons.httpclient.HttpMethod;
9   import org.apache.commons.httpclient.HttpStatus;
10  import org.apache.commons.httpclient.NameValuePair;
11  import org.apache.commons.httpclient.methods.DeleteMethod;
12  import org.apache.commons.httpclient.methods.GetMethod;
13  import org.apache.commons.httpclient.methods.PostMethod;
14  import org.custommonkey.xmlunit.Diff;
15  import org.custommonkey.xmlunit.ElementNameAndAttributeQualifier;
16  import org.custommonkey.xmlunit.XMLAssert;
17  import org.dom4j.Document;
18  import org.dom4j.DocumentException;
19  import org.dom4j.DocumentHelper;
20  import org.dom4j.Element;
21  import org.dom4j.XPath;
22  import org.jaxen.SimpleNamespaceContext;
23  import org.junit.Before;
24  import org.junit.Test;
25  
26  import java.io.IOException;
27  import java.io.InputStream;
28  import java.io.InputStreamReader;
29  import java.io.Reader;
30  import java.io.StringWriter;
31  import java.io.Writer;
32  import java.util.Map;
33  
34  import static com.atlassian.activeobjects.testplugin.BackupTestServlet.BACKUP;
35  import static com.atlassian.activeobjects.testplugin.BackupTestServlet.CREATE;
36  import static com.atlassian.dbexporter.node.NodeBackup.DatabaseInformationNode;
37  import static com.atlassian.dbexporter.node.NodeBackup.RootNode;
38  import static com.atlassian.dbexporter.node.NodeBackup.TableDataNode;
39  import static com.atlassian.dbexporter.node.NodeBackup.TableDefinitionNode;
40  import static org.junit.Assert.assertEquals;
41  import static org.junit.Assert.assertFalse;
42  import static org.junit.Assert.assertTrue;
43  
44  public final class BackupRestoreTest {
45      private static final XPath DB_INFO_XPATH;
46      private static final XPath TABLE_XPATH;
47      private static final XPath DATA_XPATH;
48  
49      static {
50          final SimpleNamespaceContext context = new SimpleNamespaceContext(ImmutableMap.builder().put("ao", "http://www.atlassian.com/ao").build());
51  
52          DB_INFO_XPATH = DocumentHelper.createXPath("/" + RootNode.NAME + "/ao:" + DatabaseInformationNode.NAME);
53          DB_INFO_XPATH.setNamespaceContext(context);
54          TABLE_XPATH = DocumentHelper.createXPath("/" + RootNode.NAME + "/ao:" + TableDefinitionNode.NAME);
55          TABLE_XPATH.setNamespaceContext(context);
56          DATA_XPATH = DocumentHelper.createXPath("/" + RootNode.NAME + "/ao:" + TableDataNode.NAME);
57          DATA_XPATH.setNamespaceContext(context);
58      }
59  
60      private static final String BASE_URL = System.getProperty("baseurl");
61      private static final String AO_TEST = BASE_URL + "/plugins/servlet/ao-test";
62  
63      private static final Predicate TEST_PLUGIN_TABLE_ELEMENTS = new Predicate() {
64          @Override
65          public boolean apply(Object node) {
66              if (node instanceof Element) {
67                  Element element = (Element) node;
68                  return element.attributeValue("name").startsWith("AO_0F732C");
69              }
70              return false;
71          }
72      };
73  
74      private static final Predicate TEST_PLUGIN_DATA_ELEMENTS = new Predicate() {
75          @Override
76          public boolean apply(Object node) {
77              if (node instanceof Element) {
78                  Element element = (Element) node;
79                  return element.attributeValue("tableName").startsWith("AO_0F732C");
80              }
81              return false;
82          }
83      };
84  
85      private HttpClient client;
86  
87      @Test
88      public void testBackup() throws Exception {
89          final String backup = get(AO_TEST, parameters(CREATE, true)); // initial backup with creation of data
90          assertBackupIsNotEmpty(backup);
91  
92          delete(AO_TEST);
93          final String empty = get(AO_TEST, parameters(CREATE, false));
94          assertBackupIsEmpty(empty.trim());
95  
96          post(AO_TEST, parameters(BACKUP, backup));  // restoring
97  
98          final String backupAfterRestore = get(AO_TEST, parameters(CREATE, false));
99          Diff diff = new Diff(backup, backupAfterRestore);
100         // we don't care about ordering
101         diff.overrideElementQualifier(new ElementNameAndAttributeQualifier());
102         XMLAssert.assertXMLEqual("BackupAfterRestore is substantially different\n" + backup + "\nAfter: " + backupAfterRestore, diff, true);
103     }
104 
105     private void assertBackupIsEmpty(String backup) throws DocumentException {
106         final Document doc = DocumentHelper.parseText(backup);
107         assertEquals(1, DB_INFO_XPATH.selectNodes(doc).size());
108         assertTrue(Collections2.filter(TABLE_XPATH.selectNodes(doc), TEST_PLUGIN_TABLE_ELEMENTS).isEmpty());
109         assertTrue(Collections2.filter(DATA_XPATH.selectNodes(doc), TEST_PLUGIN_DATA_ELEMENTS).isEmpty());
110     }
111 
112     private void assertBackupIsNotEmpty(String backup) throws DocumentException {
113         final Document doc = DocumentHelper.parseText(backup);
114         assertEquals(1, DB_INFO_XPATH.selectNodes(doc).size());
115         assertFalse(Collections2.filter(TABLE_XPATH.selectNodes(doc), TEST_PLUGIN_TABLE_ELEMENTS).isEmpty());
116         assertFalse(Collections2.filter(DATA_XPATH.selectNodes(doc), TEST_PLUGIN_DATA_ELEMENTS).isEmpty());
117     }
118 
119     @Before
120     public final void createHttpClient() {
121         if (BASE_URL == null || BASE_URL.equals("")) {
122             throw new IllegalStateException("BASE_URL is not set properly!");
123         }
124         client = new HttpClient();
125     }
126 
127     private String get(String path, Map<String, Object> parameters) throws IOException {
128         return service(newGetMethod(path, parameters));
129     }
130 
131     private HttpMethod newGetMethod(String path, Map<String, Object> parameters) {
132         final GetMethod method = new GetMethod(path);
133         method.setQueryString(toNameValuePairArray(parameters));
134         return method;
135     }
136 
137     private NameValuePair[] toNameValuePairArray(Map<String, Object> parameters) {
138         return Collections2.transform(parameters.entrySet(), new Function<Map.Entry<String, Object>, NameValuePair>() {
139             public NameValuePair apply(Map.Entry<String, Object> from) {
140                 return new NameValuePair(from.getKey(), from.getValue().toString());
141             }
142         }).toArray(new NameValuePair[parameters.size()]);
143     }
144 
145     private void post(String path, Map<String, Object> parameters) throws IOException {
146         service(newPostMethod(path, parameters));
147     }
148 
149     private HttpMethod newPostMethod(String path, Map<String, Object> parameters) {
150         final PostMethod method = new PostMethod(path);
151         method.setRequestHeader("Content-Type", PostMethod.FORM_URL_ENCODED_CONTENT_TYPE + ";charset=UTF-8");
152         method.setRequestBody(toNameValuePairArray(parameters));
153         return method;
154     }
155 
156     private void delete(String path) throws IOException {
157         service(new DeleteMethod(path));
158     }
159 
160     private String service(HttpMethod method) throws IOException {
161         final int statusCode = client.executeMethod(method);
162         if (statusCode != HttpStatus.SC_OK) {
163             throw new RuntimeException("Got status " + statusCode + " for " + method.getName() + " on URI " + method.getURI());
164         }
165         return IOUtils.toString(method.getResponseBodyAsStream());
166     }
167 
168     private Map<String, Object> parameters(String s, Object o) {
169         return ImmutableMap.of(s, o);
170     }
171 
172     /**
173      * A partial copy of IOUtils from commons-io
174      */
175     private static class IOUtils {
176         private static final int DEFAULT_BUFFER_SIZE = 1024 * 4;
177 
178         public static String toString(InputStream input) throws IOException {
179             StringWriter sw = new StringWriter();
180             copy(input, sw);
181             return sw.toString();
182         }
183 
184         public static void copy(InputStream input, Writer output) throws IOException {
185             InputStreamReader in = new InputStreamReader(input, "UTF-8");
186             copy(in, output);
187         }
188 
189         public static int copy(Reader input, Writer output) throws IOException {
190             long count = copyLarge(input, output);
191             if (count > Integer.MAX_VALUE) {
192                 return -1;
193             }
194             return (int) count;
195         }
196 
197         public static long copyLarge(Reader input, Writer output) throws IOException {
198             char[] buffer = new char[DEFAULT_BUFFER_SIZE];
199             long count = 0;
200             int n;
201             while (-1 != (n = input.read(buffer))) {
202                 output.write(buffer, 0, n);
203                 count += n;
204             }
205             return count;
206         }
207     }
208 }