All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.mongock.driver.mongodb.test.template.MongoDriverITestBase Maven / Gradle / Ivy

The newest version!
package io.mongock.driver.mongodb.test.template;


import com.github.cloudyrock.mongock.ChangeSet;
import io.mongock.driver.api.driver.ConnectionDriver;
import io.mongock.driver.api.entry.ChangeState;
import io.mongock.driver.core.driver.ConnectionDriverBase;
import io.mongock.driver.mongodb.test.template.integration.test1.withnorunalways.ChangeLogSuccess;
import io.mongock.driver.mongodb.test.template.integration.test1.withrunalways.WithRunAlways;
import io.mongock.driver.mongodb.test.template.integration.test2.ChangeLogFailure;
import io.mongock.driver.mongodb.test.template.integration.test3.ChangeLogEnsureDecorator;
import io.mongock.driver.mongodb.test.template.util.CallVerifier;
import io.mongock.driver.mongodb.test.template.util.CallVerifierImpl;
import io.mongock.driver.mongodb.test.template.util.IntegrationTestBase;
import io.mongock.driver.mongodb.test.template.util.MongoDBDriverTestAdapter;
import io.mongock.api.exception.MongockException;
import io.mongock.runner.core.executor.MongockRunner;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoDatabase;
import io.mongock.runner.test.TestMongock;
import org.bson.Document;
import org.junit.jupiter.api.Test;

import java.lang.reflect.Method;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.Mockito.mock;

public abstract class MongoDriverITestBase extends IntegrationTestBase {

  protected static final String KEY_EXECUTION_ID = "executionId";
  protected static final String KEY_CHANGE_ID = "changeId";
  protected static final String KEY_AUTHOR = "author";
  protected static final String KEY_STATE = "state";
  protected static final String KEY_TIMESTAMP = "timestamp";
  protected static final String KEY_CHANGE_LOG_CLASS = "changeLogClass";
  protected static final String KEY_CHANGE_SET_METHOD = "changeSetMethod";
  protected static final String KEY_EXECUTION_MILLIS = "executionMillis";
  protected static final String KEY_METADATA = "metadata";
  public static final String ANY_EXECUTION_ID = "any";
  public static final String TEST_USER = "testuser";
  public static final String METHOD_PREFIX = "method_";

  @Test
  public void shouldRunAllChangeLogsSuccessfully() {
    runChangesAndCheck(getDriverWithTransactionDisabled(), ChangeLogSuccess.class, CHANGELOG_COLLECTION_NAME, Collections.emptyList(), false);
  }

  @Test
  public void shouldRegisterChangeSetAsIgnored_WhenAlreadyExecuted_IfNotRunAlways() throws NoSuchMethodException {
    MongoDBDriverTestAdapter adapter = getDefaultAdapter();
    adapter.insertOne(getChangeEntryDocument(ChangeLogSuccess.class.getMethod("method_0"), ChangeState.EXECUTED));
    runChangesAndCheck( getDriverWithTransactionDisabled(),ChangeLogSuccess.class, CHANGELOG_COLLECTION_NAME, Collections.singletonList("method_0"), false);
  }

  @Test
  public void shouldRegisterChangeSetAsExecuted_WhenAlreadyExecuted_IfRunAlways() throws NoSuchMethodException {
    MongoDBDriverTestAdapter adapter = getDefaultAdapter();
    Document changeEntry = getChangeEntryDocument(WithRunAlways.class.getMethod("method_0"), ChangeState.EXECUTED);
    adapter.insertOne(changeEntry);
    String executionId = UUID.randomUUID().toString();
    runChanges(getDriverWithTransactionDisabled(), WithRunAlways.class, executionId);
    checkChanges(WithRunAlways.class, CHANGELOG_COLLECTION_NAME, Collections.singletonList(changeEntry.getString(KEY_CHANGE_ID)), false, executionId);
    MongoCollection collection = this.getDataBase().getCollection(CHANGELOG_COLLECTION_NAME);
    assertNotNull(collection.find(new Document()
        .append(KEY_EXECUTION_ID, ANY_EXECUTION_ID)
        .append(KEY_AUTHOR, TEST_USER)
        .append(KEY_CHANGE_ID, METHOD_PREFIX + "0")).first());
  }

  @Test
  public void shouldRegisterChangeSetAsExecuted_WhenAlreadyIgnored_IfNotRunAlways() throws NoSuchMethodException {
    MongoDBDriverTestAdapter adapter = getDefaultAdapter();
    adapter.insertOne(getChangeEntryDocument(ChangeLogSuccess.class.getMethod("method_0"), ChangeState.IGNORED));
    runChangesAndCheck(getDriverWithTransactionDisabled(), ChangeLogSuccess.class, CHANGELOG_COLLECTION_NAME);
  }

  @Test
  public void shouldRegisterChangeSetAsExecuted_WhenAlreadyIgnored_IfRunAlways() throws NoSuchMethodException {
    MongoDBDriverTestAdapter adapter = getDefaultAdapter();
    adapter.insertOne(getChangeEntryDocument(WithRunAlways.class.getMethod("method_0"), ChangeState.IGNORED));
    runChangesAndCheck(getDriverWithTransactionDisabled(), WithRunAlways.class, CHANGELOG_COLLECTION_NAME);
  }

  @Test
  public void shouldRegisterChangeSetAsExecuted_WhenAlreadyFailed_IfNotRunAlways() throws NoSuchMethodException {
    MongoDBDriverTestAdapter adapter = getDefaultAdapter();
    adapter.insertOne(getChangeEntryDocument(ChangeLogSuccess.class.getMethod("method_0"), ChangeState.FAILED));
    runChangesAndCheck(getDriverWithTransactionDisabled(), ChangeLogSuccess.class, CHANGELOG_COLLECTION_NAME);
  }

  @Test
  public void shouldRegisterChangeSetAsExecuted_WhenAlreadyFailed_IfRunAlways() throws NoSuchMethodException {
    MongoDBDriverTestAdapter adapter = getDefaultAdapter();
    adapter.insertOne(getChangeEntryDocument(WithRunAlways.class.getMethod("method_0"), ChangeState.FAILED));
    runChangesAndCheck(getDriverWithTransactionDisabled(), WithRunAlways.class, CHANGELOG_COLLECTION_NAME);
  }

  @Test
  public void shouldUseDifferentChangeLogCollectionName_whenSettingChangeLogCollectionName() {
    String newChangeLogCollectionName = "newChangeLogCollectionName";
    ConnectionDriverBase driver = getDriverWithTransactionDisabled();
    driver.setChangeLogRepositoryName(newChangeLogCollectionName);
    runChangesAndCheck(driver, ChangeLogSuccess.class, newChangeLogCollectionName, Collections.emptyList(), false);
  }

  private Document getChangeEntryDocument(Method method, ChangeState state) {
    ChangeSet changeSet = method.getAnnotation(ChangeSet.class);
    return new Document()
        .append(KEY_EXECUTION_ID, ANY_EXECUTION_ID)
        .append(KEY_CHANGE_ID, changeSet.id())
        .append(KEY_AUTHOR, changeSet.author())
        .append(KEY_STATE, state.name())
        .append(KEY_TIMESTAMP, new Date())
        .append(KEY_CHANGE_LOG_CLASS, method.getDeclaringClass().getName())
        .append(KEY_CHANGE_SET_METHOD, method.getName())
        .append(KEY_EXECUTION_MILLIS, 0L)
        .append(KEY_METADATA, getStringObjectMap());
  }


  @Test
  public void shouldFail_WhenRunningChangeLog_IfChangeSetIdDuplicated() {
    MongockException ex = assertThrows(MongockException.class, () ->
            TestMongock.builder()
              .setDriver(getDriverWithTransactionDisabled())
              .addChangeLogsScanPackage(ChangeLogFailure.class.getPackage().getName())
              .buildRunner()
              .execute()
    );
    assertEquals("Duplicated changeset id found: 'id_duplicated'", ex.getMessage());
  }

  @Test
  public void shouldPassMongoDatabaseDecoratorToChangeSet() {

    CallVerifierImpl callVerifier = new CallVerifierImpl();
    TestMongock.builder()
        .setDriver(getDriverWithTransactionDisabled())
        .addChangeLogsScanPackage(ChangeLogEnsureDecorator.class.getPackage().getName())
        .addDependency(CallVerifier.class, callVerifier)
        .buildRunner()
        .execute();
    assertEquals(1, callVerifier.getCounter());
  }

  @Test
  public void shouldPrioritizeConnectorOverStandardDependencies_WhenThereIsConflict() {
    CallVerifierImpl callVerifier = new CallVerifierImpl();
    TestMongock.builder()
        .setDriver(getDriverWithTransactionDisabled())
        .addChangeLogsScanPackage(ChangeLogEnsureDecorator.class.getPackage().getName())
        .addDependency(CallVerifier.class, callVerifier)
        .addDependency(MongoDatabase.class, mock(MongoDatabase.class))// shouldn't use this, the one from the connector instead
        .buildRunner()
        .execute();
    assertEquals(1, callVerifier.getCounter());
  }

  @Test
  public void shouldThrowException_WhenNotIndexCreation_IfNotCreatedBefore() {
    // given
    ConnectionDriverBase driver = getDriverWithTransactionDisabled();
    driver.setIndexCreation(false);

    //when
    MongockException ex = assertThrows(MongockException.class, () ->
            TestMongock.builder()
              .setDriver(driver)
              .addChangeLogsScanPackage(ChangeLogSuccess.class.getPackage().getName())
              .buildRunner()
              .execute()
    );
    assertEquals("Index creation not allowed, but not created or wrongly created for collection " + LOCK_COLLECTION_NAME, ex.getMessage());
  }

  @Test
  public void shouldBeOk_WhenNotIndexCreation_IfCreatedBefore() {
    // given
    ConnectionDriverBase driver = getDriverWithTransactionDisabled();
    driver.setIndexCreation(false);
    getAdapter(CHANGELOG_COLLECTION_NAME).createUniqueIndex("executionId", "author", "changeId");
    driver.setLockRepositoryName(LOCK_COLLECTION_NAME);
    getAdapter(LOCK_COLLECTION_NAME).createUniqueIndex("key");

    //when
    TestMongock.builder()
        .setDriver(driver)
        .addChangeLogsScanPackage(ChangeLogSuccess.class.getPackage().getName())
        .buildRunner()
        .execute();
  }


  @Test
  public void shouldThrowException_WhenNotIndexCreation_IfWrongLockIndexCreated() {
    // given
    ConnectionDriverBase driver = getDriverWithTransactionDisabled();
    driver.setIndexCreation(false);
    getAdapter(CHANGELOG_COLLECTION_NAME).createUniqueIndex("executionId", "author", "changeId");
    driver.setLockRepositoryName(LOCK_COLLECTION_NAME);
    getAdapter(LOCK_COLLECTION_NAME).createUniqueIndex("keywrong");

    //when
    MongockException ex = assertThrows(MongockException.class, () ->
            TestMongock.builder()
              .setDriver(driver)
              .addChangeLogsScanPackage(ChangeLogSuccess.class.getPackage().getName())
              .buildRunner()
              .execute()
    );
    assertEquals("Index creation not allowed, but not created or wrongly created for collection " + LOCK_COLLECTION_NAME, ex.getMessage());
  }

  @Test
  public void shouldThrowException_WhenNotIndexCreation_IfChangeLogIndexPartiallyCreated() {
    // given
    ConnectionDriverBase driver = getDriverWithTransactionDisabled();
    driver.setIndexCreation(false);
    getAdapter(CHANGELOG_COLLECTION_NAME).createUniqueIndex("executionId_wrong", "author", "changeId");
    driver.setLockRepositoryName(LOCK_COLLECTION_NAME);
    getAdapter(LOCK_COLLECTION_NAME).createUniqueIndex("key");

    //when
    MongockException ex = assertThrows(MongockException.class, () ->
            TestMongock.builder()
              .setDriver(driver)
              .addChangeLogsScanPackage(ChangeLogSuccess.class.getPackage().getName())
              .buildRunner()
              .execute()
    );
    assertEquals("Index creation not allowed, but not created or wrongly created for collection " + CHANGELOG_COLLECTION_NAME, ex.getMessage());
  }




  private void runChangesAndCheck(ConnectionDriver driver, Class changeLogClass, String changeLogCollectionName) {
    runChangesAndCheck(driver, changeLogClass, changeLogCollectionName, Collections.emptyList(), false);
  }



  private void runChangesAndCheck(ConnectionDriver driver, Class changeLogClass, String chageLogCollectionName, Collection ignoredChangeIds, boolean trackIgnored) {
    String executionId = UUID.randomUUID().toString();

    runChanges(driver, changeLogClass, executionId);

    checkChanges(changeLogClass, chageLogCollectionName, ignoredChangeIds, trackIgnored, executionId);
  }

  private void checkChanges(Class changeLogClass, String chageLogCollectionName, Collection ignoredOrRunAlwaysChangeIds, boolean trackIgnored, String executionId) {
    MongoCollection collection = this.getDataBase().getCollection(chageLogCollectionName);
    for (int i = 0; i < 5; i++) {
      Document change = collection.find(new Document()
          .append(KEY_EXECUTION_ID, executionId)
          .append(KEY_AUTHOR, TEST_USER)
          .append(KEY_CHANGE_ID, "method_" + i)).first();
      if(trackIgnored || !ignoredOrRunAlwaysChangeIds.contains("method_" + i)) {

        String executionIdChange = change.get(KEY_EXECUTION_ID, String.class);
        String changeId = change.get(KEY_CHANGE_ID, String.class);
        String author = change.get(KEY_AUTHOR, String.class);
        String state = change.get(KEY_STATE, String.class);
        Date timestamp = change.get(KEY_TIMESTAMP, Date.class);
        String changeLogClassInstance = change.get(KEY_CHANGE_LOG_CLASS, String.class);
        String changeSetMethod = change.get(KEY_CHANGE_SET_METHOD, String.class);
        Long executionMillis = change.get(KEY_EXECUTION_MILLIS, Long.class);
        Map metadataResult = change.get(KEY_METADATA, Map.class);
        assertNotNull(executionIdChange);
        assertEquals("method_" + i, changeId);
        assertEquals(TEST_USER, author);
        assertEquals((ignoredOrRunAlwaysChangeIds.contains(changeId) ? ChangeState.IGNORED : ChangeState.EXECUTED).name(), state);
        assertNotNull(timestamp);
        assertEquals(changeLogClass.getName(), changeLogClassInstance);
        assertEquals("method_" + i, changeSetMethod);
        assertNotNull(executionMillis);
        checkMetadata(metadataResult);
      }

    }
  }

  private void runChanges(ConnectionDriver driver, Class changeLogClass, String executionId) {
    MongockRunner runner = TestMongock.builder()
        .setDriver(driver)
        .addChangeLogsScanPackage(changeLogClass.getPackage().getName())
        .withMetadata(getStringObjectMap())
        .setExecutionId(executionId)
        .buildRunner();
    runner.execute();
  }


  private Map getStringObjectMap() {
    Map metadata = new HashMap<>();
    metadata.put("string_key", "string_value");
    metadata.put("integer_key", 10);
    metadata.put("float_key", 11.11F);
    metadata.put("double_key", 12.12D);
    metadata.put("long_key", 13L);
    metadata.put("boolean_key", true);
    return metadata;
  }


  private void checkMetadata(Map metadataResult) {
    assertEquals("string_value", metadataResult.get("string_key"));
    assertEquals(10, metadataResult.get("integer_key"));
    assertEquals(11.11F, (Double) metadataResult.get("float_key"), 0.01);
    assertEquals(12.12D, (Double) metadataResult.get("double_key"), 0.01);
    assertEquals(13L, metadataResult.get("long_key"));
    assertEquals(true, metadataResult.get("boolean_key"));
  }

  protected abstract ConnectionDriverBase getDriverWithTransactionDisabled();


}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy