/* * Copyright (c) 2013-2017 Cinchapi 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 com.cinchapi.concourse.test; import java.io.File; import org.apache.thrift.TException; import org.apache.thrift.transport.TTransportException; import org.junit.Rule; import org.junit.rules.TestWatcher; import org.junit.runner.Description; import com.cinchapi.common.io.ByteBuffers; import com.cinchapi.concourse.Concourse; import com.cinchapi.concourse.server.ConcourseServer; import com.cinchapi.concourse.server.io.FileSystem; import com.cinchapi.concourse.test.Variables; import com.cinchapi.concourse.thrift.AccessToken; import com.cinchapi.concourse.time.Time; import com.google.common.base.Throwables; /** * This is the base class for all integration tests. This class contains logic * to setup a new {@link #server} and a corresponding {@link #client} connection * before every test. At the end of each test, those resources are cleaned up. * <p> * Interaction with the server goes through the {@link #client} variable. * </p> * * @author Jeff Nelson */ public abstract class ConcourseIntegrationTest { // Initialization for all tests static { System.setProperty("test", "true"); } /** * The tests run against a local server. */ protected static final String SERVER_HOST = "localhost"; /** * The default server port is 1717, so we use 1718 as to avoid interfering * with any real servers that might be running. */ protected static final int SERVER_PORT = 1718; /** * The test server stores data in a distinct folder under the user's home * directory. This directory is deleted after each test. */ private static final String SERVER_DATA_HOME = System .getProperty("user.home") + File.separator + "concourse_" + Long.toString(Time.now()); private static final String SERVER_DATABASE_DIRECTORY = SERVER_DATA_HOME + File.separator + "db"; private static final String SERVER_BUFFER_DIRECTORY = SERVER_DATA_HOME + File.separator + "buffer"; /** * The instance of the local server that is running. The subclass should not * need to access this directly because all calls should be funneled through * the {@link #client}. */ private ConcourseServer server; /** * The client that is used to interact with the server. */ protected Concourse client; @Rule public TestWatcher __watcher = new TestWatcher() { @Override protected void failed(Throwable t, Description description) { System.err.println("TEST FAILURE in " + description.getMethodName() + ": " + t.getMessage()); System.err.println("---"); System.err.println(Variables.dump()); System.err.println(""); stop(); afterEachTest(); } @Override protected void finished(Description description) { stop(); afterEachTest(); } @Override protected void starting(Description description) { Variables.clear(); start(); beforeEachTest(); } }; /** * This method is provided for the subclass to specify additional behaviour * to be run after each test is done. The subclass should define such logic * in this method as opposed to a test watcher. */ protected void afterEachTest() {} /** * This method is provided for the subclass to specify additional behaviour * to be run before each test begins. The subclass should define such logic * in this method as opposed to a test watcher. */ protected void beforeEachTest() {} /** * Grant access to the server for a user identified by {@code username} and * {@code password}. * * @param username * @param password */ protected final void grantAccess(String username, String password) { try { AccessToken token = server.login( ByteBuffers.fromUtf8String("admin"), ByteBuffers.fromUtf8String("admin")); server.grant(ByteBuffers.fromUtf8String(username), ByteBuffers.fromUtf8String(password), token); } catch (TException e) { throw Throwables.propagate(e); } } /** * Disable access to the server for the user identified by {@code username}. * * @param username the username for which access should be disabled */ protected final void disableAccess(String username) { try { AccessToken token = server.login( ByteBuffers.fromUtf8String("admin"), ByteBuffers.fromUtf8String("admin")); server.disableUser(ByteBuffers.fromUtf8String(username), token); } catch (TException e) { throw Throwables.propagate(e); } } /** * Reset the test by stopping the server, deleting any stored data, and * starting a new server. */ protected void reset() { stop(); start(); }; /** * Restart the embedded server. This method will preserve stored data. */ protected void restartServer() { server.stop(); start(); } /** * Startup a new {@link ConcourseServer} and grab a new client connection. */ private void start() { startServer(); client = Concourse.connect(SERVER_HOST, SERVER_PORT, "admin", "admin"); } /** * Start an embedded server. */ private void startServer() { try { server = ConcourseServer.create(SERVER_PORT, SERVER_BUFFER_DIRECTORY, SERVER_DATABASE_DIRECTORY); } catch (TTransportException e1) { throw Throwables.propagate(e1); } Thread t = new Thread(new Runnable() { @Override public void run() { try { server.start(); } catch (TTransportException e) { throw Throwables.propagate(e); } } }); t.start(); }; /** * Exit the client. Stop the server. Delete any stored data. */ private void stop() { client.exit(); server.stop(); FileSystem.deleteDirectory(SERVER_DATA_HOME); FileSystem.deleteFile(".access"); // delete the creds in case there were // any changes made during a test } }