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));
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));
97
98 final String backupAfterRestore = get(AO_TEST, parameters(CREATE, false));
99 Diff diff = new Diff(backup, backupAfterRestore);
100
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
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 }