com.netflix.genie.web.tasks.leader.DatabaseCleanupTaskUnitTests.java Source code

Java tutorial

Introduction

Here is the source code for com.netflix.genie.web.tasks.leader.DatabaseCleanupTaskUnitTests.java

Source

/*
 *
 *  Copyright 2016 Netflix, 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.netflix.genie.web.tasks.leader;

import com.netflix.genie.core.jobs.JobConstants;
import com.netflix.genie.core.services.JobPersistenceService;
import com.netflix.genie.core.util.MetricsUtils;
import com.netflix.genie.test.categories.UnitTest;
import com.netflix.genie.web.properties.DatabaseCleanupProperties;
import com.netflix.genie.web.tasks.GenieTaskScheduleType;
import com.netflix.spectator.api.Id;
import com.netflix.spectator.api.Registry;
import com.netflix.spectator.api.Timer;
import org.hamcrest.Matchers;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.experimental.categories.Category;
import org.mockito.ArgumentCaptor;
import org.mockito.Mockito;
import org.springframework.scheduling.support.CronTrigger;

import java.util.Calendar;
import java.util.Date;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;

/**
 * Unit tests for DatabaseCleanupTask.
 *
 * @author tgianos
 * @since 3.0.0
 */
@Category(UnitTest.class)
public class DatabaseCleanupTaskUnitTests {

    private DatabaseCleanupProperties cleanupProperties;
    private JobPersistenceService jobPersistenceService;
    private DatabaseCleanupTask task;
    private AtomicLong numDeletedJobs;
    private Timer deletionTimer;
    private Id deletionTimerId;

    /**
     * Setup for the tests.
     */
    @Before
    public void setup() {
        this.cleanupProperties = Mockito.mock(DatabaseCleanupProperties.class);
        this.jobPersistenceService = Mockito.mock(JobPersistenceService.class);
        this.numDeletedJobs = new AtomicLong();
        this.deletionTimer = Mockito.mock(Timer.class);
        this.deletionTimerId = Mockito.mock(Id.class);
        final Registry registry = Mockito.mock(Registry.class);
        Mockito.when(registry.createId("genie.tasks.databaseCleanup.duration.timer")).thenReturn(deletionTimerId);
        Mockito.when(deletionTimerId.withTags(Mockito.anyMapOf(String.class, String.class)))
                .thenReturn(deletionTimerId);
        Mockito.when(registry.timer(deletionTimerId)).thenReturn(deletionTimer);
        Mockito.when(registry.gauge(Mockito.eq("genie.tasks.databaseCleanup.numDeletedJobs.gauge"),
                Mockito.any(AtomicLong.class))).thenReturn(this.numDeletedJobs);
        Mockito.when(registry.timer("genie.tasks.databaseCleanup.duration.timer")).thenReturn(this.deletionTimer);
        this.task = new DatabaseCleanupTask(this.cleanupProperties, this.jobPersistenceService, registry);
    }

    /**
     * Make sure the schedule type returns the correct thing.
     */
    @Test
    public void canGetScheduleType() {
        Assert.assertThat(this.task.getScheduleType(), Matchers.is(GenieTaskScheduleType.TRIGGER));
    }

    /**
     * Make sure the trigger returned is accurate.
     */
    @Test
    public void canGetTrigger() {
        Mockito.when(this.cleanupProperties.getExpression()).thenReturn("0 0 0 * * *");
        Assert.assertTrue(this.task.getTrigger() instanceof CronTrigger);
    }

    /**
     * Make sure the run method passes in the expected date.
     */
    @Test
    public void canRun() {
        final int days = 5;
        final int negativeDays = -1 * days;
        final int pageSize = 10;
        final int maxDeleted = 10_000;

        Mockito.when(this.cleanupProperties.getRetention()).thenReturn(days).thenReturn(negativeDays);
        Mockito.when(this.cleanupProperties.getPageSize()).thenReturn(pageSize);
        Mockito.when(this.cleanupProperties.getMaxDeletedPerTransaction()).thenReturn(maxDeleted);
        final ArgumentCaptor<Date> argument = ArgumentCaptor.forClass(Date.class);

        final long deletedCount1 = 6L;
        final long deletedCount2 = 18L;
        final long deletedCount3 = 2L;
        Mockito.when(this.jobPersistenceService.deleteBatchOfJobsCreatedBeforeDate(Mockito.any(Date.class),
                Mockito.anyInt(), Mockito.anyInt())).thenReturn(deletedCount1).thenReturn(0L)
                .thenReturn(deletedCount2).thenReturn(deletedCount3).thenReturn(0L);

        // The multiple calendar instances are to protect against running this test when the day flips
        final Calendar before = Calendar.getInstance(JobConstants.UTC);
        this.task.run();
        Assert.assertThat(this.numDeletedJobs.get(), Matchers.is(deletedCount1));
        Mockito.verify(this.deletionTimerId, Mockito.times(1)).withTags(MetricsUtils.newSuccessTagsMap());
        this.task.run();
        final Calendar after = Calendar.getInstance(JobConstants.UTC);
        Assert.assertThat(this.numDeletedJobs.get(), Matchers.is(deletedCount2 + deletedCount3));
        Mockito.verify(this.deletionTimerId, Mockito.times(2)).withTags(MetricsUtils.newSuccessTagsMap());
        Mockito.verify(this.deletionTimer, Mockito.times(2)).record(Mockito.anyLong(),
                Mockito.eq(TimeUnit.NANOSECONDS));

        if (before.get(Calendar.DAY_OF_YEAR) == after.get(Calendar.DAY_OF_YEAR)) {
            Mockito.verify(this.jobPersistenceService, Mockito.times(5)).deleteBatchOfJobsCreatedBeforeDate(
                    argument.capture(), Mockito.eq(maxDeleted), Mockito.eq(pageSize));
            final Calendar date = Calendar.getInstance(JobConstants.UTC);
            date.set(Calendar.HOUR_OF_DAY, 0);
            date.set(Calendar.MINUTE, 0);
            date.set(Calendar.SECOND, 0);
            date.set(Calendar.MILLISECOND, 0);
            date.add(Calendar.DAY_OF_YEAR, negativeDays);
            Assert.assertThat(argument.getAllValues().get(0), Matchers.is(date.getTime()));
            Assert.assertThat(argument.getAllValues().get(1), Matchers.is(date.getTime()));
        }
    }

    /**
     * Make sure the run method throws when an error is encountered.
     */
    @Test(expected = RuntimeException.class)
    public void cantRun() {
        final int days = 5;
        final int negativeDays = -1 * days;
        final int pageSize = 10;
        final int maxDeleted = 10_000;

        Mockito.when(this.cleanupProperties.getRetention()).thenReturn(days).thenReturn(negativeDays);
        Mockito.when(this.cleanupProperties.getPageSize()).thenReturn(pageSize);
        Mockito.when(this.cleanupProperties.getMaxDeletedPerTransaction()).thenReturn(maxDeleted);

        Mockito.when(this.jobPersistenceService.deleteBatchOfJobsCreatedBeforeDate(Mockito.any(Date.class),
                Mockito.anyInt(), Mockito.anyInt())).thenThrow(new RuntimeException("test"));

        try {
            this.task.run();
        } finally {
            Mockito.verify(this.deletionTimerId, Mockito.times(1))
                    .withTags(MetricsUtils.newFailureTagsMapForException(new RuntimeException()));
            Mockito.verify(this.deletionTimer, Mockito.times(1)).record(Mockito.anyLong(),
                    Mockito.eq(TimeUnit.NANOSECONDS));
        }
    }
}