/* * Copyright 2017 ThoughtWorks, 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.thoughtworks.go.server.persistence; import java.math.BigInteger; import java.sql.SQLException; import java.util.ArrayList; import java.util.Arrays; import java.util.Date; import com.thoughtworks.go.config.CaseInsensitiveString; import com.thoughtworks.go.database.QueryExtensions; import com.thoughtworks.go.domain.PipelineTimelineEntry; import com.thoughtworks.go.server.cache.GoCache; import com.thoughtworks.go.server.database.DatabaseStrategy; import com.thoughtworks.go.server.domain.PipelineTimeline; import com.thoughtworks.go.server.domain.user.PipelineSelections; import com.thoughtworks.go.server.transaction.TransactionSynchronizationManager; import com.thoughtworks.go.server.transaction.TransactionTemplate; import com.thoughtworks.go.util.SystemEnvironment; import org.hibernate.SQLQuery; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.junit.Before; import org.junit.Test; import org.springframework.dao.DataAccessException; import org.springframework.orm.hibernate3.HibernateCallback; import org.springframework.orm.hibernate3.HibernateTemplate; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsInAnyOrder; import static org.hamcrest.Matchers.is; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.fail; import static org.mockito.Matchers.anyString; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; public class PipelineRepositoryTest { private SessionFactory sessionFactory; private GoCache goCache; private HibernateTemplate hibernateTemplate; private PipelineRepository pipelineRepository; private SystemEnvironment systemEnvironment; private DatabaseStrategy databaseStrategy; private TransactionSynchronizationManager transactionSynchronizationManager; private TransactionTemplate transactionTemplate; private Session session; private SQLQuery sqlQuery; @Before public void setup() { session = mock(Session.class); sqlQuery = mock(SQLQuery.class); sessionFactory = mock(SessionFactory.class); hibernateTemplate = mock(HibernateTemplate.class); goCache = mock(GoCache.class); systemEnvironment = mock(SystemEnvironment.class); databaseStrategy = mock(DatabaseStrategy.class); when(databaseStrategy.getQueryExtensions()).thenReturn(mock(QueryExtensions.class)); pipelineRepository = new PipelineRepository(sessionFactory, goCache, databaseStrategy); pipelineRepository.setHibernateTemplate(hibernateTemplate); transactionTemplate = mock(TransactionTemplate.class); transactionSynchronizationManager = mock(TransactionSynchronizationManager.class); } @Test public void shouldCachePipelineSelectionForGivenUserId() throws Exception { String queryString = "FROM PipelineSelections WHERE userId = ?"; PipelineSelections pipelineSelections = new PipelineSelections(); long userId = 1L; when(hibernateTemplate.find(queryString, new Object[]{userId})).thenReturn(Arrays.asList(pipelineSelections)); //return false for first 2 calls and return true for next call when(goCache.isKeyInCache(pipelineRepository.pipelineSelectionForUserIdKey(userId))).thenReturn(false).thenReturn(false).thenReturn(true); pipelineRepository.findPipelineSelectionsByUserId(userId); pipelineRepository.findPipelineSelectionsByUserId(userId); verify(hibernateTemplate).find(queryString, new Object[]{userId}); verify(goCache).put(pipelineRepository.pipelineSelectionForUserIdKey(userId), pipelineSelections); } @Test public void shouldCachePipelineSelectionForGivenId() throws Exception { PipelineSelections pipelineSelections = new PipelineSelections(); long id = 1L; when(hibernateTemplate.get(PipelineSelections.class, id)).thenReturn(pipelineSelections); //return false for first 2 calls and return true for next call when(goCache.isKeyInCache(pipelineRepository.pipelineSelectionForCookieKey(id))).thenReturn(false).thenReturn(false).thenReturn(true); pipelineRepository.findPipelineSelectionsById(id); pipelineRepository.findPipelineSelectionsById(id); verify(hibernateTemplate).get(PipelineSelections.class, id); verify(goCache).put(pipelineRepository.pipelineSelectionForCookieKey(id), pipelineSelections); } @Test public void shouldInvalidatePipelineSelectionCacheOnSaveOrUpdate() throws Exception { PipelineSelections pipelineSelections = new PipelineSelections(); pipelineSelections.setId(1); pipelineSelections.update(new ArrayList<>(), new Date(), 2L, true); pipelineRepository.saveSelectedPipelines(pipelineSelections); verify(goCache).remove(pipelineRepository.pipelineSelectionForCookieKey(1L)); verify(goCache).remove(pipelineRepository.pipelineSelectionForUserIdKey(2L)); } @Test public void shouldUpdateTimelineEntriesAndEntriesForRollback() throws Exception { Object[] pipelineRow1 = {"p1", new BigInteger("1"), new BigInteger("1"), new Date(), "fingerprint", 1.0, "r1", null, new BigInteger("1"), new BigInteger("1")}; Object[] pipelineRow2 = {"p1", new BigInteger("2"), new BigInteger("2"), new Date(), "fingerprint", 2.0, "r2", null, new BigInteger("1"), new BigInteger("1")}; stubPipelineInstancesInDb(pipelineRow1, pipelineRow2); ArrayList<PipelineTimelineEntry> tempEntries = new ArrayList<>(); PipelineTimeline pipelineTimeline = new PipelineTimeline(pipelineRepository, transactionTemplate, transactionSynchronizationManager); pipelineRepository.updatePipelineTimeline(pipelineTimeline, tempEntries); PipelineTimelineEntry timelineEntry1 = pipelineTimeline.getEntryFor(new CaseInsensitiveString("p1"), 1); PipelineTimelineEntry timelineEntry2 = pipelineTimeline.getEntryFor(new CaseInsensitiveString("p1"), 2); assertThat(pipelineTimeline.instanceCount(new CaseInsensitiveString("p1")), is(2)); assertNotNull(timelineEntry1); assertNotNull(timelineEntry2); assertThat(tempEntries.size(), is(2)); assertThat(tempEntries, containsInAnyOrder(timelineEntry1, timelineEntry2)); } @Test public void shouldNotUpdateTimelineEntriesAndEntriesForRollbackUponFailureDuringRetrieval() throws Exception { Object[] pipelineRow1 = {"p1", new BigInteger("1"), new BigInteger("1"), new Date(), "fingerprint", 1.0, "r1", null, new BigInteger("1"), new BigInteger("1")}; Object[] pipelineRow2 = {"p1", "cause-failure-during-retrieval", new BigInteger("2"), new Date(), "fingerprint", 2.0, "r2", null, new BigInteger("1"), new BigInteger("1")}; stubPipelineInstancesInDb(pipelineRow1, pipelineRow2); ArrayList<PipelineTimelineEntry> tempEntries = new ArrayList<>(); PipelineTimeline pipelineTimeline = new PipelineTimeline(pipelineRepository, transactionTemplate, transactionSynchronizationManager); try { pipelineRepository.updatePipelineTimeline(pipelineTimeline, tempEntries); fail("Should fail to retrieve pipeline."); } catch (ClassCastException e) { assertThat(tempEntries.size(), is(0)); assertThat(pipelineTimeline.instanceCount(new CaseInsensitiveString("p1")), is(0)); } } @Test public void shouldUpdateTimelineEntriesAndEntriesForRollbackDuringFailureWhileUpdatingTheDb() throws Exception { Object[] pipelineRow1 = {"p1", new BigInteger("1"), new BigInteger("1"), new Date(), "fingerprint", 1.0, "r1", null, new BigInteger("1"), new BigInteger("1")}; Object[] pipelineRow2 = {"p1", new BigInteger("2"), new BigInteger("2"), new Date(), "fingerprint", 2.0, "r2", null, new BigInteger("1"), new BigInteger("1")}; stubPipelineInstancesInDb(pipelineRow1, pipelineRow2); when(sqlQuery.executeUpdate()).thenThrow(new RuntimeException("Failure during update natural order in db")); ArrayList<PipelineTimelineEntry> tempEntries = new ArrayList<>(); PipelineTimeline pipelineTimeline = new PipelineTimeline(pipelineRepository, transactionTemplate, transactionSynchronizationManager); try { pipelineRepository.updatePipelineTimeline(pipelineTimeline, tempEntries); } catch (RuntimeException e) { PipelineTimelineEntry timelineEntry1 = pipelineTimeline.getEntryFor(new CaseInsensitiveString("p1"), 1); PipelineTimelineEntry timelineEntry2 = pipelineTimeline.getEntryFor(new CaseInsensitiveString("p1"), 2); assertThat(pipelineTimeline.instanceCount(new CaseInsensitiveString("p1")), is(2)); assertNotNull(timelineEntry1); assertNotNull(timelineEntry2); assertThat(tempEntries.size(), is(2)); assertThat(tempEntries, containsInAnyOrder(timelineEntry1, timelineEntry2)); } } private void stubPipelineInstancesInDb(Object[]... rows) { pipelineRepository.setHibernateTemplate(new HibernateTemplate() { @Override public <T> T execute(HibernateCallback<T> action) throws DataAccessException { try { return action.doInHibernate(session); } catch (SQLException e) { throw new RuntimeException(); } } }); when(session.createSQLQuery(anyString())).thenReturn(sqlQuery); when(sqlQuery.list()).thenReturn(Arrays.asList(rows)); } }