From 71fb50427561fd85944879db2b247411b9142bac Mon Sep 17 00:00:00 2001 From: bercianor Date: Fri, 20 Mar 2026 23:06:58 +0000 Subject: [PATCH 1/4] feat: add Couchbase Test Kit --- .github/workflows/release.yml | 14 ++ .../flamingock.project-structure.gradle.kts | 6 +- .../build.gradle.kts | 6 +- .../couchbase/CouchbaseAuditStoreTest.java | 204 ++++++++---------- settings.gradle.kts | 4 + utils/couchbase-test-kit/build.gradle.kts | 15 ++ .../couchbase/kit/CouchbaseAuditStorage.java | 120 +++++++++++ .../couchbase/kit/CouchbaseLockStorage.java | 155 +++++++++++++ .../couchbase/kit/CouchbaseTestKit.java | 81 +++++++ 9 files changed, 483 insertions(+), 122 deletions(-) create mode 100644 utils/couchbase-test-kit/build.gradle.kts create mode 100644 utils/couchbase-test-kit/src/main/java/io/flamingock/couchbase/kit/CouchbaseAuditStorage.java create mode 100644 utils/couchbase-test-kit/src/main/java/io/flamingock/couchbase/kit/CouchbaseLockStorage.java create mode 100644 utils/couchbase-test-kit/src/main/java/io/flamingock/couchbase/kit/CouchbaseTestKit.java diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index c42917db4..a283d79f6 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -448,6 +448,19 @@ jobs: FLAMINGOCK_JRELEASER_GPG_SECRET_KEY: ${{ secrets.FLAMINGOCK_JRELEASER_GPG_SECRET_KEY }} FLAMINGOCK_JRELEASER_GPG_PASSPHRASE: ${{ secrets.FLAMINGOCK_JRELEASER_GPG_PASSPHRASE }} + couchbase-test-kit: + needs: [ build ] + uses: ./.github/workflows/module-release-graalvm.yml + with: + module: couchbase-test-kit + secrets: + FLAMINGOCK_JRELEASER_GITHUB_TOKEN: ${{ secrets.FLAMINGOCK_JRELEASER_GITHUB_TOKEN }} + FLAMINGOCK_JRELEASER_MAVENCENTRAL_USERNAME: ${{ secrets.FLAMINGOCK_JRELEASER_MAVENCENTRAL_USERNAME }} + FLAMINGOCK_JRELEASER_MAVENCENTRAL_PASSWORD: ${{ secrets.FLAMINGOCK_JRELEASER_MAVENCENTRAL_PASSWORD }} + FLAMINGOCK_JRELEASER_GPG_PUBLIC_KEY: ${{ secrets.FLAMINGOCK_JRELEASER_GPG_PUBLIC_KEY }} + FLAMINGOCK_JRELEASER_GPG_SECRET_KEY: ${{ secrets.FLAMINGOCK_JRELEASER_GPG_SECRET_KEY }} + FLAMINGOCK_JRELEASER_GPG_PASSPHRASE: ${{ secrets.FLAMINGOCK_JRELEASER_GPG_PASSPHRASE }} + sql-util: needs: [ build ] uses: ./.github/workflows/module-release-graalvm.yml @@ -557,6 +570,7 @@ jobs: dynamodb-util, dynamodb-test-kit, couchbase-util, + couchbase-test-kit, sql-util, sql-test-kit, mongock-support, diff --git a/buildSrc/src/main/kotlin/flamingock.project-structure.gradle.kts b/buildSrc/src/main/kotlin/flamingock.project-structure.gradle.kts index dbdd50e62..4d54bae90 100644 --- a/buildSrc/src/main/kotlin/flamingock.project-structure.gradle.kts +++ b/buildSrc/src/main/kotlin/flamingock.project-structure.gradle.kts @@ -7,6 +7,8 @@ val coreProjects = setOf( "flamingock-core", "flamingock-core-commons", + "flamingock-core-api", + "flamingock-template-api", "flamingock-processor", "flamingock-graalvm", "flamingock-test-support" @@ -49,6 +51,7 @@ val externalSystemProjects = setOf( ) val utilProjects = setOf( + "general-util", "test-util", "mongodb-util", "dynamodb-util", @@ -66,7 +69,8 @@ val legacyProjects = setOf( val testKitsProjects = setOf( "mongodb-test-kit", "dynamodb-test-kit", - "sql-test-kit" + "sql-test-kit", + "couchbase-test-kit" ) val allProjects = coreProjects + cloudProjects + communityProjects + pluginProjects + targetSystemProjects + externalSystemProjects + utilProjects + legacyProjects + testKitsProjects diff --git a/community/flamingock-auditstore-couchbase/build.gradle.kts b/community/flamingock-auditstore-couchbase/build.gradle.kts index bcefcdd69..5d79e59d6 100644 --- a/community/flamingock-auditstore-couchbase/build.gradle.kts +++ b/community/flamingock-auditstore-couchbase/build.gradle.kts @@ -2,10 +2,12 @@ dependencies { api(project(":core:flamingock-core")) api(project(":core:target-systems:couchbase-external-system-api")) implementation(project(":utils:couchbase-util")) - + compileOnly("com.couchbase.client:java-client:3.6.0") testImplementation(project(":core:target-systems:couchbase-target-system")) + testImplementation(project(":utils:test-util")) + testImplementation(project(":utils:couchbase-test-kit")) testImplementation("org.testcontainers:testcontainers-couchbase:2.0.2") testImplementation("org.testcontainers:testcontainers-junit-jupiter:2.0.2") } @@ -20,4 +22,4 @@ java { configurations.testImplementation { extendsFrom(configurations.compileOnly.get()) -} \ No newline at end of file +} diff --git a/community/flamingock-auditstore-couchbase/src/test/java/io/flamingock/store/couchbase/CouchbaseAuditStoreTest.java b/community/flamingock-auditstore-couchbase/src/test/java/io/flamingock/store/couchbase/CouchbaseAuditStoreTest.java index 3736a90f9..731663e67 100644 --- a/community/flamingock-auditstore-couchbase/src/test/java/io/flamingock/store/couchbase/CouchbaseAuditStoreTest.java +++ b/community/flamingock-auditstore-couchbase/src/test/java/io/flamingock/store/couchbase/CouchbaseAuditStoreTest.java @@ -20,21 +20,14 @@ import com.couchbase.client.java.Cluster; import com.couchbase.client.java.Collection; import com.couchbase.client.java.json.JsonObject; -import io.flamingock.store.couchbase.changes.failedWithoutRollback._001__create_index; -import io.flamingock.store.couchbase.changes.failedWithoutRollback._002__insert_document; -import io.flamingock.store.couchbase.changes.failedWithoutRollback._003__execution_with_exception; -import io.flamingock.store.couchbase.changes.happyPath._003__insert_another_document; +import io.flamingock.common.test.pipeline.CodeChangeTestDefinition; +import io.flamingock.core.kit.audit.AuditTestSupport; +import io.flamingock.couchbase.kit.CouchbaseTestKit; import io.flamingock.targetsystem.couchbase.CouchbaseTargetSystem; -import io.flamingock.internal.common.core.util.Deserializer; -import io.flamingock.internal.common.core.audit.AuditEntry; import io.flamingock.internal.common.couchbase.CouchbaseCollectionHelper; import io.flamingock.internal.core.builder.FlamingockFactory; -import io.flamingock.internal.util.constants.CommunityPersistenceConstants; import io.flamingock.internal.core.operation.OperationException; -import io.flamingock.internal.util.Trio; import org.junit.jupiter.api.*; -import org.mockito.MockedStatic; -import org.mockito.Mockito; import org.testcontainers.couchbase.BucketDefinition; import org.testcontainers.couchbase.CouchbaseContainer; import org.testcontainers.junit.jupiter.Container; @@ -42,8 +35,8 @@ import java.time.Duration; import java.util.Collections; -import java.util.List; +import static io.flamingock.core.kit.audit.AuditEntryExpectation.*; import static org.junit.jupiter.api.Assertions.*; @Testcontainers @@ -53,6 +46,9 @@ class CouchbaseAuditStoreTest { private static Cluster cluster; private static CouchbaseTestHelper couchbaseTestHelper; + private CouchbaseTargetSystem couchbaseTargetSystem; + private CouchbaseAuditStore couchbaseAuditStore; + private CouchbaseTestKit testKit; @Container public static final CouchbaseContainer couchbaseContainer = new CouchbaseContainer("couchbase/server:7.2.4") @@ -71,56 +67,46 @@ static void beforeAll() { @BeforeEach void setupEach() { + couchbaseTargetSystem = new CouchbaseTargetSystem("couchbase", cluster, BUCKET_NAME); + couchbaseAuditStore = CouchbaseAuditStore.from(couchbaseTargetSystem); + testKit = CouchbaseTestKit.create(couchbaseAuditStore, cluster, BUCKET_NAME, CollectionIdentifier.DEFAULT_SCOPE); } @AfterEach void tearDownEach() { - CouchbaseCollectionHelper.deleteAllDocuments(cluster, BUCKET_NAME, CollectionIdentifier.DEFAULT_SCOPE, CollectionIdentifier.DEFAULT_COLLECTION); - CouchbaseCollectionHelper.deleteAllDocuments(cluster, BUCKET_NAME, CollectionIdentifier.DEFAULT_SCOPE, CommunityPersistenceConstants.DEFAULT_AUDIT_STORE_NAME); - CouchbaseCollectionHelper.dropIndexIfExists(cluster, BUCKET_NAME, CollectionIdentifier.DEFAULT_SCOPE, CollectionIdentifier.DEFAULT_COLLECTION, "idx_standalone_index"); + testKit.cleanUp(); } @Test @DisplayName("When standalone runs the AuditStore should persist the audit logs and the test data") void happyPath() { - //Given-When Bucket bucket = cluster.bucket(BUCKET_NAME); Collection testCollection = bucket.defaultCollection(); - CouchbaseTargetSystem couchbaseTargetSystem = new CouchbaseTargetSystem("couchbase", cluster, BUCKET_NAME); - - try (MockedStatic mocked = Mockito.mockStatic(Deserializer.class)) { - mocked.when(Deserializer::readMetadataFromFile).thenReturn(PipelineTestHelper.getPreviewPipeline( - new Trio<>(io.flamingock.store.couchbase.changes.happyPath._001__create_index.class, Collections.singletonList(Collection.class)), - new Trio<>(io.flamingock.store.couchbase.changes.happyPath._002__insert_document.class, Collections.singletonList(Collection.class)), - new Trio<>(_003__insert_another_document.class, Collections.singletonList(Collection.class))) - ); - - FlamingockFactory.getCommunityBuilder() - .setAuditStore(CouchbaseAuditStore.from(couchbaseTargetSystem)) - .addTargetSystem(couchbaseTargetSystem) - .addDependency(testCollection) // for test purpose only - .build() - .run(); - } - - //Then - //Checking auditLog - Collection auditLogCollection = bucket.collection(CommunityPersistenceConstants.DEFAULT_AUDIT_STORE_NAME); - List auditLog = couchbaseTestHelper.getAuditEntriesSorted(auditLogCollection); - assertEquals(6, auditLog.size()); - assertEquals("create-index", auditLog.get(0).getTaskId()); - assertEquals(AuditEntry.Status.STARTED, auditLog.get(0).getState()); - assertEquals("create-index", auditLog.get(1).getTaskId()); - assertEquals(AuditEntry.Status.APPLIED, auditLog.get(1).getState()); - assertEquals("insert-document", auditLog.get(2).getTaskId()); - assertEquals(AuditEntry.Status.STARTED, auditLog.get(2).getState()); - assertEquals("insert-document", auditLog.get(3).getTaskId()); - assertEquals(AuditEntry.Status.APPLIED, auditLog.get(3).getState()); - assertEquals("insert-another-document", auditLog.get(4).getTaskId()); - assertEquals(AuditEntry.Status.STARTED, auditLog.get(4).getState()); - assertEquals("insert-another-document", auditLog.get(5).getTaskId()); - assertEquals(AuditEntry.Status.APPLIED, auditLog.get(5).getState()); + + String[] expectedTaskIds = {"create-index", "insert-document", "insert-another-document"}; + //Given-When-Then + AuditTestSupport.withTestKit(testKit) + .GIVEN_Changes( + new CodeChangeTestDefinition(io.flamingock.store.couchbase.changes.happyPath._001__create_index.class, Collections.singletonList(Collection.class)), + new CodeChangeTestDefinition(io.flamingock.store.couchbase.changes.happyPath._002__insert_document.class, Collections.singletonList(Collection.class)), + new CodeChangeTestDefinition(io.flamingock.store.couchbase.changes.happyPath._003__insert_another_document.class, Collections.singletonList(Collection.class)) + ) + .WHEN(() -> testKit.createBuilder() + .setAuditStore(couchbaseAuditStore) + .addTargetSystem(couchbaseTargetSystem) + .addDependency(testCollection) // for test purpose only + .build() + .run()) + .THEN_VerifyAuditSequenceStrict( + STARTED(expectedTaskIds[0]), + APPLIED(expectedTaskIds[0]), + STARTED(expectedTaskIds[1]), + APPLIED(expectedTaskIds[1]), + STARTED(expectedTaskIds[2]), + APPLIED(expectedTaskIds[2]) + ) + .run(); //Checking created index and documents assertTrue(CouchbaseCollectionHelper.indexExists(cluster, testCollection.bucketName(), testCollection.scopeName(), testCollection.name(), "idx_standalone_index")); @@ -137,47 +123,37 @@ void happyPath() { @Test @DisplayName("When standalone runs the AuditStore and execution fails (with rollback method) should persist all the audit logs up to the failed one (ROLLED_BACK)") void failedWithRollback() { - //Given-When Bucket bucket = cluster.bucket(BUCKET_NAME); Collection testCollection = bucket.defaultCollection(); - CouchbaseTargetSystem couchbaseTargetSystem = new CouchbaseTargetSystem("couchbase", cluster, BUCKET_NAME); - - try (MockedStatic mocked = Mockito.mockStatic(Deserializer.class)) { - mocked.when(Deserializer::readMetadataFromFile).thenReturn(PipelineTestHelper.getPreviewPipeline( - new Trio<>(io.flamingock.store.couchbase.changes.failedWithRollback._001__create_index.class, Collections.singletonList(Collection.class)), - new Trio<>(io.flamingock.store.couchbase.changes.failedWithRollback._002__insert_document.class, Collections.singletonList(Collection.class)), - new Trio<>(io.flamingock.store.couchbase.changes.failedWithRollback._003__execution_with_exception.class, Collections.singletonList(Collection.class), Collections.singletonList(Collection.class))) - ); - - assertThrows(OperationException.class, () -> { - FlamingockFactory.getCommunityBuilder() - .setAuditStore(CouchbaseAuditStore.from(couchbaseTargetSystem)) + + String[] expectedTaskIds = {"create-index", "insert-document", "execution-with-exception"}; + //Given-When-Then + AuditTestSupport.withTestKit(testKit) + .GIVEN_Changes( + new CodeChangeTestDefinition(io.flamingock.store.couchbase.changes.failedWithRollback._001__create_index.class, Collections.singletonList(Collection.class)), + new CodeChangeTestDefinition(io.flamingock.store.couchbase.changes.failedWithRollback._002__insert_document.class, Collections.singletonList(Collection.class)), + new CodeChangeTestDefinition(io.flamingock.store.couchbase.changes.failedWithRollback._003__execution_with_exception.class, Collections.singletonList(Collection.class), Collections.singletonList(Collection.class)) + ) + .WHEN(() -> { + assertThrows(OperationException.class, () -> { + FlamingockFactory.getCommunityBuilder() + .setAuditStore(couchbaseAuditStore) .addTargetSystem(couchbaseTargetSystem) .addDependency(testCollection) // for test purpose only .build() .run(); - }); - } - - //Then - //Checking auditLog - Collection auditLogCollection = bucket.collection(CommunityPersistenceConstants.DEFAULT_AUDIT_STORE_NAME); - List auditLog = couchbaseTestHelper.getAuditEntriesSorted(auditLogCollection); - assertEquals(7, auditLog.size()); - assertEquals("create-index", auditLog.get(0).getTaskId()); - assertEquals(AuditEntry.Status.STARTED, auditLog.get(0).getState()); - assertEquals("create-index", auditLog.get(1).getTaskId()); - assertEquals(AuditEntry.Status.APPLIED, auditLog.get(1).getState()); - assertEquals("insert-document", auditLog.get(2).getTaskId()); - assertEquals(AuditEntry.Status.STARTED, auditLog.get(2).getState()); - assertEquals("insert-document", auditLog.get(3).getTaskId()); - assertEquals(AuditEntry.Status.APPLIED, auditLog.get(3).getState()); - assertEquals("execution-with-exception", auditLog.get(4).getTaskId()); - assertEquals(AuditEntry.Status.STARTED, auditLog.get(4).getState()); - assertEquals("execution-with-exception", auditLog.get(5).getTaskId()); - assertEquals(AuditEntry.Status.FAILED, auditLog.get(5).getState()); - assertEquals("execution-with-exception", auditLog.get(6).getTaskId()); - assertEquals(AuditEntry.Status.ROLLED_BACK, auditLog.get(6).getState()); + }); + }) + .THEN_VerifyAuditSequenceStrict( + STARTED(expectedTaskIds[0]), + APPLIED(expectedTaskIds[0]), + STARTED(expectedTaskIds[1]), + APPLIED(expectedTaskIds[1]), + STARTED(expectedTaskIds[2]), + FAILED(expectedTaskIds[2]), + ROLLED_BACK(expectedTaskIds[2]) + ) + .run(); //Checking created index and documents assertTrue(CouchbaseCollectionHelper.indexExists(cluster, testCollection.bucketName(), testCollection.scopeName(), testCollection.name(), "idx_standalone_index")); @@ -191,47 +167,37 @@ void failedWithRollback() { @Test @DisplayName("When standalone runs the AuditStore and execution fails (without rollback method) should persist all the audit logs up to the failed one (FAILED)") void failedWithoutRollback() { - //Given-When Bucket bucket = cluster.bucket(BUCKET_NAME); Collection testCollection = bucket.defaultCollection(); - CouchbaseTargetSystem couchbaseTargetSystem = new CouchbaseTargetSystem("couchbase", cluster, BUCKET_NAME); - - try (MockedStatic mocked = Mockito.mockStatic(Deserializer.class)) { - mocked.when(Deserializer::readMetadataFromFile).thenReturn(PipelineTestHelper.getPreviewPipeline( - new Trio<>(_001__create_index.class, Collections.singletonList(Collection.class)), - new Trio<>(_002__insert_document.class, Collections.singletonList(Collection.class)), - new Trio<>(_003__execution_with_exception.class, Collections.singletonList(Collection.class))) - ); - - assertThrows(OperationException.class, () -> { - FlamingockFactory.getCommunityBuilder() - .setAuditStore(CouchbaseAuditStore.from(couchbaseTargetSystem)) + + String[] expectedTaskIds = {"create-index", "insert-document", "execution-with-exception"}; + //Given-When-Then + AuditTestSupport.withTestKit(testKit) + .GIVEN_Changes( + new CodeChangeTestDefinition(io.flamingock.store.couchbase.changes.failedWithoutRollback._001__create_index.class, Collections.singletonList(Collection.class)), + new CodeChangeTestDefinition(io.flamingock.store.couchbase.changes.failedWithoutRollback._002__insert_document.class, Collections.singletonList(Collection.class)), + new CodeChangeTestDefinition(io.flamingock.store.couchbase.changes.failedWithoutRollback._003__execution_with_exception.class, Collections.singletonList(Collection.class)) + ) + .WHEN(() -> { + assertThrows(OperationException.class, () -> { + FlamingockFactory.getCommunityBuilder() + .setAuditStore(couchbaseAuditStore) .addTargetSystem(couchbaseTargetSystem) .addDependency(testCollection) // for test purpose only .build() .run(); - }); - } - - //Then - //Checking auditLog - Collection auditLogCollection = bucket.collection(CommunityPersistenceConstants.DEFAULT_AUDIT_STORE_NAME); - List auditLog = couchbaseTestHelper.getAuditEntriesSorted(auditLogCollection); - assertEquals(7, auditLog.size()); - assertEquals("create-index", auditLog.get(0).getTaskId()); - assertEquals(AuditEntry.Status.STARTED, auditLog.get(0).getState()); - assertEquals("create-index", auditLog.get(1).getTaskId()); - assertEquals(AuditEntry.Status.APPLIED, auditLog.get(1).getState()); - assertEquals("insert-document", auditLog.get(2).getTaskId()); - assertEquals(AuditEntry.Status.STARTED, auditLog.get(2).getState()); - assertEquals("insert-document", auditLog.get(3).getTaskId()); - assertEquals(AuditEntry.Status.APPLIED, auditLog.get(3).getState()); - assertEquals("execution-with-exception", auditLog.get(4).getTaskId()); - assertEquals(AuditEntry.Status.STARTED, auditLog.get(4).getState()); - assertEquals("execution-with-exception", auditLog.get(5).getTaskId()); - assertEquals(AuditEntry.Status.FAILED, auditLog.get(5).getState()); - assertEquals("execution-with-exception", auditLog.get(6).getTaskId()); - assertEquals(AuditEntry.Status.ROLLED_BACK, auditLog.get(6).getState()); + }); + }) + .THEN_VerifyAuditSequenceStrict( + STARTED(expectedTaskIds[0]), + APPLIED(expectedTaskIds[0]), + STARTED(expectedTaskIds[1]), + APPLIED(expectedTaskIds[1]), + STARTED(expectedTaskIds[2]), + FAILED(expectedTaskIds[2]), + ROLLED_BACK(expectedTaskIds[2]) + ) + .run(); //Checking created index and documents assertTrue(CouchbaseCollectionHelper.indexExists(cluster, testCollection.bucketName(), testCollection.scopeName(), testCollection.name(), "idx_standalone_index")); diff --git a/settings.gradle.kts b/settings.gradle.kts index b1e25a385..22f5503ab 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -158,6 +158,10 @@ include("utils:couchbase-util") project(":utils:couchbase-util").name = "couchbase-util" project(":utils:couchbase-util").projectDir = file("utils/couchbase-util") +include("utils:couchbase-test-kit") +project(":utils:couchbase-test-kit").name = "couchbase-test-kit" +project(":utils:couchbase-test-kit").projectDir = file("utils/couchbase-test-kit") + include("utils:sql-util") project(":utils:sql-util").name = "sql-util" project(":utils:sql-util").projectDir = file("utils/sql-util") diff --git a/utils/couchbase-test-kit/build.gradle.kts b/utils/couchbase-test-kit/build.gradle.kts new file mode 100644 index 000000000..da206e707 --- /dev/null +++ b/utils/couchbase-test-kit/build.gradle.kts @@ -0,0 +1,15 @@ +dependencies { + implementation(project(":core:flamingock-core")) + implementation(project(":utils:couchbase-util")) + implementation(project(":utils:test-util")) + + compileOnly("com.couchbase.client:java-client:3.6.0") +} + +description = "MongoDB TestKit for Flamingock testing" + +java { + toolchain { + languageVersion.set(JavaLanguageVersion.of(8)) + } +} diff --git a/utils/couchbase-test-kit/src/main/java/io/flamingock/couchbase/kit/CouchbaseAuditStorage.java b/utils/couchbase-test-kit/src/main/java/io/flamingock/couchbase/kit/CouchbaseAuditStorage.java new file mode 100644 index 000000000..eb0edbf7a --- /dev/null +++ b/utils/couchbase-test-kit/src/main/java/io/flamingock/couchbase/kit/CouchbaseAuditStorage.java @@ -0,0 +1,120 @@ +/* + * Copyright 2025 Flamingock (https://www.flamingock.io) + * + * 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 io.flamingock.couchbase.kit; + +import com.couchbase.client.core.error.CouchbaseException; +import com.couchbase.client.core.error.DocumentNotFoundException; +import com.couchbase.client.java.Cluster; +import com.couchbase.client.java.Collection; +import com.couchbase.client.java.json.JsonObject; +import com.couchbase.client.java.json.JsonValue; +import com.couchbase.client.java.kv.GetResult; +import com.couchbase.client.java.kv.PersistTo; +import com.couchbase.client.java.kv.ReplicateTo; +import com.couchbase.client.java.kv.UpsertOptions; +import io.flamingock.core.kit.audit.AuditStorage; +import io.flamingock.internal.common.core.audit.AuditEntry; +import io.flamingock.internal.common.couchbase.CouchbaseAuditMapper; +import io.flamingock.internal.common.couchbase.CouchbaseCollectionHelper; + +import java.util.List; +import java.util.stream.Collectors; + +import static io.flamingock.internal.util.constants.AuditEntryFieldConstants.KEY_CHANGE_ID; +import static io.flamingock.internal.util.constants.AuditEntryFieldConstants.KEY_STATE; +import static io.flamingock.internal.util.constants.CommunityPersistenceConstants.DEFAULT_AUDIT_STORE_NAME; + +/** + * Couchbase implementation of AuditStorage for real database testing. + * Only depends on Couchbase client/database and core Flamingock classes. + * Does not depend on Couchbase-specific Flamingock components like CouchbaseTargetSystem. + */ +public class CouchbaseAuditStorage implements AuditStorage { + + private final Cluster cluster; + private final Collection auditCollection; + + private final CouchbaseAuditMapper mapper = new CouchbaseAuditMapper(); + + public CouchbaseAuditStorage(Cluster cluster, String bucketName, String scopeName) { + this(cluster, bucketName, scopeName, DEFAULT_AUDIT_STORE_NAME); + } + + public CouchbaseAuditStorage(Cluster cluster, String bucketName, String scopeName, String auditCollectionName) { + this.cluster = cluster; + this.auditCollection = cluster.bucket(bucketName).scope(scopeName).collection(auditCollectionName); + } + + @Override + public void addAuditEntry(AuditEntry auditEntry) { + String key = toKey(auditEntry); + + JsonObject document = mapper.toDocument(auditEntry); + + try { + auditCollection.upsert(key, document, + UpsertOptions.upsertOptions().durability(PersistTo.ACTIVE, ReplicateTo.NONE)); + } catch (CouchbaseException couchbaseException) { + throw new RuntimeException(couchbaseException); + } + } + + @Override + public List getAuditEntries() { + return CouchbaseCollectionHelper.selectAllDocuments( + cluster, auditCollection.bucketName(), auditCollection.scopeName(), auditCollection.name()) + .stream() + .map(mapper::fromDocument) + .collect(Collectors.toList()); + } + + @Override + public List getAuditEntriesForChange(String changeId) { + return CouchbaseCollectionHelper.selectAllDocuments( + cluster, auditCollection.bucketName(), auditCollection.scopeName(), auditCollection.name()) + .stream() + .filter(entry -> entry.get(KEY_CHANGE_ID).equals(changeId)) + .map(mapper::fromDocument) + .collect(Collectors.toList()); + } + + @Override + public long countAuditEntriesWithStatus(AuditEntry.Status status) { + return CouchbaseCollectionHelper.selectAllDocuments( + cluster, auditCollection.bucketName(), auditCollection.scopeName(), auditCollection.name()) + .stream() + .filter(entry -> entry.get(KEY_STATE).equals(status)) + .count(); + } + + @Override + public boolean hasAuditEntries() { + return !this.getAuditEntries().isEmpty(); + } + + @Override + public void clear() { + CouchbaseCollectionHelper.deleteAllDocuments(cluster, auditCollection.bucketName(), auditCollection.scopeName(), auditCollection.name()); + } + + private String toKey(AuditEntry auditEntry) { + return auditEntry.getExecutionId() + + '#' + + auditEntry.getTaskId() + + '#' + + auditEntry.getState().name(); + } +} diff --git a/utils/couchbase-test-kit/src/main/java/io/flamingock/couchbase/kit/CouchbaseLockStorage.java b/utils/couchbase-test-kit/src/main/java/io/flamingock/couchbase/kit/CouchbaseLockStorage.java new file mode 100644 index 000000000..9a86d2526 --- /dev/null +++ b/utils/couchbase-test-kit/src/main/java/io/flamingock/couchbase/kit/CouchbaseLockStorage.java @@ -0,0 +1,155 @@ +/* + * Copyright 2025 Flamingock (https://www.flamingock.io) + * + * 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 io.flamingock.couchbase.kit; + +import com.couchbase.client.core.error.DocumentNotFoundException; +import com.couchbase.client.java.Cluster; +import com.couchbase.client.java.Collection; +import com.couchbase.client.java.json.JsonObject; +import com.couchbase.client.java.kv.GetResult; +import com.couchbase.client.java.kv.RemoveOptions; +import com.couchbase.client.java.kv.ReplaceOptions; +import io.flamingock.core.kit.lock.LockStorage; +import io.flamingock.internal.common.couchbase.CouchbaseCollectionHelper; +import io.flamingock.internal.common.couchbase.CouchbaseLockMapper; +import io.flamingock.internal.core.external.store.lock.LockAcquisition; +import io.flamingock.internal.core.external.store.lock.LockKey; +import io.flamingock.internal.core.external.store.lock.LockServiceException; +import io.flamingock.internal.core.external.store.lock.LockStatus; +import io.flamingock.internal.core.external.store.lock.community.CommunityLockEntry; +import io.flamingock.internal.util.TimeService; +import io.flamingock.internal.util.id.RunnerId; + +import java.time.LocalDateTime; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; + +import static io.flamingock.internal.util.constants.CommunityPersistenceConstants.DEFAULT_LOCK_STORE_NAME; + +/** + * Couchbase implementation of LockStorage for real database testing. + * Only depends on Couchbase client/database and core Flamingock classes. + * Does not depend on Couchbase-specific Flamingock components like CouchbaseTargetSystem. + */ +public class CouchbaseLockStorage implements LockStorage { + + private final Cluster cluster; + private final Collection lockCollection; + private final Map metadata = new ConcurrentHashMap<>(); + private final TimeService timeService; + + private final CouchbaseLockMapper mapper = new CouchbaseLockMapper(); + + public CouchbaseLockStorage(Cluster cluster, String bucketName, String scopeName) { + this(cluster, bucketName, scopeName, DEFAULT_LOCK_STORE_NAME); + } + + public CouchbaseLockStorage(Cluster cluster, String bucketName, String scopeName, String lockCollectionName) { + this.cluster = cluster; + this.lockCollection = cluster.bucket(bucketName).scope(scopeName).collection(lockCollectionName); + this.timeService = TimeService.getDefault(); + } + + @Override + public void storeLock(LockKey key, LockAcquisition acquisition) { + CommunityLockEntry newLock = new CommunityLockEntry(key.toString(), LockStatus.LOCK_HELD, acquisition.getOwner().toString(), timeService.currentDatePlusMillis(acquisition.getAcquiredForMillis())); + String keyId = toKey(newLock); + try { + GetResult result = lockCollection.get(keyId); + CommunityLockEntry existingLock = mapper.lockEntryFromDocument(result.contentAsObject()); + if (newLock.getOwner().equals(existingLock.getOwner()) || + LocalDateTime.now().isAfter(existingLock.getExpiresAt())) { + lockCollection.replace(keyId, mapper.toDocument(newLock), ReplaceOptions.replaceOptions().cas(result.cas())); + } else if (LocalDateTime.now().isBefore(existingLock.getExpiresAt())) { + throw new LockServiceException("Get By" + keyId, newLock.toString(), + "Still locked by " + existingLock.getOwner() + " until " + existingLock.getExpiresAt()); + } + } catch (DocumentNotFoundException documentNotFoundException) { + lockCollection.insert(keyId, mapper.toDocument(newLock)); + } + } + + @Override + public LockAcquisition getLock(LockKey lockKey) { + String key = toKey(lockKey); + try { + GetResult result = lockCollection.get(key); + return mapper.lockAcquisitionFromDocument(result.contentAsObject()); + } catch (DocumentNotFoundException documentNotFoundException) { + return null; + } + } + + @Override + public Map getAllLocks() { + Map locks = new HashMap<>(); + return CouchbaseCollectionHelper.selectAllDocuments( + cluster, lockCollection.bucketName(), lockCollection.scopeName(), lockCollection.name()) + .stream() + .collect(Collectors.toMap( + entry -> LockKey.fromString(entry.getString("key")), + this::documentToLockAcquisition + )); + } + + @Override + public void removeLock(LockKey lockKey) { + String key = toKey(lockKey); + try { + GetResult result = lockCollection.get(key); + lockCollection.remove(key, RemoveOptions.removeOptions().cas(result.cas())); + } catch (DocumentNotFoundException documentNotFoundException) { + // Lock for key is not found, nothing to do + } + } + + @Override + public boolean hasLocks() { + return !this.getAllLocks().isEmpty(); + } + + @Override + public void clear() { + CouchbaseCollectionHelper.deleteAllDocuments(cluster, lockCollection.bucketName(), lockCollection.scopeName(), lockCollection.name()); + metadata.clear(); + } + + @Override + public void setMetadata(String key, Object value) { + metadata.put(key, value); + } + + @Override + public Object getMetadata(String key) { + return metadata.get(key); + } + + private String toKey(CommunityLockEntry lockEntry) { + return lockEntry.getKey(); + } + + private String toKey(LockKey lockKey) { + return lockKey.toString(); + } + + private LockAcquisition documentToLockAcquisition(JsonObject doc) { + RunnerId owner = RunnerId.fromString(doc.getString("owner")); + long leaseMillis = doc.getLong("leaseMillis"); + + return new LockAcquisition(owner, leaseMillis); + } +} diff --git a/utils/couchbase-test-kit/src/main/java/io/flamingock/couchbase/kit/CouchbaseTestKit.java b/utils/couchbase-test-kit/src/main/java/io/flamingock/couchbase/kit/CouchbaseTestKit.java new file mode 100644 index 000000000..f703e0223 --- /dev/null +++ b/utils/couchbase-test-kit/src/main/java/io/flamingock/couchbase/kit/CouchbaseTestKit.java @@ -0,0 +1,81 @@ +/* + * Copyright 2025 Flamingock (https://www.flamingock.io) + * + * 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 io.flamingock.couchbase.kit; + +import com.couchbase.client.core.io.CollectionIdentifier; +import com.couchbase.client.java.Cluster; +import io.flamingock.core.kit.AbstractTestKit; +import io.flamingock.core.kit.audit.AuditStorage; +import io.flamingock.core.kit.lock.LockStorage; +import io.flamingock.internal.common.couchbase.CouchbaseCollectionHelper; +import io.flamingock.internal.core.external.store.CommunityAuditStore; +import io.flamingock.internal.util.constants.CommunityPersistenceConstants; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +public class CouchbaseTestKit extends AbstractTestKit { + + private final Cluster cluster; + private final String bucketName; + private final String scopeName; + + public CouchbaseTestKit(AuditStorage auditStorage, LockStorage lockStorage, CommunityAuditStore AuditStore, Cluster cluster, String bucketName, String scopeName) { + super(auditStorage, lockStorage, AuditStore); + this.cluster = cluster; + this.bucketName = bucketName; + this.scopeName = scopeName; + } + + @Override + public void cleanUp() { + cluster.bucket(bucketName).collections().getAllScopes().stream() + .filter(scopeSpec -> scopeSpec.name().equals(scopeName)) + .flatMap(scopeSpec -> scopeSpec.collections().stream()) + .forEach(collectionSpec -> { + CouchbaseCollectionHelper.deleteAllDocuments(cluster, bucketName, scopeName, collectionSpec.name()); + cluster.bucket(bucketName).scope(scopeName).collection(collectionSpec.name()) + .queryIndexes().getAllIndexes().stream() + .filter(index -> !index.primary()) + .forEach(index -> + CouchbaseCollectionHelper.dropIndexIfExists(cluster, bucketName, scopeName, collectionSpec.name(), index.name()) + ); + if (!collectionSpec.name().equals(CollectionIdentifier.DEFAULT_COLLECTION)) { + CouchbaseCollectionHelper.dropPrimaryIndexIfExists(cluster, bucketName, scopeName, collectionSpec.name()); + CouchbaseCollectionHelper.dropCollectionIfExists(cluster, bucketName, scopeName, collectionSpec.name()); + } + }); + } + + /** + * Create a new CouchbaseTestKit with Couchbase cluster and bucketName + */ + public static CouchbaseTestKit create(CommunityAuditStore AuditStore, Cluster cluster, String bucketName) { + CouchbaseAuditStorage auditStorage = new CouchbaseAuditStorage(cluster, bucketName, CollectionIdentifier.DEFAULT_SCOPE); + CouchbaseLockStorage lockStorage = new CouchbaseLockStorage(cluster, bucketName, CollectionIdentifier.DEFAULT_SCOPE); + return new CouchbaseTestKit(auditStorage, lockStorage, AuditStore, cluster, bucketName, CollectionIdentifier.DEFAULT_SCOPE); + } + + /** + * Create a new CouchbaseTestKit with Couchbase cluster, bucketName and scopeName + */ + public static CouchbaseTestKit create(CommunityAuditStore AuditStore, Cluster cluster, String bucketName, String scopeName) { + CouchbaseAuditStorage auditStorage = new CouchbaseAuditStorage(cluster, bucketName, scopeName); + CouchbaseLockStorage lockStorage = new CouchbaseLockStorage(cluster, bucketName, scopeName); + return new CouchbaseTestKit(auditStorage, lockStorage, AuditStore, cluster, bucketName, scopeName); + } +} From c5275bfb993fafc189f383d0a85bbae60f12babc Mon Sep 17 00:00:00 2001 From: bercianor Date: Sat, 21 Mar 2026 17:08:40 +0000 Subject: [PATCH 2/4] fix: PR comments --- .../flamingock/store/couchbase/CouchbaseAuditStoreTest.java | 4 ++-- .../io/flamingock/couchbase/kit/CouchbaseAuditStorage.java | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/community/flamingock-auditstore-couchbase/src/test/java/io/flamingock/store/couchbase/CouchbaseAuditStoreTest.java b/community/flamingock-auditstore-couchbase/src/test/java/io/flamingock/store/couchbase/CouchbaseAuditStoreTest.java index 731663e67..ff9409058 100644 --- a/community/flamingock-auditstore-couchbase/src/test/java/io/flamingock/store/couchbase/CouchbaseAuditStoreTest.java +++ b/community/flamingock-auditstore-couchbase/src/test/java/io/flamingock/store/couchbase/CouchbaseAuditStoreTest.java @@ -136,7 +136,7 @@ void failedWithRollback() { ) .WHEN(() -> { assertThrows(OperationException.class, () -> { - FlamingockFactory.getCommunityBuilder() + testKit.createBuilder() .setAuditStore(couchbaseAuditStore) .addTargetSystem(couchbaseTargetSystem) .addDependency(testCollection) // for test purpose only @@ -180,7 +180,7 @@ void failedWithoutRollback() { ) .WHEN(() -> { assertThrows(OperationException.class, () -> { - FlamingockFactory.getCommunityBuilder() + testKit.createBuilder() .setAuditStore(couchbaseAuditStore) .addTargetSystem(couchbaseTargetSystem) .addDependency(testCollection) // for test purpose only diff --git a/utils/couchbase-test-kit/src/main/java/io/flamingock/couchbase/kit/CouchbaseAuditStorage.java b/utils/couchbase-test-kit/src/main/java/io/flamingock/couchbase/kit/CouchbaseAuditStorage.java index eb0edbf7a..f79337907 100644 --- a/utils/couchbase-test-kit/src/main/java/io/flamingock/couchbase/kit/CouchbaseAuditStorage.java +++ b/utils/couchbase-test-kit/src/main/java/io/flamingock/couchbase/kit/CouchbaseAuditStorage.java @@ -86,7 +86,7 @@ public List getAuditEntriesForChange(String changeId) { return CouchbaseCollectionHelper.selectAllDocuments( cluster, auditCollection.bucketName(), auditCollection.scopeName(), auditCollection.name()) .stream() - .filter(entry -> entry.get(KEY_CHANGE_ID).equals(changeId)) + .filter(entry -> entry.getString(KEY_CHANGE_ID).equals(changeId)) .map(mapper::fromDocument) .collect(Collectors.toList()); } @@ -96,7 +96,7 @@ public long countAuditEntriesWithStatus(AuditEntry.Status status) { return CouchbaseCollectionHelper.selectAllDocuments( cluster, auditCollection.bucketName(), auditCollection.scopeName(), auditCollection.name()) .stream() - .filter(entry -> entry.get(KEY_STATE).equals(status)) + .filter(entry -> entry.getString(KEY_STATE).equals(status.name())) .count(); } From 7b74755b2b35c368de2495bdcd7f3a72b07b9d16 Mon Sep 17 00:00:00 2001 From: bercianor Date: Sat, 21 Mar 2026 22:19:47 +0000 Subject: [PATCH 3/4] fix: update project description --- utils/couchbase-test-kit/build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/couchbase-test-kit/build.gradle.kts b/utils/couchbase-test-kit/build.gradle.kts index da206e707..3ac64dccc 100644 --- a/utils/couchbase-test-kit/build.gradle.kts +++ b/utils/couchbase-test-kit/build.gradle.kts @@ -6,7 +6,7 @@ dependencies { compileOnly("com.couchbase.client:java-client:3.6.0") } -description = "MongoDB TestKit for Flamingock testing" +description = "Couchbase TestKit for Flamingock testing" java { toolchain { From d5fb115acdf68651ec5ccdc112d6d867f9d91967 Mon Sep 17 00:00:00 2001 From: bercianor Date: Sat, 21 Mar 2026 22:26:27 +0000 Subject: [PATCH 4/4] fix: update legacy importer test with test kit --- .../couchbase/CouchbaseAuditStoreTest.java | 1 - .../build.gradle.kts | 3 +- .../couchbase/CouchbaseImporterTest.java | 320 +++++++----------- 3 files changed, 122 insertions(+), 202 deletions(-) diff --git a/community/flamingock-auditstore-couchbase/src/test/java/io/flamingock/store/couchbase/CouchbaseAuditStoreTest.java b/community/flamingock-auditstore-couchbase/src/test/java/io/flamingock/store/couchbase/CouchbaseAuditStoreTest.java index ff9409058..c844a7084 100644 --- a/community/flamingock-auditstore-couchbase/src/test/java/io/flamingock/store/couchbase/CouchbaseAuditStoreTest.java +++ b/community/flamingock-auditstore-couchbase/src/test/java/io/flamingock/store/couchbase/CouchbaseAuditStoreTest.java @@ -25,7 +25,6 @@ import io.flamingock.couchbase.kit.CouchbaseTestKit; import io.flamingock.targetsystem.couchbase.CouchbaseTargetSystem; import io.flamingock.internal.common.couchbase.CouchbaseCollectionHelper; -import io.flamingock.internal.core.builder.FlamingockFactory; import io.flamingock.internal.core.operation.OperationException; import org.junit.jupiter.api.*; import org.testcontainers.couchbase.BucketDefinition; diff --git a/legacy/mongock-importer-couchbase/build.gradle.kts b/legacy/mongock-importer-couchbase/build.gradle.kts index 81479c3f3..b7c270e5a 100644 --- a/legacy/mongock-importer-couchbase/build.gradle.kts +++ b/legacy/mongock-importer-couchbase/build.gradle.kts @@ -14,6 +14,7 @@ dependencies { testImplementation(project(":community:flamingock-auditstore-couchbase")) testImplementation(project(":utils:couchbase-util")) testImplementation(project(":utils:test-util")) + testImplementation(project(":utils:couchbase-test-kit")) testImplementation("org.testcontainers:testcontainers-couchbase:2.0.2") testImplementation("org.testcontainers:testcontainers-junit-jupiter:2.0.2") @@ -41,4 +42,4 @@ tasks.withType().configureEach { } configurations.testImplementation { extendsFrom(configurations.compileOnly.get()) -} \ No newline at end of file +} diff --git a/legacy/mongock-importer-couchbase/src/test/java/io/flamingock/importer/mongock/couchbase/CouchbaseImporterTest.java b/legacy/mongock-importer-couchbase/src/test/java/io/flamingock/importer/mongock/couchbase/CouchbaseImporterTest.java index 6d6ffc035..312d4b62c 100644 --- a/legacy/mongock-importer-couchbase/src/test/java/io/flamingock/importer/mongock/couchbase/CouchbaseImporterTest.java +++ b/legacy/mongock-importer-couchbase/src/test/java/io/flamingock/importer/mongock/couchbase/CouchbaseImporterTest.java @@ -23,6 +23,9 @@ import com.couchbase.client.java.manager.bucket.BucketSettings; import io.flamingock.api.annotations.EnableFlamingock; import io.flamingock.api.annotations.Stage; +import io.flamingock.core.kit.TestKit; +import io.flamingock.core.kit.audit.AuditTestHelper; +import io.flamingock.couchbase.kit.CouchbaseTestKit; import io.flamingock.internal.common.core.audit.AuditEntry; import io.flamingock.store.couchbase.CouchbaseAuditStore; import io.flamingock.internal.common.core.error.FlamingockException; @@ -46,6 +49,8 @@ import java.util.List; import java.util.stream.Collectors; +import static io.flamingock.core.kit.audit.AuditEntryExpectation.APPLIED; +import static io.flamingock.core.kit.audit.AuditEntryExpectation.STARTED; import static io.flamingock.internal.common.core.metadata.Constants.MONGOCK_IMPORT_EMPTY_ORIGIN_ALLOWED_PROPERTY_KEY; import static io.flamingock.internal.common.core.metadata.Constants.MONGOCK_IMPORT_ORIGIN_PROPERTY_KEY; import static io.flamingock.internal.common.core.metadata.Constants.MONGOCK_IMPORT_SKIP_PROPERTY_KEY; @@ -76,6 +81,10 @@ public class CouchbaseImporterTest { .withBucket(new org.testcontainers.couchbase.BucketDefinition(FLAMINGOCK_BUCKET_NAME)); private static Cluster cluster; + private static CouchbaseTargetSystem targetSystem; + private static CouchbaseAuditStore auditStore; + private static TestKit testKit; + private static AuditTestHelper auditHelper; @BeforeAll static void setupAll() { @@ -90,6 +99,14 @@ static void setupAll() { // Setup Mongock Bucket, Scope and Collection BucketManager bucketManager = cluster.buckets(); + targetSystem = new CouchbaseTargetSystem("couchbase-target-system", cluster, FLAMINGOCK_BUCKET_NAME); + auditStore = CouchbaseAuditStore.from(targetSystem) + .withScopeName(FLAMINGOCK_SCOPE_NAME) + .withAuditRepositoryName(FLAMINGOCK_COLLECTION_NAME); + + testKit = CouchbaseTestKit.create(auditStore, cluster, FLAMINGOCK_BUCKET_NAME, FLAMINGOCK_SCOPE_NAME); + auditHelper = testKit.getAuditHelper(); + int ramQuotaMB = 100; if (!bucketManager.getAllBuckets().containsKey(MONGOCK_BUCKET_NAME)) { @@ -127,38 +144,27 @@ void GIVEN_allMongockChangeUnitsAlreadyExecuted_WHEN_migratingToFlamingockCommun originCollection.upsert("mongock-change-1", createAuditObject("mongock-change-1")); originCollection.upsert("mongock-change-2", createAuditObject("mongock-change-2")); - CouchbaseTargetSystem targetSystem = new CouchbaseTargetSystem("couchbase-target-system", cluster, FLAMINGOCK_BUCKET_NAME); - - Runner flamingock = FlamingockFactory.getCommunityBuilder() - .setAuditStore(CouchbaseAuditStore.from(targetSystem) - .withScopeName(FLAMINGOCK_SCOPE_NAME) - .withAuditRepositoryName(FLAMINGOCK_COLLECTION_NAME)) + Runner flamingock = testKit.createBuilder() + .setAuditStore(auditStore) .addTargetSystem(targetSystem) .build(); flamingock.run(); - List auditLog = getAuditLog(); - - assertEquals(6, auditLog.size()); - - assertEquals("mongock-change-1", auditLog.get(0).getString("changeId")); - assertEquals("APPLIED", auditLog.get(0).getString("state")); - - assertEquals("mongock-change-2", auditLog.get(1).getString("changeId")); - assertEquals("APPLIED", auditLog.get(1).getString("state")); - - assertEquals("migration-mongock-to-flamingock-community", auditLog.get(2).getString("changeId")); - assertEquals("STARTED", auditLog.get(2).getString("state")); + auditHelper.verifyAuditSequenceStrict( + // Legacy imports from Mongock (APPLIED only - no STARTED for imported changes) + APPLIED("mongock-change-1"), + APPLIED("mongock-change-2"), - assertEquals("migration-mongock-to-flamingock-community", auditLog.get(3).getString("changeId")); - assertEquals("APPLIED", auditLog.get(3).getString("state")); + // System stage - actual system importer change + STARTED("migration-mongock-to-flamingock-community"), + APPLIED("migration-mongock-to-flamingock-community"), - assertEquals("flamingock-change", auditLog.get(4).getString("changeId")); - assertEquals("STARTED", auditLog.get(4).getString("state")); + // Application stage - new changes + STARTED("flamingock-change"), + APPLIED("flamingock-change") + ); - assertEquals("flamingock-change", auditLog.get(5).getString("changeId")); - assertEquals("APPLIED", auditLog.get(5).getString("state")); } @Test @@ -173,41 +179,28 @@ void GIVEN_someChangeUnitsAlreadyExecuted_WHEN_migratingToFlamingockCommunity_TH originCollection.upsert("mongock-change-1", createAuditObject("mongock-change-1")); - CouchbaseTargetSystem targetSystem = new CouchbaseTargetSystem("couchbase-target-system", cluster, FLAMINGOCK_BUCKET_NAME); - - Runner flamingock = FlamingockFactory.getCommunityBuilder() - .setAuditStore(CouchbaseAuditStore.from(targetSystem) - .withScopeName(FLAMINGOCK_SCOPE_NAME) - .withAuditRepositoryName(FLAMINGOCK_COLLECTION_NAME)) + Runner flamingock = testKit.createBuilder() + .setAuditStore(auditStore) .addTargetSystem(targetSystem) .build(); flamingock.run(); - List auditLog = getAuditLog(); - - assertEquals(7, auditLog.size()); - - assertEquals("mongock-change-1", auditLog.get(0).getString("changeId")); - assertEquals("APPLIED", auditLog.get(0).getString("state")); - - assertEquals("migration-mongock-to-flamingock-community", auditLog.get(1).getString("changeId")); - assertEquals("STARTED", auditLog.get(1).getString("state")); - - assertEquals("migration-mongock-to-flamingock-community", auditLog.get(2).getString("changeId")); - assertEquals("APPLIED", auditLog.get(2).getString("state")); + auditHelper.verifyAuditSequenceStrict( + // Legacy imports from Mongock (APPLIED only - no STARTED for imported changes) + APPLIED("mongock-change-1"), - assertEquals("mongock-change-2", auditLog.get(3).getString("changeId")); - assertEquals("STARTED", auditLog.get(3).getString("state")); + // System stage - actual system importer change + STARTED("migration-mongock-to-flamingock-community"), + APPLIED("migration-mongock-to-flamingock-community"), - assertEquals("mongock-change-2", auditLog.get(4).getString("changeId")); - assertEquals("APPLIED", auditLog.get(4).getString("state")); - - assertEquals("flamingock-change", auditLog.get(5).getString("changeId")); - assertEquals("STARTED", auditLog.get(5).getString("state")); + // Application stage - new changes + STARTED("mongock-change-2"), + APPLIED("mongock-change-2"), + STARTED("flamingock-change"), + APPLIED("flamingock-change") + ); - assertEquals("flamingock-change", auditLog.get(6).getString("changeId")); - assertEquals("APPLIED", auditLog.get(6).getString("state")); } @Test @@ -217,12 +210,8 @@ void GIVEN_someChangeUnitsAlreadyExecuted_WHEN_migratingToFlamingockCommunity_TH "THEN should throw exception") void GIVEN_mongockAuditHistoryEmptyAndNoFailIfEmptyOriginValueProvided_WHEN_migratingToFlamingockCommunity_THEN_shouldThrowException() { - CouchbaseTargetSystem targetSystem = new CouchbaseTargetSystem("couchbase-target-system", cluster, FLAMINGOCK_BUCKET_NAME); - - Runner flamingock = FlamingockFactory.getCommunityBuilder() - .setAuditStore(CouchbaseAuditStore.from(targetSystem) - .withScopeName(FLAMINGOCK_SCOPE_NAME) - .withAuditRepositoryName(FLAMINGOCK_COLLECTION_NAME)) + Runner flamingock = testKit.createBuilder() + .setAuditStore(auditStore) .addTargetSystem(targetSystem) .build(); @@ -238,12 +227,8 @@ void GIVEN_mongockAuditHistoryEmptyAndNoFailIfEmptyOriginValueProvided_WHEN_migr "THEN should throw exception") void GIVEN_mongockAuditHistoryEmptyAndFailIfEmptyOriginEnabled_WHEN_migratingToFlamingockCommunity_THEN_shouldThrowException() { - CouchbaseTargetSystem targetSystem = new CouchbaseTargetSystem("couchbase-target-system", cluster, FLAMINGOCK_BUCKET_NAME); - - Runner flamingock = FlamingockFactory.getCommunityBuilder() - .setAuditStore(CouchbaseAuditStore.from(targetSystem) - .withScopeName(FLAMINGOCK_SCOPE_NAME) - .withAuditRepositoryName(FLAMINGOCK_COLLECTION_NAME)) + Runner flamingock = testKit.createBuilder() + .setAuditStore(auditStore) .addTargetSystem(targetSystem) .setProperty(MONGOCK_IMPORT_EMPTY_ORIGIN_ALLOWED_PROPERTY_KEY, Boolean.FALSE.toString()) .build(); @@ -263,44 +248,27 @@ void GIVEN_mongockAuditHistoryEmptyAndFailIfEmptyOriginDisabled_WHEN_migratingTo CouchbaseTargetSystem targetSystem = new CouchbaseTargetSystem("couchbase-target-system", cluster, FLAMINGOCK_BUCKET_NAME); - Runner flamingock = FlamingockFactory.getCommunityBuilder() - .setAuditStore(CouchbaseAuditStore.from(targetSystem) - .withScopeName(FLAMINGOCK_SCOPE_NAME) - .withAuditRepositoryName(FLAMINGOCK_COLLECTION_NAME)) + Runner flamingock = testKit.createBuilder() + .setAuditStore(auditStore) .addTargetSystem(targetSystem) .setProperty(MONGOCK_IMPORT_EMPTY_ORIGIN_ALLOWED_PROPERTY_KEY, Boolean.TRUE.toString()) .build(); flamingock.run(); - - List auditLog = getAuditLog(); - - assertEquals(8, auditLog.size()); - - assertEquals("migration-mongock-to-flamingock-community", auditLog.get(0).getString("changeId")); - assertEquals("STARTED", auditLog.get(0).getString("state")); - - assertEquals("migration-mongock-to-flamingock-community", auditLog.get(1).getString("changeId")); - assertEquals("APPLIED", auditLog.get(1).getString("state")); - - assertEquals("mongock-change-1", auditLog.get(2).getString("changeId")); - assertEquals("STARTED", auditLog.get(2).getString("state")); - - assertEquals("mongock-change-1", auditLog.get(3).getString("changeId")); - assertEquals("APPLIED", auditLog.get(3).getString("state")); - - assertEquals("mongock-change-2", auditLog.get(4).getString("changeId")); - assertEquals("STARTED", auditLog.get(4).getString("state")); - - assertEquals("mongock-change-2", auditLog.get(5).getString("changeId")); - assertEquals("APPLIED", auditLog.get(5).getString("state")); - - assertEquals("flamingock-change", auditLog.get(6).getString("changeId")); - assertEquals("STARTED", auditLog.get(6).getString("state")); - - assertEquals("flamingock-change", auditLog.get(7).getString("changeId")); - assertEquals("APPLIED", auditLog.get(7).getString("state")); + auditHelper.verifyAuditSequenceStrict( + // System stage - actual system importer change + STARTED("migration-mongock-to-flamingock-community"), + APPLIED("migration-mongock-to-flamingock-community"), + + // Application stage - new changes + STARTED("mongock-change-1"), + APPLIED("mongock-change-1"), + STARTED("mongock-change-2"), + APPLIED("mongock-change-2"), + STARTED("flamingock-change"), + APPLIED("flamingock-change") + ); } @@ -324,42 +292,29 @@ void GIVEN_allMongockChangeUnitsAlreadyExecutedAndCustomOriginProvided_WHEN_migr CouchbaseTargetSystem targetSystem = new CouchbaseTargetSystem("couchbase-target-system", cluster, FLAMINGOCK_BUCKET_NAME); - Runner flamingock = FlamingockFactory.getCommunityBuilder() - .setAuditStore(CouchbaseAuditStore.from(targetSystem) - .withScopeName(FLAMINGOCK_SCOPE_NAME) - .withAuditRepositoryName(FLAMINGOCK_COLLECTION_NAME)) + Runner flamingock = testKit.createBuilder() + .setAuditStore(auditStore) .addTargetSystem(targetSystem) .setProperty(MONGOCK_IMPORT_ORIGIN_PROPERTY_KEY, customMongockOrigin) .build(); flamingock.run(); + auditHelper.verifyAuditSequenceStrict( + // Legacy imports from Mongock (APPLIED only - no STARTED for imported changes) + APPLIED("mongock-change-1"), + // System stage - actual system importer change + STARTED("migration-mongock-to-flamingock-community"), + APPLIED("migration-mongock-to-flamingock-community"), - List auditLog = getAuditLog(); - - assertEquals(7, auditLog.size()); - - assertEquals("mongock-change-1", auditLog.get(0).getString("changeId")); - assertEquals("APPLIED", auditLog.get(0).getString("state")); - - assertEquals("migration-mongock-to-flamingock-community", auditLog.get(1).getString("changeId")); - assertEquals("STARTED", auditLog.get(1).getString("state")); - - assertEquals("migration-mongock-to-flamingock-community", auditLog.get(2).getString("changeId")); - assertEquals("APPLIED", auditLog.get(2).getString("state")); - - assertEquals("mongock-change-2", auditLog.get(3).getString("changeId")); - assertEquals("STARTED", auditLog.get(3).getString("state")); - - assertEquals("mongock-change-2", auditLog.get(4).getString("changeId")); - assertEquals("APPLIED", auditLog.get(4).getString("state")); - - assertEquals("flamingock-change", auditLog.get(5).getString("changeId")); - assertEquals("STARTED", auditLog.get(5).getString("state")); + // Application stage - new changes + STARTED("mongock-change-2"), + APPLIED("mongock-change-2"), + STARTED("flamingock-change"), + APPLIED("flamingock-change") + ); - assertEquals("flamingock-change", auditLog.get(6).getString("changeId")); - assertEquals("APPLIED", auditLog.get(6).getString("state")); } @Test @@ -372,10 +327,8 @@ void GIVEN_skipImportFlagWithInvalidValue_WHEN_migratingToFlamingockCommunity_TH CouchbaseTargetSystem targetSystem = new CouchbaseTargetSystem("couchbase-target-system", cluster, FLAMINGOCK_BUCKET_NAME); - Runner flamingock = FlamingockFactory.getCommunityBuilder() - .setAuditStore(CouchbaseAuditStore.from(targetSystem) - .withScopeName(FLAMINGOCK_SCOPE_NAME) - .withAuditRepositoryName(FLAMINGOCK_COLLECTION_NAME)) + Runner flamingock = testKit.createBuilder() + .setAuditStore(auditStore) .addTargetSystem(targetSystem) .setProperty(MONGOCK_IMPORT_SKIP_PROPERTY_KEY, SKIP_IMPORT_VALUE) // only allows empty / true / false .build(); @@ -402,43 +355,28 @@ void GIVEN_skipImportFlagEnabled_WHEN_migratingToFlamingockCommunity_THEN_should CouchbaseTargetSystem targetSystem = new CouchbaseTargetSystem("couchbase-target-system", cluster, FLAMINGOCK_BUCKET_NAME); - Runner flamingock = FlamingockFactory.getCommunityBuilder() - .setAuditStore(CouchbaseAuditStore.from(targetSystem) - .withScopeName(FLAMINGOCK_SCOPE_NAME) - .withAuditRepositoryName(FLAMINGOCK_COLLECTION_NAME)) + Runner flamingock = testKit.createBuilder() + .setAuditStore(auditStore) .addTargetSystem(targetSystem) .setProperty(MONGOCK_IMPORT_SKIP_PROPERTY_KEY, SKIP_IMPORT_VALUE) // only allows empty / true / false .build(); flamingock.run(); - List auditLog = getAuditLog(); - - assertEquals(8, auditLog.size()); - - assertEquals("migration-mongock-to-flamingock-community", auditLog.get(0).getString("changeId")); - assertEquals("STARTED", auditLog.get(0).getString("state")); - - assertEquals("migration-mongock-to-flamingock-community", auditLog.get(1).getString("changeId")); - assertEquals("APPLIED", auditLog.get(1).getString("state")); - - assertEquals("mongock-change-1", auditLog.get(2).getString("changeId")); - assertEquals("STARTED", auditLog.get(2).getString("state")); - - assertEquals("mongock-change-1", auditLog.get(3).getString("changeId")); - assertEquals("APPLIED", auditLog.get(3).getString("state")); - - assertEquals("mongock-change-2", auditLog.get(4).getString("changeId")); - assertEquals("STARTED", auditLog.get(4).getString("state")); - - assertEquals("mongock-change-2", auditLog.get(5).getString("changeId")); - assertEquals("APPLIED", auditLog.get(5).getString("state")); - - assertEquals("flamingock-change", auditLog.get(6).getString("changeId")); - assertEquals("STARTED", auditLog.get(6).getString("state")); + auditHelper.verifyAuditSequenceStrict( + // System stage - actual system importer change + STARTED("migration-mongock-to-flamingock-community"), + APPLIED("migration-mongock-to-flamingock-community"), + + // Application stage - new changes + STARTED("mongock-change-1"), + APPLIED("mongock-change-1"), + STARTED("mongock-change-2"), + APPLIED("mongock-change-2"), + STARTED("flamingock-change"), + APPLIED("flamingock-change") + ); - assertEquals("flamingock-change", auditLog.get(7).getString("changeId")); - assertEquals("APPLIED", auditLog.get(7).getString("state")); } @Test @@ -457,37 +395,28 @@ void GIVEN_allMongockChangeUnitsAlreadyExecutedAndSkipImportFlagDisabledExplicit final String SKIP_IMPORT_VALUE = "false"; - Runner flamingock = FlamingockFactory.getCommunityBuilder() - .setAuditStore(CouchbaseAuditStore.from(targetSystem) - .withScopeName(FLAMINGOCK_SCOPE_NAME) - .withAuditRepositoryName(FLAMINGOCK_COLLECTION_NAME)) + Runner flamingock = testKit.createBuilder() + .setAuditStore(auditStore) .addTargetSystem(targetSystem) .setProperty(MONGOCK_IMPORT_SKIP_PROPERTY_KEY, SKIP_IMPORT_VALUE) // only allows empty / true / false .build(); flamingock.run(); - List auditLog = getAuditLog(); - - assertEquals(6, auditLog.size()); - - assertEquals("mongock-change-1", auditLog.get(0).getString("changeId")); - assertEquals("APPLIED", auditLog.get(0).getString("state")); - - assertEquals("mongock-change-2", auditLog.get(1).getString("changeId")); - assertEquals("APPLIED", auditLog.get(1).getString("state")); + auditHelper.verifyAuditSequenceStrict( + // Legacy imports from Mongock (APPLIED only - no STARTED for imported changes) + APPLIED("mongock-change-1"), + APPLIED("mongock-change-2"), - assertEquals("migration-mongock-to-flamingock-community", auditLog.get(2).getString("changeId")); - assertEquals("STARTED", auditLog.get(2).getString("state")); + // System stage - actual system importer change + STARTED("migration-mongock-to-flamingock-community"), + APPLIED("migration-mongock-to-flamingock-community"), - assertEquals("migration-mongock-to-flamingock-community", auditLog.get(3).getString("changeId")); - assertEquals("APPLIED", auditLog.get(3).getString("state")); - - assertEquals("flamingock-change", auditLog.get(4).getString("changeId")); - assertEquals("STARTED", auditLog.get(4).getString("state")); + // Application stage - new changes + STARTED("flamingock-change"), + APPLIED("flamingock-change") + ); - assertEquals("flamingock-change", auditLog.get(5).getString("changeId")); - assertEquals("APPLIED", auditLog.get(5).getString("state")); } @Test @@ -506,37 +435,28 @@ void GIVEN_allMongockChangeUnitsAlreadyExecutedAndSkipImportFlagDisabledImplicit final String SKIP_IMPORT_VALUE = ""; - Runner flamingock = FlamingockFactory.getCommunityBuilder() - .setAuditStore(CouchbaseAuditStore.from(targetSystem) - .withScopeName(FLAMINGOCK_SCOPE_NAME) - .withAuditRepositoryName(FLAMINGOCK_COLLECTION_NAME)) + Runner flamingock = testKit.createBuilder() + .setAuditStore(auditStore) .addTargetSystem(targetSystem) .setProperty(MONGOCK_IMPORT_SKIP_PROPERTY_KEY, SKIP_IMPORT_VALUE) // only allows empty / true / false .build(); flamingock.run(); - List auditLog = getAuditLog(); - - assertEquals(6, auditLog.size()); - - assertEquals("mongock-change-1", auditLog.get(0).getString("changeId")); - assertEquals("APPLIED", auditLog.get(0).getString("state")); + auditHelper.verifyAuditSequenceStrict( + // Legacy imports from Mongock (APPLIED only - no STARTED for imported changes) + APPLIED("mongock-change-1"), + APPLIED("mongock-change-2"), - assertEquals("mongock-change-2", auditLog.get(1).getString("changeId")); - assertEquals("APPLIED", auditLog.get(1).getString("state")); + // System stage - actual system importer change + STARTED("migration-mongock-to-flamingock-community"), + APPLIED("migration-mongock-to-flamingock-community"), - assertEquals("migration-mongock-to-flamingock-community", auditLog.get(2).getString("changeId")); - assertEquals("STARTED", auditLog.get(2).getString("state")); - - assertEquals("migration-mongock-to-flamingock-community", auditLog.get(3).getString("changeId")); - assertEquals("APPLIED", auditLog.get(3).getString("state")); - - assertEquals("flamingock-change", auditLog.get(4).getString("changeId")); - assertEquals("STARTED", auditLog.get(4).getString("state")); + // Application stage - new changes + STARTED("flamingock-change"), + APPLIED("flamingock-change") + ); - assertEquals("flamingock-change", auditLog.get(5).getString("changeId")); - assertEquals("APPLIED", auditLog.get(5).getString("state")); }