/**
* Copyright 2011 Membase, Inc.
*
* 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.couchbase.mock;
import java.io.*;
import java.net.*;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import junit.framework.TestCase;
import com.google.gson.Gson;
import com.google.gson.JsonObject;
import java.util.Map;
import org.couchbase.mock.client.*;
import org.couchbase.mock.util.Base64;
import org.couchbase.mock.harakiri.HarakiriMonitor;
import org.couchbase.mock.util.ReaderUtils;
/**
* Basic testing of JMembase
*
* @author Trond Norbye
*/
public class JMembaseTest extends TestCase {
public JMembaseTest(String testName) {
super(testName);
}
private CouchbaseMock instance;
static private final int port = 28091;
static private final int numNodes;
static private final int numVBuckets;
static {
final String platform = System.getProperty("os.name");
if (platform.equals("Mac OS X") || platform.equals("Linux")) {
numNodes = 4;
numVBuckets = 16;
} else {
numNodes = 100;
numVBuckets = 1024;
}
}
private boolean serverNotReady(int port) {
Socket socket = null;
try {
socket = new Socket("localhost", port);
return false;
} catch (UnknownHostException ex) {
} catch (IOException ex) {
} finally {
if (socket != null) {
try {
socket.close();
} catch (IOException e) {
}
}
}
return true;
}
@Override
protected void setUp() throws Exception {
super.setUp();
instance = new CouchbaseMock(null, port, numNodes, numVBuckets, "default:,protected:secret");
instance.start();
do {
Thread.sleep(100);
} while (serverNotReady(port));
}
@Override
protected void tearDown() throws Exception {
instance.stop();
super.tearDown();
}
public void testHandleHttpRequest() throws IOException {
URL url = new URL("http://localhost:" + instance.getHttpPort() + "/pools/default/buckets/protected");
HttpURLConnection conn;
try {
conn = (HttpURLConnection) url.openConnection();
assertNotNull(conn);
conn.addRequestProperty("Authorization", "Basic " + Base64.encode("protected:secret"));
assertEquals(HttpURLConnection.HTTP_OK, conn.getResponseCode());
} catch (Exception ex) {
fail(ex.getMessage());
}
}
public void testHandleHttpRequestWithTrailingSlash() throws IOException {
URL url = new URL("http://localhost:" + instance.getHttpPort() + "/pools/default/buckets/protected/");
HttpURLConnection conn;
try {
conn = (HttpURLConnection) url.openConnection();
assertNotNull(conn);
conn.addRequestProperty("Authorization", "Basic " + Base64.encode("protected:secret"));
assertEquals(HttpURLConnection.HTTP_OK, conn.getResponseCode());
} catch (Exception ex) {
fail(ex.getMessage());
}
}
public void testAdministratorCouldAccessProtectedBuckets() throws IOException {
URL url = new URL("http://localhost:" + instance.getHttpPort() + "/pools/default/buckets/protected");
HttpURLConnection conn;
try {
conn = (HttpURLConnection) url.openConnection();
assertNotNull(conn);
conn.addRequestProperty("Authorization", "Basic " + Base64.encode("Administrator:password"));
assertEquals(HttpURLConnection.HTTP_OK, conn.getResponseCode());
} catch (Exception ex) {
fail(ex.getMessage());
}
}
public void testDefaultBucketShouldBeAccessibleForEveryone() throws IOException {
URL url = new URL("http://localhost:" + instance.getHttpPort() + "/pools/default/buckets/default");
HttpURLConnection conn;
try {
conn = (HttpURLConnection) url.openConnection();
assertNotNull(conn);
assertEquals(HttpURLConnection.HTTP_OK, conn.getResponseCode());
} catch (Exception ex) {
fail(ex.getMessage());
}
}
public void testProtectedBucketsShouldBeFilteredOutFromList() throws IOException {
URL url = new URL("http://localhost:" + instance.getHttpPort() + "/pools/default/buckets");
HttpURLConnection conn;
try {
conn = (HttpURLConnection) url.openConnection();
assertNotNull(conn);
assertEquals(HttpURLConnection.HTTP_OK, conn.getResponseCode());
StringBuilder sb = new StringBuilder();
BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
String line;
while ((line = in.readLine()) != null) {
sb.append(line);
}
List json = JsonUtils.decodeAsList(sb.toString());
assertEquals(1, json.size());
Map<String,Object> bucket = (Map<String,Object>)json.get(0);
assertEquals("default", bucket.get("name"));
} catch (Exception ex) {
fail(ex.getMessage());
}
}
public void testHandleHttpRequestNetwork() throws IOException {
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
pw.print("GET /pools/default/buckets/default HTTP/1.1\r\n");
pw.print("Authorization: Basic " + Base64.encode("Administrator:password") + "\r\n");
pw.print("\r\n");
pw.flush();
Socket s = new Socket("localhost", port);
OutputStream out = s.getOutputStream();
out.write(sw.toString().getBytes());
out.flush();
BufferedReader in = new BufferedReader(new InputStreamReader(s.getInputStream()));
int content_length = 0;
String header;
while ((header = in.readLine()).length() > 0) {
String[] tokens = header.split(": ");
if (tokens[0].equals("Content-Length")) {
content_length = Integer.parseInt(tokens[1]);
}
}
char[] body = new char[content_length];
assertEquals(content_length, in.read(body));
s.close();
}
public void testHandleHttpRequestMissingAuth() throws IOException {
URL url = new URL("http://localhost:" + instance.getHttpPort() + "/pools/default/buckets/protected");
HttpURLConnection conn;
try {
conn = (HttpURLConnection) url.openConnection();
assertNotNull(conn);
assertEquals(HttpURLConnection.HTTP_UNAUTHORIZED, conn.getResponseCode());
} catch (Exception ex) {
fail(ex.getMessage());
}
}
@SuppressWarnings("SpellCheckingInspection")
public void testHandleHttpRequestIncorrectCred() throws IOException {
URL url = new URL("http://localhost:" + instance.getHttpPort() + "/pools/default/buckets/protected");
HttpURLConnection conn;
try {
conn = (HttpURLConnection) url.openConnection();
assertNotNull(conn);
conn.addRequestProperty("Authorization", "Basic " + Base64.encode("Bubba:TheHut"));
assertEquals(HttpURLConnection.HTTP_UNAUTHORIZED, conn.getResponseCode());
} catch (Exception ex) {
fail(ex.getMessage());
}
}
@SuppressWarnings("SpellCheckingInspection")
public void testHandleHttpRequestIllegalCred() throws IOException {
URL url = new URL("http://localhost:" + instance.getHttpPort() + "/pools/default/buckets/default");
HttpURLConnection conn;
try {
conn = (HttpURLConnection) url.openConnection();
assertNotNull(conn);
conn.addRequestProperty("Authorization", "Basic " + Base64.encode(":TheHut"));
assertEquals(HttpURLConnection.HTTP_UNAUTHORIZED, conn.getResponseCode());
} catch (Exception ex) {
fail(ex.getMessage());
}
}
public void testHandleHttpRequestUnkownFile() throws IOException {
URL url = new URL("http://localhost:" + instance.getHttpPort() + "/");
HttpURLConnection conn;
try {
conn = (HttpURLConnection) url.openConnection();
assertNotNull(conn);
conn.addRequestProperty("Authorization", "Basic " + Base64.encode("Administrator:password"));
assertEquals(HttpURLConnection.HTTP_NOT_FOUND, conn.getResponseCode());
} catch (Exception ex) {
fail(ex.getMessage());
}
}
// @SuppressWarnings("UnusedAssignment")
// public void testHarakiriMonitorInvalidHost() throws IOException {
// try {
// HarakiriMonitor m = new HarakiriMonitor("ItWouldSuckIfYouHadAHostNamedThis", 0, false, instance.getDispatcher());
// fail("I was not expecting to be able to connect to: \"ItWouldSuckIfYouHadAHostNamedThis:0\"");
// } catch (Throwable t) {
// }
// }
@SuppressWarnings("UnusedAssignment")
public void testHarakiriMonitorInvalidPort() throws IOException {
try {
HarakiriMonitor m = new HarakiriMonitor(instance.getDispatcher());
m.connect(null, 0);
fail("I was not expecting to be able to connect to port 0");
} catch (Throwable t) {
}
}
public void testHarakiriMonitor() throws IOException {
ServerSocket server = new ServerSocket(0);
HarakiriMonitor m;
m = new HarakiriMonitor(instance.getDispatcher());
m.connect(null, server.getLocalPort());
Thread t = new Thread(m);
t.start();
server.close();
while (t.isAlive()) {
try {
t.join();
} catch (InterruptedException ex) {
Logger.getLogger(JMembaseTest.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
private String readConfig(InputStream stream) throws IOException {
int bb, lf = 0;
StringBuilder cfg = new StringBuilder();
do {
bb = stream.read();
if (bb == '\n') {
lf++;
} else {
lf = 0;
cfg.append(bb);
}
} while (lf < 4);
return cfg.toString();
}
private static String readInput(InputStream cin) throws IOException {
byte[] inputBuffer = new byte[4096];
int nr;
StringBuilder sb = new StringBuilder();
while ((nr = cin.read(inputBuffer)) > 0) {
String s = new String(inputBuffer, 0, nr);
sb.append(s);
if (nr < inputBuffer.length) {
break;
}
}
return sb.toString();
}
private boolean readResponse(InputStream in) throws IOException {
String json = readInput(in);
try {
JsonObject response = (new Gson()).fromJson(json, JsonObject.class);
return response.get("status").getAsString().toLowerCase().equals("ok");
} catch (Throwable ex) {
System.err.println("Invalid response received from the server: [" + json + "]");
return false;
}
}
@SuppressWarnings("SpellCheckingInspection")
public void testConfigStreaming() throws IOException {
MockClient mock = new MockClient(new InetSocketAddress("localhost", 0));
instance.startHarakiriMonitor("localhost:" + mock.getPort(), false);
mock.negotiate();
Bucket bucket = instance.getBuckets().get("protected");
URL url = new URL("http://localhost:" + instance.getHttpPort() + "/pools/default/bucketsStreaming/protected");
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.addRequestProperty("Authorization", "Basic " + Base64.encode("protected:secret"));
InputStream stream = conn.getInputStream();
String currCfg, nextCfg;
currCfg = readConfig(stream);
assertEquals(numNodes, bucket.activeServers().size());
assertTrue(mock.request(new FailoverRequest(1, "protected")).isOk());
nextCfg = readConfig(stream);
assertNotSame(currCfg, nextCfg);
assertEquals(numNodes - 1, bucket.activeServers().size());
currCfg = nextCfg;
assertTrue(mock.request(new RespawnRequest(1, "protected")).isOk());
nextCfg = readConfig(stream);
assertNotSame(currCfg, nextCfg);
assertEquals(numNodes, bucket.activeServers().size());
assertTrue(mock.request(new HiccupRequest(10000, 20)).isOk());
assertTrue(mock.request(new FailoverRequest(1)).isOk());
assertTrue(mock.request(new RespawnRequest(1)).isOk());
mock.shutdown();
instance.getMonitor().stop();
}
public void testIllegalMockCommand() throws IOException {
ServerSocket server = new ServerSocket(0);
instance.startHarakiriMonitor("localhost:" + server.getLocalPort(), false);
Socket client = server.accept();
InputStream input = client.getInputStream();
OutputStream output = client.getOutputStream();
readInput(input);
output.write("Yo, this should fail!\n".getBytes());
assertFalse(readResponse(input));
}
public void testUnknownMockCommand() throws IOException {
MockClient mock = new MockClient(new InetSocketAddress("localhost", 0));
instance.startHarakiriMonitor("localhost:" + mock.getPort(), false);
mock.negotiate();
MockRequest request = MockRequest.build("foo");
MockResponse resp = mock.request(request);
assertFalse(resp.isOk());
assertNotNull(resp.getErrorMessage());
}
public void testRestFlush() throws IOException {
URL url = new URL("http://localhost:" + instance.getHttpPort() + "/pools/default/buckets/default/controller/doFlush");
HttpURLConnection conn;
try {
conn = (HttpURLConnection) url.openConnection();
assertNotNull(conn);
assertEquals(HttpURLConnection.HTTP_OK, conn.getResponseCode());
} catch (Exception ex) {
fail(ex.getMessage());
}
}
final static String DDOC = "{"
+ " \"_id\": \"_design/beer\","
+ " \"language\": \"javascript\","
+ " \"views\": {"
+ " \"all\": {"
+ " \"map\": \"function(doc){ emit(doc.id, null); }\""
+ " }"
+ " }"
+ "}";
public void testDesignManagement() throws Exception {
URL url = new URL("http://localhost:"+instance.getHttpPort()+"/default/_design/beer");
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setDoOutput(true);
conn.setRequestMethod("PUT");
conn.setRequestProperty("Content-Type", "application/json");
OutputStreamWriter osw = new OutputStreamWriter(conn.getOutputStream());
osw.write(DDOC);
osw.flush();
osw.close();
conn.getInputStream().close();
// Get it back
conn = (HttpURLConnection) url.openConnection();
String s = ReaderUtils.fromStream(conn.getInputStream());
assertEquals(s, DDOC);
}
}