Back to Repositories

Validating Release Service Operations in Apollo Config

This test suite validates the ReleaseService functionality in Apollo, focusing on release management operations including rollback, configuration handling, and release state management. It ensures proper behavior of release-related operations in the Apollo configuration system.

Test Coverage Overview

The test suite provides comprehensive coverage of the ReleaseService component:
  • Release rollback functionality and validation
  • Configuration retrieval and management
  • Release state transitions and abandonment
  • Release search by IDs and keys
  • Edge cases including invalid releases and namespace validation

Implementation Analysis

The testing approach employs JUnit with Mockito for dependency isolation:
  • Mock injection for ReleaseRepository and related services
  • Extensive use of @Mock and @InjectMocks annotations
  • Systematic test setup using @Before initialization
  • Verification of mock interactions and state changes

Technical Details

Testing infrastructure includes:
  • JUnit 4 test framework
  • Mockito mocking framework
  • Spring PageRequest for pagination testing
  • MockBeanFactory for test object creation
  • AbstractUnitTest base class integration

Best Practices Demonstrated

The test suite exemplifies several testing best practices:
  • Proper test isolation using mocks
  • Comprehensive exception testing
  • Clear test method naming conventions
  • Thorough setup and teardown management
  • Validation of both positive and negative scenarios

apolloconfig/apollo

apollo-biz/src/test/java/com/ctrip/framework/apollo/biz/service/ReleaseServiceTest.java

            
/*
 * Copyright 2024 Apollo Authors
 *
 * 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.ctrip.framework.apollo.biz.service;

import com.google.common.collect.Lists;
import com.google.common.collect.Sets;

import com.ctrip.framework.apollo.biz.AbstractUnitTest;
import com.ctrip.framework.apollo.biz.MockBeanFactory;
import com.ctrip.framework.apollo.biz.entity.Release;
import com.ctrip.framework.apollo.biz.repository.ReleaseRepository;
import com.ctrip.framework.apollo.common.exception.BadRequestException;

import java.util.ArrayList;
import java.util.Optional;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.springframework.data.domain.PageRequest;

import java.util.Arrays;
import java.util.List;
import java.util.Set;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

public class ReleaseServiceTest extends AbstractUnitTest {

  @Mock
  private ReleaseRepository releaseRepository;
  @Mock
  private NamespaceService namespaceService;
  @Mock
  private ReleaseHistoryService releaseHistoryService;
  @Mock
  private ItemSetService itemSetService;
  @InjectMocks
  private ReleaseService releaseService;

  private String appId = "appId-test";
  private String clusterName = "cluster-test";
  private String namespaceName = "namespace-test";
  private String user = "user-test";
  private long releaseId = 1;
  private Release firstRelease;
  private Release secondRelease;
  private PageRequest pageRequest;

  @Before
  public void init() {

    firstRelease = new Release();
    firstRelease.setId(releaseId);
    firstRelease.setAppId(appId);
    firstRelease.setClusterName(clusterName);
    firstRelease.setNamespaceName(namespaceName);
    firstRelease.setAbandoned(false);

    secondRelease = new Release();
    secondRelease.setAppId(appId);
    secondRelease.setClusterName(clusterName);
    secondRelease.setNamespaceName(namespaceName);
    secondRelease.setAbandoned(false);

    pageRequest = PageRequest.of(0, 2);
  }

  @Test(expected = BadRequestException.class)
  public void testNamespaceNotExist() {

    when(releaseRepository.findById(releaseId)).thenReturn(Optional.of(firstRelease));

    releaseService.rollback(releaseId, user);
  }

  @Test(expected = BadRequestException.class)
  public void testHasNoRelease() {

    when(releaseRepository.findById(releaseId)).thenReturn(Optional.of(firstRelease));
    when(releaseRepository.findByAppIdAndClusterNameAndNamespaceNameAndIsAbandonedFalseOrderByIdDesc(appId,
                                                                                                     clusterName,
                                                                                                     namespaceName,
                                                                                                     pageRequest))
        .thenReturn(null);

    releaseService.rollback(releaseId, user);
  }

  @Test
  public void testRollback() {

    when(releaseRepository.findById(releaseId)).thenReturn(Optional.of(firstRelease));
    when(releaseRepository.findByAppIdAndClusterNameAndNamespaceNameAndIsAbandonedFalseOrderByIdDesc(appId,
                                                                                                     clusterName,
                                                                                                     namespaceName,
                                                                                                     pageRequest))
        .thenReturn(
            Arrays.asList(firstRelease, secondRelease));

    releaseService.rollback(releaseId, user);

    verify(releaseRepository).save(firstRelease);
    Assert.assertEquals(true, firstRelease.isAbandoned());
    Assert.assertEquals(user, firstRelease.getDataChangeLastModifiedBy());
  }

  @Test
  public void testRollbackTo() {
    List<Release> releaseList = new ArrayList<>();
    for (int i = 0; i < 3; i++) {
      Release release = new Release();
      release.setId(3 - i);
      release.setAppId(appId);
      release.setClusterName(clusterName);
      release.setNamespaceName(namespaceName);
      release.setAbandoned(false);
      releaseList.add(release);
    }
    long releaseId1 = 1;
    long releaseId3 = 3;
    when(releaseRepository.findById(releaseId1)).thenReturn(Optional.of(releaseList.get(2)));
    when(releaseRepository.findById(releaseId3)).thenReturn(Optional.of(releaseList.get(0)));
    when(releaseRepository.findByAppIdAndClusterNameAndNamespaceNameAndIsAbandonedFalseAndIdBetweenOrderByIdDesc(appId,
                                                                                                                 clusterName,
                                                                                                                 namespaceName,
                                                                                                                 releaseId1,
                                                                                                                 releaseId3))
        .thenReturn(releaseList);

    releaseService.rollbackTo(releaseId3, releaseId1, user);

    verify(releaseRepository).saveAll(releaseList);
    Assert.assertTrue(releaseList.get(0).isAbandoned());
    Assert.assertTrue(releaseList.get(1).isAbandoned());
    Assert.assertFalse(releaseList.get(2).isAbandoned());
    Assert.assertEquals(user, releaseList.get(0).getDataChangeLastModifiedBy());
    Assert.assertEquals(user, releaseList.get(1).getDataChangeLastModifiedBy());
  }

  @Test
  public void testFindRelease() throws Exception {
    String someAppId = "1";
    String someClusterName = "someClusterName";
    String someNamespaceName = "someNamespaceName";
    long someReleaseId = 1;
    String someReleaseKey = "someKey";
    String someValidConfiguration = "{\"apollo.bar\": \"foo\"}";

    Release someRelease =
        MockBeanFactory.mockRelease(someReleaseId, someReleaseKey, someAppId, someClusterName,
                        someNamespaceName,
                        someValidConfiguration);

    when(releaseRepository.findFirstByAppIdAndClusterNameAndNamespaceNameAndIsAbandonedFalseOrderByIdDesc(someAppId,
                                                                                                          someClusterName,
                                                                                                          someNamespaceName))
        .thenReturn(someRelease);

    Release result = releaseService.findLatestActiveRelease(someAppId, someClusterName, someNamespaceName);

    verify(releaseRepository, times(1))
        .findFirstByAppIdAndClusterNameAndNamespaceNameAndIsAbandonedFalseOrderByIdDesc(someAppId, someClusterName,
                                                                                        someNamespaceName);
    assertEquals(someAppId, result.getAppId());
    assertEquals(someClusterName, result.getClusterName());
    assertEquals(someReleaseId, result.getId());
    assertEquals(someReleaseKey, result.getReleaseKey());
    assertEquals(someValidConfiguration, result.getConfigurations());
  }

  @Test
  public void testLoadConfigWithConfigNotFound() throws Exception {
    String someAppId = "1";
    String someClusterName = "someClusterName";
    String someNamespaceName = "someNamespaceName";

    when(releaseRepository.findFirstByAppIdAndClusterNameAndNamespaceNameAndIsAbandonedFalseOrderByIdDesc(someAppId,
                                                                                                          someClusterName,
                                                                                                          someNamespaceName))
        .thenReturn(null);

    Release result = releaseService.findLatestActiveRelease(someAppId, someClusterName, someNamespaceName);

    assertNull(result);
    verify(releaseRepository, times(1)).findFirstByAppIdAndClusterNameAndNamespaceNameAndIsAbandonedFalseOrderByIdDesc(
        someAppId, someClusterName, someNamespaceName);
  }

  @Test
  public void testFindByReleaseIds() throws Exception {
    Release someRelease = mock(Release.class);
    Release anotherRelease = mock(Release.class);
    long someReleaseId = 1;
    long anotherReleaseId = 2;
    List<Release> someReleases = Lists.newArrayList(someRelease, anotherRelease);
    Set<Long> someReleaseIds = Sets.newHashSet(someReleaseId, anotherReleaseId);

    when(releaseRepository.findAllById(someReleaseIds)).thenReturn(someReleases);

    List<Release> result = releaseService.findByReleaseIds(someReleaseIds);

    assertEquals(someReleases, result);
  }

  @Test
  public void testFindByReleaseKeys() throws Exception {
    Release someRelease = mock(Release.class);
    Release anotherRelease = mock(Release.class);
    String someReleaseKey = "key1";
    String anotherReleaseKey = "key2";
    List<Release> someReleases = Lists.newArrayList(someRelease, anotherRelease);
    Set<String> someReleaseKeys = Sets.newHashSet(someReleaseKey, anotherReleaseKey);

    when(releaseRepository.findByReleaseKeyIn(someReleaseKeys)).thenReturn(someReleases);

    List<Release> result = releaseService.findByReleaseKeys(someReleaseKeys);

    assertEquals(someReleases, result);
  }


}