package liquibase.lockservice;
import liquibase.database.Database;
import liquibase.database.core.MySQLDatabase;
import liquibase.database.core.OracleDatabase;
import liquibase.exception.LockException;
import liquibase.executor.Executor;
import liquibase.executor.ExecutorService;
import liquibase.statement.core.LockDatabaseChangeLogStatement;
import liquibase.statement.core.SelectFromDatabaseChangeLogLockStatement;
import liquibase.statement.core.UnlockDatabaseChangeLogStatement;
import static org.easymock.classextension.EasyMock.*;
import org.junit.After;
import static org.junit.Assert.*;
import org.junit.Test;
import org.junit.Before;
import java.lang.reflect.Field;
import java.text.DateFormat;
import java.util.*;
@SuppressWarnings({"EqualsWhichDoesntCheckParameterClass"})
public class LockServiceTest {
@Before
public void before() {
LockService.resetAll();
}
@After
public void after() {
LockService.resetAll();
}
@Test
public void getInstance() {
final Database oracle1 = new OracleDatabase() {
@Override
public boolean equals(Object o) {
return o == this;
}
};
final Database oracle2 = new OracleDatabase() {
@Override
public boolean equals(Object o) {
return o == this;
}
};
final Database mysql = new MySQLDatabase() {
@Override
public boolean equals(Object o) {
return o == this;
}
};
assertNotNull(LockService.getInstance(oracle1));
assertNotNull(LockService.getInstance(oracle2));
assertNotNull(LockService.getInstance(mysql));
assertTrue(LockService.getInstance(oracle1) == LockService.getInstance(oracle1));
assertTrue(LockService.getInstance(oracle2) == LockService.getInstance(oracle2));
assertTrue(LockService.getInstance(mysql) == LockService.getInstance(mysql));
assertTrue(LockService.getInstance(oracle1) != LockService.getInstance(oracle2));
assertTrue(LockService.getInstance(oracle1) != LockService.getInstance(mysql));
}
@Test
public void aquireLock_hasLockAlready() throws Exception {
Database database = createMock(Database.class);
replay(database);
LockService lockService = LockService.getInstance(database);
assertFalse(lockService.hasChangeLogLock());
Field field = lockService.getClass().getDeclaredField("hasChangeLogLock");
field.setAccessible(true);
field.set(lockService, true);
assertTrue(lockService.hasChangeLogLock());
assertTrue(lockService.acquireLock());
}
@Test
public void acquireLock_tableExistsNotLocked() throws Exception {
Database database = createMock(Database.class);
Executor executor = createMock(Executor.class);
database.checkDatabaseChangeLogLockTable();
expectLastCall();
database.rollback();
expectLastCall().anyTimes();
database.setCanCacheLiquibaseTableInfo(true);
expectLastCall();
expect(executor.queryForObject(isA(SelectFromDatabaseChangeLogLockStatement.class), eq(Boolean.class))).andReturn(false);
executor.comment("Lock Database");
expectLastCall();
expect(executor.update(isA(LockDatabaseChangeLogStatement.class))).andReturn(1);
database.commit();
expectLastCall();
replay(executor);
replay(database);
ExecutorService.getInstance().setExecutor(database, executor);
LockService service = LockService.getInstance(database);
assertTrue(service.acquireLock());
verify(database);
verify(executor);
}
@Test
public void acquireLock_tableExistsIsLocked() throws Exception {
Database database = createMock(Database.class);
Executor executor = createMock(Executor.class);
database.checkDatabaseChangeLogLockTable();
expectLastCall();
database.rollback();
expectLastCall().anyTimes();
expect(executor.queryForObject(isA(SelectFromDatabaseChangeLogLockStatement.class), eq(Boolean.class))).andReturn(true);
replay(database);
replay(executor);
ExecutorService.getInstance().setExecutor(database, executor);
LockService service = LockService.getInstance(database);
assertFalse(service.acquireLock());
verify(database);
}
@Test
public void waitForLock_notLocked() throws Exception {
Database database = createMock(Database.class);
Executor executor = createMock(Executor.class);
database.checkDatabaseChangeLogLockTable();
expectLastCall();
database.setCanCacheLiquibaseTableInfo(true);
expectLastCall();
database.rollback();
expectLastCall().anyTimes();
expect(executor.queryForObject(isA(SelectFromDatabaseChangeLogLockStatement.class), eq(Boolean.class))).andReturn(false);
executor.comment("Lock Database");
expectLastCall();
expect(executor.update(isA(LockDatabaseChangeLogStatement.class))).andReturn(1);
database.commit();
expectLastCall();
replay(database);
replay(executor);
ExecutorService.getInstance().setExecutor(database, executor);
LockService service = LockService.getInstance(database);
service.waitForLock();
verify(database);
verify(executor);
}
@Test
public void waitForLock_lockedThenReleased() throws Exception {
Database database = createMock(Database.class);
Executor executor = createMock(Executor.class);
database.checkDatabaseChangeLogLockTable();
expectLastCall().anyTimes();
database.setCanCacheLiquibaseTableInfo(true);
expectLastCall();
expect(executor.queryForObject(isA(SelectFromDatabaseChangeLogLockStatement.class), eq(Boolean.class))).andReturn(true);
expect(executor.queryForObject(isA(SelectFromDatabaseChangeLogLockStatement.class), eq(Boolean.class))).andReturn(true);
expect(executor.queryForObject(isA(SelectFromDatabaseChangeLogLockStatement.class), eq(Boolean.class))).andReturn(true);
expect(executor.queryForObject(isA(SelectFromDatabaseChangeLogLockStatement.class), eq(Boolean.class))).andReturn(false);
executor.comment("Lock Database");
expectLastCall();
database.rollback();
expectLastCall().anyTimes();
expect(executor.update(isA(LockDatabaseChangeLogStatement.class))).andReturn(1);
database.commit();
expectLastCall();
replay(database);
replay(executor);
ExecutorService.getInstance().setExecutor(database, executor);
LockService service = LockService.getInstance(database);
service.setChangeLogLockRecheckTime(1);
service.waitForLock();
verify(database);
verify(executor);
}
@Test
public void waitForLock_lockedThenTimeout() throws Exception {
Database database = createMock(Database.class);
Executor executor = createMock(Executor.class);
database.checkDatabaseChangeLogLockTable();
expectLastCall().anyTimes();
expect(executor.queryForObject(isA(SelectFromDatabaseChangeLogLockStatement.class), eq(Boolean.class))).andReturn(true).anyTimes();
expect(database.hasDatabaseChangeLogLockTable()).andReturn(true);
List<Map> resultList = new ArrayList<Map>();
Map<String, Object> result = new HashMap<String, Object>();
result.put("ID", 1);
result.put("LOCKED", true);
Date lockDate = new Date();
result.put("LOCKGRANTED", lockDate);
result.put("LOCKEDBY", "Locker");
resultList.add(result);
expect(executor.queryForList(isA(SelectFromDatabaseChangeLogLockStatement.class))).andReturn(resultList);
database.rollback();
expectLastCall().anyTimes();
replay(database);
replay(executor);
ExecutorService.getInstance().setExecutor(database, executor);
LockService service = LockService.getInstance(database);
service.setChangeLogLockWaitTime(10);
service.setChangeLogLockRecheckTime(5);
try {
service.waitForLock();
fail("Should have thrown exception");
} catch (LockException e) {
assertEquals("Could not acquire change log lock. Currently locked by Locker since " + DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT).format(lockDate), e.getMessage());
}
verify(database);
}
@Test
public void releaseLock_tableExistsAndLocked() throws Exception {
Database database = createMock(Database.class);
Executor executor = createMock(Executor.class);
expect(executor.update(isA(UnlockDatabaseChangeLogStatement.class))).andReturn(1);
expect(database.hasDatabaseChangeLogLockTable()).andReturn(true);
database.commit();
expectLastCall().atLeastOnce();
database.setCanCacheLiquibaseTableInfo(false);
expectLastCall();
executor.comment("Release Database Lock");
expectLastCall().anyTimes();
database.rollback();
expectLastCall().anyTimes();
replay(database);
replay(executor);
ExecutorService.getInstance().setExecutor(database, executor);
LockService service = LockService.getInstance(database);
service.releaseLock();
verify(database);
}
@Test
public void listLocks_hasLocks() throws Exception {
Database database = createMock(Database.class);
Executor executor = createMock(Executor.class);
database.checkDatabaseChangeLogLockTable();
expectLastCall().anyTimes();
expect(executor.queryForObject(isA(SelectFromDatabaseChangeLogLockStatement.class), eq(Boolean.class))).andReturn(true).anyTimes();
expect(database.hasDatabaseChangeLogLockTable()).andReturn(true);
List<Map> resultList = new ArrayList<Map>();
Map<String, Object> result = new HashMap<String, Object>();
result.put("ID", 1);
result.put("LOCKED", true);
Date lockDate = new Date();
result.put("LOCKGRANTED", lockDate);
result.put("LOCKEDBY", "Locker");
resultList.add(result);
expect(executor.queryForList(isA(SelectFromDatabaseChangeLogLockStatement.class))).andReturn(resultList);
replay(database);
replay(executor);
ExecutorService.getInstance().setExecutor(database, executor);
LockService service = LockService.getInstance(database);
DatabaseChangeLogLock[] locks = service.listLocks();
assertEquals(1, locks.length);
assertEquals(1, locks[0].getId());
assertEquals("Locker", locks[0].getLockedBy());
assertEquals(lockDate, locks[0].getLockGranted());
verify(database);
}
@Test
public void listLocks_tableExistsUnlocked() throws Exception {
Database database = createMock(Database.class);
Executor executor = createMock(Executor.class);
database.checkDatabaseChangeLogLockTable();
expectLastCall().anyTimes();
expect(executor.queryForObject(isA(SelectFromDatabaseChangeLogLockStatement.class), eq(Boolean.class))).andReturn(true).anyTimes();
expect(database.hasDatabaseChangeLogLockTable()).andReturn(true);
List<Map> resultList = new ArrayList<Map>();
expect(executor.queryForList(isA(SelectFromDatabaseChangeLogLockStatement.class))).andReturn(resultList);
replay(database);
replay(executor);
ExecutorService.getInstance().setExecutor(database, executor);
LockService service = LockService.getInstance(database);
DatabaseChangeLogLock[] locks = service.listLocks();
assertEquals(0, locks.length);
verify(database);
}
@Test
public void listLocks_tableDoesNotExists() throws Exception {
Database database = createMock(Database.class);
expect(database.hasDatabaseChangeLogLockTable()).andReturn(false);
replay(database);
LockService service = LockService.getInstance(database);
DatabaseChangeLogLock[] locks = service.listLocks();
assertEquals(0, locks.length);
verify(database);
}
}