/*
* Copyright 2014 LinkedIn Corp.
*
* 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 azkaban.project;
import java.io.File;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.List;
import javax.sql.DataSource;
import org.apache.commons.dbutils.DbUtils;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.ResultSetHandler;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Ignore;
import org.junit.Test;
import azkaban.database.DataSourceUtils;
import azkaban.flow.Edge;
import azkaban.flow.Flow;
import azkaban.flow.Node;
import azkaban.project.ProjectLogEvent.EventType;
import azkaban.user.Permission;
import azkaban.user.User;
import azkaban.utils.Pair;
import azkaban.utils.Props;
import azkaban.utils.PropsUtils;
public class JdbcProjectLoaderTest {
private static boolean testDBExists;
private static final String host = "localhost";
private static final int port = 3306;
private static final String database = "test";
private static final String user = "azkaban";
private static final String password = "azkaban";
private static final int numConnections = 10;
@BeforeClass
public static void setupDB() {
DataSource dataSource =
DataSourceUtils.getMySQLDataSource(host, port, database, user,
password, numConnections);
testDBExists = true;
Connection connection = null;
try {
connection = dataSource.getConnection();
} catch (SQLException e) {
e.printStackTrace();
testDBExists = false;
DbUtils.closeQuietly(connection);
return;
}
CountHandler countHandler = new CountHandler();
QueryRunner runner = new QueryRunner();
try {
runner.query(connection, "SELECT COUNT(1) FROM projects", countHandler);
} catch (SQLException e) {
e.printStackTrace();
testDBExists = false;
DbUtils.closeQuietly(connection);
return;
}
try {
runner.query(connection, "SELECT COUNT(1) FROM project_events",
countHandler);
} catch (SQLException e) {
e.printStackTrace();
testDBExists = false;
DbUtils.closeQuietly(connection);
return;
}
try {
runner.query(connection, "SELECT COUNT(1) FROM project_permissions",
countHandler);
} catch (SQLException e) {
e.printStackTrace();
testDBExists = false;
DbUtils.closeQuietly(connection);
return;
}
try {
runner.query(connection, "SELECT COUNT(1) FROM project_files",
countHandler);
} catch (SQLException e) {
e.printStackTrace();
testDBExists = false;
DbUtils.closeQuietly(connection);
return;
}
try {
runner.query(connection, "SELECT COUNT(1) FROM project_flows",
countHandler);
} catch (SQLException e) {
e.printStackTrace();
testDBExists = false;
DbUtils.closeQuietly(connection);
return;
}
try {
runner.query(connection, "SELECT COUNT(1) FROM project_properties",
countHandler);
} catch (SQLException e) {
e.printStackTrace();
testDBExists = false;
DbUtils.closeQuietly(connection);
return;
}
DbUtils.closeQuietly(connection);
clearDB();
}
private static void clearDB() {
if (!testDBExists) {
return;
}
DataSource dataSource =
DataSourceUtils.getMySQLDataSource(host, port, database, user,
password, numConnections);
Connection connection = null;
try {
connection = dataSource.getConnection();
} catch (SQLException e) {
e.printStackTrace();
testDBExists = false;
DbUtils.closeQuietly(connection);
return;
}
QueryRunner runner = new QueryRunner();
try {
runner.update(connection, "DELETE FROM projects");
} catch (SQLException e) {
e.printStackTrace();
testDBExists = false;
DbUtils.closeQuietly(connection);
return;
}
try {
runner.update(connection, "DELETE FROM project_events");
} catch (SQLException e) {
e.printStackTrace();
testDBExists = false;
DbUtils.closeQuietly(connection);
return;
}
try {
runner.update(connection, "DELETE FROM project_permissions");
} catch (SQLException e) {
e.printStackTrace();
testDBExists = false;
DbUtils.closeQuietly(connection);
return;
}
try {
runner.update(connection, "DELETE FROM project_files");
} catch (SQLException e) {
e.printStackTrace();
testDBExists = false;
DbUtils.closeQuietly(connection);
return;
}
try {
runner.update(connection, "DELETE FROM project_flows");
} catch (SQLException e) {
e.printStackTrace();
testDBExists = false;
DbUtils.closeQuietly(connection);
return;
}
try {
runner.update(connection, "DELETE FROM project_properties");
} catch (SQLException e) {
e.printStackTrace();
testDBExists = false;
DbUtils.closeQuietly(connection);
return;
}
DbUtils.closeQuietly(connection);
}
/** Test case to validated permissions for fetchProjectByName **/
@Test
public void testPermissionRetrivalByFetchProjectByName()
throws ProjectManagerException {
if (!isTestSetup()) {
return;
}
ProjectLoader loader = createLoader();
String projectName = "mytestProject";
String projectDescription = "This is my new project";
User user = new User("testUser");
Project project =
loader.createNewProject(projectName, projectDescription, user);
Permission perm = new Permission(0x2);
loader.updatePermission(project, user.getUserId(), perm, false);
loader.updatePermission(project, "group", perm, true);
Permission permOverride = new Permission(0x6);
loader.updatePermission(project, user.getUserId(), permOverride, false);
Project fetchedProject = loader.fetchProjectByName(project.getName());
assertProjectMemberEquals(project, fetchedProject);
Assert.assertEquals(permOverride,
fetchedProject.getUserPermission(user.getUserId()));
}
/** Default Test case for fetchProjectByName **/
@Test
public void testProjectRetrievalByFetchProjectByName()
throws ProjectManagerException {
if (!isTestSetup()) {
return;
}
ProjectLoader loader = createLoader();
String projectName = "mytestProject";
String projectDescription = "This is my new project";
User user = new User("testUser");
Project project =
loader.createNewProject(projectName, projectDescription, user);
Project fetchedProject = loader.fetchProjectByName(project.getName());
assertProjectMemberEquals(project, fetchedProject);
}
/** Default Test case for fetchProjectByName **/
@Test
public void testDuplicateRetrivalByFetchProjectByName()
throws ProjectManagerException {
if (!isTestSetup()) {
return;
}
ProjectLoader loader = createLoader();
String projectName = "mytestProject";
String projectDescription = "This is my new project";
User user = new User("testUser");
Project project =
loader.createNewProject(projectName, projectDescription, user);
loader.removeProject(project, user.getUserId());
Project newProject =
loader.createNewProject(projectName, projectDescription, user);
Project fetchedProject = loader.fetchProjectByName(project.getName());
Assert.assertEquals(newProject.getId(), fetchedProject.getId());
}
/** Test case for NonExistantProject project fetch **/
@Test
public void testInvalidProjectByFetchProjectByName() {
if (!isTestSetup()) {
return;
}
ProjectLoader loader = createLoader();
try {
loader.fetchProjectByName("NonExistantProject");
} catch (ProjectManagerException ex) {
System.out.println("Test true");
}
Assert.fail("Expecting exception, but didn't get one");
}
@Test
public void testCreateProject() throws ProjectManagerException {
if (!isTestSetup()) {
return;
}
ProjectLoader loader = createLoader();
String projectName = "mytestProject";
String projectDescription = "This is my new project";
User user = new User("testUser");
Project project =
loader.createNewProject(projectName, projectDescription, user);
Assert.assertTrue("Project Id set", project.getId() > -1);
Assert.assertEquals("Project name", projectName, project.getName());
Assert.assertEquals("Project description", projectDescription,
project.getDescription());
System.out.println("Test true");
Project project2 = loader.fetchProjectById(project.getId());
assertProjectMemberEquals(project, project2);
}
@Test
public void testRemoveProject() throws ProjectManagerException {
if (!isTestSetup()) {
return;
}
ProjectLoader loader = createLoader();
String projectName = "testRemoveProject";
String projectDescription = "This is my new project";
User user = new User("testUser");
Project project =
loader.createNewProject(projectName, projectDescription, user);
Assert.assertTrue("Project Id set", project.getId() > -1);
Assert.assertEquals("Project name", projectName, project.getName());
Assert.assertEquals("Project description", projectDescription,
project.getDescription());
Project project2 = loader.fetchProjectById(project.getId());
assertProjectMemberEquals(project, project2);
loader.removeProject(project, user.getUserId());
Project project3 = loader.fetchProjectById(project.getId());
Assert.assertFalse(project3.isActive());
List<Project> projList = loader.fetchAllActiveProjects();
for (Project proj : projList) {
Assert.assertTrue(proj.getId() != project.getId());
}
}
@Test
public void testAddRemovePermissions() throws ProjectManagerException {
if (!isTestSetup()) {
return;
}
ProjectLoader loader = createLoader();
String projectName = "mytestProject1";
String projectDescription = "This is my new project";
User user = new User("testUser");
Project project =
loader.createNewProject(projectName, projectDescription, user);
Assert.assertTrue("Project Id set", project.getId() > -1);
Assert.assertEquals("Project name", projectName, project.getName());
Assert.assertEquals("Project description", projectDescription,
project.getDescription());
Permission perm = new Permission(0x2);
loader.updatePermission(project, user.getUserId(), new Permission(0x2),
false);
loader.updatePermission(project, "group1", new Permission(0x2), true);
Assert.assertEquals(perm, project.getUserPermission(user.getUserId()));
Permission permOverride = new Permission(0x6);
loader.updatePermission(project, user.getUserId(), permOverride, false);
Assert.assertEquals(permOverride,
project.getUserPermission(user.getUserId()));
Project project2 = loader.fetchProjectById(project.getId());
assertProjectMemberEquals(project, project2);
Assert.assertEquals(permOverride,
project2.getUserPermission(user.getUserId()));
}
@Test
public void testProjectEventLogs() throws ProjectManagerException {
if (!isTestSetup()) {
return;
}
ProjectLoader loader = createLoader();
String projectName = "testProjectEventLogs";
String projectDescription = "This is my new project";
User user = new User("testUser");
String message = "My message";
EventType type = EventType.USER_PERMISSION;
Project project =
loader.createNewProject(projectName, projectDescription, user);
loader.postEvent(project, type, user.getUserId(), message);
List<ProjectLogEvent> events = loader.getProjectEvents(project, 10, 0);
Assert.assertTrue(events.size() == 1);
ProjectLogEvent event = events.get(0);
Assert.assertEquals(event.getProjectId(), project.getId());
Assert.assertEquals(event.getUser(), user.getUserId());
Assert.assertEquals(event.getMessage(), message);
Assert.assertEquals(event.getType(), type);
}
@Ignore @Test
public void testFlowUpload() throws ProjectManagerException {
ProjectLoader loader = createLoader();
((JdbcProjectLoader) loader)
.setDefaultEncodingType(JdbcProjectLoader.EncodingType.GZIP);
String projectName = "mytestFlowUpload1";
String projectDescription = "This is my new project";
User user = new User("testUser");
Project project =
loader.createNewProject(projectName, projectDescription, user);
Flow flow = new Flow("MyNewFlow");
flow.addNode(new Node("A"));
flow.addNode(new Node("B"));
flow.addNode(new Node("C"));
flow.addNode(new Node("D"));
flow.addEdge(new Edge("A", "B"));
flow.addEdge(new Edge("A", "C"));
flow.addEdge(new Edge("B", "D"));
flow.addEdge(new Edge("C", "D"));
flow.initialize();
loader.uploadFlow(project, 4, flow);
project.setVersion(4);
Flow newFlow = loader.fetchFlow(project, flow.getId());
Assert.assertTrue(newFlow != null);
Assert.assertEquals(flow.getId(), newFlow.getId());
Assert.assertEquals(flow.getEdges().size(), newFlow.getEdges().size());
Assert.assertEquals(flow.getNodes().size(), newFlow.getNodes().size());
}
@Ignore @Test
public void testFlowUploadPlain() throws ProjectManagerException {
ProjectLoader loader = createLoader();
((JdbcProjectLoader) loader)
.setDefaultEncodingType(JdbcProjectLoader.EncodingType.PLAIN);
String projectName = "mytestFlowUpload2";
String projectDescription = "This is my new project";
User user = new User("testUser");
Project project =
loader.createNewProject(projectName, projectDescription, user);
Flow flow = new Flow("MyNewFlow2");
flow.addNode(new Node("A1"));
flow.addNode(new Node("B1"));
flow.addNode(new Node("C1"));
flow.addNode(new Node("D1"));
flow.addEdge(new Edge("A1", "B1"));
flow.addEdge(new Edge("A1", "C1"));
flow.addEdge(new Edge("B1", "D1"));
flow.addEdge(new Edge("C1", "D1"));
flow.initialize();
loader.uploadFlow(project, 4, flow);
project.setVersion(4);
Flow newFlow = loader.fetchFlow(project, flow.getId());
Assert.assertTrue(newFlow != null);
Assert.assertEquals(flow.getId(), newFlow.getId());
Assert.assertEquals(flow.getEdges().size(), newFlow.getEdges().size());
Assert.assertEquals(flow.getNodes().size(), newFlow.getNodes().size());
List<Flow> flows = loader.fetchAllProjectFlows(project);
Assert.assertTrue(flows.size() == 1);
}
@Ignore @Test
public void testProjectProperties() throws ProjectManagerException {
ProjectLoader loader = createLoader();
((JdbcProjectLoader) loader)
.setDefaultEncodingType(JdbcProjectLoader.EncodingType.PLAIN);
String projectName = "testProjectProperties";
String projectDescription = "This is my new project";
User user = new User("testUser");
Project project =
loader.createNewProject(projectName, projectDescription, user);
project.setVersion(5);
Props props = new Props();
props.put("a", "abc");
props.put("b", "bcd");
props.put("c", "cde");
props.setSource("mysource");
loader.uploadProjectProperty(project, props);
Props retProps = loader.fetchProjectProperty(project, "mysource");
Assert.assertEquals(retProps.getSource(), props.getSource());
Assert.assertEquals(retProps.getKeySet(), props.getKeySet());
Assert.assertEquals(PropsUtils.toStringMap(retProps, true),
PropsUtils.toStringMap(props, true));
}
@Test
public void testProjectFilesUpload() throws ProjectManagerException {
if (!isTestSetup()) {
return;
}
ProjectLoader loader = createLoader();
String projectName = "testProjectFilesUpload1";
String projectDescription = "This is my new project";
User user = new User("testUser");
Project project =
loader.createNewProject(projectName, projectDescription, user);
Assert.assertTrue("Project Id set", project.getId() > -1);
Assert.assertEquals("Project name", projectName, project.getName());
Assert.assertEquals("Project description", projectDescription,
project.getDescription());
File testFile = new File("unit/project/testjob/testjob.zip");
loader.uploadProjectFile(project.getId(), 1, testFile, user.getUserId());
ProjectFileHandler handler = loader.getUploadedFile(project.getId(), 1);
Assert.assertEquals(handler.getProjectId(), project.getId());
Assert.assertEquals(handler.getFileName(), "testjob.zip");
Assert.assertEquals(handler.getVersion(), 1);
Assert.assertEquals(handler.getFileType(), "zip");
File file = handler.getLocalFile();
Assert.assertTrue(handler.getLocalFile().exists());
Assert.assertEquals(handler.getFileName(), "testjob.zip");
Assert.assertEquals(handler.getUploader(), user.getUserId());
handler.deleteLocalFile();
Assert.assertTrue(handler.getLocalFile() == null);
Assert.assertFalse(file.exists());
}
// Custom equals for what I think is important
private void assertProjectMemberEquals(Project p1, Project p2) {
Assert.assertEquals(p1.getId(), p2.getId());
Assert.assertEquals(p1.getName(), p2.getName());
Assert.assertEquals(p1.getCreateTimestamp(), p2.getCreateTimestamp());
Assert.assertEquals(p1.getDescription(), p2.getDescription());
Assert.assertEquals(p1.getLastModifiedUser(), p2.getLastModifiedUser());
Assert.assertEquals(p1.getVersion(), p2.getVersion());
Assert.assertEquals(p1.isActive(), p2.isActive());
Assert.assertEquals(p1.getLastModifiedUser(), p2.getLastModifiedUser());
assertUserPermissionsEqual(p1, p2);
assertGroupPermissionsEqual(p1, p2);
}
private void assertUserPermissionsEqual(Project p1, Project p2) {
List<Pair<String, Permission>> perm1 = p1.getUserPermissions();
List<Pair<String, Permission>> perm2 = p2.getUserPermissions();
Assert.assertEquals(perm1.size(), perm2.size());
{
HashMap<String, Permission> perm1Map = new HashMap<String, Permission>();
for (Pair<String, Permission> p : perm1) {
perm1Map.put(p.getFirst(), p.getSecond());
}
for (Pair<String, Permission> p : perm2) {
Assert.assertTrue(perm1Map.containsKey(p.getFirst()));
Permission perm = perm1Map.get(p.getFirst());
Assert.assertEquals(perm, p.getSecond());
}
}
{
HashMap<String, Permission> perm2Map = new HashMap<String, Permission>();
for (Pair<String, Permission> p : perm2) {
perm2Map.put(p.getFirst(), p.getSecond());
}
for (Pair<String, Permission> p : perm1) {
Assert.assertTrue(perm2Map.containsKey(p.getFirst()));
Permission perm = perm2Map.get(p.getFirst());
Assert.assertEquals(perm, p.getSecond());
}
}
}
private void assertGroupPermissionsEqual(Project p1, Project p2) {
List<Pair<String, Permission>> perm1 = p1.getGroupPermissions();
List<Pair<String, Permission>> perm2 = p2.getGroupPermissions();
Assert.assertEquals(perm1.size(), perm2.size());
{
HashMap<String, Permission> perm1Map = new HashMap<String, Permission>();
for (Pair<String, Permission> p : perm1) {
perm1Map.put(p.getFirst(), p.getSecond());
}
for (Pair<String, Permission> p : perm2) {
Assert.assertTrue(perm1Map.containsKey(p.getFirst()));
Permission perm = perm1Map.get(p.getFirst());
Assert.assertEquals(perm, p.getSecond());
}
}
{
HashMap<String, Permission> perm2Map = new HashMap<String, Permission>();
for (Pair<String, Permission> p : perm2) {
perm2Map.put(p.getFirst(), p.getSecond());
}
for (Pair<String, Permission> p : perm1) {
Assert.assertTrue(perm2Map.containsKey(p.getFirst()));
Permission perm = perm2Map.get(p.getFirst());
Assert.assertEquals(perm, p.getSecond());
}
}
}
private ProjectLoader createLoader() {
Props props = new Props();
props.put("database.type", "mysql");
props.put("mysql.host", host);
props.put("mysql.port", port);
props.put("mysql.user", user);
props.put("mysql.database", database);
props.put("mysql.password", password);
props.put("mysql.numconnections", numConnections);
return new JdbcProjectLoader(props);
}
private boolean isTestSetup() {
if (!testDBExists) {
System.err.println("Skipping DB test because Db not setup.");
return false;
}
System.out.println("Running DB test because Db setup.");
return true;
}
public static class CountHandler implements ResultSetHandler<Integer> {
@Override
public Integer handle(ResultSet rs) throws SQLException {
int val = 0;
while (rs.next()) {
val++;
}
return val;
}
}
}