From f3899200b0653dd40705efebad1145aef7aca37f Mon Sep 17 00:00:00 2001 From: Sarah Lander <70885646+salander85@users.noreply.github.com> Date: Mon, 23 Oct 2023 10:42:22 +0200 Subject: [PATCH] Migrate SyncerFactory and CliRunner (#496) * Migrate SyncerFactory * Migrate client utils and rename * Migrate CliRunner * Migrate unittests for SyncerFactory * Migrate unittests for CLIRunner * Cleanup test code * Add final modifier Co-authored-by: Lam Tran --------- Co-authored-by: Lam Tran --- ...CategorySyncWithReferenceResolutionIT.java | 4 +- .../project/sync/CliRunnerIT.java | 4 +- .../sync/InventorySyncMultiChannelIT.java | 4 +- .../sync/ProductSyncWithDiscountedPrice.java | 4 +- .../ProductSyncWithMasterVariantSwitchIT.java | 4 +- .../ProductSyncWithNestedReferencesIT.java | 4 +- .../ProductSyncWithReferenceResolutionIT.java | 4 +- .../sync/ProductSyncWithReferencesIT.java | 4 +- .../sync/util/IntegrationTestUtils.java | 4 +- .../commercetools/project/sync/CliRunner.java | 2 +- .../project/sync/SyncerApplication.java | 4 +- .../project/sync/SyncerFactory.java | 57 +- .../project/sync/util/CtpClientUtils.java | 156 + .../project/sync/util/SphereClientUtils.java | 52 - .../ctp.credentials.properties.skeleton | 2 +- .../project/sync/CliRunnerTest.java | 1789 ++++++------ .../project/sync/SyncerFactoryTest.java | 2518 +++++++++-------- .../project/sync/util/TestUtils.java | 55 +- 18 files changed, 2334 insertions(+), 2337 deletions(-) create mode 100644 src/main/java/com/commercetools/project/sync/util/CtpClientUtils.java delete mode 100644 src/main/java/com/commercetools/project/sync/util/SphereClientUtils.java diff --git a/src/integration-test/java/com/commercetools/project/sync/CategorySyncWithReferenceResolutionIT.java b/src/integration-test/java/com/commercetools/project/sync/CategorySyncWithReferenceResolutionIT.java index fdab1105..c6aa072b 100644 --- a/src/integration-test/java/com/commercetools/project/sync/CategorySyncWithReferenceResolutionIT.java +++ b/src/integration-test/java/com/commercetools/project/sync/CategorySyncWithReferenceResolutionIT.java @@ -1,10 +1,10 @@ package com.commercetools.project.sync; +import static com.commercetools.project.sync.util.CtpClientUtils.CTP_SOURCE_CLIENT; +import static com.commercetools.project.sync.util.CtpClientUtils.CTP_TARGET_CLIENT; import static com.commercetools.project.sync.util.IntegrationTestUtils.assertCategoryExists; import static com.commercetools.project.sync.util.IntegrationTestUtils.cleanUpProjects; import static com.commercetools.project.sync.util.IntegrationTestUtils.createITSyncerFactory; -import static com.commercetools.project.sync.util.SphereClientUtils.CTP_SOURCE_CLIENT; -import static com.commercetools.project.sync.util.SphereClientUtils.CTP_TARGET_CLIENT; import static io.sphere.sdk.models.LocalizedString.ofEnglish; import static java.util.Arrays.asList; import static java.util.Collections.emptyList; diff --git a/src/integration-test/java/com/commercetools/project/sync/CliRunnerIT.java b/src/integration-test/java/com/commercetools/project/sync/CliRunnerIT.java index 2ed2f8b7..cf2ef8a4 100644 --- a/src/integration-test/java/com/commercetools/project/sync/CliRunnerIT.java +++ b/src/integration-test/java/com/commercetools/project/sync/CliRunnerIT.java @@ -1,14 +1,14 @@ package com.commercetools.project.sync; import static com.commercetools.project.sync.service.impl.CustomObjectServiceImpl.TIMESTAMP_GENERATOR_KEY; +import static com.commercetools.project.sync.util.CtpClientUtils.CTP_SOURCE_CLIENT; +import static com.commercetools.project.sync.util.CtpClientUtils.CTP_TARGET_CLIENT; import static com.commercetools.project.sync.util.IntegrationTestUtils.assertCategoryExists; import static com.commercetools.project.sync.util.IntegrationTestUtils.assertProductExists; import static com.commercetools.project.sync.util.IntegrationTestUtils.assertProductTypeExists; import static com.commercetools.project.sync.util.IntegrationTestUtils.cleanUpProjects; import static com.commercetools.project.sync.util.IntegrationTestUtils.createITSyncerFactory; import static com.commercetools.project.sync.util.QueryUtils.queryAndExecute; -import static com.commercetools.project.sync.util.SphereClientUtils.CTP_SOURCE_CLIENT; -import static com.commercetools.project.sync.util.SphereClientUtils.CTP_TARGET_CLIENT; import static com.commercetools.project.sync.util.SyncUtils.APPLICATION_DEFAULT_NAME; import static com.commercetools.project.sync.util.SyncUtils.DEFAULT_RUNNER_NAME; import static com.commercetools.project.sync.util.TestUtils.assertCartDiscountSyncerLoggingEvents; diff --git a/src/integration-test/java/com/commercetools/project/sync/InventorySyncMultiChannelIT.java b/src/integration-test/java/com/commercetools/project/sync/InventorySyncMultiChannelIT.java index eaa1ea8c..5ab9d2c7 100644 --- a/src/integration-test/java/com/commercetools/project/sync/InventorySyncMultiChannelIT.java +++ b/src/integration-test/java/com/commercetools/project/sync/InventorySyncMultiChannelIT.java @@ -1,9 +1,9 @@ package com.commercetools.project.sync; +import static com.commercetools.project.sync.util.CtpClientUtils.CTP_SOURCE_CLIENT; +import static com.commercetools.project.sync.util.CtpClientUtils.CTP_TARGET_CLIENT; import static com.commercetools.project.sync.util.IntegrationTestUtils.cleanUpProjects; import static com.commercetools.project.sync.util.IntegrationTestUtils.createITSyncerFactory; -import static com.commercetools.project.sync.util.SphereClientUtils.CTP_SOURCE_CLIENT; -import static com.commercetools.project.sync.util.SphereClientUtils.CTP_TARGET_CLIENT; import static java.util.Collections.singletonList; import static org.assertj.core.api.Assertions.assertThat; diff --git a/src/integration-test/java/com/commercetools/project/sync/ProductSyncWithDiscountedPrice.java b/src/integration-test/java/com/commercetools/project/sync/ProductSyncWithDiscountedPrice.java index 85516e6b..bfae1407 100644 --- a/src/integration-test/java/com/commercetools/project/sync/ProductSyncWithDiscountedPrice.java +++ b/src/integration-test/java/com/commercetools/project/sync/ProductSyncWithDiscountedPrice.java @@ -1,9 +1,9 @@ package com.commercetools.project.sync; +import static com.commercetools.project.sync.util.CtpClientUtils.CTP_SOURCE_CLIENT; +import static com.commercetools.project.sync.util.CtpClientUtils.CTP_TARGET_CLIENT; import static com.commercetools.project.sync.util.IntegrationTestUtils.cleanUpProjects; import static com.commercetools.project.sync.util.IntegrationTestUtils.createITSyncerFactory; -import static com.commercetools.project.sync.util.SphereClientUtils.CTP_SOURCE_CLIENT; -import static com.commercetools.project.sync.util.SphereClientUtils.CTP_TARGET_CLIENT; import static com.neovisionaries.i18n.CountryCode.DE; import static io.sphere.sdk.models.DefaultCurrencyUnits.EUR; import static io.sphere.sdk.models.LocalizedString.ofEnglish; diff --git a/src/integration-test/java/com/commercetools/project/sync/ProductSyncWithMasterVariantSwitchIT.java b/src/integration-test/java/com/commercetools/project/sync/ProductSyncWithMasterVariantSwitchIT.java index 7b3e2308..6fed98fb 100644 --- a/src/integration-test/java/com/commercetools/project/sync/ProductSyncWithMasterVariantSwitchIT.java +++ b/src/integration-test/java/com/commercetools/project/sync/ProductSyncWithMasterVariantSwitchIT.java @@ -1,9 +1,9 @@ package com.commercetools.project.sync; +import static com.commercetools.project.sync.util.CtpClientUtils.CTP_SOURCE_CLIENT; +import static com.commercetools.project.sync.util.CtpClientUtils.CTP_TARGET_CLIENT; import static com.commercetools.project.sync.util.IntegrationTestUtils.cleanUpProjects; import static com.commercetools.project.sync.util.IntegrationTestUtils.createITSyncerFactory; -import static com.commercetools.project.sync.util.SphereClientUtils.CTP_SOURCE_CLIENT; -import static com.commercetools.project.sync.util.SphereClientUtils.CTP_TARGET_CLIENT; import static io.sphere.sdk.models.LocalizedString.ofEnglish; import static org.assertj.core.api.Assertions.assertThat; diff --git a/src/integration-test/java/com/commercetools/project/sync/ProductSyncWithNestedReferencesIT.java b/src/integration-test/java/com/commercetools/project/sync/ProductSyncWithNestedReferencesIT.java index 86bc1666..62c626a9 100644 --- a/src/integration-test/java/com/commercetools/project/sync/ProductSyncWithNestedReferencesIT.java +++ b/src/integration-test/java/com/commercetools/project/sync/ProductSyncWithNestedReferencesIT.java @@ -1,5 +1,7 @@ package com.commercetools.project.sync; +import static com.commercetools.project.sync.util.CtpClientUtils.CTP_SOURCE_CLIENT; +import static com.commercetools.project.sync.util.CtpClientUtils.CTP_TARGET_CLIENT; import static com.commercetools.project.sync.util.IntegrationTestUtils.assertCategoryExists; import static com.commercetools.project.sync.util.IntegrationTestUtils.assertCustomerExists; import static com.commercetools.project.sync.util.IntegrationTestUtils.assertProductTypeExists; @@ -8,8 +10,6 @@ import static com.commercetools.project.sync.util.IntegrationTestUtils.createAttributeObject; import static com.commercetools.project.sync.util.IntegrationTestUtils.createITSyncerFactory; import static com.commercetools.project.sync.util.IntegrationTestUtils.createReferenceObject; -import static com.commercetools.project.sync.util.SphereClientUtils.CTP_SOURCE_CLIENT; -import static com.commercetools.project.sync.util.SphereClientUtils.CTP_TARGET_CLIENT; import static com.commercetools.project.sync.util.TestUtils.assertCartDiscountSyncerLoggingEvents; import static com.commercetools.project.sync.util.TestUtils.assertCategorySyncerLoggingEvents; import static com.commercetools.project.sync.util.TestUtils.assertCustomObjectSyncerLoggingEvents; diff --git a/src/integration-test/java/com/commercetools/project/sync/ProductSyncWithReferenceResolutionIT.java b/src/integration-test/java/com/commercetools/project/sync/ProductSyncWithReferenceResolutionIT.java index 5766fbc6..cf03f1ac 100644 --- a/src/integration-test/java/com/commercetools/project/sync/ProductSyncWithReferenceResolutionIT.java +++ b/src/integration-test/java/com/commercetools/project/sync/ProductSyncWithReferenceResolutionIT.java @@ -1,12 +1,12 @@ package com.commercetools.project.sync; +import static com.commercetools.project.sync.util.CtpClientUtils.CTP_SOURCE_CLIENT; +import static com.commercetools.project.sync.util.CtpClientUtils.CTP_TARGET_CLIENT; import static com.commercetools.project.sync.util.IntegrationTestUtils.assertCategoryExists; import static com.commercetools.project.sync.util.IntegrationTestUtils.assertProductExists; import static com.commercetools.project.sync.util.IntegrationTestUtils.assertProductTypeExists; import static com.commercetools.project.sync.util.IntegrationTestUtils.cleanUpProjects; import static com.commercetools.project.sync.util.IntegrationTestUtils.createITSyncerFactory; -import static com.commercetools.project.sync.util.SphereClientUtils.CTP_SOURCE_CLIENT; -import static com.commercetools.project.sync.util.SphereClientUtils.CTP_TARGET_CLIENT; import static com.neovisionaries.i18n.CountryCode.DE; import static io.sphere.sdk.models.DefaultCurrencyUnits.EUR; import static io.sphere.sdk.models.LocalizedString.ofEnglish; diff --git a/src/integration-test/java/com/commercetools/project/sync/ProductSyncWithReferencesIT.java b/src/integration-test/java/com/commercetools/project/sync/ProductSyncWithReferencesIT.java index 08015ba4..87d33306 100644 --- a/src/integration-test/java/com/commercetools/project/sync/ProductSyncWithReferencesIT.java +++ b/src/integration-test/java/com/commercetools/project/sync/ProductSyncWithReferencesIT.java @@ -1,11 +1,11 @@ package com.commercetools.project.sync; +import static com.commercetools.project.sync.util.CtpClientUtils.CTP_SOURCE_CLIENT; +import static com.commercetools.project.sync.util.CtpClientUtils.CTP_TARGET_CLIENT; import static com.commercetools.project.sync.util.IntegrationTestUtils.assertProductTypeExists; import static com.commercetools.project.sync.util.IntegrationTestUtils.cleanUpProjects; import static com.commercetools.project.sync.util.IntegrationTestUtils.createITSyncerFactory; import static com.commercetools.project.sync.util.IntegrationTestUtils.createReferenceObject; -import static com.commercetools.project.sync.util.SphereClientUtils.CTP_SOURCE_CLIENT; -import static com.commercetools.project.sync.util.SphereClientUtils.CTP_TARGET_CLIENT; import static com.commercetools.project.sync.util.TestUtils.assertCartDiscountSyncerLoggingEvents; import static com.commercetools.project.sync.util.TestUtils.assertCategorySyncerLoggingEvents; import static com.commercetools.project.sync.util.TestUtils.assertCustomObjectSyncerLoggingEvents; diff --git a/src/integration-test/java/com/commercetools/project/sync/util/IntegrationTestUtils.java b/src/integration-test/java/com/commercetools/project/sync/util/IntegrationTestUtils.java index c24cab53..5dbbcf5e 100644 --- a/src/integration-test/java/com/commercetools/project/sync/util/IntegrationTestUtils.java +++ b/src/integration-test/java/com/commercetools/project/sync/util/IntegrationTestUtils.java @@ -1,9 +1,9 @@ package com.commercetools.project.sync.util; import static com.commercetools.project.sync.service.impl.CustomObjectServiceImpl.TIMESTAMP_GENERATOR_KEY; +import static com.commercetools.project.sync.util.CtpClientUtils.CTP_SOURCE_CLIENT; +import static com.commercetools.project.sync.util.CtpClientUtils.CTP_TARGET_CLIENT; import static com.commercetools.project.sync.util.QueryUtils.queryAndExecute; -import static com.commercetools.project.sync.util.SphereClientUtils.CTP_SOURCE_CLIENT; -import static com.commercetools.project.sync.util.SphereClientUtils.CTP_TARGET_CLIENT; import static java.lang.String.format; import static org.assertj.core.api.Assertions.assertThat; diff --git a/src/main/java/com/commercetools/project/sync/CliRunner.java b/src/main/java/com/commercetools/project/sync/CliRunner.java index 14406af8..f5cb9aa0 100644 --- a/src/main/java/com/commercetools/project/sync/CliRunner.java +++ b/src/main/java/com/commercetools/project/sync/CliRunner.java @@ -3,7 +3,7 @@ import static com.commercetools.project.sync.model.ProductSyncCustomRequest.parseProductQueryParametersOption; import static com.commercetools.project.sync.util.SyncUtils.getApplicationName; import static com.commercetools.project.sync.util.SyncUtils.getApplicationVersion; -import static io.sphere.sdk.utils.CompletableFutureUtils.exceptionallyCompletedFuture; +import static io.vrap.rmf.base.client.utils.CompletableFutureUtils.exceptionallyCompletedFuture; import static java.lang.String.format; import com.commercetools.project.sync.exception.CliException; diff --git a/src/main/java/com/commercetools/project/sync/SyncerApplication.java b/src/main/java/com/commercetools/project/sync/SyncerApplication.java index 40284fa5..a424cd3d 100644 --- a/src/main/java/com/commercetools/project/sync/SyncerApplication.java +++ b/src/main/java/com/commercetools/project/sync/SyncerApplication.java @@ -1,7 +1,7 @@ package com.commercetools.project.sync; -import static com.commercetools.project.sync.util.SphereClientUtils.CTP_SOURCE_CLIENT; -import static com.commercetools.project.sync.util.SphereClientUtils.CTP_TARGET_CLIENT; +import static com.commercetools.project.sync.util.CtpClientUtils.CTP_SOURCE_CLIENT; +import static com.commercetools.project.sync.util.CtpClientUtils.CTP_TARGET_CLIENT; import java.time.Clock; diff --git a/src/main/java/com/commercetools/project/sync/SyncerFactory.java b/src/main/java/com/commercetools/project/sync/SyncerFactory.java index 6057e267..7610d27d 100644 --- a/src/main/java/com/commercetools/project/sync/SyncerFactory.java +++ b/src/main/java/com/commercetools/project/sync/SyncerFactory.java @@ -4,10 +4,15 @@ import static com.commercetools.project.sync.CliRunner.SYNC_MODULE_OPTION_DESCRIPTION; import static com.commercetools.project.sync.CliRunner.SYNC_MODULE_OPTION_LONG; import static com.commercetools.project.sync.CliRunner.SYNC_MODULE_OPTION_SHORT; -import static io.sphere.sdk.utils.CompletableFutureUtils.exceptionallyCompletedFuture; +import static io.vrap.rmf.base.client.utils.CompletableFutureUtils.exceptionallyCompletedFuture; import static java.lang.String.format; import static org.apache.commons.lang3.StringUtils.isBlank; +import com.commercetools.api.client.ProjectApiRoot; +import com.commercetools.api.models.PagedQueryResourceRequest; +import com.commercetools.api.models.ResourcePagedQueryResponse; +import com.commercetools.api.models.ResourceUpdateAction; +import com.commercetools.api.models.common.BaseResource; import com.commercetools.project.sync.cartdiscount.CartDiscountSyncer; import com.commercetools.project.sync.category.CategorySyncer; import com.commercetools.project.sync.customer.CustomerSyncer; @@ -24,10 +29,6 @@ import com.commercetools.sync.commons.BaseSync; import com.commercetools.sync.commons.BaseSyncOptions; import com.commercetools.sync.commons.helpers.BaseSyncStatistics; -import io.sphere.sdk.client.SphereClient; -import io.sphere.sdk.models.ResourceView; -import io.sphere.sdk.models.Versioned; -import io.sphere.sdk.queries.QueryDsl; import java.time.Clock; import java.util.ArrayList; import java.util.Arrays; @@ -43,14 +44,14 @@ import javax.annotation.Nullable; public final class SyncerFactory { - private final Supplier targetClientSupplier; - private final Supplier sourceClientSupplier; + private final Supplier targetClientSupplier; + private final Supplier sourceClientSupplier; private final Clock clock; private final boolean shouldCloseClients; private SyncerFactory( - @Nonnull final Supplier sourceClient, - @Nonnull final Supplier targetClient, + @Nonnull final Supplier sourceClient, + @Nonnull final Supplier targetClient, @Nonnull final Clock clock, final boolean closeClients) { this.targetClientSupplier = targetClient; @@ -61,16 +62,16 @@ private SyncerFactory( @Nonnull public static SyncerFactory of( - @Nonnull final Supplier sourceClient, - @Nonnull final Supplier targetClient, + @Nonnull final Supplier sourceClient, + @Nonnull final Supplier targetClient, @Nonnull final Clock clock) { return new SyncerFactory(sourceClient, targetClient, clock, true); } @Nonnull public static SyncerFactory of( - @Nonnull final Supplier sourceClient, - @Nonnull final Supplier targetClient, + @Nonnull final Supplier sourceClient, + @Nonnull final Supplier targetClient, @Nonnull final Clock clock, final boolean closeClients) { return new SyncerFactory(sourceClient, targetClient, clock, closeClients); @@ -127,14 +128,14 @@ private CompletableFuture chainSyncExecution( for (SyncModuleOption syncOptionValue : syncOptions) { Syncer< - ? extends ResourceView, - ? extends ResourceView, + ? extends BaseResource, + ? extends ResourceUpdateAction, ?, - ? extends Versioned, ? extends BaseSyncStatistics, ? extends BaseSyncOptions, - ? extends QueryDsl, - ? extends BaseSync> + ? extends PagedQueryResourceRequest, + ? extends ResourcePagedQueryResponse, + ? extends BaseSync> syncer = buildSyncer( syncOptionValue, @@ -286,14 +287,14 @@ private void closeClients() { * @return The instance of the syncer corresponding to the passed option value. */ private Syncer< - ? extends ResourceView, - ? extends ResourceView, + ? extends BaseResource, + ? extends ResourceUpdateAction, ?, - ? extends Versioned, ? extends BaseSyncStatistics, ? extends BaseSyncOptions, - ? extends QueryDsl, - ? extends BaseSync> + ? extends PagedQueryResourceRequest, + ? extends ResourcePagedQueryResponse, + ? extends BaseSync> buildSyncer( @Nonnull final SyncModuleOption syncModuleOption, @Nonnull final String runnerNameOptionValue, @@ -301,14 +302,14 @@ private void closeClients() { @Nullable final ProductSyncCustomRequest productSyncCustomRequest) { Syncer< - ? extends ResourceView, - ? extends ResourceView, + ? extends BaseResource, + ? extends ResourceUpdateAction, ?, - ? extends Versioned, ? extends BaseSyncStatistics, ? extends BaseSyncOptions, - ? extends QueryDsl, - ? extends BaseSync> + ? extends PagedQueryResourceRequest, + ? extends ResourcePagedQueryResponse, + ? extends BaseSync> syncer = null; switch (syncModuleOption) { diff --git a/src/main/java/com/commercetools/project/sync/util/CtpClientUtils.java b/src/main/java/com/commercetools/project/sync/util/CtpClientUtils.java new file mode 100644 index 00000000..d304e6c9 --- /dev/null +++ b/src/main/java/com/commercetools/project/sync/util/CtpClientUtils.java @@ -0,0 +1,156 @@ +package com.commercetools.project.sync.util; + +import static java.lang.String.format; + +import com.commercetools.api.client.ProjectApiRoot; +import com.commercetools.api.defaultconfig.ServiceRegion; +import com.commercetools.sync.commons.utils.ClientConfigurationUtils; +import io.vrap.rmf.base.client.oauth2.ClientCredentials; +import java.io.InputStream; +import java.util.InvalidPropertiesFormatException; +import java.util.Properties; +import javax.annotation.Nonnull; + +public final class CtpClientUtils { + private static final String CTP_CREDENTIALS_PROPERTIES = "ctp.credentials.properties"; + public static final String PROPERTIES_KEY_API_URL_SUFFIX = "apiUrl"; + public static final String PROPERTIES_KEY_AUTH_URL_SUFFIX = "authUrl"; + public static final String PROPERTIES_KEY_PROJECT_KEY_SUFFIX = "projectKey"; + public static final String PROPERTIES_KEY_CLIENT_ID_SUFFIX = "clientId"; + public static final String PROPERTIES_KEY_CLIENT_SECRET_SUFFIX = "clientSecret"; + public static final String PROPERTIES_KEY_SCOPES_SUFFIX = "scopes"; + + public static final ProjectApiRoot CTP_SOURCE_CLIENT = getCtpSourceClient(); + public static final ProjectApiRoot CTP_TARGET_CLIENT = getCtpTargetClient(); + + private static ProjectApiRoot getCtpSourceClient() { + return getCtpClient("source."); + } + + private static ProjectApiRoot getCtpTargetClient() { + return getCtpClient("target."); + } + + private static ProjectApiRoot getCtpClient(@Nonnull final String propertiesPrefix) { + try { + InputStream propStream = + CtpClientUtils.class.getClassLoader().getResourceAsStream(CTP_CREDENTIALS_PROPERTIES); + Properties properties = new Properties(); + if (propStream != null) { + properties.load(propStream); + } + if (properties.isEmpty()) { + properties = loadFromEnvVars(propertiesPrefix); + } + if (properties.isEmpty()) { + throw new InvalidPropertiesFormatException("Please provide CTP credentials for running project sync."); + } + + final String projectKey = + extract(properties, propertiesPrefix, PROPERTIES_KEY_PROJECT_KEY_SUFFIX); + final String clientId = + extract(properties, propertiesPrefix, PROPERTIES_KEY_CLIENT_ID_SUFFIX); + final String clientSecret = + extract(properties, propertiesPrefix, PROPERTIES_KEY_CLIENT_SECRET_SUFFIX); + final String apiUrl = + extract( + properties, + propertiesPrefix, + PROPERTIES_KEY_API_URL_SUFFIX, + ServiceRegion.GCP_EUROPE_WEST1.getApiUrl()); + + final String authUrl = + extract( + properties, + propertiesPrefix, + PROPERTIES_KEY_AUTH_URL_SUFFIX, + ServiceRegion.GCP_EUROPE_WEST1.getOAuthTokenUrl()); + final String scopes = + extract( + properties, + propertiesPrefix, + PROPERTIES_KEY_SCOPES_SUFFIX, + "manage_project:" + projectKey); + + final ClientCredentials credentials = + ClientCredentials.of() + .withClientId(clientId) + .withClientSecret(clientSecret) + .withScopes(scopes) + .build(); + + return ClientConfigurationUtils.createClient(projectKey, credentials, authUrl, apiUrl); + } catch (Exception exception) { + throw new IllegalStateException( + format( + "IT properties file \"%s\" found, but CTP properties" + + " for prefix \"%s\" can't be read", + CTP_CREDENTIALS_PROPERTIES, propertiesPrefix), + exception); + } + } + + private static Properties loadFromEnvVars(String propertiesPrefix) { + String projectKeyKey = propertiesPrefix.toUpperCase().replace(".", "_") + "PROJECT_KEY"; + String projectKey = System.getenv(projectKeyKey); + String clientIdKey = propertiesPrefix.toUpperCase().replace(".", "_") + "CLIENT_ID"; + String clientId = System.getenv(clientIdKey); + String clientSecretKey = propertiesPrefix.toUpperCase().replace(".", "_") + "CLIENT_SECRET"; + String clientSecret = System.getenv(clientSecretKey); + Properties properties = new Properties(); + properties.put(propertiesPrefix + PROPERTIES_KEY_PROJECT_KEY_SUFFIX, projectKey); + properties.put(propertiesPrefix + PROPERTIES_KEY_CLIENT_ID_SUFFIX, clientId); + properties.put(propertiesPrefix + PROPERTIES_KEY_CLIENT_SECRET_SUFFIX, clientSecret); + return properties; + } + + private static String extract( + final Properties properties, + final String prefix, + final String suffix, + final String defaultValue) { + return properties.getProperty(buildPropKey(prefix, suffix), defaultValue); + } + + private static String extract( + final Properties properties, final String prefix, final String suffix) { + final String mapKey = buildPropKey(prefix, suffix); + return properties + .computeIfAbsent(mapKey, key -> throwPropertiesException(prefix, mapKey)) + .toString(); + } + + private static String buildPropKey(final String prefix, final String suffix) { + return prefix + suffix; + } + + private static String throwPropertiesException(final String prefix, final String missingKey) { + throw new IllegalArgumentException( + "Missing property value '" + + missingKey + + "'.\n" + + "Usage:\n" + + "" + + buildPropKey(prefix, PROPERTIES_KEY_PROJECT_KEY_SUFFIX) + + "=YOUR project key\n" + + "" + + buildPropKey(prefix, PROPERTIES_KEY_CLIENT_ID_SUFFIX) + + "=YOUR client id\n" + + "" + + buildPropKey(prefix, PROPERTIES_KEY_CLIENT_SECRET_SUFFIX) + + "=YOUR client secret\n" + + "#optional:\n" + + "" + + buildPropKey(prefix, PROPERTIES_KEY_API_URL_SUFFIX) + + "=https://api.europe-west1.gcp.commercetools.com\n" + + "" + + buildPropKey(prefix, PROPERTIES_KEY_AUTH_URL_SUFFIX) + + "=https://auth.europe-west1.gcp.commercetools.com\n" + + "" + + buildPropKey(prefix, PROPERTIES_KEY_SCOPES_SUFFIX) + + "=manage_project" + + "#don't use quotes for the property values\n"); + } + + private CtpClientUtils() {} +} diff --git a/src/main/java/com/commercetools/project/sync/util/SphereClientUtils.java b/src/main/java/com/commercetools/project/sync/util/SphereClientUtils.java deleted file mode 100644 index 220d8284..00000000 --- a/src/main/java/com/commercetools/project/sync/util/SphereClientUtils.java +++ /dev/null @@ -1,52 +0,0 @@ -package com.commercetools.project.sync.util; - -import static java.lang.String.format; - -import com.commercetools.sync.commons.utils.ClientConfigurationUtils; -import io.sphere.sdk.client.SphereClient; -import io.sphere.sdk.client.SphereClientConfig; -import java.io.InputStream; -import java.util.Properties; -import javax.annotation.Nonnull; - -public final class SphereClientUtils { - private static final String CTP_CREDENTIALS_PROPERTIES = "ctp.credentials.properties"; - public static final SphereClientConfig CTP_SOURCE_CLIENT_CONFIG = getCtpSourceClientConfig(); - public static final SphereClientConfig CTP_TARGET_CLIENT_CONFIG = getCtpTargetClientConfig(); - public static final SphereClient CTP_SOURCE_CLIENT = - ClientConfigurationUtils.createClient(CTP_SOURCE_CLIENT_CONFIG); - public static final SphereClient CTP_TARGET_CLIENT = - ClientConfigurationUtils.createClient(CTP_TARGET_CLIENT_CONFIG); - - private static SphereClientConfig getCtpSourceClientConfig() { - return getCtpClientConfig("source.", "SOURCE"); - } - - private static SphereClientConfig getCtpTargetClientConfig() { - return getCtpClientConfig("target.", "TARGET"); - } - - private static SphereClientConfig getCtpClientConfig( - @Nonnull final String propertiesPrefix, @Nonnull final String envVarPrefix) { - try { - final InputStream propStream = - SphereClientUtils.class.getClassLoader().getResourceAsStream(CTP_CREDENTIALS_PROPERTIES); - if (propStream != null) { - final Properties ctpCredsProperties = new Properties(); - ctpCredsProperties.load(propStream); - return SphereClientConfig.ofProperties(ctpCredsProperties, propertiesPrefix); - } - } catch (Exception exception) { - throw new IllegalStateException( - format( - "CTP credentials file \"%s\" found, but CTP properties" - + " for prefix \"%s\" can't be read", - CTP_CREDENTIALS_PROPERTIES, propertiesPrefix), - exception); - } - - return SphereClientConfig.ofEnvironmentVariables(envVarPrefix); - } - - private SphereClientUtils() {} -} diff --git a/src/main/resources/ctp.credentials.properties.skeleton b/src/main/resources/ctp.credentials.properties.skeleton index 5f9dbdc9..6e426f69 100644 --- a/src/main/resources/ctp.credentials.properties.skeleton +++ b/src/main/resources/ctp.credentials.properties.skeleton @@ -1,5 +1,5 @@ # rename to ctp.credentials.properties and fill the values to run the job locally without env variables -# see SphereClientUtils for more info +# see CtpClientUtils for more info source.projectKey=<> source.clientId=YOUR client id without quotes source.clientSecret=YOUR client secret without quotes diff --git a/src/test/java/com/commercetools/project/sync/CliRunnerTest.java b/src/test/java/com/commercetools/project/sync/CliRunnerTest.java index 117556d9..020a7326 100644 --- a/src/test/java/com/commercetools/project/sync/CliRunnerTest.java +++ b/src/test/java/com/commercetools/project/sync/CliRunnerTest.java @@ -1,20 +1,64 @@ package com.commercetools.project.sync; +import static com.commercetools.project.sync.CliRunner.HELP_OPTION_DESCRIPTION; +import static com.commercetools.project.sync.CliRunner.HELP_OPTION_LONG; +import static com.commercetools.project.sync.CliRunner.HELP_OPTION_SHORT; +import static com.commercetools.project.sync.CliRunner.RUNNER_NAME_OPTION_DESCRIPTION; +import static com.commercetools.project.sync.CliRunner.RUNNER_NAME_OPTION_LONG; +import static com.commercetools.project.sync.CliRunner.RUNNER_NAME_OPTION_SHORT; +import static com.commercetools.project.sync.CliRunner.SYNC_MODULE_OPTION_DESCRIPTION; +import static com.commercetools.project.sync.CliRunner.SYNC_MODULE_OPTION_LONG; +import static com.commercetools.project.sync.CliRunner.SYNC_MODULE_OPTION_SHORT; +import static com.commercetools.project.sync.CliRunner.VERSION_OPTION_DESCRIPTION; +import static com.commercetools.project.sync.CliRunner.VERSION_OPTION_LONG; +import static com.commercetools.project.sync.CliRunner.VERSION_OPTION_SHORT; +import static com.commercetools.project.sync.util.SyncUtils.APPLICATION_DEFAULT_NAME; +import static com.commercetools.project.sync.util.SyncUtils.APPLICATION_DEFAULT_VERSION; +import static com.commercetools.project.sync.util.TestUtils.getMockedClock; +import static com.commercetools.project.sync.util.TestUtils.stubClientsCustomObjectService; +import static com.commercetools.project.sync.util.TestUtils.withTestClient; +import static java.lang.String.format; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import com.commercetools.api.client.ByProjectKeyProductProjectionsGet; +import com.commercetools.api.client.ProjectApiRoot; +import com.commercetools.project.sync.exception.CliException; +import com.google.common.base.Optional; +import io.vrap.rmf.base.client.ApiHttpResponse; import java.io.ByteArrayOutputStream; import java.io.PrintStream; import java.io.UnsupportedEncodingException; +import java.nio.charset.StandardCharsets; +import java.time.ZonedDateTime; +import java.util.concurrent.CompletableFuture; import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.InOrder; +import org.mockito.Mockito; +import uk.org.lidalia.slf4jext.Level; import uk.org.lidalia.slf4jtest.TestLogger; import uk.org.lidalia.slf4jtest.TestLoggerFactory; -// These tests aren't migrated -// TODO: Migrate tests class CliRunnerTest { private static final TestLogger testLogger = TestLoggerFactory.getTestLogger(CliRunner.class); private static ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); private static PrintStream originalSystemOut; + private ProjectApiRoot sourceClient; + private ProjectApiRoot targetClient; @BeforeAll static void setupSuite() throws UnsupportedEncodingException { @@ -29,999 +73,758 @@ static void tearDownSuite() { } @BeforeEach + void setupTest() { + final ProjectApiRoot sourceClientWithEmtpyResults = + mockClientResourceGetRequestsWithEmptyResult("testProjectKey"); + sourceClient = spy(sourceClientWithEmtpyResults); + targetClient = mock(ProjectApiRoot.class); + when(targetClient.getProjectKey()).thenReturn("testTargetProjectKey"); + } + + @AfterEach void tearDownTest() { testLogger.clearAll(); + reset(sourceClient, targetClient); + } + + private ProjectApiRoot mockClientResourceGetRequestsWithEmptyResult(final String projectKey) { + return withTestClient( + projectKey, + (uri, method, encodedRequetsBody) -> { + final String responseString = "{\"results\":[]}"; + return CompletableFuture.completedFuture( + new ApiHttpResponse<>(200, null, responseString.getBytes(StandardCharsets.UTF_8))); + }); + } + + @Test + void run_WithEmptyArgumentList_ShouldFailAndLogError() { + // preparation + final SyncerFactory syncerFactory = + SyncerFactory.of(() -> sourceClient, () -> targetClient, getMockedClock()); + + // test + CliRunner.of().run(new String[] {}, syncerFactory); + + // assertion + assertThat(testLogger.getAllLoggingEvents()) + .hasSize(1) + .singleElement() + .satisfies( + loggingEvent -> { + assertThat(loggingEvent.getLevel()).isEqualTo(Level.ERROR); + assertThat(loggingEvent.getMessage()).contains("Failed to run sync process."); + final Optional actualThrowableOpt = loggingEvent.getThrowable(); + assertThat(actualThrowableOpt).isNotNull(); + assertThat(actualThrowableOpt.isPresent()).isTrue(); + final Throwable actualThrowable = actualThrowableOpt.get(); + assertThat(actualThrowable).isExactlyInstanceOf(CliException.class); + assertThat(actualThrowable.getMessage()) + .contains("Please pass at least 1 option to the CLI."); + }); + } + + @Test + void run_WithHelpAsLongArgument_ShouldPrintUsageHelpToStandardOut() + throws UnsupportedEncodingException { + // preparation + final SyncerFactory syncerFactory = + SyncerFactory.of(() -> sourceClient, () -> targetClient, getMockedClock()); + + // test + CliRunner.of().run(new String[] {"-help"}, syncerFactory); + + assertOutputStreamContainsHelpUsageWithSpecifiedCliOptions(); + } + + private void assertOutputStreamContainsHelpUsageWithSpecifiedCliOptions() + throws UnsupportedEncodingException { + assertThat(outputStream.toString("UTF-8")) + .contains(format("usage: %s", APPLICATION_DEFAULT_NAME)) + .contains(format("-%s,--%s", HELP_OPTION_SHORT, HELP_OPTION_LONG)) + .contains(format("-%s,--%s", SYNC_MODULE_OPTION_SHORT, SYNC_MODULE_OPTION_LONG)) + .contains(format("-%s,--%s", VERSION_OPTION_SHORT, VERSION_OPTION_LONG)); + } + + @Test + void run_WithHelpAsShortArgument_ShouldPrintUsageHelpToStandardOut() + throws UnsupportedEncodingException { + // preparation + final SyncerFactory syncerFactory = + SyncerFactory.of(() -> sourceClient, () -> targetClient, getMockedClock()); + + // test + CliRunner.of().run(new String[] {"-h"}, syncerFactory); + + assertOutputStreamContainsHelpUsageWithSpecifiedCliOptions(); + } + + @Test + void run_WithVersionAsShortArgument_ShouldPrintApplicationVersionToStandardOut() + throws UnsupportedEncodingException { + // preparation + final SyncerFactory syncerFactory = + SyncerFactory.of(() -> sourceClient, () -> targetClient, getMockedClock()); + + // test + CliRunner.of().run(new String[] {"-v"}, syncerFactory); + + assertThat(outputStream.toString("UTF-8")).contains(APPLICATION_DEFAULT_VERSION); + } + + @Test + void run_WithVersionAsLongArgument_ShouldPrintApplicationVersionToStandardOut() + throws UnsupportedEncodingException { + // preparation + final SyncerFactory syncerFactory = + SyncerFactory.of(() -> sourceClient, () -> targetClient, getMockedClock()); + + // test + CliRunner.of().run(new String[] {"--version"}, syncerFactory); + + assertThat(outputStream.toString("UTF-8")).contains(APPLICATION_DEFAULT_VERSION); } - // @Test - // void run_WithEmptyArgumentList_ShouldFailAndLogError() { - // // preparation - // final SyncerFactory syncerFactory = - // SyncerFactory.of( - // () -> mock(SphereClient.class), () -> mock(SphereClient.class), getMockedClock()); - // - // // test - // CliRunner.of().run(new String[] {}, syncerFactory); - // - // // assertion - // assertThat(testLogger.getAllLoggingEvents()) - // .hasSize(1) - // .singleElement() - // .satisfies( - // loggingEvent -> { - // assertThat(loggingEvent.getLevel()).isEqualTo(Level.ERROR); - // assertThat(loggingEvent.getMessage()).contains("Failed to run sync process."); - // final Optional actualThrowableOpt = loggingEvent.getThrowable(); - // assertThat(actualThrowableOpt).isNotNull(); - // assertThat(actualThrowableOpt.isPresent()).isTrue(); - // final Throwable actualThrowable = actualThrowableOpt.get(); - // assertThat(actualThrowable).isExactlyInstanceOf(CliException.class); - // assertThat(actualThrowable.getMessage()) - // .contains("Please pass at least 1 option to the CLI."); - // }); - // } - // - // @Test - // void run_WithHelpAsLongArgument_ShouldPrintUsageHelpToStandardOut() - // throws UnsupportedEncodingException { - // // preparation - // final SyncerFactory syncerFactory = - // SyncerFactory.of( - // () -> mock(SphereClient.class), () -> mock(SphereClient.class), getMockedClock()); - // - // // test - // CliRunner.of().run(new String[] {"-help"}, syncerFactory); - // - // assertOutputStreamContainsHelpUsageWithSpecifiedCliOptions(); - // } - // - // private void assertOutputStreamContainsHelpUsageWithSpecifiedCliOptions() - // throws UnsupportedEncodingException { - // assertThat(outputStream.toString("UTF-8")) - // .contains(format("usage: %s", APPLICATION_DEFAULT_NAME)) - // .contains(format("-%s,--%s", HELP_OPTION_SHORT, HELP_OPTION_LONG)) - // .contains(format("-%s,--%s", SYNC_MODULE_OPTION_SHORT, SYNC_MODULE_OPTION_LONG)) - // .contains(format("-%s,--%s", VERSION_OPTION_SHORT, VERSION_OPTION_LONG)); - // } - // - // @Test - // void run_WithHelpAsShortArgument_ShouldPrintUsageHelpToStandardOut() - // throws UnsupportedEncodingException { - // // preparation - // final SyncerFactory syncerFactory = - // SyncerFactory.of( - // () -> mock(SphereClient.class), () -> mock(SphereClient.class), getMockedClock()); - // - // // test - // CliRunner.of().run(new String[] {"-h"}, syncerFactory); - // - // assertOutputStreamContainsHelpUsageWithSpecifiedCliOptions(); - // } - // - // @Test - // void run_WithVersionAsShortArgument_ShouldPrintApplicationVersionToStandardOut() - // throws UnsupportedEncodingException { - // // preparation - // final SyncerFactory syncerFactory = - // SyncerFactory.of( - // () -> mock(SphereClient.class), () -> mock(SphereClient.class), getMockedClock()); - // - // // test - // CliRunner.of().run(new String[] {"-v"}, syncerFactory); - // - // assertThat(outputStream.toString("UTF-8")).contains(APPLICATION_DEFAULT_VERSION); - // } - // - // @Test - // void run_WithVersionAsLongArgument_ShouldPrintApplicationVersionToStandardOut() - // throws UnsupportedEncodingException { - // // preparation - // final SyncerFactory syncerFactory = - // SyncerFactory.of( - // () -> mock(SphereClient.class), () -> mock(SphereClient.class), getMockedClock()); - // - // // test - // CliRunner.of().run(new String[] {"--version"}, syncerFactory); - // - // assertThat(outputStream.toString("UTF-8")).contains(APPLICATION_DEFAULT_VERSION); - // } - // - // @Test - // void run_WithSyncAsArgumentWithNoArgs_ShouldFailAndLogError() { - // // preparation - // final SyncerFactory syncerFactory = - // SyncerFactory.of( - // () -> mock(SphereClient.class), () -> mock(SphereClient.class), getMockedClock()); - // - // // test - // CliRunner.of().run(new String[] {"-s"}, syncerFactory); - // - // // assertion - // assertThat(testLogger.getAllLoggingEvents()) - // .hasSize(1) - // .singleElement() - // .satisfies( - // loggingEvent -> { - // assertThat(loggingEvent.getLevel()).isEqualTo(Level.ERROR); - // assertThat(loggingEvent.getMessage()).contains("Failed to run sync process."); - // final Optional actualThrowableOpt = loggingEvent.getThrowable(); - // assertThat(actualThrowableOpt).isNotNull(); - // assertThat(actualThrowableOpt.isPresent()).isTrue(); - // final Throwable actualThrowable = actualThrowableOpt.get(); - // assertThat(actualThrowable).isExactlyInstanceOf(CliException.class); - // assertThat(actualThrowable.getMessage()).contains("Missing argument for option: - // s"); - // }); - // } - // - // @Test - // void run_WithFullSyncAsFirstArgument_ShouldFailAndLogError() { - // // preparation - // final SyncerFactory syncerFactory = - // SyncerFactory.of( - // () -> mock(SphereClient.class), () -> mock(SphereClient.class), getMockedClock()); - // - // // test - // CliRunner.of().run(new String[] {"-f"}, syncerFactory); - // - // // assertion - // assertThat(testLogger.getAllLoggingEvents()) - // .hasSize(1) - // .singleElement() - // .satisfies( - // loggingEvent -> { - // assertThat(loggingEvent.getLevel()).isEqualTo(Level.ERROR); - // assertThat(loggingEvent.getMessage()).contains("Failed to run sync process."); - // final Optional actualThrowableOpt = loggingEvent.getThrowable(); - // assertThat(actualThrowableOpt).isNotNull(); - // assertThat(actualThrowableOpt.isPresent()).isTrue(); - // final Throwable actualThrowable = actualThrowableOpt.get(); - // assertThat(actualThrowable).isExactlyInstanceOf(CliException.class); - // assertThat(actualThrowable.getMessage()) - // .contains("Please check that the first sync option is either -s, -h or -v."); - // }); - // } - // - // @Test - // void run_AsProductDeltaSync_ShouldBuildSyncerAndExecuteSync() { - // // preparation - // final SphereClient sourceClient = mock(SphereClient.class); - // when(sourceClient.getConfig()).thenReturn(SphereClientConfig.of("foo", "foo", "foo")); - // - // final SphereClient targetClient = mock(SphereClient.class); - // when(targetClient.getConfig()).thenReturn(SphereClientConfig.of("bar", "bar", "bar")); - // - // when(sourceClient.execute(any(ProductProjectionQuery.class))) - // .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); - // - // final SyncerFactory syncerFactory = - // spy(SyncerFactory.of(() -> sourceClient, () -> targetClient, getMockedClock())); - // - // stubClientsCustomObjectService(targetClient, ZonedDateTime.now()); - // - // // test - // CliRunner.of().run(new String[] {"-s", "products"}, syncerFactory); - // - // // assertions - // verify(syncerFactory, times(1)).sync(new String[] {"products"}, null, false, false, null); - // verify(sourceClient, times(1)).execute(any(ProductProjectionQuery.class)); - // } - // - // @Test - // void run_AsProductFullSync_ShouldBuildSyncerAndExecuteSync() { - // // preparation - // final SphereClient sourceClient = mock(SphereClient.class); - // when(sourceClient.getConfig()).thenReturn(SphereClientConfig.of("foo", "foo", "foo")); - // - // final SphereClient targetClient = mock(SphereClient.class); - // when(targetClient.getConfig()).thenReturn(SphereClientConfig.of("bar", "bar", "bar")); - // - // when(sourceClient.execute(any(ProductProjectionQuery.class))) - // .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); - // - // final SyncerFactory syncerFactory = - // spy(SyncerFactory.of(() -> sourceClient, () -> targetClient, getMockedClock())); - // - // stubClientsCustomObjectService(targetClient, ZonedDateTime.now()); - // - // // test - // CliRunner.of().run(new String[] {"-s", "products", "-f"}, syncerFactory); - // - // // assertions - // verify(syncerFactory, times(1)).sync(new String[] {"products"}, null, true, false, null); - // verify(sourceClient, times(1)).execute(any(ProductProjectionQuery.class)); - // } - // - // @Test - // void - // - // run_AsProductSyncWithCustomProductQueriesAndLimit_ShouldBuildSyncerAndExecuteQuerySuccessfully() { - // // preparation - // final SphereClient sourceClient = mock(SphereClient.class); - // when(sourceClient.getConfig()).thenReturn(SphereClientConfig.of("foo", "foo", "foo")); - // - // final SphereClient targetClient = mock(SphereClient.class); - // when(targetClient.getConfig()).thenReturn(SphereClientConfig.of("bar", "bar", "bar")); - // - // when(sourceClient.execute(any(ProductProjectionQuery.class))) - // .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); - // - // final Long limit = 100L; - // final String customQuery = - // "\"published=true AND masterData(masterVariant(attributes(name= \\\"abc\\\" AND - // value=123)))\""; - // final String productQueryParametersValue = - // "{\"limit\": " + limit + ", \"where\": " + customQuery + "}"; - // - // final SyncerFactory syncerFactory = - // spy(SyncerFactory.of(() -> sourceClient, () -> targetClient, getMockedClock())); - // - // stubClientsCustomObjectService(targetClient, ZonedDateTime.now()); - // // test - // CliRunner.of() - // .run( - // new String[] { - // "-s", "products", "-f", "-productQueryParameters", productQueryParametersValue - // }, - // syncerFactory); - // - // // assertions - // verify(sourceClient, times(1)).execute(any(ProductProjectionQuery.class)); - // } - // - // @Test - // void - // - // run_AsProductSyncWithProductQueryParametersAndOnlyLimit_ShouldBuildSyncerAndExecuteQuerySuccessfully() { - // // preparation - // final SphereClient sourceClient = mock(SphereClient.class); - // when(sourceClient.getConfig()).thenReturn(SphereClientConfig.of("foo", "foo", "foo")); - // - // final SphereClient targetClient = mock(SphereClient.class); - // when(targetClient.getConfig()).thenReturn(SphereClientConfig.of("bar", "bar", "bar")); - // - // when(sourceClient.execute(any(ProductProjectionQuery.class))) - // .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); - // - // final Long limit = 100L; - // final String productQueryParametersValue = "{\"limit\": " + limit + "}"; - // - // final SyncerFactory syncerFactory = - // spy(SyncerFactory.of(() -> sourceClient, () -> targetClient, getMockedClock())); - // - // stubClientsCustomObjectService(targetClient, ZonedDateTime.now()); - // // test - // CliRunner.of() - // .run( - // new String[] { - // "-s", "products", "-f", "-productQueryParameters", productQueryParametersValue - // }, - // syncerFactory); - // - // // assertions - // verify(sourceClient, times(1)).execute(any(ProductProjectionQuery.class)); - // } - // - // @Test - // void - // - // run_AsProductSyncWithProductQueryParametersAndOnlyWhere_ShouldBuildSyncerAndExecuteQuerySuccessfully() { - // // preparation - // final SphereClient sourceClient = mock(SphereClient.class); - // when(sourceClient.getConfig()).thenReturn(SphereClientConfig.of("foo", "foo", "foo")); - // - // final SphereClient targetClient = mock(SphereClient.class); - // when(targetClient.getConfig()).thenReturn(SphereClientConfig.of("bar", "bar", "bar")); - // - // when(sourceClient.execute(any(ProductProjectionQuery.class))) - // .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); - // - // final String customQuery = - // "\"published=true AND masterVariant(attributes(name= \\\"abc\\\" AND value=123))\""; - // final String productQueryParametersValue = "{\"where\": " + customQuery + "}"; - // - // final SyncerFactory syncerFactory = - // spy(SyncerFactory.of(() -> sourceClient, () -> targetClient, getMockedClock())); - // - // stubClientsCustomObjectService(targetClient, ZonedDateTime.now()); - // // test - // CliRunner.of() - // .run( - // new String[] { - // "-s", "products", "-f", "-productQueryParameters", productQueryParametersValue - // }, - // syncerFactory); - // - // // assertions - // verify(sourceClient, times(1)).execute(any(ProductProjectionQuery.class)); - // } - // - // @Test - // void run_WithWrongFormatProductQueryParametersArgument_ShouldThrowCLIException() { - // // preparation - // final SphereClient sourceClient = mock(SphereClient.class); - // when(sourceClient.getConfig()).thenReturn(SphereClientConfig.of("foo", "foo", "foo")); - // - // final SphereClient targetClient = mock(SphereClient.class); - // when(targetClient.getConfig()).thenReturn(SphereClientConfig.of("bar", "bar", "bar")); - // - // final Long limit = 100L; - // final String customQuery = - // "\"published=true AND masterVariant(attributes(name= \"abc\\\" AND value=123))\""; - // final String productQueryParametersValue = - // "{\"limit\": " + limit + ", \"where\": " + customQuery + "}"; - // - // final SyncerFactory syncerFactory = - // spy(SyncerFactory.of(() -> sourceClient, () -> targetClient, getMockedClock())); - // - // // test - // CliRunner.of() - // .run( - // new String[] { - // "-s", "products", "-f", "-productQueryParameters", productQueryParametersValue - // }, - // syncerFactory); - // - // // assertion - // assertThat(testLogger.getAllLoggingEvents()) - // .hasSize(1) - // .singleElement() - // .satisfies( - // loggingEvent -> { - // assertThat(loggingEvent.getLevel()).isEqualTo(Level.ERROR); - // assertThat(loggingEvent.getMessage()).contains("Failed to run sync process."); - // final Optional actualThrowableOpt = loggingEvent.getThrowable(); - // assertThat(actualThrowableOpt).isNotNull(); - // assertThat(actualThrowableOpt.isPresent()).isTrue(); - // final Throwable actualThrowable = actualThrowableOpt.get(); - // assertThat(actualThrowable).isExactlyInstanceOf(CliException.class); - // }); - // } - // - // @Test - // void run_WithInvalidLimitInProductQueryParametersArgument_ShouldThrowCLIException() { - // // preparation - // final SphereClient sourceClient = mock(SphereClient.class); - // when(sourceClient.getConfig()).thenReturn(SphereClientConfig.of("foo", "foo", "foo")); - // - // final SphereClient targetClient = mock(SphereClient.class); - // when(targetClient.getConfig()).thenReturn(SphereClientConfig.of("bar", "bar", "bar")); - // - // final Long limit = -100L; - // final String customQuery = - // "\"published=true AND masterVariant(attributes(name= \"abc\\\" AND value=123))\""; - // final String productQueryParametersValue = - // "{\"limit\": " + limit + ", \"where\": " + customQuery + "}"; - // - // final SyncerFactory syncerFactory = - // spy(SyncerFactory.of(() -> sourceClient, () -> targetClient, getMockedClock())); - // - // // test - // CliRunner.of() - // .run( - // new String[] { - // "-s", "products", "-f", "-productQueryParameters", productQueryParametersValue - // }, - // syncerFactory); - // - // // assertion - // assertThat(testLogger.getAllLoggingEvents()) - // .hasSize(1) - // .singleElement() - // .satisfies( - // loggingEvent -> { - // assertThat(loggingEvent.getLevel()).isEqualTo(Level.ERROR); - // assertThat(loggingEvent.getMessage()).contains("Failed to run sync process."); - // final Optional actualThrowableOpt = loggingEvent.getThrowable(); - // assertThat(actualThrowableOpt).isNotNull(); - // assertThat(actualThrowableOpt.isPresent()).isTrue(); - // final Throwable actualThrowable = actualThrowableOpt.get(); - // assertThat(actualThrowable).isExactlyInstanceOf(CliException.class); - // assertThat(actualThrowable.getMessage()) - // .contains("limit -100 cannot be less than 1."); - // }); - // } - // - // @Test - // void run_AsTaxCategoryDeltaSync_ShouldBuildSyncerAndExecuteSync() { - // // preparation - // final SphereClient sourceClient = mock(SphereClient.class); - // when(sourceClient.getConfig()).thenReturn(SphereClientConfig.of("foo", "foo", "foo")); - // - // final SphereClient targetClient = mock(SphereClient.class); - // when(targetClient.getConfig()).thenReturn(SphereClientConfig.of("bar", "bar", "bar")); - // - // when(sourceClient.execute(any(ProductProjectionQuery.class))) - // .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); - // - // final SyncerFactory syncerFactory = - // spy(SyncerFactory.of(() -> sourceClient, () -> targetClient, getMockedClock())); - // - // stubClientsCustomObjectService(targetClient, ZonedDateTime.now()); - // - // // test - // CliRunner.of().run(new String[] {"-s", "taxCategories"}, syncerFactory); - // - // // assertions - // verify(syncerFactory, times(1)).sync(new String[] {"taxCategories"}, null, false, false, - // null); - // verify(sourceClient, times(1)).execute(any(TaxCategoryQuery.class)); - // } - // - // @Test - // void run_AsTaxCategoryFullSync_ShouldBuildSyncerAndExecuteSync() { - // // preparation - // final SphereClient sourceClient = mock(SphereClient.class); - // when(sourceClient.getConfig()).thenReturn(SphereClientConfig.of("foo", "foo", "foo")); - // - // final SphereClient targetClient = mock(SphereClient.class); - // when(targetClient.getConfig()).thenReturn(SphereClientConfig.of("bar", "bar", "bar")); - // - // when(sourceClient.execute(any(TaxCategoryQuery.class))) - // .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); - // - // final SyncerFactory syncerFactory = - // spy(SyncerFactory.of(() -> sourceClient, () -> targetClient, getMockedClock())); - // - // stubClientsCustomObjectService(targetClient, ZonedDateTime.now()); - // - // // test - // CliRunner.of().run(new String[] {"-s", "taxCategories", "-f"}, syncerFactory); - // - // // assertions - // verify(syncerFactory, times(1)).sync(new String[] {"taxCategories"}, null, true, false, - // null); - // verify(sourceClient, times(1)).execute(any(TaxCategoryQuery.class)); - // } - // - // @Test - // void run_AsCustomerDeltaSync_ShouldBuildSyncerAndExecuteSync() { - // // preparation - // final SphereClient sourceClient = mock(SphereClient.class); - // when(sourceClient.getConfig()).thenReturn(SphereClientConfig.of("foo", "foo", "foo")); - // - // final SphereClient targetClient = mock(SphereClient.class); - // when(targetClient.getConfig()).thenReturn(SphereClientConfig.of("bar", "bar", "bar")); - // - // final SyncerFactory syncerFactory = - // spy(SyncerFactory.of(() -> sourceClient, () -> targetClient, getMockedClock())); - // - // stubClientsCustomObjectService(targetClient, ZonedDateTime.now()); - // - // // test - // CliRunner.of().run(new String[] {"-s", "customers"}, syncerFactory); - // - // // assertions - // verify(syncerFactory, times(1)).sync(new String[] {"customers"}, null, false, false, null); - // verify(sourceClient, times(1)).execute(any(CustomerQuery.class)); - // } - // - // @Test - // void run_AsCustomerFullSync_ShouldBuildSyncerAndExecuteSync() { - // // preparation - // final SphereClient sourceClient = mock(SphereClient.class); - // when(sourceClient.getConfig()).thenReturn(SphereClientConfig.of("foo", "foo", "foo")); - // - // final SphereClient targetClient = mock(SphereClient.class); - // when(targetClient.getConfig()).thenReturn(SphereClientConfig.of("bar", "bar", "bar")); - // - // when(sourceClient.execute(any(CustomerQuery.class))) - // .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); - // - // final SyncerFactory syncerFactory = - // spy(SyncerFactory.of(() -> sourceClient, () -> targetClient, getMockedClock())); - // - // stubClientsCustomObjectService(targetClient, ZonedDateTime.now()); - // - // // test - // CliRunner.of().run(new String[] {"-s", "customers", "-f"}, syncerFactory); - // - // // assertions - // verify(syncerFactory, times(1)).sync(new String[] {"customers"}, null, true, false, null); - // verify(sourceClient, times(1)).execute(any(CustomerQuery.class)); - // } - // - // @Test - // void run_AsShoppingListDeltaSync_ShouldBuildSyncerAndExecuteSync() { - // // preparation - // final SphereClient sourceClient = mock(SphereClient.class); - // when(sourceClient.getConfig()).thenReturn(SphereClientConfig.of("foo", "foo", "foo")); - // - // final SphereClient targetClient = mock(SphereClient.class); - // when(targetClient.getConfig()).thenReturn(SphereClientConfig.of("bar", "bar", "bar")); - // - // final SyncerFactory syncerFactory = - // spy(SyncerFactory.of(() -> sourceClient, () -> targetClient, getMockedClock())); - // - // stubClientsCustomObjectService(targetClient, ZonedDateTime.now()); - // - // // test - // CliRunner.of().run(new String[] {"-s", "shoppingLists"}, syncerFactory); - // - // // assertions - // verify(syncerFactory, times(1)).sync(new String[] {"shoppingLists"}, null, false, false, - // null); - // verify(sourceClient, times(1)).execute(any(ShoppingListQuery.class)); - // } - // - // @Test - // void run_AsShoppingListFullSync_ShouldBuildSyncerAndExecuteSync() { - // // preparation - // final SphereClient sourceClient = mock(SphereClient.class); - // when(sourceClient.getConfig()).thenReturn(SphereClientConfig.of("foo", "foo", "foo")); - // - // final SphereClient targetClient = mock(SphereClient.class); - // when(targetClient.getConfig()).thenReturn(SphereClientConfig.of("bar", "bar", "bar")); - // - // when(sourceClient.execute(any(ShoppingListQuery.class))) - // .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); - // - // final SyncerFactory syncerFactory = - // spy(SyncerFactory.of(() -> sourceClient, () -> targetClient, getMockedClock())); - // - // stubClientsCustomObjectService(targetClient, ZonedDateTime.now()); - // - // // test - // CliRunner.of().run(new String[] {"-s", "shoppingLists", "-f"}, syncerFactory); - // - // // assertions - // verify(syncerFactory, times(1)).sync(new String[] {"shoppingLists"}, null, true, false, - // null); - // verify(sourceClient, times(1)).execute(any(ShoppingListQuery.class)); - // } - // - // @Test - // void run_AsCustomObjectDeltaSync_ShouldBuildSyncerAndExecuteSync() { - // // preparation - // final SphereClient sourceClient = mock(SphereClient.class); - // when(sourceClient.getConfig()).thenReturn(SphereClientConfig.of("foo", "foo", "foo")); - // - // final SphereClient targetClient = mock(SphereClient.class); - // when(targetClient.getConfig()).thenReturn(SphereClientConfig.of("bar", "bar", "bar")); - // - // when(sourceClient.execute(any(CustomObjectQuery.class))) - // .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); - // - // final SyncerFactory syncerFactory = - // spy(SyncerFactory.of(() -> sourceClient, () -> targetClient, getMockedClock())); - // - // stubClientsCustomObjectService(targetClient, ZonedDateTime.now()); - // - // // test - // CliRunner.of().run(new String[] {"-s", "customObjects"}, syncerFactory); - // - // // assertions - // verify(syncerFactory, times(1)).sync(new String[] {"customObjects"}, null, false, false, - // null); - // verify(sourceClient, times(1)).execute(any(CustomObjectQuery.class)); - // } - // - // @Test - // void run_AsCustomObjectFullSync_ShouldBuildSyncerAndExecuteSync() { - // // preparation - // final SphereClient sourceClient = mock(SphereClient.class); - // when(sourceClient.getConfig()).thenReturn(SphereClientConfig.of("foo", "foo", "foo")); - // - // final SphereClient targetClient = mock(SphereClient.class); - // when(targetClient.getConfig()).thenReturn(SphereClientConfig.of("bar", "bar", "bar")); - // - // when(sourceClient.execute(any(CustomObjectQuery.class))) - // .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); - // - // final SyncerFactory syncerFactory = - // spy(SyncerFactory.of(() -> sourceClient, () -> targetClient, getMockedClock())); - // - // stubClientsCustomObjectService(targetClient, ZonedDateTime.now()); - // - // // test - // CliRunner.of().run(new String[] {"-s", "customObjects", "-f"}, syncerFactory); - // - // // assertions - // verify(syncerFactory, times(1)).sync(new String[] {"customObjects"}, null, true, false, - // null); - // verify(sourceClient, times(1)).execute(any(CustomObjectQuery.class)); - // } - // - // @Test - // void run_AsCartDiscountFullSync_ShouldBuildSyncerAndExecuteSync() { - // // preparation - // final SphereClient sourceClient = mock(SphereClient.class); - // when(sourceClient.getConfig()).thenReturn(SphereClientConfig.of("foo", "foo", "foo")); - // - // final SphereClient targetClient = mock(SphereClient.class); - // when(targetClient.getConfig()).thenReturn(SphereClientConfig.of("bar", "bar", "bar")); - // - // when(sourceClient.execute(any(CartDiscountQuery.class))) - // .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); - // - // final SyncerFactory syncerFactory = - // spy(SyncerFactory.of(() -> sourceClient, () -> targetClient, getMockedClock())); - // - // stubClientsCustomObjectService(targetClient, ZonedDateTime.now()); - // - // // test - // CliRunner.of().run(new String[] {"-s", "cartDiscounts", "-f"}, syncerFactory); - // - // // assertions - // verify(syncerFactory, times(1)).sync(new String[] {"cartDiscounts"}, null, true, false, - // null); - // verify(sourceClient, times(1)).execute(any(CartDiscountQuery.class)); - // } - // - // @Test - // void run_AsStateFullSync_ShouldBuildSyncerAndExecuteSync() { - // // preparation - // final SphereClient sourceClient = mock(SphereClient.class); - // when(sourceClient.getConfig()).thenReturn(SphereClientConfig.of("foo", "foo", "foo")); - // - // final SphereClient targetClient = mock(SphereClient.class); - // when(targetClient.getConfig()).thenReturn(SphereClientConfig.of("bar", "bar", "bar")); - // - // when(sourceClient.execute(any(StateQuery.class))) - // .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); - // - // final SyncerFactory syncerFactory = - // spy(SyncerFactory.of(() -> sourceClient, () -> targetClient, getMockedClock())); - // - // stubClientsCustomObjectService(targetClient, ZonedDateTime.now()); - // - // // test - // CliRunner.of().run(new String[] {"-s", "states", "-f"}, syncerFactory); - // - // // assertions - // verify(syncerFactory, times(1)).sync(new String[] {"states"}, null, true, false, null); - // verify(sourceClient, times(1)).execute(any(StateQuery.class)); - // } - // - // @Test - // void run_WithSyncAsLongArgument_ShouldBuildSyncerAndExecuteSync() { - // // preparation - // final SphereClient sourceClient = mock(SphereClient.class); - // when(sourceClient.getConfig()).thenReturn(SphereClientConfig.of("foo", "foo", "foo")); - // - // final SphereClient targetClient = mock(SphereClient.class); - // when(targetClient.getConfig()).thenReturn(SphereClientConfig.of("bar", "bar", "bar")); - // - // when(sourceClient.execute(any(ProductProjectionQuery.class))) - // .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); - // - // final SyncerFactory syncerFactory = - // spy(SyncerFactory.of(() -> sourceClient, () -> targetClient, getMockedClock())); - // - // stubClientsCustomObjectService(targetClient, ZonedDateTime.now()); - // - // // test - // CliRunner.of().run(new String[] {"--sync", "products"}, syncerFactory); - // - // // assertions - // verify(syncerFactory, times(1)).sync(new String[] {"products"}, null, false, false, null); - // verify(sourceClient, times(1)).execute(any(ProductProjectionQuery.class)); - // } - // - // @Test - // void run_WithRunnerName_ShouldProcessSyncOption() { - // // preparation - // final SphereClient sourceClient = mock(SphereClient.class); - // when(sourceClient.getConfig()).thenReturn(SphereClientConfig.of("foo", "foo", "foo")); - // - // final SphereClient targetClient = mock(SphereClient.class); - // when(targetClient.getConfig()).thenReturn(SphereClientConfig.of("bar", "bar", "bar")); - // - // when(sourceClient.execute(any(ProductProjectionQuery.class))) - // .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); - // - // final SyncerFactory syncerFactory = - // spy(SyncerFactory.of(() -> sourceClient, () -> targetClient, getMockedClock())); - // - // stubClientsCustomObjectService(targetClient, ZonedDateTime.now()); - // - // // test - // CliRunner.of().run(new String[] {"--sync", "products", "-r", "Runner123"}, syncerFactory); - // - // // assertions - // verify(syncerFactory, times(1)) - // .sync(new String[] {"products"}, "Runner123", false, false, null); - // verify(sourceClient, times(1)).execute(any(ProductProjectionQuery.class)); - // } - // - // @Test - // void run_WithRunnerNameLong_ShouldProcessSyncOption() { - // // preparation - // final SphereClient sourceClient = mock(SphereClient.class); - // when(sourceClient.getConfig()).thenReturn(SphereClientConfig.of("foo", "foo", "foo")); - // - // final SphereClient targetClient = mock(SphereClient.class); - // when(targetClient.getConfig()).thenReturn(SphereClientConfig.of("bar", "bar", "bar")); - // - // when(sourceClient.execute(any(ProductProjectionQuery.class))) - // .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); - // - // final SyncerFactory syncerFactory = - // spy(SyncerFactory.of(() -> sourceClient, () -> targetClient, getMockedClock())); - // - // stubClientsCustomObjectService(targetClient, ZonedDateTime.now()); - // - // // test - // CliRunner.of() - // .run( - // new String[] {"--sync", "products", "--runnerName", "Runner123", "--full"}, - // syncerFactory); - // - // // assertions - // verify(syncerFactory, times(1)).sync(new String[] {"products"}, "Runner123", true, false, - // null); - // verify(sourceClient, times(1)).execute(any(ProductProjectionQuery.class)); - // } - // - // @Test - // void run_WithUnknownArgument_ShouldPrintAndLogError() { - // // preparation - // final SyncerFactory syncerFactory = - // spy( - // SyncerFactory.of( - // () -> mock(SphereClient.class), () -> mock(SphereClient.class), - // getMockedClock())); - // // test - // CliRunner.of().run(new String[] {"-u"}, syncerFactory); - // - // // Assert error log - // verify(syncerFactory, never()).sync(any(), any(), anyBoolean(), anyBoolean(), any()); - // } - // - // @Test - // void run_WithHelpAsArgument_ShouldPrintThreeOptionsWithDescriptionsToSystemOut() - // throws UnsupportedEncodingException { - // // preparation - // final SyncerFactory syncerFactory = - // spy( - // SyncerFactory.of( - // () -> mock(SphereClient.class), () -> mock(SphereClient.class), - // getMockedClock())); - // - // // test - // CliRunner.of().run(new String[] {"-h"}, syncerFactory); - // - // // assertions - // assertThat(testLogger.getAllLoggingEvents()).isEmpty(); - // - // // Remove line breaks from output stream string. - // final String outputStreamWithoutLineBreaks = outputStream.toString("UTF-8").replace("\n", - // ""); - // - // // Replace multiple spaces with single space in output stream string. - // final String outputStreamWithSingleSpaces = - // outputStreamWithoutLineBreaks.trim().replaceAll(" +", " "); - // - // assertThat(outputStreamWithSingleSpaces) - // .contains( - // format("-%s,--%s %s", HELP_OPTION_SHORT, HELP_OPTION_LONG, HELP_OPTION_DESCRIPTION)) - // .contains( - // format( - // "-%s,--%s %s", - // SYNC_MODULE_OPTION_SHORT, SYNC_MODULE_OPTION_LONG, - // SYNC_MODULE_OPTION_DESCRIPTION)) - // .contains( - // format( - // "-%s,--%s %s", - // RUNNER_NAME_OPTION_SHORT, RUNNER_NAME_OPTION_LONG, - // RUNNER_NAME_OPTION_DESCRIPTION)) - // .contains( - // format( - // "-%s,--%s %s", - // VERSION_OPTION_SHORT, VERSION_OPTION_LONG, VERSION_OPTION_DESCRIPTION)); - // verify(syncerFactory, never()).sync(any(), any(), anyBoolean(), anyBoolean(), any()); - // } - // - // @Test - // void run_WithSyncAsArgumentWithAllArg_ShouldExecuteAllSyncers() { - // // preparation - // final SphereClient sourceClient = mock(SphereClient.class); - // when(sourceClient.getConfig()).thenReturn(SphereClientConfig.of("foo", "foo", "foo")); - // - // final SphereClient targetClient = mock(SphereClient.class); - // when(targetClient.getConfig()).thenReturn(SphereClientConfig.of("bar", "bar", "bar")); - // - // when(sourceClient.execute(any(ProductTypeQuery.class))) - // .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); - // when(sourceClient.execute(any(TypeQuery.class))) - // .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); - // when(sourceClient.execute(any(CategoryQuery.class))) - // .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); - // when(sourceClient.execute(any(InventoryEntryQuery.class))) - // .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); - // when(sourceClient.execute(any(ProductProjectionQuery.class))) - // .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); - // when(sourceClient.execute(any(CartDiscountQuery.class))) - // .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); - // when(sourceClient.execute(any(StateQuery.class))) - // .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); - // when(sourceClient.execute(any(TaxCategoryQuery.class))) - // .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); - // when(sourceClient.execute(any(CustomObjectQuery.class))) - // .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); - // when(sourceClient.execute(any(CustomerQuery.class))) - // .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); - // when(sourceClient.execute(any(ShoppingListQuery.class))) - // .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); - // - // stubClientsCustomObjectService(targetClient, ZonedDateTime.now()); - // - // final SyncerFactory syncerFactory = - // spy(SyncerFactory.of(() -> sourceClient, () -> targetClient, getMockedClock())); - // - // // test - // CliRunner.of().run(new String[] {"-s", "all"}, syncerFactory); - // - // // assertions - // verify(syncerFactory, times(1)).sync(new String[] {"all"}, null, false, false, null); - // verify(sourceClient, times(1)).execute(any(ProductTypeQuery.class)); - // verify(sourceClient, times(1)).execute(any(TypeQuery.class)); - // verify(sourceClient, times(1)).execute(any(TaxCategoryQuery.class)); - // verify(sourceClient, times(1)).execute(any(CategoryQuery.class)); - // verify(sourceClient, times(1)).execute(any(ProductProjectionQuery.class)); - // verify(sourceClient, times(1)).execute(any(InventoryEntryQuery.class)); - // verify(sourceClient, times(1)).execute(any(CartDiscountQuery.class)); - // verify(sourceClient, times(1)).execute(any(StateQuery.class)); - // verify(sourceClient, times(1)).execute(any(CustomObjectQuery.class)); - // verify(sourceClient, times(1)).execute(any(CustomerQuery.class)); - // verify(sourceClient, times(1)).execute(any(ShoppingListQuery.class)); - // } - // - // @Test - // void run_WithSyncAsArgumentWithAllArgWithRunnerName_ShouldExecuteAllSyncers() { - // // preparation - // final SphereClient sourceClient = mock(SphereClient.class); - // when(sourceClient.getConfig()).thenReturn(SphereClientConfig.of("foo", "foo", "foo")); - // - // final SphereClient targetClient = mock(SphereClient.class); - // when(targetClient.getConfig()).thenReturn(SphereClientConfig.of("bar", "bar", "bar")); - // - // when(sourceClient.execute(any(ProductTypeQuery.class))) - // .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); - // when(sourceClient.execute(any(TypeQuery.class))) - // .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); - // when(sourceClient.execute(any(CategoryQuery.class))) - // .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); - // when(sourceClient.execute(any(InventoryEntryQuery.class))) - // .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); - // when(sourceClient.execute(any(ProductProjectionQuery.class))) - // .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); - // when(sourceClient.execute(any(CartDiscountQuery.class))) - // .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); - // when(sourceClient.execute(any(StateQuery.class))) - // .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); - // when(sourceClient.execute(any(TaxCategoryQuery.class))) - // .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); - // when(sourceClient.execute(any(CustomObjectQuery.class))) - // .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); - // when(sourceClient.execute(any(CustomerQuery.class))) - // .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); - // when(sourceClient.execute(any(ShoppingListQuery.class))) - // .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); - // - // stubClientsCustomObjectService(targetClient, ZonedDateTime.now()); - // - // final SyncerFactory syncerFactory = - // spy(SyncerFactory.of(() -> sourceClient, () -> targetClient, getMockedClock())); - // - // // test - // CliRunner.of().run(new String[] {"-s", "all", "-r", "myRunner"}, syncerFactory); - // - // // assertions - // verify(syncerFactory, times(1)).sync(new String[] {"all"}, "myRunner", false, false, null); - // verify(sourceClient, times(1)).execute(any(ProductTypeQuery.class)); - // verify(sourceClient, times(1)).execute(any(TypeQuery.class)); - // verify(sourceClient, times(1)).execute(any(TaxCategoryQuery.class)); - // verify(sourceClient, times(1)).execute(any(CategoryQuery.class)); - // verify(sourceClient, times(1)).execute(any(ProductProjectionQuery.class)); - // verify(sourceClient, times(1)).execute(any(InventoryEntryQuery.class)); - // verify(sourceClient, times(1)).execute(any(CartDiscountQuery.class)); - // verify(sourceClient, times(1)).execute(any(StateQuery.class)); - // verify(sourceClient, times(1)).execute(any(CustomObjectQuery.class)); - // verify(sourceClient, times(1)).execute(any(CustomerQuery.class)); - // verify(sourceClient, times(1)).execute(any(ShoppingListQuery.class)); - // } - // - // @Test - // void run_WithSyncAsArgumentWithAllArg_ShouldExecuteAllSyncersInCorrectOrder() { - // // preparation - // final SphereClient sourceClient = mock(SphereClient.class); - // when(sourceClient.getConfig()).thenReturn(SphereClientConfig.of("foo", "foo", "foo")); - // - // final SphereClient targetClient = mock(SphereClient.class); - // when(targetClient.getConfig()).thenReturn(SphereClientConfig.of("bar", "bar", "bar")); - // - // when(sourceClient.execute(any(ProductTypeQuery.class))) - // .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); - // when(sourceClient.execute(any(TypeQuery.class))) - // .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); - // when(sourceClient.execute(any(CategoryQuery.class))) - // .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); - // when(sourceClient.execute(any(InventoryEntryQuery.class))) - // .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); - // when(sourceClient.execute(any(ProductProjectionQuery.class))) - // .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); - // when(sourceClient.execute(any(CartDiscountQuery.class))) - // .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); - // when(sourceClient.execute(any(StateQuery.class))) - // .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); - // when(sourceClient.execute(any(TaxCategoryQuery.class))) - // .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); - // when(sourceClient.execute(any(CustomObjectQuery.class))) - // .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); - // when(sourceClient.execute(any(CustomerQuery.class))) - // .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); - // when(sourceClient.execute(any(ShoppingListQuery.class))) - // .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); - // - // stubClientsCustomObjectService(targetClient, ZonedDateTime.now()); - // - // final SyncerFactory syncerFactory = - // spy(SyncerFactory.of(() -> sourceClient, () -> targetClient, getMockedClock())); - // - // // test - // CliRunner.of().run(new String[] {"-s", "all", "-f"}, syncerFactory); - // - // // assertions - // verify(syncerFactory, times(1)).sync(new String[] {"all"}, null, true, false, null); - // - // final InOrder inOrder = Mockito.inOrder(sourceClient); - // - // // Resources are grouped based on their references count. - // // Each group will run sequentially but the sync within the group runs in parallel. - // // So verifying the order of one resource in each group. - // inOrder.verify(sourceClient).execute(any(ProductTypeQuery.class)); - // verify(sourceClient, times(1)).execute(any(TypeQuery.class)); - // verify(sourceClient, times(1)).execute(any(CustomObjectQuery.class)); - // verify(sourceClient, times(1)).execute(any(StateQuery.class)); - // verify(sourceClient, times(1)).execute(any(TaxCategoryQuery.class)); - // - // inOrder.verify(sourceClient).execute(any(InventoryEntryQuery.class)); - // verify(sourceClient, times(1)).execute(any(CategoryQuery.class)); - // verify(sourceClient, times(1)).execute(any(CartDiscountQuery.class)); - // verify(sourceClient, times(1)).execute(any(CustomerQuery.class)); - // - // inOrder.verify(sourceClient).execute(any(ProductProjectionQuery.class)); - // - // inOrder.verify(sourceClient).execute(any(ShoppingListQuery.class)); - // - // verify(sourceClient, times(1)).close(); - // verify(sourceClient, times(11)).getConfig(); - // } - // - // @Test - // void run_WithOnlySyncCustomObjectArgument_ShouldThrowException() { - // // preparation - // final SphereClient sourceClient = mock(SphereClient.class); - // when(sourceClient.getConfig()).thenReturn(SphereClientConfig.of("foo", "foo", "foo")); - // - // final SphereClient targetClient = mock(SphereClient.class); - // when(targetClient.getConfig()).thenReturn(SphereClientConfig.of("bar", "bar", "bar")); - // - // final SyncerFactory syncerFactory = - // spy(SyncerFactory.of(() -> sourceClient, () -> targetClient, getMockedClock())); - // - // // test - // CliRunner.of().run(new String[] {"--syncProjectSyncCustomObjects"}, syncerFactory); - // - // // assertions - // assertThat(testLogger.getAllLoggingEvents()) - // .hasSize(1) - // .singleElement() - // .satisfies( - // loggingEvent -> { - // assertThat(loggingEvent.getLevel()).isEqualTo(Level.ERROR); - // assertThat(loggingEvent.getMessage()).contains("Failed to run sync process."); - // final Optional actualThrowableOpt = loggingEvent.getThrowable(); - // assertThat(actualThrowableOpt).isNotNull(); - // assertThat(actualThrowableOpt.isPresent()).isTrue(); - // final Throwable actualThrowable = actualThrowableOpt.get(); - // assertThat(actualThrowable).isExactlyInstanceOf(CliException.class); - // assertThat(actualThrowable.getMessage()) - // .isEqualTo( - // format( - // "Please pass at least 1 more option other than %s to the CLI.", - // CliRunner.SYNC_PROJECT_SYNC_CUSTOM_OBJECTS_OPTION_LONG)); - // }); - // } + @Test + void run_WithSyncAsArgumentWithNoArgs_ShouldFailAndLogError() { + // preparation + final SyncerFactory syncerFactory = + SyncerFactory.of(() -> sourceClient, () -> targetClient, getMockedClock()); + + // test + CliRunner.of().run(new String[] {"-s"}, syncerFactory); + + // assertion + assertThat(testLogger.getAllLoggingEvents()) + .hasSize(1) + .singleElement() + .satisfies( + loggingEvent -> { + assertThat(loggingEvent.getLevel()).isEqualTo(Level.ERROR); + assertThat(loggingEvent.getMessage()).contains("Failed to run sync process."); + final Optional actualThrowableOpt = loggingEvent.getThrowable(); + assertThat(actualThrowableOpt).isNotNull(); + assertThat(actualThrowableOpt.isPresent()).isTrue(); + final Throwable actualThrowable = actualThrowableOpt.get(); + assertThat(actualThrowable).isExactlyInstanceOf(CliException.class); + assertThat(actualThrowable.getMessage()).contains("Missing argument for option: s"); + }); + } + + @Test + void run_WithFullSyncAsFirstArgument_ShouldFailAndLogError() { + // preparation + final SyncerFactory syncerFactory = + SyncerFactory.of(() -> sourceClient, () -> targetClient, getMockedClock()); + + // test + CliRunner.of().run(new String[] {"-f"}, syncerFactory); + + // assertion + assertThat(testLogger.getAllLoggingEvents()) + .hasSize(1) + .singleElement() + .satisfies( + loggingEvent -> { + assertThat(loggingEvent.getLevel()).isEqualTo(Level.ERROR); + assertThat(loggingEvent.getMessage()).contains("Failed to run sync process."); + final Optional actualThrowableOpt = loggingEvent.getThrowable(); + assertThat(actualThrowableOpt).isNotNull(); + assertThat(actualThrowableOpt.isPresent()).isTrue(); + final Throwable actualThrowable = actualThrowableOpt.get(); + assertThat(actualThrowable).isExactlyInstanceOf(CliException.class); + assertThat(actualThrowable.getMessage()) + .contains("Please check that the first sync option is either -s, -h or -v."); + }); + } + + @Test + void run_AsProductDeltaSync_ShouldBuildSyncerAndExecuteSync() { + // preparation + stubClientsCustomObjectService(targetClient, ZonedDateTime.now()); + + final SyncerFactory syncerFactory = + spy(SyncerFactory.of(() -> sourceClient, () -> targetClient, getMockedClock())); + + // test + CliRunner.of().run(new String[] {"-s", "products"}, syncerFactory); + + // assertions + verify(syncerFactory, times(1)).sync(new String[] {"products"}, null, false, false, null); + verify(sourceClient, times(1)).productProjections(); + } + + @Test + void run_AsProductFullSync_ShouldBuildSyncerAndExecuteSync() { + // preparation + stubClientsCustomObjectService(targetClient, ZonedDateTime.now()); + + final SyncerFactory syncerFactory = + spy(SyncerFactory.of(() -> sourceClient, () -> targetClient, getMockedClock())); + + // test + CliRunner.of().run(new String[] {"-s", "products", "-f"}, syncerFactory); + + // assertions + verify(syncerFactory, times(1)).sync(new String[] {"products"}, null, true, false, null); + verify(sourceClient, times(1)).productProjections(); + } + + @Test + void + run_AsProductSyncWithCustomProductQueriesAndLimit_ShouldBuildSyncerAndExecuteQuerySuccessfully() { + // preparation + stubClientsCustomObjectService(targetClient, ZonedDateTime.now()); + final ByProjectKeyProductProjectionsGet getMock = mock(ByProjectKeyProductProjectionsGet.class); + when(getMock.addStaged(anyBoolean())).thenReturn(getMock); + when(getMock.withLimit(anyLong())).thenReturn(getMock); + when(getMock.withWhere(anyString())).thenReturn(getMock); + when(sourceClient.productProjections()).thenReturn(mock()); + when(sourceClient.productProjections().get()).thenReturn(getMock); + + final Long limit = 100L; + final String customQuery = + "published=true AND masterData(masterVariant(attributes(name=abc AND value=123)))"; + final String productQueryParametersValue = + "{\"limit\": " + limit + ", \"where\": \"" + customQuery + "\"}"; + + final SyncerFactory syncerFactory = + spy(SyncerFactory.of(() -> sourceClient, () -> targetClient, getMockedClock())); + // test + CliRunner.of() + .run( + new String[] { + "-s", "products", "-f", "-productQueryParameters", productQueryParametersValue + }, + syncerFactory); + + // assertions + verify(sourceClient.productProjections(), times(1)).get(); + verify(getMock, times(1)).withLimit(limit); + verify(getMock, times(1)).withWhere(customQuery); + } + + @Test + void + run_AsProductSyncWithProductQueryParametersAndOnlyLimit_ShouldBuildSyncerAndExecuteQuerySuccessfully() { + // preparation + stubClientsCustomObjectService(targetClient, ZonedDateTime.now()); + final ByProjectKeyProductProjectionsGet getMock = mock(ByProjectKeyProductProjectionsGet.class); + when(getMock.addStaged(anyBoolean())).thenReturn(getMock); + when(getMock.withLimit(anyLong())).thenReturn(getMock); + when(getMock.withWhere(anyString())).thenReturn(getMock); + when(sourceClient.productProjections()).thenReturn(mock()); + when(sourceClient.productProjections().get()).thenReturn(getMock); + + final Long limit = 100L; + final String productQueryParametersValue = "{\"limit\": " + limit + "}"; + + final SyncerFactory syncerFactory = + spy(SyncerFactory.of(() -> sourceClient, () -> targetClient, getMockedClock())); + + // test + CliRunner.of() + .run( + new String[] { + "-s", "products", "-f", "-productQueryParameters", productQueryParametersValue + }, + syncerFactory); + + // assertions + verify(sourceClient.productProjections(), times(1)).get(); + verify(getMock, times(1)).withLimit(limit); + verify(getMock, times(0)).withWhere(""); + } + + @Test + void + run_AsProductSyncWithProductQueryParametersAndOnlyWhere_ShouldBuildSyncerAndExecuteQuerySuccessfully() { + // preparation + stubClientsCustomObjectService(targetClient, ZonedDateTime.now()); + + final ByProjectKeyProductProjectionsGet getMock = mock(ByProjectKeyProductProjectionsGet.class); + when(getMock.addStaged(anyBoolean())).thenReturn(getMock); + when(getMock.withLimit(anyLong())).thenReturn(getMock); + when(getMock.withWhere(anyString())).thenReturn(getMock); + when(sourceClient.productProjections()).thenReturn(mock()); + when(sourceClient.productProjections().get()).thenReturn(getMock); + + final String customQuery = + "published=true AND masterVariant(attributes(name= \\\"abc\\\" AND value=123))"; + final String productQueryParametersValue = "{\"where\": \"" + customQuery + "\"}"; + + final SyncerFactory syncerFactory = + spy(SyncerFactory.of(() -> sourceClient, () -> targetClient, getMockedClock())); + + // test + CliRunner.of() + .run( + new String[] { + "-s", "products", "-f", "-productQueryParameters", productQueryParametersValue + }, + syncerFactory); + + // assertions + verify(sourceClient.productProjections(), times(1)).get(); + verify(getMock, times(0)).withLimit(1L); + verify(getMock, times(1)) + .withWhere("published=true AND masterVariant(attributes(name= \"abc\" AND value=123))"); + } + + @Test + void run_WithWrongFormatProductQueryParametersArgument_ShouldThrowCLIException() { + // preparation + final Long limit = 100L; + final String customQuery = + "\"published=true AND masterVariant(attributes(name= \"abc\\\" AND value=123))\""; + final String productQueryParametersValue = + "{\"limit\": " + limit + ", \"where\": " + customQuery + "}"; + + final SyncerFactory syncerFactory = + spy(SyncerFactory.of(() -> sourceClient, () -> targetClient, getMockedClock())); + + // test + CliRunner.of() + .run( + new String[] { + "-s", "products", "-f", "-productQueryParameters", productQueryParametersValue + }, + syncerFactory); + + // assertion + assertThat(testLogger.getAllLoggingEvents()) + .hasSize(1) + .singleElement() + .satisfies( + loggingEvent -> { + assertThat(loggingEvent.getLevel()).isEqualTo(Level.ERROR); + assertThat(loggingEvent.getMessage()).contains("Failed to run sync process."); + final Optional actualThrowableOpt = loggingEvent.getThrowable(); + assertThat(actualThrowableOpt).isNotNull(); + assertThat(actualThrowableOpt.isPresent()).isTrue(); + final Throwable actualThrowable = actualThrowableOpt.get(); + assertThat(actualThrowable).isExactlyInstanceOf(CliException.class); + }); + } + + @Test + void run_WithInvalidLimitInProductQueryParametersArgument_ShouldThrowCLIException() { + // preparation + final Long limit = -100L; + final String customQuery = + "\"published=true AND masterVariant(attributes(name= \"abc\\\" AND value=123))\""; + final String productQueryParametersValue = + "{\"limit\": " + limit + ", \"where\": " + customQuery + "}"; + + final SyncerFactory syncerFactory = + spy(SyncerFactory.of(() -> sourceClient, () -> targetClient, getMockedClock())); + + // test + CliRunner.of() + .run( + new String[] { + "-s", "products", "-f", "-productQueryParameters", productQueryParametersValue + }, + syncerFactory); + + // assertion + assertThat(testLogger.getAllLoggingEvents()) + .hasSize(1) + .singleElement() + .satisfies( + loggingEvent -> { + assertThat(loggingEvent.getLevel()).isEqualTo(Level.ERROR); + assertThat(loggingEvent.getMessage()).contains("Failed to run sync process."); + final Optional actualThrowableOpt = loggingEvent.getThrowable(); + assertThat(actualThrowableOpt).isNotNull(); + assertThat(actualThrowableOpt.isPresent()).isTrue(); + final Throwable actualThrowable = actualThrowableOpt.get(); + assertThat(actualThrowable).isExactlyInstanceOf(CliException.class); + assertThat(actualThrowable.getMessage()) + .contains("limit -100 cannot be less than 1."); + }); + } + + @Test + void run_AsTaxCategoryDeltaSync_ShouldBuildSyncerAndExecuteSync() { + // preparation + stubClientsCustomObjectService(targetClient, ZonedDateTime.now()); + final SyncerFactory syncerFactory = + spy(SyncerFactory.of(() -> sourceClient, () -> targetClient, getMockedClock())); + + // test + CliRunner.of().run(new String[] {"-s", "taxCategories"}, syncerFactory); + + // assertions + verify(syncerFactory, times(1)).sync(new String[] {"taxCategories"}, null, false, false, null); + verify(sourceClient, times(1)).taxCategories(); + } + + @Test + void run_AsTaxCategoryFullSync_ShouldBuildSyncerAndExecuteSync() { + // preparation + stubClientsCustomObjectService(targetClient, ZonedDateTime.now()); + + final SyncerFactory syncerFactory = + spy(SyncerFactory.of(() -> sourceClient, () -> targetClient, getMockedClock())); + + // test + CliRunner.of().run(new String[] {"-s", "taxCategories", "-f"}, syncerFactory); + + // assertions + verify(syncerFactory, times(1)).sync(new String[] {"taxCategories"}, null, true, false, null); + verify(sourceClient, times(1)).taxCategories(); + } + + @Test + void run_AsCustomerDeltaSync_ShouldBuildSyncerAndExecuteSync() { + // preparation + stubClientsCustomObjectService(targetClient, ZonedDateTime.now()); + final SyncerFactory syncerFactory = + spy(SyncerFactory.of(() -> sourceClient, () -> targetClient, getMockedClock())); + + // test + CliRunner.of().run(new String[] {"-s", "customers"}, syncerFactory); + + // assertions + verify(syncerFactory, times(1)).sync(new String[] {"customers"}, null, false, false, null); + verify(sourceClient, times(1)).customers(); + } + + @Test + void run_AsCustomerFullSync_ShouldBuildSyncerAndExecuteSync() { + // preparation + stubClientsCustomObjectService(targetClient, ZonedDateTime.now()); + final SyncerFactory syncerFactory = + spy(SyncerFactory.of(() -> sourceClient, () -> targetClient, getMockedClock())); + + // test + CliRunner.of().run(new String[] {"-s", "customers", "-f"}, syncerFactory); + + // assertions + verify(syncerFactory, times(1)).sync(new String[] {"customers"}, null, true, false, null); + verify(sourceClient, times(1)).customers(); + } + + @Test + void run_AsShoppingListDeltaSync_ShouldBuildSyncerAndExecuteSync() { + // preparation + stubClientsCustomObjectService(targetClient, ZonedDateTime.now()); + final SyncerFactory syncerFactory = + spy(SyncerFactory.of(() -> sourceClient, () -> targetClient, getMockedClock())); + + // test + CliRunner.of().run(new String[] {"-s", "shoppingLists"}, syncerFactory); + + // assertions + verify(syncerFactory, times(1)).sync(new String[] {"shoppingLists"}, null, false, false, null); + verify(sourceClient, times(1)).shoppingLists(); + } + + @Test + void run_AsShoppingListFullSync_ShouldBuildSyncerAndExecuteSync() { + // preparation + stubClientsCustomObjectService(targetClient, ZonedDateTime.now()); + + final SyncerFactory syncerFactory = + spy(SyncerFactory.of(() -> sourceClient, () -> targetClient, getMockedClock())); + + // test + CliRunner.of().run(new String[] {"-s", "shoppingLists", "-f"}, syncerFactory); + + // assertions + verify(syncerFactory, times(1)).sync(new String[] {"shoppingLists"}, null, true, false, null); + verify(sourceClient, times(1)).shoppingLists(); + } + + @Test + void run_AsCustomObjectDeltaSync_ShouldBuildSyncerAndExecuteSync() { + // preparation + stubClientsCustomObjectService(targetClient, ZonedDateTime.now()); + + final SyncerFactory syncerFactory = + spy(SyncerFactory.of(() -> sourceClient, () -> targetClient, getMockedClock())); + + // test + CliRunner.of().run(new String[] {"-s", "customObjects"}, syncerFactory); + + // assertions + verify(syncerFactory, times(1)).sync(new String[] {"customObjects"}, null, false, false, null); + verify(sourceClient, times(1)).customObjects(); + } + + @Test + void run_AsCustomObjectFullSync_ShouldBuildSyncerAndExecuteSync() { + // preparation + stubClientsCustomObjectService(targetClient, ZonedDateTime.now()); + final SyncerFactory syncerFactory = + spy(SyncerFactory.of(() -> sourceClient, () -> targetClient, getMockedClock())); + + // test + CliRunner.of().run(new String[] {"-s", "customObjects", "-f"}, syncerFactory); + + // assertions + verify(syncerFactory, times(1)).sync(new String[] {"customObjects"}, null, true, false, null); + verify(sourceClient, times(1)).customObjects(); + } + + @Test + void run_AsCartDiscountFullSync_ShouldBuildSyncerAndExecuteSync() { + // preparation + final SyncerFactory syncerFactory = + spy(SyncerFactory.of(() -> sourceClient, () -> targetClient, getMockedClock())); + + stubClientsCustomObjectService(targetClient, ZonedDateTime.now()); + + // test + CliRunner.of().run(new String[] {"-s", "cartDiscounts", "-f"}, syncerFactory); + + // assertions + verify(syncerFactory, times(1)).sync(new String[] {"cartDiscounts"}, null, true, false, null); + verify(sourceClient, times(1)).cartDiscounts(); + } + + @Test + void run_AsStateFullSync_ShouldBuildSyncerAndExecuteSync() { + // preparation + stubClientsCustomObjectService(targetClient, ZonedDateTime.now()); + final SyncerFactory syncerFactory = + spy(SyncerFactory.of(() -> sourceClient, () -> targetClient, getMockedClock())); + + // test + CliRunner.of().run(new String[] {"-s", "states", "-f"}, syncerFactory); + + // assertions + verify(syncerFactory, times(1)).sync(new String[] {"states"}, null, true, false, null); + verify(sourceClient, times(1)).states(); + } + + @Test + void run_WithSyncAsLongArgument_ShouldBuildSyncerAndExecuteSync() { + // preparation + stubClientsCustomObjectService(targetClient, ZonedDateTime.now()); + final SyncerFactory syncerFactory = + spy(SyncerFactory.of(() -> sourceClient, () -> targetClient, getMockedClock())); + + // test + CliRunner.of().run(new String[] {"--sync", "products"}, syncerFactory); + + // assertions + verify(syncerFactory, times(1)).sync(new String[] {"products"}, null, false, false, null); + verify(sourceClient, times(1)).productProjections(); + } + + @Test + void run_WithRunnerName_ShouldProcessSyncOption() { + // preparation + stubClientsCustomObjectService(targetClient, ZonedDateTime.now()); + + final SyncerFactory syncerFactory = + spy(SyncerFactory.of(() -> sourceClient, () -> targetClient, getMockedClock())); + + // test + CliRunner.of().run(new String[] {"--sync", "products", "-r", "Runner123"}, syncerFactory); + + // assertions + verify(syncerFactory, times(1)) + .sync(new String[] {"products"}, "Runner123", false, false, null); + verify(sourceClient, times(1)).productProjections(); + } + + @Test + void run_WithRunnerNameLong_ShouldProcessSyncOption() { + // preparation + stubClientsCustomObjectService(targetClient, ZonedDateTime.now()); + + final SyncerFactory syncerFactory = + spy(SyncerFactory.of(() -> sourceClient, () -> targetClient, getMockedClock())); + + // test + CliRunner.of() + .run( + new String[] {"--sync", "products", "--runnerName", "Runner123", "--full"}, + syncerFactory); + + // assertions + verify(syncerFactory, times(1)).sync(new String[] {"products"}, "Runner123", true, false, null); + verify(sourceClient, times(1)).productProjections(); + } + + @Test + void run_WithUnknownArgument_ShouldPrintAndLogError() { + // preparation + final SyncerFactory syncerFactory = + spy( + SyncerFactory.of( + () -> mock(ProjectApiRoot.class), + () -> mock(ProjectApiRoot.class), + getMockedClock())); + // test + CliRunner.of().run(new String[] {"-u"}, syncerFactory); + + // Assert error log + verify(syncerFactory, never()).sync(any(), any(), anyBoolean(), anyBoolean(), any()); + } + + @Test + void run_WithHelpAsArgument_ShouldPrintThreeOptionsWithDescriptionsToSystemOut() + throws UnsupportedEncodingException { + // preparation + final SyncerFactory syncerFactory = + spy( + SyncerFactory.of( + () -> mock(ProjectApiRoot.class), + () -> mock(ProjectApiRoot.class), + getMockedClock())); + + // test + CliRunner.of().run(new String[] {"-h"}, syncerFactory); + + // assertions + assertThat(testLogger.getAllLoggingEvents()).isEmpty(); + + // Remove line breaks from output stream string. + final String outputStreamWithoutLineBreaks = outputStream.toString("UTF-8").replace("\n", ""); + + // Replace multiple spaces with single space in output stream string. + final String outputStreamWithSingleSpaces = + outputStreamWithoutLineBreaks.trim().replaceAll(" +", " "); + + assertThat(outputStreamWithSingleSpaces) + .contains( + format("-%s,--%s %s", HELP_OPTION_SHORT, HELP_OPTION_LONG, HELP_OPTION_DESCRIPTION)) + .contains( + format( + "-%s,--%s %s", + SYNC_MODULE_OPTION_SHORT, SYNC_MODULE_OPTION_LONG, SYNC_MODULE_OPTION_DESCRIPTION)) + .contains( + format( + "-%s,--%s %s", + RUNNER_NAME_OPTION_SHORT, RUNNER_NAME_OPTION_LONG, RUNNER_NAME_OPTION_DESCRIPTION)) + .contains( + format( + "-%s,--%s %s", + VERSION_OPTION_SHORT, VERSION_OPTION_LONG, VERSION_OPTION_DESCRIPTION)); + verify(syncerFactory, never()).sync(any(), any(), anyBoolean(), anyBoolean(), any()); + } + + @Test + void run_WithSyncAsArgumentWithAllArg_ShouldExecuteAllSyncers() { + // preparation + stubClientsCustomObjectService(targetClient, ZonedDateTime.now()); + + final SyncerFactory syncerFactory = + spy(SyncerFactory.of(() -> sourceClient, () -> targetClient, getMockedClock())); + + // test + CliRunner.of().run(new String[] {"-s", "all"}, syncerFactory); + + // assertions + verify(syncerFactory, times(1)).sync(new String[] {"all"}, null, false, false, null); + verify(sourceClient, times(1)).productTypes(); + verify(sourceClient, times(1)).types(); + verify(sourceClient, times(1)).taxCategories(); + verify(sourceClient, times(1)).categories(); + verify(sourceClient, times(1)).productProjections(); + verify(sourceClient, times(1)).inventory(); + verify(sourceClient, times(1)).cartDiscounts(); + verify(sourceClient, times(1)).states(); + verify(sourceClient, times(1)).customObjects(); + verify(sourceClient, times(1)).customers(); + verify(sourceClient, times(1)).shoppingLists(); + } + + @Test + void run_WithSyncAsArgumentWithAllArgWithRunnerName_ShouldExecuteAllSyncers() { + // preparation + stubClientsCustomObjectService(targetClient, ZonedDateTime.now()); + + final SyncerFactory syncerFactory = + spy(SyncerFactory.of(() -> sourceClient, () -> targetClient, getMockedClock())); + + // test + CliRunner.of().run(new String[] {"-s", "all", "-r", "myRunner"}, syncerFactory); + + // assertions + verify(syncerFactory, times(1)).sync(new String[] {"all"}, "myRunner", false, false, null); + verify(sourceClient, times(1)).productTypes(); + verify(sourceClient, times(1)).types(); + verify(sourceClient, times(1)).taxCategories(); + verify(sourceClient, times(1)).categories(); + verify(sourceClient, times(1)).productProjections(); + verify(sourceClient, times(1)).inventory(); + verify(sourceClient, times(1)).cartDiscounts(); + verify(sourceClient, times(1)).states(); + verify(sourceClient, times(1)).customObjects(); + verify(sourceClient, times(1)).customers(); + verify(sourceClient, times(1)).shoppingLists(); + } + + @Test + void run_WithSyncAsArgumentWithAllArg_ShouldExecuteAllSyncersInCorrectOrder() { + // preparation + stubClientsCustomObjectService(targetClient, ZonedDateTime.now()); + + final SyncerFactory syncerFactory = + spy(SyncerFactory.of(() -> sourceClient, () -> targetClient, getMockedClock())); + + // test + CliRunner.of().run(new String[] {"-s", "all", "-f"}, syncerFactory); + + // assertions + verify(syncerFactory, times(1)).sync(new String[] {"all"}, null, true, false, null); + + final InOrder inOrder = Mockito.inOrder(sourceClient); + + // Resources are grouped based on their references count. + // Each group will run sequentially but the sync within the group runs in parallel. + // So verifying the order of one resource in each group. + inOrder.verify(sourceClient).productTypes(); + verify(sourceClient, times(1)).types(); + verify(sourceClient, times(1)).customObjects(); + verify(sourceClient, times(1)).states(); + verify(sourceClient, times(1)).taxCategories(); + + inOrder.verify(sourceClient).inventory(); + verify(sourceClient, times(1)).categories(); + verify(sourceClient, times(1)).cartDiscounts(); + verify(sourceClient, times(1)).customers(); + + inOrder.verify(sourceClient).productProjections(); + + inOrder.verify(sourceClient).shoppingLists(); + + verify(sourceClient, times(1)).close(); + // verify(sourceClient, times(11)).getConfig + } + + @Test + void run_WithOnlySyncCustomObjectArgument_ShouldThrowException() { + // preparation + final SyncerFactory syncerFactory = + spy(SyncerFactory.of(() -> sourceClient, () -> targetClient, getMockedClock())); + + // test + CliRunner.of().run(new String[] {"--syncProjectSyncCustomObjects"}, syncerFactory); + + // assertions + assertThat(testLogger.getAllLoggingEvents()) + .hasSize(1) + .singleElement() + .satisfies( + loggingEvent -> { + assertThat(loggingEvent.getLevel()).isEqualTo(Level.ERROR); + assertThat(loggingEvent.getMessage()).contains("Failed to run sync process."); + final Optional actualThrowableOpt = loggingEvent.getThrowable(); + assertThat(actualThrowableOpt).isNotNull(); + assertThat(actualThrowableOpt.isPresent()).isTrue(); + final Throwable actualThrowable = actualThrowableOpt.get(); + assertThat(actualThrowable).isExactlyInstanceOf(CliException.class); + assertThat(actualThrowable.getMessage()) + .isEqualTo( + format( + "Please pass at least 1 more option other than %s to the CLI.", + CliRunner.SYNC_PROJECT_SYNC_CUSTOM_OBJECTS_OPTION_LONG)); + }); + } } diff --git a/src/test/java/com/commercetools/project/sync/SyncerFactoryTest.java b/src/test/java/com/commercetools/project/sync/SyncerFactoryTest.java index 046d1214..f178d25b 100644 --- a/src/test/java/com/commercetools/project/sync/SyncerFactoryTest.java +++ b/src/test/java/com/commercetools/project/sync/SyncerFactoryTest.java @@ -1,9 +1,60 @@ package com.commercetools.project.sync; +import static com.commercetools.project.sync.CliRunner.SYNC_MODULE_OPTION_DESCRIPTION; +import static com.commercetools.project.sync.service.impl.CustomObjectServiceImpl.TIMESTAMP_GENERATOR_KEY; +import static com.commercetools.project.sync.util.SyncUtils.DEFAULT_RUNNER_NAME; +import static com.commercetools.project.sync.util.SyncUtils.getApplicationName; +import static com.commercetools.project.sync.util.TestUtils.assertCartDiscountSyncerLoggingEvents; +import static com.commercetools.project.sync.util.TestUtils.assertCategorySyncerLoggingEvents; +import static com.commercetools.project.sync.util.TestUtils.assertCustomObjectSyncerLoggingEvents; +import static com.commercetools.project.sync.util.TestUtils.assertCustomerSyncerLoggingEvents; +import static com.commercetools.project.sync.util.TestUtils.assertInventoryEntrySyncerLoggingEvents; +import static com.commercetools.project.sync.util.TestUtils.assertProductSyncerLoggingEvents; +import static com.commercetools.project.sync.util.TestUtils.assertProductTypeSyncerLoggingEvents; +import static com.commercetools.project.sync.util.TestUtils.assertShoppingListSyncerLoggingEvents; +import static com.commercetools.project.sync.util.TestUtils.assertStateSyncerLoggingEvents; +import static com.commercetools.project.sync.util.TestUtils.assertTaxCategorySyncerLoggingEvents; +import static com.commercetools.project.sync.util.TestUtils.assertTypeSyncerLoggingEvents; +import static com.commercetools.project.sync.util.TestUtils.createBadGatewayException; +import static com.commercetools.project.sync.util.TestUtils.getMockedClock; +import static com.commercetools.project.sync.util.TestUtils.mockLastSyncCustomObject; +import static com.commercetools.project.sync.util.TestUtils.readObjectFromResource; +import static com.commercetools.project.sync.util.TestUtils.readStringFromFile; +import static com.commercetools.project.sync.util.TestUtils.stubClientsCustomObjectService; +import static com.commercetools.project.sync.util.TestUtils.withTestClient; +import static java.lang.String.format; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.atLeast; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoInteractions; +import static org.mockito.Mockito.when; + +import com.commercetools.api.client.ByProjectKeyCustomObjectsPost; +import com.commercetools.api.client.ByProjectKeyProductsPost; +import com.commercetools.api.client.ProjectApiRoot; +import com.commercetools.api.defaultconfig.ApiRootBuilder; +import com.commercetools.api.models.ResourcePagedQueryResponse; +import com.commercetools.api.models.custom_object.CustomObject; +import com.commercetools.api.models.custom_object.CustomObjectDraft; +import com.commercetools.api.models.graph_ql.GraphQLRequest; +import com.commercetools.api.models.graph_ql.GraphQLRequestBuilder; +import com.commercetools.api.models.product.Product; +import com.commercetools.api.models.product.ProductDraft; +import com.commercetools.api.models.product.ProductMixin; +import com.commercetools.api.models.product.ProductProjection; +import com.commercetools.api.models.product.ProductProjectionPagedQueryResponse; +import com.commercetools.api.models.product.ProductProjectionPagedQueryResponseBuilder; +import com.commercetools.api.models.product.ProductProjectionType; import com.commercetools.project.sync.cartdiscount.CartDiscountSyncer; import com.commercetools.project.sync.category.CategorySyncer; import com.commercetools.project.sync.customer.CustomerSyncer; import com.commercetools.project.sync.customobject.CustomObjectSyncer; +import com.commercetools.project.sync.exception.CliException; import com.commercetools.project.sync.inventoryentry.InventoryEntrySyncer; import com.commercetools.project.sync.product.ProductSyncer; import com.commercetools.project.sync.producttype.ProductTypeSyncer; @@ -11,15 +62,41 @@ import com.commercetools.project.sync.state.StateSyncer; import com.commercetools.project.sync.taxcategory.TaxCategorySyncer; import com.commercetools.project.sync.type.TypeSyncer; +import com.commercetools.sync.commons.exceptions.ReferenceTransformException; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import io.vrap.rmf.base.client.ApiHttpMethod; +import io.vrap.rmf.base.client.ApiHttpResponse; +import io.vrap.rmf.base.client.error.BadGatewayException; +import io.vrap.rmf.base.client.utils.CompletableFutureUtils; +import io.vrap.rmf.base.client.utils.json.JsonUtils; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.time.ZonedDateTime; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionStage; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.Collectors; +import java.util.stream.IntStream; +import javax.annotation.Nonnull; +import org.assertj.core.api.Condition; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.InOrder; +import org.mockito.Mockito; +import uk.org.lidalia.slf4jext.Level; +import uk.org.lidalia.slf4jtest.LoggingEvent; import uk.org.lidalia.slf4jtest.TestLogger; import uk.org.lidalia.slf4jtest.TestLoggerFactory; // This will suppress MoreThanOneLogger warnings in this class @SuppressWarnings("PMD.MoreThanOneLogger") - -// These tests aren't migrated -// TODO: Migrate tests class SyncerFactoryTest { private static final TestLogger productSyncerTestLogger = TestLoggerFactory.getTestLogger(ProductSyncer.class); @@ -46,7 +123,19 @@ class SyncerFactoryTest { private static final TestLogger taxCategorySyncerTestLogger = TestLoggerFactory.getTestLogger(TaxCategorySyncer.class); + private ProjectApiRoot sourceClient; + private ProjectApiRoot targetClient; + @BeforeEach + void setupTest() { + final ProjectApiRoot clientWithEmptyResourceResults = + mockClientResourceRequests("testProjectKey"); + sourceClient = spy(clientWithEmptyResourceResults); + targetClient = mock(ProjectApiRoot.class); + when(targetClient.getProjectKey()).thenReturn("testTargetProjectKey"); + } + + @AfterEach void tearDownTest() { cliRunnerTestLogger.clearAll(); productSyncerTestLogger.clearAll(); @@ -62,1242 +151,1189 @@ void tearDownTest() { taxCategorySyncerTestLogger.clearAll(); } - // @Test - // void sync_WithNullOptionValue_ShouldCompleteExceptionallyWithIllegalArgumentException() { - // assertThat( - // SyncerFactory.of( - // () -> mock(SphereClient.class), - // () -> mock(SphereClient.class), - // getMockedClock()) - // .sync(new String[] {null}, "myRunnerName", false, false, null)) - // .failsWithin(1, TimeUnit.SECONDS) - // .withThrowableOfType(ExecutionException.class) - // .withCauseExactlyInstanceOf(CliException.class) - // .withMessageContaining( - // format( - // "Blank argument supplied to \"-s\" or \"--sync\" option! %s", - // SYNC_MODULE_OPTION_DESCRIPTION)); - // } - // - // @Test - // void sync_WithEmptyOptionValue_ShouldCompleteExceptionallyWithIllegalArgumentException() { - // assertThat( - // SyncerFactory.of( - // () -> mock(SphereClient.class), - // () -> mock(SphereClient.class), - // getMockedClock()) - // .sync(new String[] {""}, "myRunnerName", false, false, null)) - // .failsWithin(1, TimeUnit.SECONDS) - // .withThrowableOfType(ExecutionException.class) - // .withCauseExactlyInstanceOf(CliException.class) - // .withMessageContaining( - // format( - // "Blank argument supplied to \"-s\" or \"--sync\" option! %s", - // SYNC_MODULE_OPTION_DESCRIPTION)); - // } - // - // @Test - // void sync_WithUnknownOptionValue_ShouldCompleteExceptionallyWithIllegalArgumentException() { - // final String[] unknownOptionValue = {"anyOption"}; - // - // assertThat( - // SyncerFactory.of( - // () -> mock(SphereClient.class), - // () -> mock(SphereClient.class), - // getMockedClock()) - // .sync(unknownOptionValue, "myRunnerName", false, false, null)) - // .failsWithin(1, TimeUnit.SECONDS) - // .withThrowableOfType(ExecutionException.class) - // .withCauseExactlyInstanceOf(CliException.class) - // .withMessageContaining( - // format( - // "Unknown argument \"%s\" supplied to \"-s\" or \"--sync\" option! %s", - // unknownOptionValue[0], SYNC_MODULE_OPTION_DESCRIPTION)); - // } - // - // @Test - // @SuppressWarnings("unchecked") - // void sync_AsProductsDeltaSync_ShouldBuildSyncerAndExecuteSync() { - // // preparation - // final SphereClient sourceClient = mock(SphereClient.class); - // when(sourceClient.getConfig()).thenReturn(SphereClientConfig.of("foo", "foo", "foo")); - // - // final SphereClient targetClient = mock(SphereClient.class); - // when(targetClient.getConfig()).thenReturn(SphereClientConfig.of("bar", "bar", "bar")); - // - // when(sourceClient.execute(any(ProductProjectionQuery.class))) - // .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); - // - // final SyncerFactory syncerFactory = - // SyncerFactory.of(() -> sourceClient, () -> targetClient, getMockedClock()); - // - // final ZonedDateTime currentCtpTimestamp = ZonedDateTime.now(); - // stubClientsCustomObjectService(targetClient, currentCtpTimestamp); - // - // // test - // syncerFactory.sync(new String[] {"products"}, "myRunnerName", false, false, null); - // - // // assertions - // verify(sourceClient, times(1)).execute(any(ProductProjectionQuery.class)); - // - // verifyTimestampGeneratorCustomObjectUpsertIsCalled(targetClient, "ProductSync", - // "myRunnerName"); - // verifyLastSyncCustomObjectQuery(targetClient, "productSync", "myRunnerName", "foo", 1); - // // verify two custom object upserts : 1. current ctp timestamp and 2. last sync timestamp - // // creation) - // verify(targetClient, times(2)).execute(any(CustomObjectUpsertCommand.class)); - // // TODO: Assert on actual last sync timestamp creation in detail after Statistics classes in - // // java-sync library - // // TODO: override #equals method: - // // https://github.com/commercetools/commercetools-sync-java/issues/376 - // // TODO: e.g. verifyNewLastSyncCustomObjectCreation(targetClient, - // // currentCtpTimestamp.minusMinutes(2), any(ProductSyncStatistics.class), 0L, "productSync", - // // "foo"); - // verifyInteractionsWithClientAfterSync(sourceClient, 1); - // - // final Condition startLog = - // new Condition<>( - // loggingEvent -> - // Level.INFO.equals(loggingEvent.getLevel()) - // && loggingEvent.getMessage().contains("Starting ProductSync"), - // "start log"); - // - // final Condition statisticsLog = - // new Condition<>( - // loggingEvent -> - // Level.INFO.equals(loggingEvent.getLevel()) - // && loggingEvent - // .getMessage() - // .contains( - // "Summary: 0 product(s) were processed in total (0 created, 0 - // updated, " - // + "0 failed to sync and 0 product(s) with missing - // reference(s))."), - // "statistics log"); - // - // assertThat(productSyncerTestLogger.getAllLoggingEvents()) - // .hasSize(2) - // .haveExactly(1, startLog) - // .haveExactly(1, statisticsLog); - // } - // - // @Test - // @SuppressWarnings("unchecked") - // void sync_AsProductsFullSync_ShouldBuildSyncerAndExecuteSync() { - // // preparation - // final SphereClient sourceClient = mock(SphereClient.class); - // when(sourceClient.getConfig()).thenReturn(SphereClientConfig.of("foo", "foo", "foo")); - // - // final SphereClient targetClient = mock(SphereClient.class); - // when(targetClient.getConfig()).thenReturn(SphereClientConfig.of("bar", "bar", "bar")); - // - // when(sourceClient.execute(any(ProductProjectionQuery.class))) - // .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); - // - // final SyncerFactory syncerFactory = - // SyncerFactory.of(() -> sourceClient, () -> targetClient, getMockedClock()); - // - // final ZonedDateTime currentCtpTimestamp = ZonedDateTime.now(); - // stubClientsCustomObjectService(targetClient, currentCtpTimestamp); - // - // // test - // syncerFactory.sync(new String[] {"products"}, "myRunnerName", true, false, null); - // - // // assertions - // verify(sourceClient, times(1)).execute(any(ProductProjectionQuery.class)); - // - // verifyTimestampGeneratorCustomObjectUpsertIsNotCalled( - // targetClient, "ProductSync", "myRunnerName"); - // verifyLastSyncCustomObjectQuery(targetClient, "productSync", "myRunnerName", "foo", 0); - // verify(targetClient, times(0)).execute(any(CustomObjectUpsertCommand.class)); - // verifyInteractionsWithClientAfterSync(sourceClient, 1); - // - // final Condition startLog = - // new Condition<>( - // loggingEvent -> - // Level.INFO.equals(loggingEvent.getLevel()) - // && loggingEvent.getMessage().contains("Starting ProductSync"), - // "start log"); - // - // final Condition statisticsLog = - // new Condition<>( - // loggingEvent -> - // Level.INFO.equals(loggingEvent.getLevel()) - // && loggingEvent - // .getMessage() - // .contains( - // "Summary: 0 product(s) were processed in total (0 created, 0 - // updated, " - // + "0 failed to sync and 0 product(s) with missing - // reference(s))."), - // "statistics log"); - // - // assertThat(productSyncerTestLogger.getAllLoggingEvents()) - // .hasSize(2) - // .haveExactly(1, startLog) - // .haveExactly(1, statisticsLog); - // } - // - // @Test - // void - // - // sync_AsProductsFullSyncWithExceptionDuringAttributeReferenceReplacement_ShouldBuildSyncerAndExecuteSync() { - // // preparation - // final SphereClient sourceClient = mock(SphereClient.class); - // when(sourceClient.getConfig()).thenReturn(SphereClientConfig.of("foo", "foo", "foo")); - // - // final SphereClient targetClient = mock(SphereClient.class); - // when(targetClient.getConfig()).thenReturn(SphereClientConfig.of("bar", "bar", "bar")); - // - // final ProductProjection product5 = - // SphereJsonUtils.readObjectFromResource("product-key-5.json", Product.class) - // .toProjection(STAGED); - // final ProductProjection product6 = - // SphereJsonUtils.readObjectFromResource("product-key-6.json", Product.class) - // .toProjection(STAGED); - // final PagedQueryResult twoProductResult = - // MockPagedQueryResult.of(asList(product5, product6)); - // - // when(sourceClient.execute(any(ProductProjectionQuery.class))) - // .thenReturn(CompletableFuture.completedFuture(twoProductResult)); - // - // when(targetClient.execute(any())).thenReturn(CompletableFuture.completedFuture(null)); - // final BadGatewayException badGatewayException = new BadGatewayException("Error!"); - // when(targetClient.execute(any(ProductCreateCommand.class))) - // .thenReturn(CompletableFutureUtils.failed(badGatewayException)); - // when(sourceClient.execute(any(ResourceIdsGraphQlRequest.class))) - // .thenReturn(CompletableFutureUtils.failed(badGatewayException)); - // - // final SyncerFactory syncerFactory = - // SyncerFactory.of(() -> sourceClient, () -> targetClient, getMockedClock()); - // - // // test - // syncerFactory.sync(new String[] {"products"}, "myRunnerName", true, false, null); - // - // // assertions - // verify(sourceClient, times(1)).execute(any(ProductProjectionQuery.class)); - // verify(sourceClient, times(3)).execute(any(ResourceIdsGraphQlRequest.class)); - // verifyInteractionsWithClientAfterSync(sourceClient, 1); - // - // final Condition startLog = - // new Condition<>( - // loggingEvent -> - // Level.INFO.equals(loggingEvent.getLevel()) - // && loggingEvent.getMessage().contains("Starting ProductSync"), - // "start log"); - // - // final Condition statisticsLog = - // new Condition<>( - // loggingEvent -> - // Level.INFO.equals(loggingEvent.getLevel()) - // && loggingEvent - // .getMessage() - // .contains( - // "Summary: 0 product(s) were processed in total (0 created, 0 - // updated, " - // + "0 failed to sync and 0 product(s) with missing - // reference(s))."), - // "statistics log"); - // - // assertThat(productSyncerTestLogger.getAllLoggingEvents()) - // .hasSize(3) - // .haveExactly(1, startLog) - // .haveExactly(1, statisticsLog); - // - // assertThat(productSyncerTestLogger.getAllLoggingEvents()) - // .anySatisfy( - // loggingEvent -> { - // assertThat(loggingEvent.getMessage()) - // .contains( - // ReferenceTransformException.class.getCanonicalName() - // + ": Failed to replace referenced resource ids with keys on the - // attributes of the " - // + "products in the current fetched page from the source project. " - // + "This page will not be synced to the target project."); - // assertThat(loggingEvent.getThrowable().isPresent()).isTrue(); - // assertThat(loggingEvent.getThrowable().get().getCause().getCause()) - // .isEqualTo(badGatewayException); - // }); - // } - // - // @Test - // void - // - // sync_AsProductsFullSyncWithExceptionDuringAttributeReferenceReplacement_ShouldContinueWithPages() { - // // preparation - // final SphereClient sourceClient = mock(SphereClient.class); - // when(sourceClient.getConfig()).thenReturn(SphereClientConfig.of("foo", "foo", "foo")); - // - // final SphereClient targetClient = mock(SphereClient.class); - // when(targetClient.getConfig()).thenReturn(SphereClientConfig.of("bar", "bar", "bar")); - // - // final ProductProjection product1 = - // SphereJsonUtils.readObjectFromResource("product-key-7.json", Product.class) - // .toProjection(STAGED); - // final ProductProjection product2 = - // SphereJsonUtils.readObjectFromResource("product-key-8.json", Product.class) - // .toProjection(STAGED); - // final ProductProjection product3 = - // SphereJsonUtils.readObjectFromResource("product-key-9.json", Product.class) - // .toProjection(STAGED); - // - // final List fullPageOfProducts = - // IntStream.range(0, 500).mapToObj(o -> product1).collect(Collectors.toList()); - // - // when(sourceClient.execute(any(ProductProjectionQuery.class))) - // - // .thenReturn(CompletableFuture.completedFuture(MockPagedQueryResult.of(fullPageOfProducts))) - // .thenReturn( - // CompletableFuture.completedFuture(MockPagedQueryResult.of(asList(product3, - // product2)))); - // - // when(targetClient.execute(any(ProductProjectionQuery.class))) - // .thenReturn(CompletableFuture.completedFuture(MockPagedQueryResult.of(emptyList()))); - // when(targetClient.execute(any(ProductTypeQuery.class))) - // .thenReturn(CompletableFuture.completedFuture(MockPagedQueryResult.of(emptyList()))); - // when(targetClient.execute(any(CategoryQuery.class))) - // .thenReturn(CompletableFuture.completedFuture(MockPagedQueryResult.of(emptyList()))); - // when(targetClient.execute(any(TaxCategoryQuery.class))) - // .thenReturn(CompletableFuture.completedFuture(MockPagedQueryResult.of(emptyList()))); - // when(targetClient.execute(any(StateQuery.class))) - // .thenReturn(CompletableFuture.completedFuture(MockPagedQueryResult.of(emptyList()))); - // when(targetClient.execute(any(CustomObjectQuery.class))) - // .thenReturn(CompletableFuture.completedFuture(MockPagedQueryResult.of(emptyList()))); - // - // final Product product4 = - // SphereJsonUtils.readObjectFromResource("product-key-8.json", Product.class); - // - // when(targetClient.execute(any(ProductCreateCommand.class))) - // .thenReturn(CompletableFuture.completedFuture(product4)); - // - // String jsonAsString = - // - // "{\"results\":[{\"id\":\"53c4a8b4-865f-4b95-b6f2-3e1e70e3d0c1\",\"key\":\"productKey3\"}]}"; - // final ResourceKeyIdGraphQlResult productsResult = - // SphereJsonUtils.readObject(jsonAsString, ResourceKeyIdGraphQlResult.class); - // - // String jsonStringProductTypes = - // - // "{\"results\":[{\"id\":\"53c4a8b4-865f-4b95-b6f2-3e1e70e3d0c2\",\"key\":\"prodType1\"}]}"; - // final ResourceKeyIdGraphQlResult productTypesResult = - // SphereJsonUtils.readObject(jsonStringProductTypes, ResourceKeyIdGraphQlResult.class); - // - // String jsonStringCategories = - // "{\"results\":[{\"id\":\"53c4a8b4-865f-4b95-b6f2-3e1e70e3d0c3\",\"key\":\"cat1\"}]}"; - // final ResourceKeyIdGraphQlResult categoriesResult = - // SphereJsonUtils.readObject(jsonStringCategories, ResourceKeyIdGraphQlResult.class); - // - // final BadGatewayException badGatewayException = new BadGatewayException("Error!"); - // when(sourceClient.execute(any(ResourceIdsGraphQlRequest.class))) - // .thenReturn(CompletableFutureUtils.failed(badGatewayException)) - // .thenReturn(CompletableFutureUtils.failed(badGatewayException)) - // .thenReturn(CompletableFutureUtils.failed(badGatewayException)) - // .thenReturn(CompletableFuture.completedFuture(productsResult)) - // .thenReturn(CompletableFuture.completedFuture(categoriesResult)) - // .thenReturn(CompletableFuture.completedFuture(productTypesResult)); - // - // final ResourceKeyIdGraphQlResult resourceKeyIdGraphQlResult = - // mock(ResourceKeyIdGraphQlResult.class); - // when(resourceKeyIdGraphQlResult.getResults()) - // .thenReturn( - // singleton(new ResourceKeyId("productKey3", - // "53c4a8b4-865f-4b95-b6f2-3e1e70e3d0c1"))); - // when(targetClient.execute(any(ResourceKeyIdGraphQlRequest.class))) - // .thenReturn(CompletableFuture.completedFuture(resourceKeyIdGraphQlResult)); - // - // final SyncerFactory syncerFactory = - // SyncerFactory.of(() -> sourceClient, () -> targetClient, getMockedClock()); - // - // // test - // syncerFactory.sync(new String[] {"products"}, "myRunnerName", true, false, null); - // - // // assertions - // verify(sourceClient, times(2)).execute(any(ProductProjectionQuery.class)); - // verify(sourceClient, times(9)).execute(any(ResourceIdsGraphQlRequest.class)); - // verifyInteractionsWithClientAfterSync(sourceClient, 1); - // - // final Condition startLog = - // new Condition<>( - // loggingEvent -> - // Level.INFO.equals(loggingEvent.getLevel()) - // && loggingEvent.getMessage().contains("Starting ProductSync"), - // "start log"); - // - // final Condition statisticsLog = - // new Condition<>( - // loggingEvent -> - // Level.INFO.equals(loggingEvent.getLevel()) - // && loggingEvent - // .getMessage() - // .contains( - // "Summary: 2 product(s) were processed in total (2 created, 0 - // updated, " - // + "0 failed to sync and 0 product(s) with missing - // reference(s))."), - // "statistics log"); - // - // assertThat(productSyncerTestLogger.getAllLoggingEvents()) - // .hasSize(3) - // .haveExactly(1, startLog) - // .haveExactly(1, statisticsLog); - // - // assertThat(productSyncerTestLogger.getAllLoggingEvents()) - // .anySatisfy( - // loggingEvent -> { - // assertThat(loggingEvent.getMessage()) - // .contains( - // ReferenceTransformException.class.getCanonicalName() - // + ": Failed to replace referenced resource ids with keys on the - // attributes of the " - // + "products in the current fetched page from the source project. " - // + "This page will not be synced to the target project."); - // assertThat(loggingEvent.getThrowable().isPresent()).isTrue(); - // assertThat(loggingEvent.getThrowable().get().getCause().getCause()) - // .isEqualTo(badGatewayException); - // }); - // } - // - // private static void verifyTimestampGeneratorCustomObjectUpsertIsCalled( - // @Nonnull final SphereClient client, - // @Nonnull final String syncMethodName, - // @Nonnull final String syncRunnerName) { - // final CustomObjectDraft customObjectDraft = - // findTimestampGeneratorCustomObjectUpsert(client, syncMethodName, syncRunnerName); - // assertThat(customObjectDraft).isNotNull(); - // assertThat((String) customObjectDraft.getValue()) - // .matches( - // - // "[0-9a-fA-F]{8}\\-[0-9a-fA-F]{4}\\-[0-9a-fA-F]{4}\\-[0-9a-fA-F]{4}\\-[0-9a-fA-F]{12}"); - // } - // - // private static void verifyTimestampGeneratorCustomObjectUpsertIsNotCalled( - // @Nonnull final SphereClient client, - // @Nonnull final String syncMethodName, - // @Nonnull final String syncRunnerName) { - // final CustomObjectDraft customObjectDraft = - // findTimestampGeneratorCustomObjectUpsert(client, syncMethodName, syncRunnerName); - // assertThat(customObjectDraft).isNull(); - // } - // - // private static CustomObjectDraft findTimestampGeneratorCustomObjectUpsert( - // @Nonnull SphereClient client, - // @Nonnull String syncMethodName, - // @Nonnull String syncRunnerName) { - // // fact: SphereRequest is a very broad interface and we actually wanted to capture only - // // CustomObjectUpsertCommand. - // // I tried it but argumentcaptor captures also CustomObjectQueryImpl classes, because we - // call - // // both query and upsert in the mocked SphereClient. - // // This situation throws runtime NPE error later in the method as query doesnt contain a - // draft. - // // I guess generics doesnt work here as type is not know on compile time. - // // That's why we need to filter instanceof CustomObjectUpsertCommand in the streams. - // final ArgumentCaptor sphereClientArgumentCaptor = - // ArgumentCaptor.forClass(CustomObjectUpsertCommand.class); - // - // verify(client, atLeast(0)).execute(sphereClientArgumentCaptor.capture()); - // final List allValues = sphereClientArgumentCaptor.getAllValues(); - // final CustomObjectDraft customObjectDraft = - // allValues.stream() - // .filter(sphereRequest -> sphereRequest instanceof CustomObjectUpsertCommand) - // .map(sphereRequest -> (CustomObjectUpsertCommand) sphereRequest) - // .map(command -> (CustomObjectDraft) command.getDraft()) - // .filter( - // draft -> { - // return draft - // .getContainer() - // .equals( - // format( - // "%s.%s.%s.%s", - // getApplicationName(), - // syncRunnerName, - // syncMethodName, - // TIMESTAMP_GENERATOR_KEY)) - // && draft.getKey().equals(TIMESTAMP_GENERATOR_KEY); - // }) - // .findAny() - // .orElse(null); - // return customObjectDraft; - // } - // - // private static void verifyLastSyncCustomObjectQuery( - // @Nonnull final SphereClient client, - // @Nonnull final String syncModuleName, - // @Nonnull final String syncRunnerName, - // @Nonnull final String sourceProjectKey, - // final int expectedInvocations) { - // - // final QueryPredicate> queryPredicate = - // QueryPredicate.of( - // format( - // "container=\"commercetools-project-sync.%s.%s\" AND key=\"%s\"", - // syncRunnerName, syncModuleName, sourceProjectKey)); - // - // verify(client, times(expectedInvocations)) - // - // .execute(CustomObjectQuery.of(LastSyncCustomObject.class).plusPredicates(queryPredicate)); - // } - // - // @Test - // @SuppressWarnings("unchecked") - // void sync_AsCategoriesDeltaSync_ShouldBuildSyncerAndExecuteSync() { - // // preparation - // final SphereClient sourceClient = mock(SphereClient.class); - // when(sourceClient.getConfig()).thenReturn(SphereClientConfig.of("foo", "foo", "foo")); - // - // final SphereClient targetClient = mock(SphereClient.class); - // when(targetClient.getConfig()).thenReturn(SphereClientConfig.of("bar", "bar", "bar")); - // - // when(sourceClient.execute(any(CategoryQuery.class))) - // .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); - // - // final SyncerFactory syncerFactory = - // SyncerFactory.of(() -> sourceClient, () -> targetClient, getMockedClock()); - // - // final ZonedDateTime currentCtpTimestamp = ZonedDateTime.now(); - // stubClientsCustomObjectService(targetClient, currentCtpTimestamp); - // - // // test - // syncerFactory.sync(new String[] {"categories"}, null, false, false, null); - // - // // assertions - // verify(sourceClient, times(1)).execute(any(CategoryQuery.class)); - // verifyTimestampGeneratorCustomObjectUpsertIsCalled( - // targetClient, "CategorySync", DEFAULT_RUNNER_NAME); - // verifyLastSyncCustomObjectQuery(targetClient, "categorySync", DEFAULT_RUNNER_NAME, "foo", - // 1); - // // verify two custom object upserts : 1. current ctp timestamp and 2. last sync timestamp - // // creation) - // verify(targetClient, times(2)).execute(any(CustomObjectUpsertCommand.class)); - // // TODO: Assert on actual last sync timestamp creation in detail after Statistics classes in - // // java-sync library - // // TODO: override #equals method: - // // https://github.com/commercetools/commercetools-sync-java/issues/376 - // // TODO: e.g. verifyNewLastSyncCustomObjectCreation(targetClient, - // // currentCtpTimestamp.minusMinutes(2), any(ProductSyncStatistics.class), 0L, "productSync", - // // "foo"); - // verifyInteractionsWithClientAfterSync(sourceClient, 1); - // - // final Condition startLog = - // new Condition<>( - // loggingEvent -> - // Level.INFO.equals(loggingEvent.getLevel()) - // && loggingEvent.getMessage().contains("Starting CategorySync"), - // "start log"); - // - // final Condition statisticsLog = - // new Condition<>( - // loggingEvent -> - // Level.INFO.equals(loggingEvent.getLevel()) - // && loggingEvent - // .getMessage() - // .contains( - // "Summary: 0 categories were processed in total (0 created, 0 - // updated, " - // + "0 failed to sync and 0 categories with a missing parent)."), - // "statistics log"); - // - // assertThat(categorySyncerTestLogger.getAllLoggingEvents()) - // .hasSize(2) - // .haveExactly(1, startLog) - // .haveExactly(1, statisticsLog); - // } - // - // @Test - // @SuppressWarnings("unchecked") - // void sync_AsProductTypesDeltaSync_ShouldBuildSyncerAndExecuteSync() { - // // preparation - // final SphereClient sourceClient = mock(SphereClient.class); - // when(sourceClient.getConfig()).thenReturn(SphereClientConfig.of("foo", "foo", "foo")); - // - // final SphereClient targetClient = mock(SphereClient.class); - // when(targetClient.getConfig()).thenReturn(SphereClientConfig.of("bar", "bar", "bar")); - // - // when(sourceClient.execute(any(ProductTypeQuery.class))) - // .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); - // - // final SyncerFactory syncerFactory = - // SyncerFactory.of(() -> sourceClient, () -> targetClient, getMockedClock()); - // - // final ZonedDateTime currentCtpTimestamp = ZonedDateTime.now(); - // stubClientsCustomObjectService(targetClient, currentCtpTimestamp); - // - // // test - // syncerFactory.sync(new String[] {"productTypes"}, "", false, false, null); - // - // // assertions - // verify(sourceClient, times(1)).execute(any(ProductTypeQuery.class)); - // verifyTimestampGeneratorCustomObjectUpsertIsCalled( - // targetClient, "ProductTypeSync", DEFAULT_RUNNER_NAME); - // verifyLastSyncCustomObjectQuery(targetClient, "productTypeSync", DEFAULT_RUNNER_NAME, "foo", - // 1); - // // verify two custom object upserts : 1. current ctp timestamp and 2. last sync timestamp - // // creation) - // verify(targetClient, times(2)).execute(any(CustomObjectUpsertCommand.class)); - // // TODO: Assert on actual last sync timestamp creation in detail after Statistics classes in - // // java-sync library - // // TODO: override #equals method: - // // https://github.com/commercetools/commercetools-sync-java/issues/376 - // // TODO: e.g. verifyNewLastSyncCustomObjectCreation(targetClient, - // // currentCtpTimestamp.minusMinutes(2), any(ProductSyncStatistics.class), 0L, "productSync", - // // "foo"); - // verifyInteractionsWithClientAfterSync(sourceClient, 1); - // - // final Condition startLog = - // new Condition<>( - // loggingEvent -> - // Level.INFO.equals(loggingEvent.getLevel()) - // && loggingEvent.getMessage().contains("Starting ProductTypeSync"), - // "start log"); - // - // final Condition statisticsLog = - // new Condition<>( - // loggingEvent -> - // Level.INFO.equals(loggingEvent.getLevel()) - // && loggingEvent - // .getMessage() - // .contains( - // "Summary: 0 product types were processed in total (0 created, 0 - // updated, 0 failed to sync" - // + " and 0 product types with at least one NestedType or a Set of - // NestedType attribute" - // + " definition(s) referencing a missing product type)."), - // "statistics log"); - // - // assertThat(productTypeSyncerTestLogger.getAllLoggingEvents()) - // .hasSize(2) - // .haveExactly(1, startLog) - // .haveExactly(1, statisticsLog); - // } - // - // @Test - // @SuppressWarnings("unchecked") - // void sync_AsTypesDeltaSync_ShouldBuildSyncerAndExecuteSync() { - // // preparation - // final SphereClient sourceClient = mock(SphereClient.class); - // when(sourceClient.getConfig()).thenReturn(SphereClientConfig.of("foo", "foo", "foo")); - // - // final SphereClient targetClient = mock(SphereClient.class); - // when(targetClient.getConfig()).thenReturn(SphereClientConfig.of("bar", "bar", "bar")); - // - // when(sourceClient.execute(any(TypeQuery.class))) - // .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); - // - // final ZonedDateTime currentCtpTimestamp = ZonedDateTime.now(); - // stubClientsCustomObjectService(targetClient, currentCtpTimestamp); - // - // final SyncerFactory syncerFactory = - // SyncerFactory.of(() -> sourceClient, () -> targetClient, getMockedClock()); - // - // // test - // syncerFactory.sync(new String[] {"types"}, "foo", false, false, null); - // - // // assertions - // verify(sourceClient, times(1)).execute(any(TypeQuery.class)); - // verifyTimestampGeneratorCustomObjectUpsertIsCalled(targetClient, "TypeSync", "foo"); - // verifyLastSyncCustomObjectQuery(targetClient, "typeSync", "foo", "foo", 1); - // // verify two custom object upserts : 1. current ctp timestamp and 2. last sync timestamp - // // creation) - // verify(targetClient, times(2)).execute(any(CustomObjectUpsertCommand.class)); - // // TODO: Assert on actual last sync timestamp creation in detail after Statistics classes in - // // java-sync library - // // TODO: override #equals method: - // // https://github.com/commercetools/commercetools-sync-java/issues/376 - // // TODO: e.g. verifyNewLastSyncCustomObjectCreation(targetClient, - // // currentCtpTimestamp.minusMinutes(2), any(ProductSyncStatistics.class), 0L, "productSync", - // // "foo"); - // verifyInteractionsWithClientAfterSync(sourceClient, 1); - // - // final Condition startLog = - // new Condition<>( - // loggingEvent -> - // Level.INFO.equals(loggingEvent.getLevel()) - // && loggingEvent.getMessage().contains("Starting TypeSync"), - // "start log"); - // - // final Condition statisticsLog = - // new Condition<>( - // loggingEvent -> - // Level.INFO.equals(loggingEvent.getLevel()) - // && loggingEvent - // .getMessage() - // .contains( - // "Summary: 0 types were processed in total (0 created, 0 updated " - // + "and 0 failed to sync)."), - // "statistics log"); - // - // assertThat(typeSyncerTestLogger.getAllLoggingEvents()) - // .hasSize(2) - // .haveExactly(1, startLog) - // .haveExactly(1, statisticsLog); - // } - // - // @Test - // @SuppressWarnings("unchecked") - // void sync_AsInventoryEntriesDeltaSync_ShouldBuildSyncerAndExecuteSync() { - // // preparation - // final SphereClient sourceClient = mock(SphereClient.class); - // when(sourceClient.getConfig()).thenReturn(SphereClientConfig.of("foo", "foo", "foo")); - // - // final SphereClient targetClient = mock(SphereClient.class); - // when(targetClient.getConfig()).thenReturn(SphereClientConfig.of("bar", "bar", "bar")); - // - // when(sourceClient.execute(any(InventoryEntryQuery.class))) - // .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); - // - // final ZonedDateTime currentCtpTimestamp = ZonedDateTime.now(); - // stubClientsCustomObjectService(targetClient, currentCtpTimestamp); - // - // final SyncerFactory syncerFactory = - // SyncerFactory.of(() -> sourceClient, () -> targetClient, getMockedClock()); - // - // // test - // syncerFactory.sync(new String[] {"inventoryEntries"}, null, false, false, null); - // - // // assertions - // verify(sourceClient, times(1)).execute(any(InventoryEntryQuery.class)); - // verifyTimestampGeneratorCustomObjectUpsertIsCalled( - // targetClient, "InventorySync", DEFAULT_RUNNER_NAME); - // verifyLastSyncCustomObjectQuery(targetClient, "inventorySync", DEFAULT_RUNNER_NAME, "foo", - // 1); - // // verify two custom object upserts : 1. current ctp timestamp and 2. last sync timestamp - // // creation) - // verify(targetClient, times(2)).execute(any(CustomObjectUpsertCommand.class)); - // // TODO: Assert on actual last sync timestamp creation in detail after Statistics classes in - // // java-sync library - // // TODO: override #equals method: - // // https://github.com/commercetools/commercetools-sync-java/issues/376 - // // TODO: e.g. verifyNewLastSyncCustomObjectCreation(targetClient, - // // currentCtpTimestamp.minusMinutes(2), any(ProductSyncStatistics.class), 0L, "productSync", - // // "foo"); - // verifyInteractionsWithClientAfterSync(sourceClient, 1); - // - // final Condition startLog = - // new Condition<>( - // loggingEvent -> - // Level.INFO.equals(loggingEvent.getLevel()) - // && loggingEvent.getMessage().contains("Starting InventorySync"), - // "start log"); - // - // final Condition statisticsLog = - // new Condition<>( - // loggingEvent -> - // Level.INFO.equals(loggingEvent.getLevel()) - // && loggingEvent - // .getMessage() - // .contains( - // "Summary: 0 inventory entries were processed in total (0 created, 0 - // updated " - // + "and 0 failed to sync)."), - // "statistics log"); - // - // assertThat(inventoryEntrySyncerTestLogger.getAllLoggingEvents()) - // .hasSize(2) - // .haveExactly(1, startLog) - // .haveExactly(1, statisticsLog); - // } - // - // @Test - // void sync_WithErrorOnFetch_ShouldCloseClientAndCompleteExceptionally() { - // // preparation - // final SphereClient sourceClient = mock(SphereClient.class); - // when(sourceClient.getConfig()).thenReturn(SphereClientConfig.of("foo", "foo", "foo")); - // - // final SphereClient targetClient = mock(SphereClient.class); - // when(targetClient.getConfig()).thenReturn(SphereClientConfig.of("bar", "bar", "bar")); - // - // final BadGatewayException badGatewayException = new BadGatewayException(); - // when(sourceClient.execute(any(InventoryEntryQuery.class))) - // .thenReturn(CompletableFutureUtils.exceptionallyCompletedFuture(badGatewayException)); - // - // final ZonedDateTime currentCtpTimestamp = ZonedDateTime.now(); - // stubClientsCustomObjectService(targetClient, currentCtpTimestamp); - // - // final SyncerFactory syncerFactory = - // SyncerFactory.of(() -> sourceClient, () -> targetClient, getMockedClock()); - // - // // test - // final CompletionStage result = - // syncerFactory.sync(new String[] {"inventoryEntries"}, null, false, false, null); - // - // // assertions - // verifyTimestampGeneratorCustomObjectUpsertIsCalled( - // targetClient, "InventorySync", DEFAULT_RUNNER_NAME); - // verify(sourceClient, times(1)).execute(any(InventoryEntryQuery.class)); - // verifyInteractionsWithClientAfterSync(sourceClient, 1); - // assertThat(result) - // .failsWithin(1, TimeUnit.SECONDS) - // .withThrowableOfType(ExecutionException.class) - // .withCauseExactlyInstanceOf(BadGatewayException.class); - // } - // - // @Test - // @SuppressWarnings("unchecked") - // void - // - // sync_WithErrorOnCurrentCtpTimestampUpsert_ShouldCloseClientAndCompleteExceptionallyWithoutSyncing() { - // // preparation - // final SphereClient sourceClient = mock(SphereClient.class); - // when(sourceClient.getConfig()).thenReturn(SphereClientConfig.of("foo", "foo", "foo")); - // - // final SphereClient targetClient = mock(SphereClient.class); - // when(targetClient.getConfig()).thenReturn(SphereClientConfig.of("bar", "bar", "bar")); - // - // final BadGatewayException badGatewayException = new BadGatewayException(); - // when(targetClient.execute(any(CustomObjectUpsertCommand.class))) - // .thenReturn(CompletableFutureUtils.exceptionallyCompletedFuture(badGatewayException)); - // - // final SyncerFactory syncerFactory = - // SyncerFactory.of(() -> sourceClient, () -> targetClient, getMockedClock()); - // - // // test - // final CompletionStage result = - // syncerFactory.sync(new String[] {"inventoryEntries"}, "", false, false, null); - // - // // assertions - // verifyTimestampGeneratorCustomObjectUpsertIsCalled( - // targetClient, "InventorySync", DEFAULT_RUNNER_NAME); - // verify(sourceClient, times(0)).execute(any(InventoryEntryQuery.class)); - // verifyInteractionsWithClientAfterSync(sourceClient, 1); - // assertThat(result) - // .failsWithin(1, TimeUnit.SECONDS) - // .withThrowableOfType(ExecutionException.class) - // .withCauseExactlyInstanceOf(BadGatewayException.class); - // } - // - // @Test - // @SuppressWarnings("unchecked") - // void - // - // sync_WithErrorOnQueryLastSyncTimestamp_ShouldCloseClientAndCompleteExceptionallyWithoutSyncing() { - // // preparation - // final SphereClient sourceClient = mock(SphereClient.class); - // when(sourceClient.getConfig()).thenReturn(SphereClientConfig.of("foo", "foo", "foo")); - // - // final SphereClient targetClient = mock(SphereClient.class); - // when(targetClient.getConfig()).thenReturn(SphereClientConfig.of("bar", "bar", "bar")); - // - // final BadGatewayException badGatewayException = new BadGatewayException(); - // when(targetClient.execute(any(CustomObjectQuery.class))) - // .thenReturn(CompletableFutureUtils.exceptionallyCompletedFuture(badGatewayException)); - // - // final CustomObject> - // lastSyncCustomObjectCustomObject = mockLastSyncCustomObject(ZonedDateTime.now()); - // when(targetClient.execute(any(CustomObjectUpsertCommand.class))) - // .thenReturn(CompletableFuture.completedFuture(lastSyncCustomObjectCustomObject)); - // - // final SyncerFactory syncerFactory = - // SyncerFactory.of(() -> sourceClient, () -> targetClient, getMockedClock()); - // - // // test - // final CompletionStage result = - // syncerFactory.sync(new String[] {"inventoryEntries"}, "bar", false, false, null); - // - // // assertions - // verifyTimestampGeneratorCustomObjectUpsertIsCalled(targetClient, "InventorySync", "bar"); - // verifyLastSyncCustomObjectQuery(targetClient, "inventorySync", "bar", "foo", 1); - // verify(sourceClient, times(0)).execute(any(InventoryEntryQuery.class)); - // verifyInteractionsWithClientAfterSync(sourceClient, 1); - // assertThat(result) - // .failsWithin(1, TimeUnit.SECONDS) - // .withThrowableOfType(ExecutionException.class) - // .withCauseExactlyInstanceOf(BadGatewayException.class); - // } - // - // @Test - // @SuppressWarnings("unchecked") - // void syncAll_AsDelta_ShouldBuildSyncerAndExecuteSync() { - // // preparation - // final SphereClient sourceClient = mock(SphereClient.class); - // when(sourceClient.getConfig()).thenReturn(SphereClientConfig.of("foo", "foo", "foo")); - // - // final SphereClient targetClient = mock(SphereClient.class); - // when(targetClient.getConfig()).thenReturn(SphereClientConfig.of("bar", "bar", "bar")); - // - // when(sourceClient.execute(any(ProductTypeQuery.class))) - // .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); - // when(sourceClient.execute(any(TypeQuery.class))) - // .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); - // when(sourceClient.execute(any(StateQuery.class))) - // .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); - // when(sourceClient.execute(any(TaxCategoryQuery.class))) - // .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); - // when(sourceClient.execute(any(CategoryQuery.class))) - // .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); - // when(sourceClient.execute(any(InventoryEntryQuery.class))) - // .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); - // when(sourceClient.execute(any(ProductProjectionQuery.class))) - // .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); - // when(sourceClient.execute(any(CartDiscountQuery.class))) - // .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); - // when(sourceClient.execute(any(CustomObjectQuery.class))) - // .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); - // when(sourceClient.execute(any(CustomerQuery.class))) - // .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); - // when(sourceClient.execute(any(ShoppingListQuery.class))) - // .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); - // - // final ZonedDateTime currentCtpTimestamp = ZonedDateTime.now(); - // stubClientsCustomObjectService(targetClient, currentCtpTimestamp); - // - // final SyncerFactory syncerFactory = - // SyncerFactory.of(() -> sourceClient, () -> targetClient, getMockedClock()); - // - // // test - // syncerFactory.sync(new String[] {"all"}, null, false, false, null).join(); - // - // // assertions - // verifyTimestampGeneratorCustomObjectUpsertIsCalled( - // targetClient, "ProductTypeSync", DEFAULT_RUNNER_NAME); - // verifyTimestampGeneratorCustomObjectUpsertIsCalled( - // targetClient, "ProductSync", DEFAULT_RUNNER_NAME); - // verifyTimestampGeneratorCustomObjectUpsertIsCalled( - // targetClient, "CategorySync", DEFAULT_RUNNER_NAME); - // verifyTimestampGeneratorCustomObjectUpsertIsCalled( - // targetClient, "TypeSync", DEFAULT_RUNNER_NAME); - // verifyTimestampGeneratorCustomObjectUpsertIsCalled( - // targetClient, "InventorySync", DEFAULT_RUNNER_NAME); - // verifyTimestampGeneratorCustomObjectUpsertIsCalled( - // targetClient, "CartDiscountSync", DEFAULT_RUNNER_NAME); - // verifyTimestampGeneratorCustomObjectUpsertIsCalled( - // targetClient, "StateSync", DEFAULT_RUNNER_NAME); - // verifyTimestampGeneratorCustomObjectUpsertIsCalled( - // targetClient, "TaxCategorySync", DEFAULT_RUNNER_NAME); - // verifyTimestampGeneratorCustomObjectUpsertIsCalled( - // targetClient, "CustomObjectSync", DEFAULT_RUNNER_NAME); - // verifyTimestampGeneratorCustomObjectUpsertIsCalled( - // targetClient, "CustomerSync", DEFAULT_RUNNER_NAME); - // verifyTimestampGeneratorCustomObjectUpsertIsCalled( - // targetClient, "ShoppingListSync", DEFAULT_RUNNER_NAME); - // verify(targetClient, times(22)).execute(any(CustomObjectUpsertCommand.class)); - // verifyLastSyncCustomObjectQuery(targetClient, "inventorySync", DEFAULT_RUNNER_NAME, "foo", - // 1); - // verifyLastSyncCustomObjectQuery(targetClient, "productTypeSync", DEFAULT_RUNNER_NAME, "foo", - // 1); - // verifyLastSyncCustomObjectQuery(targetClient, "productSync", DEFAULT_RUNNER_NAME, "foo", 1); - // verifyLastSyncCustomObjectQuery(targetClient, "categorySync", DEFAULT_RUNNER_NAME, "foo", - // 1); - // verifyLastSyncCustomObjectQuery(targetClient, "typeSync", DEFAULT_RUNNER_NAME, "foo", 1); - // verifyLastSyncCustomObjectQuery( - // targetClient, "cartDiscountSync", DEFAULT_RUNNER_NAME, "foo", 1); - // verifyLastSyncCustomObjectQuery(targetClient, "stateSync", DEFAULT_RUNNER_NAME, "foo", 1); - // verifyLastSyncCustomObjectQuery(targetClient, "taxCategorySync", DEFAULT_RUNNER_NAME, "foo", - // 1); - // verifyLastSyncCustomObjectQuery( - // targetClient, "customObjectSync", DEFAULT_RUNNER_NAME, "foo", 1); - // verifyLastSyncCustomObjectQuery(targetClient, "customerSync", DEFAULT_RUNNER_NAME, "foo", - // 1); - // verifyLastSyncCustomObjectQuery( - // targetClient, "shoppingListSync", DEFAULT_RUNNER_NAME, "foo", 1); - // verify(sourceClient, times(1)).execute(any(ProductTypeQuery.class)); - // verify(sourceClient, times(1)).execute(any(TypeQuery.class)); - // verify(sourceClient, times(1)).execute(any(CategoryQuery.class)); - // verify(sourceClient, times(1)).execute(any(ProductProjectionQuery.class)); - // verify(sourceClient, times(1)).execute(any(InventoryEntryQuery.class)); - // verify(sourceClient, times(1)).execute(any(CartDiscountQuery.class)); - // verify(sourceClient, times(1)).execute(any(StateQuery.class)); - // verify(sourceClient, times(1)).execute(any(TaxCategoryQuery.class)); - // verify(sourceClient, times(1)).execute(any(CustomObjectQuery.class)); - // verify(sourceClient, times(1)).execute(any(CustomerQuery.class)); - // verify(sourceClient, times(1)).execute(any(ShoppingListQuery.class)); - // verifyInteractionsWithClientAfterSync(sourceClient, 11); - // - // assertThat(cliRunnerTestLogger.getAllLoggingEvents()) - // .allMatch(loggingEvent -> !Level.ERROR.equals(loggingEvent.getLevel())); - // - // assertThat(productSyncerTestLogger.getAllLoggingEvents()) - // .allMatch(loggingEvent -> !Level.ERROR.equals(loggingEvent.getLevel())); - // - // assertTypeSyncerLoggingEvents(typeSyncerTestLogger, 0); - // assertProductTypeSyncerLoggingEvents(productTypeSyncerTestLogger, 0); - // assertCategorySyncerLoggingEvents(categorySyncerTestLogger, 0); - // assertProductSyncerLoggingEvents(productSyncerTestLogger, 0); - // assertInventoryEntrySyncerLoggingEvents(inventoryEntrySyncerTestLogger, 0); - // assertCartDiscountSyncerLoggingEvents(cartDiscountSyncerTestLogger, 0); - // // +1 state is a built-in state and it cant be deleted - // assertStateSyncerLoggingEvents(stateSyncerTestLogger, 0); - // assertTaxCategorySyncerLoggingEvents(taxCategorySyncerTestLogger, 0); - // assertCustomObjectSyncerLoggingEvents(customObjectSyncerTestLogger, 0); - // assertCustomerSyncerLoggingEvents(customerSyncerTestLogger, 0); - // assertShoppingListSyncerLoggingEvents(shoppingListSyncerTestLogger, 0); - // - // // Every sync module is expected to have 2 logs (start and stats summary) - // assertThat(typeSyncerTestLogger.getAllLoggingEvents()).hasSize(2); - // assertThat(productTypeSyncerTestLogger.getAllLoggingEvents()).hasSize(2); - // assertThat(categorySyncerTestLogger.getAllLoggingEvents()).hasSize(2); - // assertThat(productSyncerTestLogger.getAllLoggingEvents()).hasSize(2); - // assertThat(inventoryEntrySyncerTestLogger.getAllLoggingEvents()).hasSize(2); - // assertThat(cartDiscountSyncerTestLogger.getAllLoggingEvents()).hasSize(2); - // assertThat(stateSyncerTestLogger.getAllLoggingEvents()).hasSize(2); - // assertThat(taxCategorySyncerTestLogger.getAllLoggingEvents()).hasSize(2); - // assertThat(customObjectSyncerTestLogger.getAllLoggingEvents()).hasSize(2); - // assertThat(customerSyncerTestLogger.getAllLoggingEvents()).hasSize(2); - // assertThat(shoppingListSyncerTestLogger.getAllLoggingEvents()).hasSize(2); - // } - // - // @Test - // @SuppressWarnings("unchecked") - // void - // syncProductTypesProductsCustomersAndShoppingLists_AsDelta_ShouldBuildSyncerAndExecuteSync() { - // // preparation - // final SphereClient sourceClient = mock(SphereClient.class); - // when(sourceClient.getConfig()).thenReturn(SphereClientConfig.of("foo", "foo", "foo")); - // - // final SphereClient targetClient = mock(SphereClient.class); - // when(targetClient.getConfig()).thenReturn(SphereClientConfig.of("bar", "bar", "bar")); - // - // when(sourceClient.execute(any(ProductTypeQuery.class))) - // .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); - // when(sourceClient.execute(any(ProductProjectionQuery.class))) - // .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); - // when(sourceClient.execute(any(CustomerQuery.class))) - // .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); - // when(sourceClient.execute(any(ShoppingListQuery.class))) - // .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); - // - // final ZonedDateTime currentCtpTimestamp = ZonedDateTime.now(); - // stubClientsCustomObjectService(targetClient, currentCtpTimestamp); - // - // final SyncerFactory syncerFactory = - // SyncerFactory.of(() -> sourceClient, () -> targetClient, getMockedClock()); - // - // // test - // String[] syncModuleOptions = {"productTypes", "products", "customers", "shoppingLists"}; - // syncerFactory.sync(syncModuleOptions, null, false, false, null).join(); - // - // // assertions - // verifyTimestampGeneratorCustomObjectUpsertIsCalled( - // targetClient, "ProductTypeSync", DEFAULT_RUNNER_NAME); - // verifyTimestampGeneratorCustomObjectUpsertIsCalled( - // targetClient, "ProductSync", DEFAULT_RUNNER_NAME); - // verifyTimestampGeneratorCustomObjectUpsertIsCalled( - // targetClient, "CustomerSync", DEFAULT_RUNNER_NAME); - // verifyTimestampGeneratorCustomObjectUpsertIsCalled( - // targetClient, "ShoppingListSync", DEFAULT_RUNNER_NAME); - // verify(targetClient, times(8)).execute(any(CustomObjectUpsertCommand.class)); - // verifyLastSyncCustomObjectQuery(targetClient, "productTypeSync", DEFAULT_RUNNER_NAME, "foo", - // 1); - // verifyLastSyncCustomObjectQuery(targetClient, "productSync", DEFAULT_RUNNER_NAME, "foo", 1); - // verifyLastSyncCustomObjectQuery(targetClient, "customerSync", DEFAULT_RUNNER_NAME, "foo", - // 1); - // verifyLastSyncCustomObjectQuery( - // targetClient, "shoppingListSync", DEFAULT_RUNNER_NAME, "foo", 1); - // - // final InOrder inOrder = Mockito.inOrder(sourceClient); - // - // // According to sync algorithm, ProductType and Customer will run sync in parallel, Product - // and - // // ShoppingList sequentially. - // // Example: Given: ['productTypes', 'customers', 'products', 'shoppingLists'] - // // From the given arguments, algorithm will group the resources as below, - // // [productTypes, customers] [products] [shoppingLists] - // inOrder.verify(sourceClient, times(1)).execute(any(ProductTypeQuery.class)); - // verify(sourceClient, times(1)).execute(any(CustomerQuery.class)); - // - // inOrder.verify(sourceClient, times(1)).execute(any(ProductProjectionQuery.class)); - // - // inOrder.verify(sourceClient, times(1)).execute(any(ShoppingListQuery.class)); - // verifyInteractionsWithClientAfterSync(sourceClient, 4); - // - // assertProductTypeSyncerLoggingEvents(productTypeSyncerTestLogger, 0); - // assertProductSyncerLoggingEvents(productSyncerTestLogger, 0); - // assertCustomerSyncerLoggingEvents(customerSyncerTestLogger, 0); - // assertShoppingListSyncerLoggingEvents(shoppingListSyncerTestLogger, 0); - // } - // - // @Test - // @SuppressWarnings("unchecked") - // void syncStatesInventoryEntriesAndCustomObjects_AsDelta_ShouldBuildSyncerAndExecuteSync() { - // // preparation - // final SphereClient sourceClient = mock(SphereClient.class); - // when(sourceClient.getConfig()).thenReturn(SphereClientConfig.of("foo", "foo", "foo")); - // - // final SphereClient targetClient = mock(SphereClient.class); - // when(targetClient.getConfig()).thenReturn(SphereClientConfig.of("bar", "bar", "bar")); - // - // when(sourceClient.execute(any(StateQuery.class))) - // .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); - // when(sourceClient.execute(any(InventoryEntryQuery.class))) - // .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); - // when(sourceClient.execute(any(CustomObjectQuery.class))) - // .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); - // - // final ZonedDateTime currentCtpTimestamp = ZonedDateTime.now(); - // stubClientsCustomObjectService(targetClient, currentCtpTimestamp); - // - // final SyncerFactory syncerFactory = - // SyncerFactory.of(() -> sourceClient, () -> targetClient, getMockedClock()); - // - // // test - // String[] syncModuleOptions = {"states", "inventoryEntries", "customObjects"}; - // syncerFactory.sync(syncModuleOptions, null, false, false, null).join(); - // - // // assertions - // verifyTimestampGeneratorCustomObjectUpsertIsCalled( - // targetClient, "StateSync", DEFAULT_RUNNER_NAME); - // verifyTimestampGeneratorCustomObjectUpsertIsCalled( - // targetClient, "InventorySync", DEFAULT_RUNNER_NAME); - // verifyTimestampGeneratorCustomObjectUpsertIsCalled( - // targetClient, "CustomObjectSync", DEFAULT_RUNNER_NAME); - // verify(targetClient, times(6)).execute(any(CustomObjectUpsertCommand.class)); - // verifyLastSyncCustomObjectQuery(targetClient, "stateSync", DEFAULT_RUNNER_NAME, "foo", 1); - // verifyLastSyncCustomObjectQuery(targetClient, "inventorySync", DEFAULT_RUNNER_NAME, "foo", - // 1); - // verifyLastSyncCustomObjectQuery( - // targetClient, "customObjectSync", DEFAULT_RUNNER_NAME, "foo", 1); - // verify(sourceClient, times(1)).execute(any(StateQuery.class)); - // verify(sourceClient, times(1)).execute(any(InventoryEntryQuery.class)); - // verify(sourceClient, times(1)).execute(any(CustomObjectQuery.class)); - // verifyInteractionsWithClientAfterSync(sourceClient, 3); - // - // assertStateSyncerLoggingEvents(stateSyncerTestLogger, 0); - // assertInventoryEntrySyncerLoggingEvents(inventoryEntrySyncerTestLogger, 0); - // assertCustomObjectSyncerLoggingEvents(customObjectSyncerTestLogger, 0); - // } - // - // @Test - // @SuppressWarnings("unchecked") - // void syncTypesAndCategories_AsDelta_ShouldBuildSyncerAndExecuteSync() { - // // preparation - // final SphereClient sourceClient = mock(SphereClient.class); - // when(sourceClient.getConfig()).thenReturn(SphereClientConfig.of("foo", "foo", "foo")); - // - // final SphereClient targetClient = mock(SphereClient.class); - // when(targetClient.getConfig()).thenReturn(SphereClientConfig.of("bar", "bar", "bar")); - // - // when(sourceClient.execute(any(TypeQuery.class))) - // .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); - // when(sourceClient.execute(any(CategoryQuery.class))) - // .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); - // - // final ZonedDateTime currentCtpTimestamp = ZonedDateTime.now(); - // stubClientsCustomObjectService(targetClient, currentCtpTimestamp); - // - // final SyncerFactory syncerFactory = - // SyncerFactory.of(() -> sourceClient, () -> targetClient, getMockedClock()); - // - // // test - // String[] syncModuleOptions = {"types", "categories"}; - // syncerFactory.sync(syncModuleOptions, null, false, false, null).join(); - // - // // assertions - // verifyTimestampGeneratorCustomObjectUpsertIsCalled( - // targetClient, "TypeSync", DEFAULT_RUNNER_NAME); - // verifyTimestampGeneratorCustomObjectUpsertIsCalled( - // targetClient, "CategorySync", DEFAULT_RUNNER_NAME); - // verify(targetClient, times(4)).execute(any(CustomObjectUpsertCommand.class)); - // verifyLastSyncCustomObjectQuery(targetClient, "typeSync", DEFAULT_RUNNER_NAME, "foo", 1); - // verifyLastSyncCustomObjectQuery(targetClient, "categorySync", DEFAULT_RUNNER_NAME, "foo", - // 1); - // - // final InOrder inOrder = Mockito.inOrder(sourceClient); - // - // inOrder.verify(sourceClient, times(1)).execute(any(TypeQuery.class)); - // inOrder.verify(sourceClient, times(1)).execute(any(CategoryQuery.class)); - // verifyInteractionsWithClientAfterSync(sourceClient, 2); - // - // assertTypeSyncerLoggingEvents(typeSyncerTestLogger, 0); - // assertCategorySyncerLoggingEvents(categorySyncerTestLogger, 0); - // assertThat(typeSyncerTestLogger.getAllLoggingEvents()).hasSize(2); - // assertThat(categorySyncerTestLogger.getAllLoggingEvents()).hasSize(2); - // } - // - // @Test - // @SuppressWarnings("unchecked") - // void syncProductsAndShoppingLists_AsDelta_ShouldBuildSyncerAndExecuteSync() { - // // preparation - // final SphereClient sourceClient = mock(SphereClient.class); - // when(sourceClient.getConfig()).thenReturn(SphereClientConfig.of("foo", "foo", "foo")); - // - // final SphereClient targetClient = mock(SphereClient.class); - // when(targetClient.getConfig()).thenReturn(SphereClientConfig.of("bar", "bar", "bar")); - // - // when(sourceClient.execute(any(ProductProjectionQuery.class))) - // .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); - // when(sourceClient.execute(any(ShoppingListQuery.class))) - // .thenReturn(CompletableFuture.completedFuture(PagedQueryResult.empty())); - // - // final ZonedDateTime currentCtpTimestamp = ZonedDateTime.now(); - // stubClientsCustomObjectService(targetClient, currentCtpTimestamp); - // - // final SyncerFactory syncerFactory = - // SyncerFactory.of(() -> sourceClient, () -> targetClient, getMockedClock()); - // - // // test - // String[] syncModuleOptions = {"products", "shoppingLists"}; - // syncerFactory.sync(syncModuleOptions, null, false, false, null).join(); - // - // // assertions - // verifyTimestampGeneratorCustomObjectUpsertIsCalled( - // targetClient, "ProductSync", DEFAULT_RUNNER_NAME); - // verifyTimestampGeneratorCustomObjectUpsertIsCalled( - // targetClient, "ShoppingListSync", DEFAULT_RUNNER_NAME); - // verify(targetClient, times(4)).execute(any(CustomObjectUpsertCommand.class)); - // verifyLastSyncCustomObjectQuery(targetClient, "productSync", DEFAULT_RUNNER_NAME, "foo", 1); - // verifyLastSyncCustomObjectQuery( - // targetClient, "shoppingListSync", DEFAULT_RUNNER_NAME, "foo", 1); - // verify(sourceClient, times(1)).execute(any(ProductProjectionQuery.class)); - // verify(sourceClient, times(1)).execute(any(ShoppingListQuery.class)); - // verifyInteractionsWithClientAfterSync(sourceClient, 2); - // - // assertProductSyncerLoggingEvents(productSyncerTestLogger, 0); - // assertShoppingListSyncerLoggingEvents(shoppingListSyncerTestLogger, 0); - // assertThat(productSyncerTestLogger.getAllLoggingEvents()).hasSize(2); - // assertThat(shoppingListSyncerTestLogger.getAllLoggingEvents()).hasSize(2); - // } - // - // @Test - // void sync_AsDelta_WithOneUnmatchedSyncOptionValue_ShouldResultIllegalArgumentException() { - // final SphereClient sourceClient = mock(SphereClient.class); - // when(sourceClient.getConfig()).thenReturn(SphereClientConfig.of("foo", "foo", "foo")); - // - // final SphereClient targetClient = mock(SphereClient.class); - // when(targetClient.getConfig()).thenReturn(SphereClientConfig.of("bar", "bar", "bar")); - // - // final SyncerFactory syncerFactory = - // SyncerFactory.of(() -> sourceClient, () -> targetClient, getMockedClock()); - // - // // test - // String[] syncResources = {"productTypes", "unknown", "shoppingLists"}; - // CompletionStage result = syncerFactory.sync(syncResources, null, false, false, null); - // - // String errorMessage = - // format( - // "Unknown argument \"%s\" supplied to \"-s\" or \"--sync\" option! %s", - // syncResources[1], SYNC_MODULE_OPTION_DESCRIPTION); - // - // assertThat(result) - // .failsWithin(1, TimeUnit.SECONDS) - // .withThrowableOfType(ExecutionException.class) - // .withCauseExactlyInstanceOf(CliException.class) - // .withMessageContaining(errorMessage); - // } - // - // @Test - // void sync_AsDelta_WithSyncOptionValuesAndAll_ShouldResultIllegalArgumentException() { - // final SphereClient sourceClient = mock(SphereClient.class); - // when(sourceClient.getConfig()).thenReturn(SphereClientConfig.of("foo", "foo", "foo")); - // - // final SphereClient targetClient = mock(SphereClient.class); - // when(targetClient.getConfig()).thenReturn(SphereClientConfig.of("bar", "bar", "bar")); - // - // final SyncerFactory syncerFactory = - // SyncerFactory.of(() -> sourceClient, () -> targetClient, getMockedClock()); - // - // // test - // String[] syncResources = {"productTypes", "all", "shoppingLists"}; - // CompletionStage result = syncerFactory.sync(syncResources, null, false, false, null); - // - // String errorMessage = - // format( - // "Wrong arguments supplied to \"-s\" or \"--sync\" option! " - // + "'all' option cannot be passed along with other arguments.\" %s", - // SYNC_MODULE_OPTION_DESCRIPTION); - // - // assertThat(result) - // .failsWithin(1, TimeUnit.SECONDS) - // .withThrowableOfType(ExecutionException.class) - // .withCauseExactlyInstanceOf(CliException.class) - // .withMessageContaining(errorMessage); - // } + private ProjectApiRoot mockClientResourceRequests(final String projectKey) { + return withTestClient( + projectKey, + (uri, method, encodedRequetsBody) -> { + final String responseString = "{\"results\":[]}"; + return CompletableFuture.completedFuture( + new ApiHttpResponse<>(200, null, responseString.getBytes(StandardCharsets.UTF_8))); + }); + } + + @Test + void sync_WithNullOptionValue_ShouldCompleteExceptionallyWithIllegalArgumentException() { + assertThat( + SyncerFactory.of(() -> sourceClient, () -> targetClient, getMockedClock()) + .sync(new String[] {null}, "myRunnerName", false, false, null)) + .failsWithin(1, TimeUnit.SECONDS) + .withThrowableOfType(ExecutionException.class) + .withCauseExactlyInstanceOf(CliException.class) + .withMessageContaining( + format( + "Blank argument supplied to \"-s\" or \"--sync\" option! %s", + SYNC_MODULE_OPTION_DESCRIPTION)); + } + + @Test + void sync_WithEmptyOptionValue_ShouldCompleteExceptionallyWithIllegalArgumentException() { + assertThat( + SyncerFactory.of(() -> sourceClient, () -> targetClient, getMockedClock()) + .sync(new String[] {""}, "myRunnerName", false, false, null)) + .failsWithin(1, TimeUnit.SECONDS) + .withThrowableOfType(ExecutionException.class) + .withCauseExactlyInstanceOf(CliException.class) + .withMessageContaining( + format( + "Blank argument supplied to \"-s\" or \"--sync\" option! %s", + SYNC_MODULE_OPTION_DESCRIPTION)); + } + + @Test + void sync_WithUnknownOptionValue_ShouldCompleteExceptionallyWithIllegalArgumentException() { + final String[] unknownOptionValue = {"anyOption"}; + + assertThat( + SyncerFactory.of(() -> sourceClient, () -> targetClient, getMockedClock()) + .sync(unknownOptionValue, "myRunnerName", false, false, null)) + .failsWithin(1, TimeUnit.SECONDS) + .withThrowableOfType(ExecutionException.class) + .withCauseExactlyInstanceOf(CliException.class) + .withMessageContaining( + format( + "Unknown argument \"%s\" supplied to \"-s\" or \"--sync\" option! %s", + unknownOptionValue[0], SYNC_MODULE_OPTION_DESCRIPTION)); + } + + @Test + @SuppressWarnings("unchecked") + void sync_AsProductsDeltaSync_ShouldBuildSyncerAndExecuteSync() { + // preparation + final ZonedDateTime currentCtpTimestamp = ZonedDateTime.now(); + stubClientsCustomObjectService(targetClient, currentCtpTimestamp); + + final SyncerFactory syncerFactory = + SyncerFactory.of(() -> sourceClient, () -> targetClient, getMockedClock()); + + // test + syncerFactory.sync(new String[] {"products"}, "myRunnerName", false, false, null); + + // assertions + // verify product-projections are queried once + verify(sourceClient, times(1)).productProjections(); + // assertThat(verifyProductProjectionsGetCounter.get()).isEqualTo(1); + verifyTimestampGeneratorCustomObjectUpsertIsCalled(targetClient, "ProductSync", "myRunnerName"); + verifyLastSyncCustomObjectQuery( + targetClient, "productSync", "myRunnerName", "testProjectKey", 1); + // verify two custom object upserts : 1. current ctp timestamp and 2. last sync timestamp + // creation) + verify(targetClient.customObjects(), times(2)).post(any(CustomObjectDraft.class)); + // TODO: Assert on actual last sync timestamp creation in detail after Statistics classes in + // java-sync library + // TODO: override #equals method: + // https://github.com/commercetools/commercetools-sync-java/issues/376 + // TODO: e.g. verifyNewLastSyncCustomObjectCreation(targetClient, + // currentCtpTimestamp.minusMinutes(2), any(ProductSyncStatistics.class), 0L, "productSync", + // "foo"); + verifyInteractionsWithClientAfterSync(sourceClient, 1); + + final Condition startLog = + new Condition<>( + loggingEvent -> + Level.INFO.equals(loggingEvent.getLevel()) + && loggingEvent.getMessage().contains("Starting ProductSync"), + "start log"); + + final Condition statisticsLog = + new Condition<>( + loggingEvent -> + Level.INFO.equals(loggingEvent.getLevel()) + && loggingEvent + .getMessage() + .contains( + "Summary: 0 product(s) were processed in total (0 created, 0 updated, " + + "0 failed to sync and 0 product(s) with missing reference(s))."), + "statistics log"); + + assertThat(productSyncerTestLogger.getAllLoggingEvents()) + .hasSize(2) + .haveExactly(1, startLog) + .haveExactly(1, statisticsLog); + } + + @Test + @SuppressWarnings("unchecked") + void sync_AsProductsFullSync_ShouldBuildSyncerAndExecuteSync() { + // preparation + final ZonedDateTime currentCtpTimestamp = ZonedDateTime.now(); + stubClientsCustomObjectService(targetClient, currentCtpTimestamp); + + final SyncerFactory syncerFactory = + SyncerFactory.of(() -> sourceClient, () -> targetClient, getMockedClock()); + + // test + syncerFactory.sync(new String[] {"products"}, "myRunnerName", true, false, null); + + // assertions + verify(sourceClient, times(1)).productProjections(); + // assertThat(verifyProductProjectionsGetCounter.get()).isEqualTo(1); + verifyTimestampGeneratorCustomObjectUpsertIsNotCalled( + targetClient, "ProductSync", "myRunnerName"); + verifyLastSyncCustomObjectQuery(targetClient, "productSync", "myRunnerName", "foo", 0); + verify(targetClient.customObjects(), times(0)).post(any(CustomObjectDraft.class)); + verifyInteractionsWithClientAfterSync(sourceClient, 1); + + final Condition startLog = + new Condition<>( + loggingEvent -> + Level.INFO.equals(loggingEvent.getLevel()) + && loggingEvent.getMessage().contains("Starting ProductSync"), + "start log"); + + final Condition statisticsLog = + new Condition<>( + loggingEvent -> + Level.INFO.equals(loggingEvent.getLevel()) + && loggingEvent + .getMessage() + .contains( + "Summary: 0 product(s) were processed in total (0 created, 0 updated, " + + "0 failed to sync and 0 product(s) with missing reference(s))."), + "statistics log"); + + assertThat(productSyncerTestLogger.getAllLoggingEvents()) + .hasSize(2) + .haveExactly(1, startLog) + .haveExactly(1, statisticsLog); + } + + @Test + void + sync_AsProductsFullSyncWithExceptionDuringAttributeReferenceReplacement_ShouldBuildSyncerAndExecuteSync() { + // preparation + final ProductProjection product5 = + ProductMixin.toProjection( + readObjectFromResource("product-key-5.json", Product.class), + ProductProjectionType.STAGED); + final ProductProjection product6 = + ProductMixin.toProjection( + readObjectFromResource("product-key-6.json", Product.class), + ProductProjectionType.STAGED); + final ProductProjectionPagedQueryResponse twoProductResult = + ProductProjectionPagedQueryResponseBuilder.of() + .results(product5, product6) + .limit(10L) + .count(2L) + .offset(0L) + .total(2L) + .build(); + + final AtomicInteger verifyProductProjectionsGetCounter = new AtomicInteger(0); + final BadGatewayException badGatewayException = createBadGatewayException(); + final ProjectApiRoot sourceClient = + withTestClient( + "testProjectKey", + (uri, method, encodedRequetsBody) -> { + if (uri.contains("graphql") && ApiHttpMethod.POST.equals(method)) { + return CompletableFutureUtils.failed(badGatewayException); + } + if (uri.contains("product-projections") && ApiHttpMethod.GET.equals(method)) { + if (verifyProductProjectionsGetCounter.get() == 0) { + verifyProductProjectionsGetCounter.incrementAndGet(); + final String jsonString = + createJsonStringFromPagedQueryResponse(twoProductResult); + return CompletableFuture.completedFuture( + new ApiHttpResponse<>( + 200, null, jsonString.getBytes(StandardCharsets.UTF_8))); + } + } + return null; + }); + + stubClientsCustomObjectService(targetClient, ZonedDateTime.now()); + final ByProjectKeyProductsPost byProjectKeyProductsPost = mock(ByProjectKeyProductsPost.class); + when(byProjectKeyProductsPost.execute()) + .thenReturn(CompletableFutureUtils.failed(badGatewayException)); + when(targetClient.products()).thenReturn(mock()); + when(targetClient.products().post(any(ProductDraft.class))) + .thenReturn(byProjectKeyProductsPost); + + final ProjectApiRoot sourceSpy = spy(sourceClient); + final SyncerFactory syncerFactory = + SyncerFactory.of(() -> sourceSpy, () -> targetClient, getMockedClock()); + + // test + syncerFactory.sync(new String[] {"products"}, "myRunnerName", true, false, null); + + // assertions + verify(sourceSpy, times(1)).productProjections(); + verify(sourceSpy, times(3)).graphql(); + verifyInteractionsWithClientAfterSync(sourceSpy, 1); + + final Condition startLog = + new Condition<>( + loggingEvent -> + Level.INFO.equals(loggingEvent.getLevel()) + && loggingEvent.getMessage().contains("Starting ProductSync"), + "start log"); + + final Condition statisticsLog = + new Condition<>( + loggingEvent -> + Level.INFO.equals(loggingEvent.getLevel()) + && loggingEvent + .getMessage() + .contains( + "Summary: 0 product(s) were processed in total (0 created, 0 updated, " + + "0 failed to sync and 0 product(s) with missing reference(s))."), + "statistics log"); + + assertThat(productSyncerTestLogger.getAllLoggingEvents()) + .hasSize(3) + .haveExactly(1, startLog) + .haveExactly(1, statisticsLog); + + assertThat(productSyncerTestLogger.getAllLoggingEvents()) + .anySatisfy( + loggingEvent -> { + assertThat(loggingEvent.getMessage()) + .contains( + ReferenceTransformException.class.getCanonicalName() + + ": Failed to replace referenced resource ids with keys on the attributes of the " + + "products in the current fetched page from the source project. " + + "This page will not be synced to the target project."); + assertThat(loggingEvent.getThrowable().isPresent()).isTrue(); + assertThat(loggingEvent.getThrowable().get().getCause().getCause()) + .isEqualTo(badGatewayException); + }); + } + + // TODO: Enable test when issue with java-sync (NPE) is solved. + // See https://github.com/commercetools/commercetools-sync-java/issues/1101 + @Disabled + @Test + void + sync_AsProductsFullSyncWithExceptionDuringAttributeReferenceReplacement_ShouldContinueWithPages() { + // preparation + final AtomicInteger verifyProductProjectionsGetCounter = new AtomicInteger(0); + final AtomicInteger sourceGraphQLPostCounter = new AtomicInteger(0); + final ProductProjection product1 = + ProductMixin.toProjection( + readObjectFromResource("product-key-7.json", Product.class), + ProductProjectionType.STAGED); + final ProductProjection product2 = + ProductMixin.toProjection( + readObjectFromResource("product-key-8.json", Product.class), + ProductProjectionType.STAGED); + final ProductProjection product3 = + ProductMixin.toProjection( + readObjectFromResource("product-key-9.json", Product.class), + ProductProjectionType.STAGED); + + final List fullPageOfProducts = + IntStream.range(0, 500).mapToObj(o -> product1).collect(Collectors.toList()); + + final Long pageSize = Long.valueOf(fullPageOfProducts.size()); + + final ProductProjectionPagedQueryResponse fullPageResponse = + ProductProjectionPagedQueryResponseBuilder.of() + .results(fullPageOfProducts) + .limit(10L) + .count(pageSize) + .offset(0L) + .total(pageSize) + .build(); + final ProductProjectionPagedQueryResponse twoProductResult = + ProductProjectionPagedQueryResponseBuilder.of() + .results(product3, product2) + .limit(10L) + .count(2L) + .offset(0L) + .total(2L) + .build(); + + final ProjectApiRoot srcClient = + withTestClient( + "testProjectKey", + (uri, method, encodedRequestBody) -> { + final Charset charsetUTF8 = Charset.forName(StandardCharsets.UTF_8.name()); + if (uri.contains("graphql") && ApiHttpMethod.POST.equals(method)) { + final ObjectMapper objectMapper = JsonUtils.getConfiguredObjectMapper(); + GraphQLRequest graphQLRequest; + try { + graphQLRequest = objectMapper.readValue(encodedRequestBody, GraphQLRequest.class); + } catch (JsonProcessingException e) { + graphQLRequest = GraphQLRequestBuilder.of().build(); + } + final String graphQLRequestQuery = graphQLRequest.getQuery(); + final String bodyData = "{\"data\": %s}"; + String result = String.format(bodyData, "{}"); + if (graphQLRequestQuery != null) { + int graphQlPostCounter = sourceGraphQLPostCounter.getAndIncrement(); + if (graphQlPostCounter < 3) { + return CompletableFutureUtils.failed(createBadGatewayException()); + } + if (graphQLRequestQuery.contains("products")) { + final String jsonAsString = + "{\"results\":[{\"id\":\"53c4a8b4-865f-4b95-b6f2-3e1e70e3d0c1\",\"key\":\"productKey3\"}]}"; + result = String.format(bodyData, jsonAsString); + return CompletableFuture.completedFuture( + new ApiHttpResponse<>(200, null, result.getBytes(charsetUTF8))); + } else if (graphQLRequestQuery.contains("productTypes")) { + final String jsonAsString = + "{\"results\":[{\"id\":\"53c4a8b4-865f-4b95-b6f2-3e1e70e3d0c2\",\"key\":\"prodType1\"}]}"; + result = String.format(bodyData, jsonAsString); + return CompletableFuture.completedFuture( + new ApiHttpResponse<>(200, null, result.getBytes(charsetUTF8))); + } else if (graphQLRequestQuery.contains("categories")) { + final String jsonAsString = + "{\"results\":[{\"id\":\"53c4a8b4-865f-4b95-b6f2-3e1e70e3d0c3\",\"key\":\"cat1\"}]}"; + result = String.format(bodyData, jsonAsString); + return CompletableFuture.completedFuture( + new ApiHttpResponse<>(200, null, result.getBytes(charsetUTF8))); + } else { + return CompletableFuture.completedFuture( + new ApiHttpResponse<>(200, null, result.getBytes(charsetUTF8))); + } + } + } + if (uri.contains("product-projections") && ApiHttpMethod.GET.equals(method)) { + int sourceGetCounter = verifyProductProjectionsGetCounter.getAndIncrement(); + if (sourceGetCounter == 0) { + final String fullPageResponseAsString = + createJsonStringFromPagedQueryResponse(fullPageResponse); + return CompletableFuture.completedFuture( + new ApiHttpResponse<>( + 200, null, fullPageResponseAsString.getBytes(charsetUTF8))); + } else if (sourceGetCounter == 1) { + final String twoResultsResponseAsString = + createJsonStringFromPagedQueryResponse(twoProductResult); + return CompletableFuture.completedFuture( + new ApiHttpResponse<>( + 200, null, twoResultsResponseAsString.getBytes(charsetUTF8))); + + } else { + final String emptyResultsAsString = "{\"results\":[]}"; + return CompletableFuture.completedFuture( + new ApiHttpResponse<>(200, null, emptyResultsAsString.getBytes(charsetUTF8))); + } + } + return null; + }); + + final ProjectApiRoot trgClient = + ApiRootBuilder.of( + request -> { + final String uri = request.getUri() != null ? request.getUri().toString() : ""; + final ApiHttpMethod method = request.getMethod(); + final Charset charsetUTF8 = Charset.forName(StandardCharsets.UTF_8.name()); + if (uri.contains("graphql") && ApiHttpMethod.POST.equals(method)) { + final String encodedRequestBody = + new String(request.getBody(), StandardCharsets.UTF_8); + ObjectMapper objectMapper = JsonUtils.getConfiguredObjectMapper(); + GraphQLRequest graphQLRequest; + try { + graphQLRequest = + objectMapper.readValue(encodedRequestBody, GraphQLRequest.class); + } catch (JsonProcessingException e) { + graphQLRequest = GraphQLRequestBuilder.of().build(); + } + final String graphQLRequestQuery = graphQLRequest.getQuery(); + final String bodyData = "{\"data\": %s}"; + String result; + if (graphQLRequestQuery != null) { + if (graphQLRequestQuery.contains("products")) { + final String jsonAsString = + "{\"results\":[{\"id\":\"53c4a8b4-865f-4b95-b6f2-3e1e70e3d0c1\",\"key\":\"productKey3\"}]}"; + result = String.format(bodyData, jsonAsString); + return CompletableFuture.completedFuture( + new ApiHttpResponse<>(200, null, result.getBytes(charsetUTF8))); + } else { + result = String.format(bodyData, "{\"results\": []}"); + return CompletableFuture.completedFuture( + new ApiHttpResponse<>(200, null, result.getBytes(charsetUTF8))); + } + } + } + if (uri.contains("products") && ApiHttpMethod.POST.equals(method)) { + final String productsResultAsString = readStringFromFile("product-key-8.json"); + return CompletableFuture.completedFuture( + new ApiHttpResponse<>( + 200, null, productsResultAsString.getBytes(charsetUTF8))); + + } else { + final String emptyResultsAsString = "{\"results\":[]}"; + return CompletableFuture.completedFuture( + new ApiHttpResponse<>( + 200, null, emptyResultsAsString.getBytes(charsetUTF8))); + } + }) + .withApiBaseUrl("testBaseUrl") + .build("testProjectKey2"); + + final ProjectApiRoot sourceClientSpy = spy(srcClient); + final ProjectApiRoot targetClientSpy = spy(trgClient); + + final SyncerFactory syncerFactory = + SyncerFactory.of(() -> sourceClientSpy, () -> targetClientSpy, getMockedClock()); + + // test + syncerFactory.sync(new String[] {"products"}, "myRunnerName", true, false, null); + + // assertions + assertThat(verifyProductProjectionsGetCounter.get()).isEqualTo(2); + verify(sourceClientSpy, times(9)).graphql(); + verifyInteractionsWithClientAfterSync(sourceClientSpy, 1); + + final Condition startLog = + new Condition<>( + loggingEvent -> + Level.INFO.equals(loggingEvent.getLevel()) + && loggingEvent.getMessage().contains("Starting ProductSync"), + "start log"); + + final Condition statisticsLog = + new Condition<>( + loggingEvent -> + Level.INFO.equals(loggingEvent.getLevel()) + && loggingEvent + .getMessage() + .contains( + "Summary: 2 product(s) were processed in total (2 created, 0 updated, " + + "0 failed to sync and 0 product(s) with missing reference(s))."), + "statistics log"); + + assertThat(productSyncerTestLogger.getAllLoggingEvents()) + .hasSize(3) + .haveExactly(1, startLog) + .haveExactly(1, statisticsLog); + + assertThat(productSyncerTestLogger.getAllLoggingEvents()) + .anySatisfy( + loggingEvent -> { + assertThat(loggingEvent.getMessage()) + .contains( + ReferenceTransformException.class.getCanonicalName() + + ": Failed to replace referenced resource ids with keys on the attributes of the " + + "products in the current fetched page from the source project. " + + "This page will not be synced to the target project."); + assertThat(loggingEvent.getThrowable().isPresent()).isTrue(); + assertThat(loggingEvent.getThrowable().get().getCause().getCause()) + .isInstanceOf(BadGatewayException.class); + }); + } + + private static void verifyTimestampGeneratorCustomObjectUpsertIsCalled( + @Nonnull final ProjectApiRoot client, + @Nonnull final String syncMethodName, + @Nonnull final String syncRunnerName) { + final CustomObjectDraft customObjectDraft = + findTimestampGeneratorCustomObjectUpsert(client, syncMethodName, syncRunnerName); + assertThat(customObjectDraft).isNotNull(); + assertThat((String) customObjectDraft.getValue()) + .matches( + "[0-9a-fA-F]{8}\\-[0-9a-fA-F]{4}\\-[0-9a-fA-F]{4}\\-[0-9a-fA-F]{4}\\-[0-9a-fA-F]{12}"); + } + + private static void verifyTimestampGeneratorCustomObjectUpsertIsNotCalled( + @Nonnull final ProjectApiRoot client, + @Nonnull final String syncMethodName, + @Nonnull final String syncRunnerName) { + final CustomObjectDraft customObjectDraft = + findTimestampGeneratorCustomObjectUpsert(client, syncMethodName, syncRunnerName); + assertThat(customObjectDraft).isNull(); + } + + private static CustomObjectDraft findTimestampGeneratorCustomObjectUpsert( + @Nonnull ProjectApiRoot client, + @Nonnull String syncMethodName, + @Nonnull String syncRunnerName) { + final ArgumentCaptor customObjectDraftArgumentCaptor = + ArgumentCaptor.forClass(CustomObjectDraft.class); + + verify(client.customObjects(), atLeast(0)).post(customObjectDraftArgumentCaptor.capture()); + final List allValues = customObjectDraftArgumentCaptor.getAllValues(); + final CustomObjectDraft customObjectDraft = + allValues.stream() + .filter( + draft -> + draft + .getContainer() + .equals( + format( + "%s.%s.%s.%s", + getApplicationName(), + syncRunnerName, + syncMethodName, + TIMESTAMP_GENERATOR_KEY)) + && draft.getKey().equals(TIMESTAMP_GENERATOR_KEY)) + .findAny() + .orElse(null); + return customObjectDraft; + } + + private static void verifyLastSyncCustomObjectQuery( + @Nonnull final ProjectApiRoot client, + @Nonnull final String syncModuleName, + @Nonnull final String syncRunnerName, + @Nonnull final String sourceProjectKey, + final int expectedInvocations) { + + final String container = + format("commercetools-project-sync.%s.%s", syncRunnerName, syncModuleName); + + if (expectedInvocations > 0) { + verify(client.customObjects(), times(expectedInvocations)) + .withContainerAndKey(container, sourceProjectKey); + } else { + verifyNoInteractions( + client.customObjects().withContainerAndKey(anyString(), anyString()).get()); + } + } + + private static String createJsonStringFromPagedQueryResponse( + final ResourcePagedQueryResponse pagedQueryResponse) { + try { + return JsonUtils.toJsonString(pagedQueryResponse); + } catch (JsonProcessingException e) { + return "{\"results\": []}"; + } + } + + @Test + @SuppressWarnings("unchecked") + void sync_AsCategoriesDeltaSync_ShouldBuildSyncerAndExecuteSync() { + // preparation + final ZonedDateTime currentCtpTimestamp = ZonedDateTime.now(); + stubClientsCustomObjectService(targetClient, currentCtpTimestamp); + + final SyncerFactory syncerFactory = + SyncerFactory.of(() -> sourceClient, () -> targetClient, getMockedClock()); + // test + syncerFactory.sync(new String[] {"categories"}, null, false, false, null); + + // assertions + verify(sourceClient, times(1)).categories(); + verifyTimestampGeneratorCustomObjectUpsertIsCalled( + targetClient, "CategorySync", DEFAULT_RUNNER_NAME); + verifyLastSyncCustomObjectQuery( + targetClient, "categorySync", DEFAULT_RUNNER_NAME, "testProjectKey", 1); + // verify two custom object upserts : 1. current ctp timestamp and 2. last sync timestamp + verify(targetClient.customObjects(), times(2)).post(any(CustomObjectDraft.class)); + // TODO: Assert on actual last sync timestamp creation in detail after Statistics classes in + // java-sync library + // TODO: override #equals method: + // https://github.com/commercetools/commercetools-sync-java/issues/376 + // TODO: e.g. verifyNewLastSyncCustomObjectCreation(targetClient, + // currentCtpTimestamp.minusMinutes(2), any(ProductSyncStatistics.class), 0L, "productSync", + // "foo"); + verifyInteractionsWithClientAfterSync(sourceClient, 1); + + final Condition startLog = + new Condition<>( + loggingEvent -> + Level.INFO.equals(loggingEvent.getLevel()) + && loggingEvent.getMessage().contains("Starting CategorySync"), + "start log"); + + final Condition statisticsLog = + new Condition<>( + loggingEvent -> + Level.INFO.equals(loggingEvent.getLevel()) + && loggingEvent + .getMessage() + .contains( + "Summary: 0 categories were processed in total (0 created, 0 updated, " + + "0 failed to sync and 0 categories with a missing parent)."), + "statistics log"); + + assertThat(categorySyncerTestLogger.getAllLoggingEvents()) + .hasSize(2) + .haveExactly(1, startLog) + .haveExactly(1, statisticsLog); + } + + @Test + @SuppressWarnings("unchecked") + void sync_AsProductTypesDeltaSync_ShouldBuildSyncerAndExecuteSync() { + // preparation + final ZonedDateTime currentCtpTimestamp = ZonedDateTime.now(); + stubClientsCustomObjectService(targetClient, currentCtpTimestamp); + + final SyncerFactory syncerFactory = + SyncerFactory.of(() -> sourceClient, () -> targetClient, getMockedClock()); + + // test + syncerFactory.sync(new String[] {"productTypes"}, "", false, false, null); + + // assertions + verify(sourceClient, times(1)).productTypes(); + verifyTimestampGeneratorCustomObjectUpsertIsCalled( + targetClient, "ProductTypeSync", DEFAULT_RUNNER_NAME); + verifyLastSyncCustomObjectQuery( + targetClient, "productTypeSync", DEFAULT_RUNNER_NAME, "testProjectKey", 1); + // verify two custom object upserts : 1. current ctp timestamp and 2. last sync timestamp + // creation) + verify(targetClient.customObjects(), times(2)).post(any(CustomObjectDraft.class)); + // TODO: Assert on actual last sync timestamp creation in detail after Statistics classes in + // java-sync library + // TODO: override #equals method: + // https://github.com/commercetools/commercetools-sync-java/issues/376 + // TODO: e.g. verifyNewLastSyncCustomObjectCreation(targetClient, + // currentCtpTimestamp.minusMinutes(2), any(ProductSyncStatistics.class), 0L, "productSync", + // "foo"); + verifyInteractionsWithClientAfterSync(sourceClient, 1); + + final Condition startLog = + new Condition<>( + loggingEvent -> + Level.INFO.equals(loggingEvent.getLevel()) + && loggingEvent.getMessage().contains("Starting ProductTypeSync"), + "start log"); + + final Condition statisticsLog = + new Condition<>( + loggingEvent -> + Level.INFO.equals(loggingEvent.getLevel()) + && loggingEvent + .getMessage() + .contains( + "Summary: 0 product types were processed in total (0 created, 0 updated, 0 failed to sync" + + " and 0 product types with at least one NestedType or a Set of NestedType attribute" + + " definition(s) referencing a missing product type)."), + "statistics log"); + + assertThat(productTypeSyncerTestLogger.getAllLoggingEvents()) + .hasSize(2) + .haveExactly(1, startLog) + .haveExactly(1, statisticsLog); + } + + @Test + @SuppressWarnings("unchecked") + void sync_AsTypesDeltaSync_ShouldBuildSyncerAndExecuteSync() { + // preparation + final ZonedDateTime currentCtpTimestamp = ZonedDateTime.now(); + stubClientsCustomObjectService(targetClient, ZonedDateTime.now()); + + final SyncerFactory syncerFactory = + SyncerFactory.of(() -> sourceClient, () -> targetClient, getMockedClock()); + + // test + syncerFactory.sync(new String[] {"types"}, "foo", false, false, null); + + // assertions + verify(sourceClient, times(1)).types(); + verifyTimestampGeneratorCustomObjectUpsertIsCalled(targetClient, "TypeSync", "foo"); + verifyLastSyncCustomObjectQuery(targetClient, "typeSync", "foo", "testProjectKey", 1); + // verify two custom object upserts : 1. current ctp timestamp and 2. last sync timestamp + // creation) + verify(targetClient.customObjects(), times(2)).post(any(CustomObjectDraft.class)); + // TODO: Assert on actual last sync timestamp creation in detail after Statistics classes in + // java-sync library + // TODO: override #equals method: + // https://github.com/commercetools/commercetools-sync-java/issues/376 + // TODO: e.g. verifyNewLastSyncCustomObjectCreation(targetClient, + // currentCtpTimestamp.minusMinutes(2), any(ProductSyncStatistics.class), 0L, "productSync", + // "foo"); + verifyInteractionsWithClientAfterSync(sourceClient, 1); + + final Condition startLog = + new Condition<>( + loggingEvent -> + Level.INFO.equals(loggingEvent.getLevel()) + && loggingEvent.getMessage().contains("Starting TypeSync"), + "start log"); + + final Condition statisticsLog = + new Condition<>( + loggingEvent -> + Level.INFO.equals(loggingEvent.getLevel()) + && loggingEvent + .getMessage() + .contains( + "Summary: 0 types were processed in total (0 created, 0 updated " + + "and 0 failed to sync)."), + "statistics log"); + + assertThat(typeSyncerTestLogger.getAllLoggingEvents()) + .hasSize(2) + .haveExactly(1, startLog) + .haveExactly(1, statisticsLog); + } + + @Test + @SuppressWarnings("unchecked") + void sync_AsInventoryEntriesDeltaSync_ShouldBuildSyncerAndExecuteSync() { + // preparation + stubClientsCustomObjectService(targetClient, ZonedDateTime.now()); + + final SyncerFactory syncerFactory = + SyncerFactory.of(() -> sourceClient, () -> targetClient, getMockedClock()); + + // test + syncerFactory.sync(new String[] {"inventoryEntries"}, null, false, false, null); + + // assertions + verify(sourceClient, times(1)).inventory(); + verifyTimestampGeneratorCustomObjectUpsertIsCalled( + targetClient, "InventorySync", DEFAULT_RUNNER_NAME); + verifyLastSyncCustomObjectQuery( + targetClient, "inventorySync", DEFAULT_RUNNER_NAME, "testProjectKey", 1); + // verify two custom object upserts : 1. current ctp timestamp and 2. last sync timestamp + // creation) + verify(targetClient.customObjects(), times(2)).post(any(CustomObjectDraft.class)); + // TODO: Assert on actual last sync timestamp creation in detail after Statistics classes in + // java-sync library + // TODO: override #equals method: + // https://github.com/commercetools/commercetools-sync-java/issues/376 + // TODO: e.g. verifyNewLastSyncCustomObjectCreation(targetClient, + // currentCtpTimestamp.minusMinutes(2), any(ProductSyncStatistics.class), 0L, "productSync", + // "foo"); + verifyInteractionsWithClientAfterSync(sourceClient, 1); + + final Condition startLog = + new Condition<>( + loggingEvent -> + Level.INFO.equals(loggingEvent.getLevel()) + && loggingEvent.getMessage().contains("Starting InventorySync"), + "start log"); + + final Condition statisticsLog = + new Condition<>( + loggingEvent -> + Level.INFO.equals(loggingEvent.getLevel()) + && loggingEvent + .getMessage() + .contains( + "Summary: 0 inventory entries were processed in total (0 created, 0 updated " + + "and 0 failed to sync)."), + "statistics log"); + + assertThat(inventoryEntrySyncerTestLogger.getAllLoggingEvents()) + .hasSize(2) + .haveExactly(1, startLog) + .haveExactly(1, statisticsLog); + } + + @Test + void sync_WithErrorOnFetch_ShouldCloseClientAndCompleteExceptionally() { + // preparation + final ProjectApiRoot mockSource = + withTestClient( + "testProjectKey", + (uri, method, encodedRequestBody) -> { + if (uri.contains("inventory") && ApiHttpMethod.GET.equals(method)) { + return CompletableFutureUtils.exceptionallyCompletedFuture( + createBadGatewayException()); + } + return null; + }); + sourceClient = spy(mockSource); + + stubClientsCustomObjectService(targetClient, ZonedDateTime.now()); + + final SyncerFactory syncerFactory = + SyncerFactory.of(() -> sourceClient, () -> targetClient, getMockedClock()); + + // test + final CompletionStage result = + syncerFactory.sync(new String[] {"inventoryEntries"}, null, false, false, null); + + // assertions + verifyTimestampGeneratorCustomObjectUpsertIsCalled( + targetClient, "InventorySync", DEFAULT_RUNNER_NAME); + verify(sourceClient, times(1)).inventory(); + verifyInteractionsWithClientAfterSync(sourceClient, 1); + + assertThat(result) + .failsWithin(1, TimeUnit.SECONDS) + .withThrowableOfType(ExecutionException.class) + .withCauseExactlyInstanceOf(BadGatewayException.class); + } + + @Test + @SuppressWarnings("unchecked") + void + sync_WithErrorOnCurrentCtpTimestampUpsert_ShouldCloseClientAndCompleteExceptionallyWithoutSyncing() { + // preparation + final ByProjectKeyCustomObjectsPost customObjectsPost = + mock(ByProjectKeyCustomObjectsPost.class); + when(customObjectsPost.execute()) + .thenReturn( + CompletableFutureUtils.exceptionallyCompletedFuture(createBadGatewayException())); + when(targetClient.customObjects()).thenReturn(mock()); + when(targetClient.customObjects().post(any(CustomObjectDraft.class))) + .thenReturn(customObjectsPost); + + final SyncerFactory syncerFactory = + SyncerFactory.of(() -> sourceClient, () -> targetClient, getMockedClock()); + + // test + final CompletionStage result = + syncerFactory.sync(new String[] {"inventoryEntries"}, "", false, false, null); + + // assertions + verifyTimestampGeneratorCustomObjectUpsertIsCalled( + targetClient, "InventorySync", DEFAULT_RUNNER_NAME); + verify(sourceClient, times(0)).inventory(); + verifyInteractionsWithClientAfterSync(sourceClient, 1); + assertThat(result) + .failsWithin(1, TimeUnit.SECONDS) + .withThrowableOfType(ExecutionException.class) + .withCauseExactlyInstanceOf(BadGatewayException.class); + } + + @Test + @SuppressWarnings("unchecked") + void + sync_WithErrorOnQueryLastSyncTimestamp_ShouldCloseClientAndCompleteExceptionallyWithoutSyncing() { + // preparation + final ByProjectKeyCustomObjectsPost customObjectsPost = + mock(ByProjectKeyCustomObjectsPost.class); + final CustomObject lastSyncCustomObjectCustomObject = + mockLastSyncCustomObject(ZonedDateTime.now()); + final ApiHttpResponse apiHttpResponse = mock(ApiHttpResponse.class); + when(apiHttpResponse.getBody()).thenReturn(lastSyncCustomObjectCustomObject); + when(customObjectsPost.execute()) + .thenReturn(CompletableFuture.completedFuture(apiHttpResponse)); + when(targetClient.customObjects()).thenReturn(mock()); + when(targetClient.customObjects().withContainerAndKey(anyString(), anyString())) + .thenReturn(mock()); + when(targetClient.customObjects().withContainerAndKey(anyString(), anyString()).get()) + .thenReturn(mock()); + when(targetClient.customObjects().withContainerAndKey(anyString(), anyString()).get().execute()) + .thenReturn( + CompletableFutureUtils.exceptionallyCompletedFuture(createBadGatewayException())); + when(targetClient.customObjects().post(any(CustomObjectDraft.class))) + .thenReturn(customObjectsPost); + + final SyncerFactory syncerFactory = + SyncerFactory.of(() -> sourceClient, () -> targetClient, getMockedClock()); + + // test + final CompletionStage result = + syncerFactory.sync(new String[] {"inventoryEntries"}, "bar", false, false, null); + + // assertions + verifyTimestampGeneratorCustomObjectUpsertIsCalled(targetClient, "InventorySync", "bar"); + verifyLastSyncCustomObjectQuery(targetClient, "inventorySync", "bar", "testProjectKey", 1); + verify(sourceClient, times(0)).inventory(); + verifyInteractionsWithClientAfterSync(sourceClient, 1); + assertThat(result) + .failsWithin(1, TimeUnit.SECONDS) + .withThrowableOfType(ExecutionException.class) + .withCauseExactlyInstanceOf(BadGatewayException.class); + } + + @Test + @SuppressWarnings("unchecked") + void syncAll_AsDelta_ShouldBuildSyncerAndExecuteSync() { + // preparation + stubClientsCustomObjectService(targetClient, ZonedDateTime.now()); + final SyncerFactory syncerFactory = + SyncerFactory.of(() -> sourceClient, () -> targetClient, getMockedClock()); + + // test + syncerFactory.sync(new String[] {"all"}, null, false, false, null).join(); + + // assertions + verifyTimestampGeneratorCustomObjectUpsertIsCalled( + targetClient, "ProductTypeSync", DEFAULT_RUNNER_NAME); + verifyTimestampGeneratorCustomObjectUpsertIsCalled( + targetClient, "ProductSync", DEFAULT_RUNNER_NAME); + verifyTimestampGeneratorCustomObjectUpsertIsCalled( + targetClient, "CategorySync", DEFAULT_RUNNER_NAME); + verifyTimestampGeneratorCustomObjectUpsertIsCalled( + targetClient, "TypeSync", DEFAULT_RUNNER_NAME); + verifyTimestampGeneratorCustomObjectUpsertIsCalled( + targetClient, "InventorySync", DEFAULT_RUNNER_NAME); + verifyTimestampGeneratorCustomObjectUpsertIsCalled( + targetClient, "CartDiscountSync", DEFAULT_RUNNER_NAME); + verifyTimestampGeneratorCustomObjectUpsertIsCalled( + targetClient, "StateSync", DEFAULT_RUNNER_NAME); + verifyTimestampGeneratorCustomObjectUpsertIsCalled( + targetClient, "TaxCategorySync", DEFAULT_RUNNER_NAME); + verifyTimestampGeneratorCustomObjectUpsertIsCalled( + targetClient, "CustomObjectSync", DEFAULT_RUNNER_NAME); + verifyTimestampGeneratorCustomObjectUpsertIsCalled( + targetClient, "CustomerSync", DEFAULT_RUNNER_NAME); + verifyTimestampGeneratorCustomObjectUpsertIsCalled( + targetClient, "ShoppingListSync", DEFAULT_RUNNER_NAME); + verify(targetClient.customObjects(), times(22)).post(any(CustomObjectDraft.class)); + verifyLastSyncCustomObjectQuery( + targetClient, "inventorySync", DEFAULT_RUNNER_NAME, "testProjectKey", 1); + verifyLastSyncCustomObjectQuery( + targetClient, "productTypeSync", DEFAULT_RUNNER_NAME, "testProjectKey", 1); + verifyLastSyncCustomObjectQuery( + targetClient, "productSync", DEFAULT_RUNNER_NAME, "testProjectKey", 1); + verifyLastSyncCustomObjectQuery( + targetClient, "categorySync", DEFAULT_RUNNER_NAME, "testProjectKey", 1); + verifyLastSyncCustomObjectQuery( + targetClient, "typeSync", DEFAULT_RUNNER_NAME, "testProjectKey", 1); + verifyLastSyncCustomObjectQuery( + targetClient, "cartDiscountSync", DEFAULT_RUNNER_NAME, "testProjectKey", 1); + verifyLastSyncCustomObjectQuery( + targetClient, "stateSync", DEFAULT_RUNNER_NAME, "testProjectKey", 1); + verifyLastSyncCustomObjectQuery( + targetClient, "taxCategorySync", DEFAULT_RUNNER_NAME, "testProjectKey", 1); + verifyLastSyncCustomObjectQuery( + targetClient, "customObjectSync", DEFAULT_RUNNER_NAME, "testProjectKey", 1); + verifyLastSyncCustomObjectQuery( + targetClient, "customerSync", DEFAULT_RUNNER_NAME, "testProjectKey", 1); + verifyLastSyncCustomObjectQuery( + targetClient, "shoppingListSync", DEFAULT_RUNNER_NAME, "testProjectKey", 1); + verify(sourceClient, times(1)).productTypes(); + verify(sourceClient, times(1)).types(); + verify(sourceClient, times(1)).categories(); + verify(sourceClient, times(1)).productProjections(); + verify(sourceClient, times(1)).inventory(); + verify(sourceClient, times(1)).cartDiscounts(); + verify(sourceClient, times(1)).states(); + verify(sourceClient, times(1)).taxCategories(); + verify(sourceClient, times(1)).customObjects(); + verify(sourceClient, times(1)).customers(); + verify(sourceClient, times(1)).shoppingLists(); + verifyInteractionsWithClientAfterSync(sourceClient, 11); + + assertThat(cliRunnerTestLogger.getAllLoggingEvents()) + .allMatch(loggingEvent -> !Level.ERROR.equals(loggingEvent.getLevel())); + + assertThat(productSyncerTestLogger.getAllLoggingEvents()) + .allMatch(loggingEvent -> !Level.ERROR.equals(loggingEvent.getLevel())); + + assertTypeSyncerLoggingEvents(typeSyncerTestLogger, 0); + assertProductTypeSyncerLoggingEvents(productTypeSyncerTestLogger, 0); + assertCategorySyncerLoggingEvents(categorySyncerTestLogger, 0); + assertProductSyncerLoggingEvents(productSyncerTestLogger, 0); + assertInventoryEntrySyncerLoggingEvents(inventoryEntrySyncerTestLogger, 0); + assertCartDiscountSyncerLoggingEvents(cartDiscountSyncerTestLogger, 0); + // +1 state is a built-in state and it cant be deleted + assertStateSyncerLoggingEvents(stateSyncerTestLogger, 0); + assertTaxCategorySyncerLoggingEvents(taxCategorySyncerTestLogger, 0); + assertCustomObjectSyncerLoggingEvents(customObjectSyncerTestLogger, 0); + assertCustomerSyncerLoggingEvents(customerSyncerTestLogger, 0); + assertShoppingListSyncerLoggingEvents(shoppingListSyncerTestLogger, 0); + + // Every sync module is expected to have 2 logs (start and stats summary) + assertThat(typeSyncerTestLogger.getAllLoggingEvents()).hasSize(2); + assertThat(productTypeSyncerTestLogger.getAllLoggingEvents()).hasSize(2); + assertThat(categorySyncerTestLogger.getAllLoggingEvents()).hasSize(2); + assertThat(productSyncerTestLogger.getAllLoggingEvents()).hasSize(2); + assertThat(inventoryEntrySyncerTestLogger.getAllLoggingEvents()).hasSize(2); + assertThat(cartDiscountSyncerTestLogger.getAllLoggingEvents()).hasSize(2); + assertThat(stateSyncerTestLogger.getAllLoggingEvents()).hasSize(2); + assertThat(taxCategorySyncerTestLogger.getAllLoggingEvents()).hasSize(2); + assertThat(customObjectSyncerTestLogger.getAllLoggingEvents()).hasSize(2); + assertThat(customerSyncerTestLogger.getAllLoggingEvents()).hasSize(2); + assertThat(shoppingListSyncerTestLogger.getAllLoggingEvents()).hasSize(2); + } + + @Test + @SuppressWarnings("unchecked") + void syncProductTypesProductsCustomersAndShoppingLists_AsDelta_ShouldBuildSyncerAndExecuteSync() { + // preparation + stubClientsCustomObjectService(targetClient, ZonedDateTime.now()); + final SyncerFactory syncerFactory = + SyncerFactory.of(() -> sourceClient, () -> targetClient, getMockedClock()); + + // test + String[] syncModuleOptions = {"productTypes", "products", "customers", "shoppingLists"}; + syncerFactory.sync(syncModuleOptions, null, false, false, null).join(); + + // assertions + verifyTimestampGeneratorCustomObjectUpsertIsCalled( + targetClient, "ProductTypeSync", DEFAULT_RUNNER_NAME); + verifyTimestampGeneratorCustomObjectUpsertIsCalled( + targetClient, "ProductSync", DEFAULT_RUNNER_NAME); + verifyTimestampGeneratorCustomObjectUpsertIsCalled( + targetClient, "CustomerSync", DEFAULT_RUNNER_NAME); + verifyTimestampGeneratorCustomObjectUpsertIsCalled( + targetClient, "ShoppingListSync", DEFAULT_RUNNER_NAME); + verify(targetClient.customObjects(), times(8)).post(any(CustomObjectDraft.class)); + verifyLastSyncCustomObjectQuery( + targetClient, "productTypeSync", DEFAULT_RUNNER_NAME, "testProjectKey", 1); + verifyLastSyncCustomObjectQuery( + targetClient, "productSync", DEFAULT_RUNNER_NAME, "testProjectKey", 1); + verifyLastSyncCustomObjectQuery( + targetClient, "customerSync", DEFAULT_RUNNER_NAME, "testProjectKey", 1); + verifyLastSyncCustomObjectQuery( + targetClient, "shoppingListSync", DEFAULT_RUNNER_NAME, "testProjectKey", 1); + + final InOrder inOrder = Mockito.inOrder(sourceClient); + + // According to sync algorithm, ProductType and Customer will run sync in parallel, Product and + // ShoppingList sequentially. + // Example: Given: ['productTypes', 'customers', 'products', 'shoppingLists'] + // From the given arguments, algorithm will group the resources as below, + // [productTypes, customers] [products] [shoppingLists] + inOrder.verify(sourceClient, times(1)).productTypes(); + verify(sourceClient, times(1)).customers(); + inOrder.verify(sourceClient, times(1)).productProjections(); + inOrder.verify(sourceClient, times(1)).shoppingLists(); + verifyInteractionsWithClientAfterSync(sourceClient, 4); + + assertProductTypeSyncerLoggingEvents(productTypeSyncerTestLogger, 0); + assertProductSyncerLoggingEvents(productSyncerTestLogger, 0); + assertCustomerSyncerLoggingEvents(customerSyncerTestLogger, 0); + assertShoppingListSyncerLoggingEvents(shoppingListSyncerTestLogger, 0); + } + + @Test + @SuppressWarnings("unchecked") + void syncStatesInventoryEntriesAndCustomObjects_AsDelta_ShouldBuildSyncerAndExecuteSync() { + // preparation + stubClientsCustomObjectService(targetClient, ZonedDateTime.now()); + final SyncerFactory syncerFactory = + SyncerFactory.of(() -> sourceClient, () -> targetClient, getMockedClock()); + + // test + String[] syncModuleOptions = {"states", "inventoryEntries", "customObjects"}; + syncerFactory.sync(syncModuleOptions, null, false, false, null).join(); + + // assertions + verifyTimestampGeneratorCustomObjectUpsertIsCalled( + targetClient, "StateSync", DEFAULT_RUNNER_NAME); + verifyTimestampGeneratorCustomObjectUpsertIsCalled( + targetClient, "InventorySync", DEFAULT_RUNNER_NAME); + verifyTimestampGeneratorCustomObjectUpsertIsCalled( + targetClient, "CustomObjectSync", DEFAULT_RUNNER_NAME); + verify(targetClient.customObjects(), times(6)).post(any(CustomObjectDraft.class)); + verifyLastSyncCustomObjectQuery( + targetClient, "stateSync", DEFAULT_RUNNER_NAME, "testProjectKey", 1); + verifyLastSyncCustomObjectQuery( + targetClient, "inventorySync", DEFAULT_RUNNER_NAME, "testProjectKey", 1); + verifyLastSyncCustomObjectQuery( + targetClient, "customObjectSync", DEFAULT_RUNNER_NAME, "testProjectKey", 1); + verify(sourceClient, times(1)).states(); + verify(sourceClient, times(1)).inventory(); + verify(sourceClient, times(1)).customObjects(); + + verifyInteractionsWithClientAfterSync(sourceClient, 3); + + assertStateSyncerLoggingEvents(stateSyncerTestLogger, 0); + assertInventoryEntrySyncerLoggingEvents(inventoryEntrySyncerTestLogger, 0); + assertCustomObjectSyncerLoggingEvents(customObjectSyncerTestLogger, 0); + } + + @Test + @SuppressWarnings("unchecked") + void syncTypesAndCategories_AsDelta_ShouldBuildSyncerAndExecuteSync() { + // preparation + stubClientsCustomObjectService(targetClient, ZonedDateTime.now()); + + final SyncerFactory syncerFactory = + SyncerFactory.of(() -> sourceClient, () -> targetClient, getMockedClock()); + + // test + String[] syncModuleOptions = {"types", "categories"}; + syncerFactory.sync(syncModuleOptions, null, false, false, null).join(); + + // assertions + verifyTimestampGeneratorCustomObjectUpsertIsCalled( + targetClient, "TypeSync", DEFAULT_RUNNER_NAME); + verifyTimestampGeneratorCustomObjectUpsertIsCalled( + targetClient, "CategorySync", DEFAULT_RUNNER_NAME); + verify(targetClient.customObjects(), times(4)).post(any(CustomObjectDraft.class)); + verifyLastSyncCustomObjectQuery( + targetClient, "typeSync", DEFAULT_RUNNER_NAME, "testProjectKey", 1); + verifyLastSyncCustomObjectQuery( + targetClient, "categorySync", DEFAULT_RUNNER_NAME, "testProjectKey", 1); + + final InOrder inOrder = Mockito.inOrder(sourceClient); + + inOrder.verify(sourceClient, times(1)).types(); + inOrder.verify(sourceClient, times(1)).categories(); + verifyInteractionsWithClientAfterSync(sourceClient, 2); + + assertTypeSyncerLoggingEvents(typeSyncerTestLogger, 0); + assertCategorySyncerLoggingEvents(categorySyncerTestLogger, 0); + assertThat(typeSyncerTestLogger.getAllLoggingEvents()).hasSize(2); + assertThat(categorySyncerTestLogger.getAllLoggingEvents()).hasSize(2); + } + + @Test + @SuppressWarnings("unchecked") + void syncProductsAndShoppingLists_AsDelta_ShouldBuildSyncerAndExecuteSync() { + // preparation + stubClientsCustomObjectService(targetClient, ZonedDateTime.now()); + + final SyncerFactory syncerFactory = + SyncerFactory.of(() -> sourceClient, () -> targetClient, getMockedClock()); + + // test + String[] syncModuleOptions = {"products", "shoppingLists"}; + syncerFactory.sync(syncModuleOptions, null, false, false, null).join(); + + // assertions + verifyTimestampGeneratorCustomObjectUpsertIsCalled( + targetClient, "ProductSync", DEFAULT_RUNNER_NAME); + verifyTimestampGeneratorCustomObjectUpsertIsCalled( + targetClient, "ShoppingListSync", DEFAULT_RUNNER_NAME); + verify(targetClient.customObjects(), times(4)).post(any(CustomObjectDraft.class)); + verifyLastSyncCustomObjectQuery( + targetClient, "productSync", DEFAULT_RUNNER_NAME, "testProjectKey", 1); + verifyLastSyncCustomObjectQuery( + targetClient, "shoppingListSync", DEFAULT_RUNNER_NAME, "testProjectKey", 1); + verify(sourceClient, times(1)).productProjections(); + verify(sourceClient, times(1)).shoppingLists(); + verifyInteractionsWithClientAfterSync(sourceClient, 2); + + assertProductSyncerLoggingEvents(productSyncerTestLogger, 0); + assertShoppingListSyncerLoggingEvents(shoppingListSyncerTestLogger, 0); + assertThat(productSyncerTestLogger.getAllLoggingEvents()).hasSize(2); + assertThat(shoppingListSyncerTestLogger.getAllLoggingEvents()).hasSize(2); + } + + @Test + void sync_AsDelta_WithOneUnmatchedSyncOptionValue_ShouldResultIllegalArgumentException() { + final SyncerFactory syncerFactory = + SyncerFactory.of(() -> sourceClient, () -> targetClient, getMockedClock()); + + // test + String[] syncResources = {"productTypes", "unknown", "shoppingLists"}; + CompletionStage result = syncerFactory.sync(syncResources, null, false, false, null); + + String errorMessage = + format( + "Unknown argument \"%s\" supplied to \"-s\" or \"--sync\" option! %s", + syncResources[1], SYNC_MODULE_OPTION_DESCRIPTION); + + assertThat(result) + .failsWithin(1, TimeUnit.SECONDS) + .withThrowableOfType(ExecutionException.class) + .withCauseExactlyInstanceOf(CliException.class) + .withMessageContaining(errorMessage); + } + + @Test + void sync_AsDelta_WithSyncOptionValuesAndAll_ShouldResultIllegalArgumentException() { + final SyncerFactory syncerFactory = + SyncerFactory.of(() -> sourceClient, () -> targetClient, getMockedClock()); + + // test + String[] syncResources = {"productTypes", "all", "shoppingLists"}; + CompletionStage result = syncerFactory.sync(syncResources, null, false, false, null); + + String errorMessage = + format( + "Wrong arguments supplied to \"-s\" or \"--sync\" option! " + + "'all' option cannot be passed along with other arguments.\" %s", + SYNC_MODULE_OPTION_DESCRIPTION); + + assertThat(result) + .failsWithin(1, TimeUnit.SECONDS) + .withThrowableOfType(ExecutionException.class) + .withCauseExactlyInstanceOf(CliException.class) + .withMessageContaining(errorMessage); + } + + private void verifyInteractionsWithClientAfterSync( + @Nonnull final ProjectApiRoot client, final int numberOfGetConfigInvocations) { + + verify(client, times(1)).close(); + // Verify config is accessed for the success message after sync: + // " example: Syncing products from CTP project with key 'x' to project with key 'y' is done"," + verify(client, times(numberOfGetConfigInvocations)).getProjectKey(); + } } diff --git a/src/test/java/com/commercetools/project/sync/util/TestUtils.java b/src/test/java/com/commercetools/project/sync/util/TestUtils.java index 937804c0..c3abaf02 100644 --- a/src/test/java/com/commercetools/project/sync/util/TestUtils.java +++ b/src/test/java/com/commercetools/project/sync/util/TestUtils.java @@ -1,5 +1,6 @@ package com.commercetools.project.sync.util; +import static io.vrap.rmf.base.client.utils.json.JsonUtils.fromInputStream; import static java.lang.String.format; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; @@ -10,7 +11,9 @@ import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; +import com.commercetools.api.client.ByProjectKeyCustomObjectsPost; import com.commercetools.api.client.ProjectApiRoot; +import com.commercetools.api.defaultconfig.ApiRootBuilder; import com.commercetools.api.models.custom_object.CustomObject; import com.commercetools.api.models.custom_object.CustomObjectDraft; import com.commercetools.api.models.error.ErrorResponse; @@ -23,15 +26,20 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectWriter; +import io.vrap.rmf.base.client.ApiHttpMethod; import io.vrap.rmf.base.client.ApiHttpResponse; import io.vrap.rmf.base.client.error.BadGatewayException; +import java.io.IOException; +import java.io.InputStream; import java.nio.charset.StandardCharsets; import java.time.Clock; import java.time.ZonedDateTime; import java.util.Collections; import java.util.concurrent.CompletableFuture; import javax.annotation.Nonnull; +import org.apache.commons.lang3.StringUtils; import org.assertj.core.api.Condition; +import org.assertj.core.util.TriFunction; import uk.org.lidalia.slf4jext.Level; import uk.org.lidalia.slf4jtest.LoggingEvent; import uk.org.lidalia.slf4jtest.TestLogger; @@ -197,6 +205,22 @@ public static void verifyInteractionsWithClientAfterSync( verifyNoMoreInteractions(client); } + public static T readObjectFromResource(final String resourcePath, final Class objectType) { + final InputStream resourceAsStream = + Thread.currentThread().getContextClassLoader().getResourceAsStream(resourcePath); + return fromInputStream(resourceAsStream, objectType); + } + + public static String readStringFromFile(final String resourcePath) { + final InputStream resourceAsStream = + Thread.currentThread().getContextClassLoader().getResourceAsStream(resourcePath); + try { + return new String(resourceAsStream.readAllBytes(), StandardCharsets.UTF_8); + } catch (IOException e) { + return StringUtils.EMPTY; + } + } + @SuppressWarnings("unchecked") public static void stubClientsCustomObjectService( @Nonnull final ProjectApiRoot client, @Nonnull final ZonedDateTime currentCtpTimestamp) { @@ -204,12 +228,41 @@ public static void stubClientsCustomObjectService( final CustomObject customObject = mockLastSyncCustomObject(currentCtpTimestamp); final ApiHttpResponse apiHttpResponse = mock(ApiHttpResponse.class); when(apiHttpResponse.getBody()).thenReturn(customObject); - when(client.customObjects().post(any(CustomObjectDraft.class)).execute()) + final ByProjectKeyCustomObjectsPost customObjectsPost = + mock(ByProjectKeyCustomObjectsPost.class); + when(customObjectsPost.execute()) .thenReturn(CompletableFuture.completedFuture(apiHttpResponse)); + when(client.customObjects()).thenReturn(mock()); + when(client.customObjects().post(any(CustomObjectDraft.class))).thenReturn(customObjectsPost); + when(client.customObjects().withContainerAndKey(anyString(), anyString())).thenReturn(mock()); + when(client.customObjects().withContainerAndKey(anyString(), anyString()).get()) + .thenReturn(mock()); when(client.customObjects().withContainerAndKey(anyString(), anyString()).get().execute()) .thenReturn(CompletableFuture.completedFuture(apiHttpResponse)); } + public static ProjectApiRoot withTestClient( + final String projectKey, + final TriFunction>> fn) { + return ApiRootBuilder.of( + request -> { + final String uri = request.getUri() != null ? request.getUri().toString() : ""; + final ApiHttpMethod method = request.getMethod(); + final String encodedRequestBody = + uri.contains("graphql") + ? new String(request.getBody(), StandardCharsets.UTF_8) + : ""; + final CompletableFuture> response = + fn.apply(uri, method, encodedRequestBody); + if (response != null) { + return response; + } + return null; + }) + .withApiBaseUrl("testBaseUri") + .build(projectKey); + } + @Nonnull @SuppressWarnings("unchecked") public static CustomObject mockLastSyncCustomObject(@Nonnull ZonedDateTime currentCtpTimestamp) {