content: /*
   * Licensed to the Apache Software Foundation (ASF) under one
   * or more contributor license agreements.  See the NOTICE file
   * distributed with this work for additional information
   * regarding copyright ownership.  The ASF licenses this file
   * to you 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.apache.ambari.server.orm.dao;
  
  import java.util.List;
  
  import jakarta.persistence.EntityManager;
  import jakarta.persistence.TypedQuery;
  
  import org.apache.ambari.server.configuration.Configuration;
  import org.apache.ambari.server.orm.entities.AlertCurrentEntity;
  import org.apache.ambari.server.orm.entities.AlertDefinitionEntity;
  import org.apache.ambari.server.orm.entities.AlertHistoryEntity;
  import org.apache.ambari.server.testutils.PartialNiceMockBinder;
  import org.easymock.EasyMock;
  import org.junit.Assert;
  import org.junit.Before;
  import org.junit.Test;
  
  import com.google.common.collect.Lists;
  import com.google.inject.Binder;
  import com.google.inject.Guice;
  import com.google.inject.Injector;
  import com.google.inject.Module;
  
  /**
   * Tests cached alerts are merged correct with the set from JPA.
   */
  public class AlertsDAOCachedTest {
  
    private static final String HOST = "c6401.ambari.apache.org";
  
    private Injector m_injector;
  
    private enum CachedAlertTestArea {
      FIND_ALL {
        @Override
        List<AlertCurrentEntity> execute(AlertsDAO alertsDAO) throws Exception {
          return alertsDAO.findCurrent();
        }
  
        @Override
        String getNamedQuery() {
          return "AlertCurrentEntity.findAll";
        }
      },
  
      FIND_BY_DEFINITION_ID {
        @Override
        List<AlertCurrentEntity> execute(AlertsDAO alertsDAO) throws Exception {
          return alertsDAO.findCurrentByDefinitionId(0L);
        }
  
        @Override
        String getNamedQuery() {
          return "AlertCurrentEntity.findByDefinitionId";
        }
      },
  
      FIND_BY_CLUSTER_ID {
        @Override
        List<AlertCurrentEntity> execute(AlertsDAO alertsDAO) throws Exception {
          return alertsDAO.findCurrentByCluster(0L);
        }
  
        @Override
        String getNamedQuery() {
          return "AlertCurrentEntity.findByCluster";
        }
      },
  
      FIND_BY_SERVICE {
        @Override
        List<AlertCurrentEntity> execute(AlertsDAO alertsDAO) throws Exception {
          return alertsDAO.findCurrentByService(0L, HOST);
        }
  
        @Override
        String getNamedQuery() {
          return "AlertCurrentEntity.findByService";
        }
      };
  
      abstract List<AlertCurrentEntity> execute(AlertsDAO alertsDAO) throws Exception;
  
      abstract String getNamedQuery();
    }
  
    @Before
    public void before() {
      // create an injector which will inject the mocks
      m_injector = Guice.createInjector(new MockModule());
    }
  
    /**
     * Tests that finding all alerts supplements with the cache.
     *
     * @throws Exception
     */
    @Test
    public void testFindAll() throws Exception {
      testFindUsesCache(CachedAlertTestArea.FIND_ALL);
    }
  
    /**
     * Tests that finding alerts by cluster ID supplements with the cache.
     *
     * @throws Exception
     */
    @Test
    public void testFindByClusterId() throws Exception {
      testFindUsesCache(CachedAlertTestArea.FIND_BY_CLUSTER_ID);
    }
  
    /**
     * Tests that finding alerts by definition ID supplements with the cache.
     *
     * @throws Exception
     */
    @Test
    public void testFindByDefinitionId() throws Exception {
      testFindUsesCache(CachedAlertTestArea.FIND_BY_DEFINITION_ID);
    }
  
    /**
     * Tests that finding alerts by service supplements with the cache.
     *
     * @throws Exception
     */
    @Test
    public void testFindByService() throws Exception {
      testFindUsesCache(CachedAlertTestArea.FIND_BY_SERVICE);
    }
  
    @Test
    @SuppressWarnings("unchecked")
    public void testMergeIntoCacheOnly() throws Exception {
      EntityManager entityManager = m_injector.getInstance(EntityManager.class);
      DaoUtils daoUtils = m_injector.getInstance(DaoUtils.class);
  
      AlertHistoryEntity history = EasyMock.createNiceMock(AlertHistoryEntity.class);
      AlertDefinitionEntity definition = EasyMock.createNiceMock(AlertDefinitionEntity.class);
      mock(definition, history);
  
      AlertCurrentEntity jpaCurrent = new AlertCurrentEntity();
      jpaCurrent.setAlertHistory(history);
      jpaCurrent.setOriginalTimestamp(1L);
      jpaCurrent.setLatestTimestamp(2L);
  
      AlertCurrentEntity memoryCurrent = new AlertCurrentEntity();
      memoryCurrent.setAlertHistory(history);
      memoryCurrent.setOriginalTimestamp(1L);
      memoryCurrent.setLatestTimestamp(3L);
  
      // mock the EM; notice we do not mock merge since this should not call merge
      TypedQuery<AlertCurrentEntity> typedQuery = EasyMock.createNiceMock(TypedQuery.class);
      EasyMock.expect(entityManager.createNamedQuery(CachedAlertTestArea.FIND_ALL.getNamedQuery(),
          AlertCurrentEntity.class)).andReturn(typedQuery).atLeastOnce();
  
      // create the data to return from mocked JPA
      List<AlertCurrentEntity> jpaCurrentAlerts = Lists.newArrayList(jpaCurrent);
      EasyMock.expect(daoUtils.selectList(typedQuery)).andReturn(jpaCurrentAlerts).atLeastOnce();
  
      EasyMock.replay(entityManager, daoUtils, typedQuery);
  
      // invoke merge on our in-memory current entity
      AlertsDAO alertsDAO = m_injector.getInstance(AlertsDAO.class);
      alertsDAO.merge(memoryCurrent, true);
      List<AlertCurrentEntity> testCurrentAlerts = alertsDAO.findCurrent();
  
      // verify that the stale JPA data is augemented with the cached data
      Assert.assertEquals(1, testCurrentAlerts.size());
      Assert.assertEquals(Long.valueOf(3), testCurrentAlerts.get(0).getLatestTimestamp());
  
      EasyMock.verify(definition, history, entityManager, daoUtils);
    }
  
    @SuppressWarnings("unchecked")
    private void testFindUsesCache(CachedAlertTestArea testArea) throws Exception {
      EntityManager entityManager = m_injector.getInstance(EntityManager.class);
      DaoUtils daoUtils = m_injector.getInstance(DaoUtils.class);
  
      AlertHistoryEntity history = EasyMock.createNiceMock(AlertHistoryEntity.class);
      AlertDefinitionEntity definition = EasyMock.createNiceMock(AlertDefinitionEntity.class);
      mock(definition, history);
  
      AlertCurrentEntity jpaCurrent = new AlertCurrentEntity();
      jpaCurrent.setAlertHistory(history);
      jpaCurrent.setOriginalTimestamp(1L);
      jpaCurrent.setLatestTimestamp(2L);
  
      AlertCurrentEntity memoryCurrent = new AlertCurrentEntity();
      memoryCurrent.setAlertHistory(history);
      memoryCurrent.setOriginalTimestamp(1L);
      memoryCurrent.setLatestTimestamp(3L);
  
      // mock the call to merge
      EasyMock.expect(entityManager.merge(memoryCurrent)).andReturn(memoryCurrent).atLeastOnce();
  
      // create the data to return from mocked JPA
      TypedQuery<AlertCurrentEntity> typedQuery = EasyMock.createNiceMock(TypedQuery.class);
  
      // mock the call to find alerts from JPA
      EasyMock.expect(
          entityManager.createNamedQuery(testArea.getNamedQuery(),
              AlertCurrentEntity.class)).andReturn(
                  typedQuery).atLeastOnce();
  
      List<AlertCurrentEntity> jpaCurrentAlerts = Lists.newArrayList(jpaCurrent);
  
      EasyMock.expect(daoUtils.selectList(typedQuery)).andReturn(
          jpaCurrentAlerts).atLeastOnce();
  
      EasyMock.replay(entityManager, daoUtils, typedQuery);
  
      // invoke merge on our in-memory current entity
      AlertsDAO alertsDAO = m_injector.getInstance(AlertsDAO.class);
      alertsDAO.merge(memoryCurrent);
      List<AlertCurrentEntity> testCurrentAlerts = testArea.execute(alertsDAO);
  
      // verify that the stale JPA data is augemented with the cached data
      Assert.assertEquals(1, testCurrentAlerts.size());
      Assert.assertEquals(Long.valueOf(3), testCurrentAlerts.get(0).getLatestTimestamp());
  
      EasyMock.verify(definition, history, entityManager, daoUtils);
    }
  
    /**
     * Mocks and replays the mocked definition and history
     *
     * @param definition
     * @param history
     */
    private void mock(AlertDefinitionEntity definition, AlertHistoryEntity history) {
      EasyMock.expect(definition.getDefinitionName()).andReturn("definitionName").atLeastOnce();
      EasyMock.expect(history.getClusterId()).andReturn(1L).atLeastOnce();
      EasyMock.expect(history.getHostName()).andReturn(HOST).atLeastOnce();
      EasyMock.expect(history.getAlertDefinition()).andReturn(definition).atLeastOnce();
      EasyMock.expect(history.getAlertDefinitionId()).andReturn(1L).atLeastOnce();
      EasyMock.expect(history.getAlertId()).andReturn(1L).atLeastOnce();
      EasyMock.expect(history.getAlertText()).andReturn("alertText").atLeastOnce();
  
      EasyMock.replay(definition, history);
    }
  
    /**
     *
     */
    private class MockModule implements Module {
      /**
       * {@inheritDoc}
       */
      @Override
      public void configure(Binder binder) {
        // required for since the configuration is being mocked
        Configuration configuration = EasyMock.createNiceMock(Configuration.class);
        EasyMock.expect(configuration.getAlertEventPublisherCorePoolSize()).andReturn(Integer.valueOf(Configuration.ALERTS_EXECUTION_SCHEDULER_THREADS_CORE_SIZE.getDefaultValue())).anyTimes();
        EasyMock.expect(configuration.getAlertEventPublisherMaxPoolSize()).andReturn(Integer.valueOf(Configuration.ALERTS_EXECUTION_SCHEDULER_THREADS_MAX_SIZE.getDefaultValue())).anyTimes();
        EasyMock.expect(configuration.getAlertEventPublisherWorkerQueueSize()).andReturn(Integer.valueOf(Configuration.ALERTS_EXECUTION_SCHEDULER_WORKER_QUEUE_SIZE.getDefaultValue())).anyTimes();
        EasyMock.expect(configuration.isAlertCacheEnabled()).andReturn(Boolean.TRUE).anyTimes();
        EasyMock.expect(configuration.getAlertCacheSize()).andReturn(100).anyTimes();
        EasyMock.replay(configuration);
  
        binder.bind(Configuration.class).toInstance(configuration);
  
        PartialNiceMockBinder.newBuilder().addConfigsBindings().addAlertDefinitionBinding().addLdapBindings().build().configure(binder);
      }
    }
  }
  