package com.thinkbiganalytics.metadata.sla.spi.core; /*- * #%L * thinkbig-sla-metrics-default * %% * Copyright (C) 2017 ThinkBig Analytics * %% * 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. * #L% */ import com.thinkbiganalytics.metadata.api.MetadataAccess; import com.thinkbiganalytics.metadata.api.MetadataAction; import com.thinkbiganalytics.metadata.api.MetadataCommand; import com.thinkbiganalytics.metadata.api.MetadataRollbackAction; import com.thinkbiganalytics.metadata.api.MetadataRollbackCommand; import com.thinkbiganalytics.metadata.api.jobrepo.job.BatchJobExecution; import com.thinkbiganalytics.metadata.api.jobrepo.job.BatchJobExecutionProvider; import com.thinkbiganalytics.metadata.sla.api.AssessmentResult; import com.thinkbiganalytics.metadata.sla.api.Metric; import com.thinkbiganalytics.metadata.sla.api.core.FeedOnTimeArrivalMetric; import com.thinkbiganalytics.metadata.sla.api.core.FeedOnTimeArrivalMetricAssessor; import com.thinkbiganalytics.metadata.sla.spi.MetricAssessmentBuilder; import com.thinkbiganalytics.scheduler.util.CronExpressionUtil; import org.joda.time.DateTime; import org.joda.time.Period; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.BDDMockito; import org.mockito.InjectMocks; import org.mockito.Mock; import org.powermock.api.mockito.PowerMockito; import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.modules.junit4.PowerMockRunner; import org.quartz.CronExpression; import org.testng.Assert; import java.security.Principal; import java.text.ParseException; import static org.mockito.Matchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static org.mockito.MockitoAnnotations.initMocks; @RunWith(PowerMockRunner.class) @PrepareForTest({DateTime.class, CronExpressionUtil.class}) public class FeedOnTimeArrivalMetricAssessorTest { private DateTime lateTime; private FeedOnTimeArrivalMetric metric; @Mock private BatchJobExecutionProvider jobExecutionProvider; @Mock private MetricAssessmentBuilder builder; private MetadataAccess metadataAccess = new MockMetadataAccess(); @InjectMocks private FeedOnTimeArrivalMetricAssessor assessor = new FeedOnTimeArrivalMetricAssessor(); private int lateTimeGracePeriod = 4; @Before public void setUp() throws Exception { initMocks(this); when(this.builder.message(any(String.class))).thenReturn(this.builder); when(this.builder.metric(any(Metric.class))).thenReturn(this.builder); when(this.builder.result(any(AssessmentResult.class))).thenReturn(this.builder); this.assessor.setMetadataAccess(this.metadataAccess); CronExpression cron = new CronExpression("0 0 12 1/1 * ? *"); // Noon every day this.lateTime = new DateTime(CronExpressionUtil.getPreviousFireTime(cron)).plusHours(4); this.metric = new FeedOnTimeArrivalMetric("feed", cron, Period.hours(lateTimeGracePeriod)); } @Test public void testMinuteBeforeLate() throws ParseException { DateTime feedEnd = this.lateTime.minusMinutes(1); BatchJobExecution feed = createFeedJobExecution(feedEnd); when(this.jobExecutionProvider.findLatestCompletedJobForFeed("feed")).thenReturn(feed); this.assessor.assess(metric, this.builder); verify(this.builder).result(AssessmentResult.SUCCESS); } @Test /** * test use case where the date window is still valid, but still no data has been found for the Feed. * This will return no Assessment result as it still could process data */ public void testNoDataButStillBeforeLateTime() throws ParseException { PowerMockito.mockStatic(DateTime.class); PowerMockito.mockStatic(CronExpressionUtil.class); DateTime now = new DateTime().minusHours(2); //Some Cron Expression .. Mockito will overwrite CronExpression cron = new CronExpression("0 0 0/2 1/1 * ? *"); //set the current time to a known time BDDMockito.given(DateTime.now()).willReturn(now); //set the previous fire date to a known time in the past,but within the window DateTime previousFireTime = new DateTime(now).minusHours(3); BDDMockito.given(CronExpressionUtil.getPreviousFireTime(cron)).willReturn(previousFireTime.toDate()); //window is = (now - 3) - (now -3) + lateTime) //Some Feed End Time to a time not within this window DateTime lastFeedTime = new DateTime().minusWeeks(2); BatchJobExecution feed = createFeedJobExecution(lastFeedTime); when(this.jobExecutionProvider.findLatestCompletedJobForFeed("feed")).thenReturn(feed); this.metric = new FeedOnTimeArrivalMetric("feed", cron, Period.hours(lateTimeGracePeriod)); this.assessor.assess(metric, this.builder); //assert values DateTime lateTime = previousFireTime.plusHours(lateTimeGracePeriod); Assert.assertTrue(now.isBefore(lateTime) && !(lastFeedTime.isAfter(previousFireTime) && lastFeedTime.isBefore(lateTime))); verify(this.builder); } @Test public void testSuccess() throws ParseException { PowerMockito.mockStatic(DateTime.class); PowerMockito.mockStatic(CronExpressionUtil.class); DateTime now = new DateTime().minusHours(2); //set the current time to a known time BDDMockito.given(DateTime.now()).willReturn(now); //set the previous fire date to a known time in the past,but within the window DateTime previousFireTime = new DateTime(now).minusHours(3); //Some Cron Expression .. Mockito will overwrite CronExpression cron = new CronExpression("0 0 0/2 1/1 * ? *"); BDDMockito.given(CronExpressionUtil.getPreviousFireTime(cron)).willReturn(previousFireTime.toDate()); //window is = (now - 3) - (now -3) + lateTime) //Some Feed End Time to a time within this window DateTime lastFeedTime = new DateTime(previousFireTime).plus(lateTimeGracePeriod - 1); BatchJobExecution feed = createFeedJobExecution(lastFeedTime); when(this.jobExecutionProvider.findLatestCompletedJobForFeed("feed")).thenReturn(feed); this.metric = new FeedOnTimeArrivalMetric("feed", cron, Period.hours(lateTimeGracePeriod)); this.assessor.assess(metric, this.builder); //assert values DateTime lateTime = previousFireTime.plusHours(lateTimeGracePeriod); Assert.assertTrue((lastFeedTime.isAfter(previousFireTime) && lastFeedTime.isBefore(lateTime))); verify(this.builder.result(AssessmentResult.SUCCESS)); } @Test public void testFailure() throws ParseException { PowerMockito.mockStatic(DateTime.class); PowerMockito.mockStatic(CronExpressionUtil.class); DateTime now = new DateTime().minusHours(2); //Some Cron Expression .. Mockito will overwrite CronExpression cron = new CronExpression("0 0 0/2 1/1 * ? *"); //set the current time to a known time BDDMockito.given(DateTime.now()).willReturn(now); //set the previous fire date to a known time in the past DateTime previousFireTime = new DateTime(now).minusHours(4); BDDMockito.given(CronExpressionUtil.getPreviousFireTime(cron)).willReturn(previousFireTime.toDate()); //window is = (now - 4) - (now -4) + lateTimeGracePeriod) //Some Feed End Time to a time outside the window DateTime lastFeedTime = new DateTime(previousFireTime).minusHours(lateTimeGracePeriod + 1); BatchJobExecution feed = createFeedJobExecution(lastFeedTime); when(this.jobExecutionProvider.findLatestCompletedJobForFeed("feed")).thenReturn(feed); this.metric = new FeedOnTimeArrivalMetric("feed", cron, Period.hours(lateTimeGracePeriod)); this.assessor.assess(metric, this.builder); //assert values verify(this.builder.result(AssessmentResult.FAILURE)); } @Test public void testMinuteAfterLate() throws ParseException { DateTime now = this.lateTime.plusMinutes(2); DateTime feedEnd = this.lateTime.plusMinutes(1); BatchJobExecution feed = createFeedJobExecution(feedEnd); PowerMockito.mockStatic(DateTime.class); BDDMockito.given(DateTime.now()).willReturn(now); when(this.jobExecutionProvider.findLatestCompletedJobForFeed("feed")).thenReturn(feed); this.assessor.assess(metric, this.builder); verify(this.builder).result(AssessmentResult.FAILURE); } @Test public void testFeedNotFound() throws ParseException { when(this.jobExecutionProvider.findLatestCompletedJobForFeed("feed")).thenReturn(null); this.assessor.assess(metric, this.builder); verify(this.builder).result(AssessmentResult.WARNING); } private BatchJobExecution createFeedJobExecution(DateTime endTime) { BatchJobExecution feed = mock(BatchJobExecution.class); when(feed.getEndTime()).thenReturn(endTime); return feed; } public class MockMetadataAccess implements MetadataAccess { public MockMetadataAccess() { } @Override public <R> R commit(MetadataCommand<R> cmd, Principal... principals) { try { return cmd.execute(); } catch (Exception e) { } return null; } @Override public <R> R commit(MetadataCommand<R> cmd, MetadataRollbackCommand rollbackCmd, Principal... principals) { try { return cmd.execute(); } catch (Exception e) { } return null; } @Override public void commit(MetadataAction cmd, Principal... principals) { try { cmd.execute(); } catch (Exception e) { } } @Override public void commit(MetadataAction cmd, MetadataRollbackAction rollbackAction, Principal... principals) { try { cmd.execute(); } catch (Exception e) { } } @Override public <R> R read(MetadataCommand<R> cmd, Principal... principals) { try { return cmd.execute(); } catch (Exception e) { } return null; } @Override public void read(MetadataAction cmd, Principal... principals) { try { cmd.execute(); } catch (Exception e) { } } } }