From 60ebcbde9f95946a96c88328e1ff92d5f5a6768a Mon Sep 17 00:00:00 2001 From: Hesham Massoud Date: Thu, 10 Oct 2019 16:26:29 +0200 Subject: [PATCH] #54: Bump commercetoools-sync-java --- build.gradle | 2 +- gradle-scripts/dependencies.gradle | 5 +- .../ProductSyncWithNestedReferencesIT.java | 21 +- .../sync/ProductSyncWithReferencesIT.java | 227 ++++++++++++++++++ .../sync/util/IntegrationTestUtils.java | 21 ++ .../request/CombinedResourceKeysRequest.java | 12 +- .../project/sync/SyncerFactoryTest.java | 58 +++-- .../CombinedResourceKeysRequestTest.java | 14 +- .../project/sync/util/TestUtils.java | 4 +- src/test/resources/product-key-3.json | 131 ++++++++++ 10 files changed, 442 insertions(+), 53 deletions(-) create mode 100644 src/integration-test/java/com/commercetools/project/sync/ProductSyncWithReferencesIT.java create mode 100644 src/test/resources/product-key-3.json diff --git a/build.gradle b/build.gradle index 4527c67c..3b4ad343 100644 --- a/build.gradle +++ b/build.gradle @@ -12,7 +12,7 @@ buildscript { plugins { id "com.github.ben-manes.versions" version '0.26.0' - id 'com.adarshr.test-logger' version '1.7.1' + id 'com.adarshr.test-logger' version '2.0.0' id "com.diffplug.gradle.spotless" version '3.25.0' id 'com.bmuschko.docker-java-application' version '5.2.0' } diff --git a/gradle-scripts/dependencies.gradle b/gradle-scripts/dependencies.gradle index 13a9526f..6b04f7be 100644 --- a/gradle-scripts/dependencies.gradle +++ b/gradle-scripts/dependencies.gradle @@ -1,13 +1,13 @@ ext { commercetoolsJvmSdkVersion = '1.46.0' - mockitoVersion = '3.0.0' + mockitoVersion = '3.1.0' slf4jVersion = '1.7.25' slf4jTestVersion = '1.2.0' assertjVersion = '3.13.2' pmdVersion = '5.6.1' jacocoVersion = '0.7.9' findbugsVersion = '3.0.1' - commercetoolsSyncJava='1.5.0' + commercetoolsSyncJava='1.6.0' apacheCliVersion = '1.4' jupiterApiVersion = '5.5.2' } @@ -16,6 +16,7 @@ dependencies { implementation "com.commercetools.sdk.jvm.core:commercetools-models:${commercetoolsJvmSdkVersion}" implementation "com.commercetools.sdk.jvm.core:commercetools-java-client:${commercetoolsJvmSdkVersion}" implementation "com.commercetools:commercetools-sync-java:${commercetoolsSyncJava}" + implementation "com.commercetools.sdk.jvm.core:commercetools-convenience:${commercetoolsJvmSdkVersion}" implementation "commons-cli:commons-cli:${apacheCliVersion}" implementation "org.slf4j:slf4j-api:${slf4jVersion}" implementation "org.slf4j:slf4j-simple:${slf4jVersion}" 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 e78b66ed..56e39186 100644 --- a/src/integration-test/java/com/commercetools/project/sync/ProductSyncWithNestedReferencesIT.java +++ b/src/integration-test/java/com/commercetools/project/sync/ProductSyncWithNestedReferencesIT.java @@ -4,6 +4,8 @@ import static com.commercetools.project.sync.util.IntegrationTestUtils.assertCategoryExists; 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.createAttributeObject; +import static com.commercetools.project.sync.util.IntegrationTestUtils.createReferenceObject; import static com.commercetools.project.sync.util.SphereClientUtils.CTP_SOURCE_CLIENT_CONFIG; import static com.commercetools.project.sync.util.SphereClientUtils.CTP_TARGET_CLIENT_CONFIG; import static com.commercetools.project.sync.util.TestUtils.assertCartDiscountSyncerLoggingEvents; @@ -21,7 +23,6 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.ArrayNode; import com.fasterxml.jackson.databind.node.JsonNodeFactory; -import com.fasterxml.jackson.databind.node.ObjectNode; import io.sphere.sdk.categories.Category; import io.sphere.sdk.categories.CategoryDraft; import io.sphere.sdk.categories.CategoryDraftBuilder; @@ -195,24 +196,6 @@ static void setupSourceProjectData(@Nonnull final SphereClient sourceProjectClie .join(); } - @Nonnull - private static ObjectNode createReferenceObject( - @Nonnull final String typeId, @Nonnull final String id) { - final ObjectNode referenceObject = JsonNodeFactory.instance.objectNode(); - referenceObject.set("typeId", JsonNodeFactory.instance.textNode(typeId)); - referenceObject.set("id", JsonNodeFactory.instance.textNode(id)); - return referenceObject; - } - - @Nonnull - private static ObjectNode createAttributeObject( - @Nonnull final String name, @Nonnull final ArrayNode value) { - final ObjectNode attributeObject = JsonNodeFactory.instance.objectNode(); - attributeObject.set("name", JsonNodeFactory.instance.textNode(name)); - attributeObject.set("value", value); - return attributeObject; - } - @AfterAll static void tearDownSuite() { cleanUpProjects(createClient(CTP_SOURCE_CLIENT_CONFIG), createClient(CTP_TARGET_CLIENT_CONFIG)); diff --git a/src/integration-test/java/com/commercetools/project/sync/ProductSyncWithReferencesIT.java b/src/integration-test/java/com/commercetools/project/sync/ProductSyncWithReferencesIT.java new file mode 100644 index 00000000..e38ef208 --- /dev/null +++ b/src/integration-test/java/com/commercetools/project/sync/ProductSyncWithReferencesIT.java @@ -0,0 +1,227 @@ +package com.commercetools.project.sync; + +import static com.commercetools.project.sync.util.ClientConfigurationUtils.createClient; +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.createReferenceObject; +import static com.commercetools.project.sync.util.SphereClientUtils.CTP_SOURCE_CLIENT; +import static com.commercetools.project.sync.util.SphereClientUtils.CTP_SOURCE_CLIENT_CONFIG; +import static com.commercetools.project.sync.util.SphereClientUtils.CTP_TARGET_CLIENT_CONFIG; +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.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.assertTypeSyncerLoggingEvents; +import static io.sphere.sdk.models.LocalizedString.ofEnglish; +import static java.lang.String.format; +import static java.util.Collections.emptyList; +import static java.util.Collections.singletonList; +import static org.assertj.core.api.Assertions.assertThat; + +import com.commercetools.sync.commons.models.WaitingToBeResolved; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.JsonNodeFactory; +import com.fasterxml.jackson.databind.node.ObjectNode; +import io.sphere.sdk.client.SphereClient; +import io.sphere.sdk.customobjects.CustomObject; +import io.sphere.sdk.customobjects.queries.CustomObjectQuery; +import io.sphere.sdk.products.Product; +import io.sphere.sdk.products.ProductDraft; +import io.sphere.sdk.products.ProductDraftBuilder; +import io.sphere.sdk.products.ProductVariantDraft; +import io.sphere.sdk.products.ProductVariantDraftBuilder; +import io.sphere.sdk.products.attributes.AttributeConstraint; +import io.sphere.sdk.products.attributes.AttributeDefinitionDraft; +import io.sphere.sdk.products.attributes.AttributeDefinitionDraftBuilder; +import io.sphere.sdk.products.attributes.AttributeDraft; +import io.sphere.sdk.products.attributes.ReferenceAttributeType; +import io.sphere.sdk.products.attributes.SetAttributeType; +import io.sphere.sdk.products.commands.ProductCreateCommand; +import io.sphere.sdk.products.queries.ProductQuery; +import io.sphere.sdk.producttypes.ProductType; +import io.sphere.sdk.producttypes.ProductTypeDraft; +import io.sphere.sdk.producttypes.ProductTypeDraftBuilder; +import io.sphere.sdk.producttypes.commands.ProductTypeCreateCommand; +import io.sphere.sdk.queries.PagedQueryResult; +import io.sphere.sdk.queries.QueryExecutionUtils; +import java.time.Clock; +import java.util.List; +import java.util.Set; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; +import java.util.stream.IntStream; +import javax.annotation.Nonnull; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import uk.org.lidalia.slf4jext.Level; +import uk.org.lidalia.slf4jtest.TestLogger; +import uk.org.lidalia.slf4jtest.TestLoggerFactory; + +class ProductSyncWithReferencesIT { + + private static final TestLogger syncerTestLogger = TestLoggerFactory.getTestLogger(Syncer.class); + private static final TestLogger cliRunnerTestLogger = + TestLoggerFactory.getTestLogger(CliRunner.class); + + private static final String MAIN_PRODUCT_TYPE_KEY = "main-product-type"; + private static final String MAIN_PRODUCT_MASTER_VARIANT_KEY = "main-product-master-variant-key"; + private static final String MAIN_PRODUCT_KEY = "product-with-references"; + + @BeforeEach + void setup() { + syncerTestLogger.clearAll(); + cliRunnerTestLogger.clearAll(); + cleanUpProjects(createClient(CTP_SOURCE_CLIENT_CONFIG), createClient(CTP_TARGET_CLIENT_CONFIG)); + setupSourceProjectData(createClient(CTP_SOURCE_CLIENT_CONFIG)); + } + + static void setupSourceProjectData(@Nonnull final SphereClient sourceProjectClient) { + final AttributeDefinitionDraft setOfProductsAttributeDef = + AttributeDefinitionDraftBuilder.of( + SetAttributeType.of(ReferenceAttributeType.ofProduct()), + "products", + ofEnglish("products"), + false) + .searchable(false) + .attributeConstraint(AttributeConstraint.NONE) + .build(); + + final ProductTypeDraft productTypeDraft = + ProductTypeDraftBuilder.of( + MAIN_PRODUCT_TYPE_KEY, + MAIN_PRODUCT_TYPE_KEY, + "a productType for t-shirts", + emptyList()) + .attributes(singletonList(setOfProductsAttributeDef)) + .build(); + + final ProductType productType = + sourceProjectClient + .execute(ProductTypeCreateCommand.of(productTypeDraft)) + .toCompletableFuture() + .join(); + + final ConcurrentHashMap.KeySetView productIds = ConcurrentHashMap.newKeySet(); + + // create 500 products + final CompletableFuture[] creationFutures = + IntStream.range(0, 500) + .mapToObj( + index -> { + final ProductVariantDraft masterVariant = + ProductVariantDraftBuilder.of() + .key(format("%d-mv-key", index)) + .sku(format("%d-mv-sku", index)) + .build(); + + final ProductDraft draft = + ProductDraftBuilder.of( + productType, + ofEnglish(Integer.toString(index)), + ofEnglish(format("%d-slug", index)), + masterVariant) + .key(format("%d-key", index)) + .build(); + return CTP_SOURCE_CLIENT + .execute(ProductCreateCommand.of(draft)) + .thenAccept(createdProduct -> productIds.add(createdProduct.getId())) + .toCompletableFuture(); + }) + .toArray(CompletableFuture[]::new); + + CompletableFuture.allOf(creationFutures).join(); + + final ArrayNode setAttributeValue = JsonNodeFactory.instance.arrayNode(); + final Set productReferences = + productIds + .stream() + .map(productId -> createReferenceObject(Product.referenceTypeId(), productId)) + .collect(Collectors.toSet()); + setAttributeValue.addAll(productReferences); + + final ProductVariantDraft masterVariant = + ProductVariantDraftBuilder.of() + .key(MAIN_PRODUCT_MASTER_VARIANT_KEY) + .sku(MAIN_PRODUCT_MASTER_VARIANT_KEY) + .attributes(AttributeDraft.of(setOfProductsAttributeDef.getName(), setAttributeValue)) + .build(); + + final ProductDraft draft = + ProductDraftBuilder.of( + productType, + ofEnglish(MAIN_PRODUCT_KEY), + ofEnglish(MAIN_PRODUCT_KEY), + masterVariant) + .key(MAIN_PRODUCT_KEY) + .build(); + + sourceProjectClient.execute(ProductCreateCommand.of(draft)).toCompletableFuture().join(); + } + + @AfterAll + static void tearDownSuite() { + cleanUpProjects(createClient(CTP_SOURCE_CLIENT_CONFIG), createClient(CTP_TARGET_CLIENT_CONFIG)); + } + + @Test + void run_WithAProductWith500DistinctReferences_ShouldSyncCorrectly() { + // preparation + try (final SphereClient targetClient = createClient(CTP_TARGET_CLIENT_CONFIG)) { + try (final SphereClient sourceClient = createClient(CTP_SOURCE_CLIENT_CONFIG)) { + + final SyncerFactory syncerFactory = + SyncerFactory.of(() -> sourceClient, () -> targetClient, Clock.systemDefaultZone()); + + // test + CliRunner.of().run(new String[] {"-s", "all", "-r", "runnerName", "-f"}, syncerFactory); + } + } + + // create clients again (for assertions and cleanup), since the run method closes the clients + // after execution + // is done. + try (final SphereClient postTargetClient = createClient(CTP_TARGET_CLIENT_CONFIG)) { + + // assertions + assertThat(cliRunnerTestLogger.getAllLoggingEvents()) + .allMatch(loggingEvent -> !Level.ERROR.equals(loggingEvent.getLevel())); + + assertThat(syncerTestLogger.getAllLoggingEvents()) + .allMatch(loggingEvent -> !Level.ERROR.equals(loggingEvent.getLevel())); + + assertTypeSyncerLoggingEvents(syncerTestLogger, 0); + assertProductTypeSyncerLoggingEvents(syncerTestLogger, 1); + assertCategorySyncerLoggingEvents(syncerTestLogger, 0); + assertProductSyncerLoggingEvents(syncerTestLogger, 501); + assertInventoryEntrySyncerLoggingEvents(syncerTestLogger, 0); + assertCartDiscountSyncerLoggingEvents(syncerTestLogger, 0); + + // Every sync module (6 modules) is expected to have 2 logs (start and stats summary) = 12 + assertThat(syncerTestLogger.getAllLoggingEvents()).hasSize(12); + + assertAllResourcesAreSyncedToTarget(postTargetClient); + } + } + + private static void assertAllResourcesAreSyncedToTarget( + @Nonnull final SphereClient targetClient) { + + assertProductTypeExists(targetClient, MAIN_PRODUCT_TYPE_KEY); + + final List products = + QueryExecutionUtils.queryAll(targetClient, ProductQuery.of()).toCompletableFuture().join(); + assertThat(products).hasSize(501); + + final CustomObjectQuery customObjectQuery = + CustomObjectQuery.of(WaitingToBeResolved.class) + .byContainer("commercetools-sync-java.UnresolvedReferencesService.productDrafts"); + + final PagedQueryResult> queryResult = + targetClient.execute(customObjectQuery).toCompletableFuture().join(); + + assertThat(queryResult.getResults()).isEmpty(); + } +} 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 bb8e8275..ae2f056d 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 @@ -7,6 +7,9 @@ import com.commercetools.project.sync.model.response.LastSyncCustomObject; import com.commercetools.sync.commons.utils.CtpQueryUtils; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.JsonNodeFactory; +import com.fasterxml.jackson.databind.node.ObjectNode; import io.sphere.sdk.cartdiscounts.commands.CartDiscountDeleteCommand; import io.sphere.sdk.cartdiscounts.queries.CartDiscountQuery; import io.sphere.sdk.categories.Category; @@ -226,5 +229,23 @@ public static Product assertProductExists( return productQueryResult.getResults().get(0); } + @Nonnull + public static ObjectNode createReferenceObject( + @Nonnull final String typeId, @Nonnull final String id) { + final ObjectNode referenceObject = JsonNodeFactory.instance.objectNode(); + referenceObject.set("typeId", JsonNodeFactory.instance.textNode(typeId)); + referenceObject.set("id", JsonNodeFactory.instance.textNode(id)); + return referenceObject; + } + + @Nonnull + public static ObjectNode createAttributeObject( + @Nonnull final String name, @Nonnull final ArrayNode value) { + final ObjectNode attributeObject = JsonNodeFactory.instance.objectNode(); + attributeObject.set("name", JsonNodeFactory.instance.textNode(name)); + attributeObject.set("value", value); + return attributeObject; + } + private IntegrationTestUtils() {} } diff --git a/src/main/java/com/commercetools/project/sync/model/request/CombinedResourceKeysRequest.java b/src/main/java/com/commercetools/project/sync/model/request/CombinedResourceKeysRequest.java index 5a7d9268..1e69badf 100644 --- a/src/main/java/com/commercetools/project/sync/model/request/CombinedResourceKeysRequest.java +++ b/src/main/java/com/commercetools/project/sync/model/request/CombinedResourceKeysRequest.java @@ -21,6 +21,7 @@ public class CombinedResourceKeysRequest implements SphereRequest productIds; private final Set categoryIds; private final Set productTypeIds; + private static final int QUERY_LIMIT = 500; public CombinedResourceKeysRequest( @Nonnull final Set productIds, @@ -67,18 +68,23 @@ public HttpRequestIntent httpRequestIntent() { @Nonnull private static String createProductsGraphQlQuery(@Nonnull final Set productIds) { - return format("products(where: %s) { results { id key } }", createWhereQuery(productIds)); + return format( + "products(limit: %d, where: %s) { results { id key } }", + QUERY_LIMIT, createWhereQuery(productIds)); } @Nonnull private static String createCategoriesGraphQlQuery(@Nonnull final Set categoryIds) { - return format("categories(where: %s) { results { id key } }", createWhereQuery(categoryIds)); + return format( + "categories(limit: %d, where: %s) { results { id key } }", + QUERY_LIMIT, createWhereQuery(categoryIds)); } @Nonnull private static String createProductTypesGraphQlQuery(@Nonnull final Set productTypeIds) { return format( - "productTypes(where: %s) { results { id key } }", createWhereQuery(productTypeIds)); + "productTypes(limit: %d, where: %s) { results { id key } }", + QUERY_LIMIT, createWhereQuery(productTypeIds)); } @Nonnull diff --git a/src/test/java/com/commercetools/project/sync/SyncerFactoryTest.java b/src/test/java/com/commercetools/project/sync/SyncerFactoryTest.java index 35473dc4..fb8b64df 100644 --- a/src/test/java/com/commercetools/project/sync/SyncerFactoryTest.java +++ b/src/test/java/com/commercetools/project/sync/SyncerFactoryTest.java @@ -13,7 +13,7 @@ import static io.sphere.sdk.utils.SphereInternalUtils.asSet; import static java.lang.String.format; import static java.util.Arrays.asList; -import static java.util.Collections.singletonList; +import static java.util.Collections.emptyList; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; @@ -47,6 +47,8 @@ import io.sphere.sdk.producttypes.queries.ProductTypeQuery; import io.sphere.sdk.queries.PagedQueryResult; import io.sphere.sdk.queries.QueryPredicate; +import io.sphere.sdk.states.queries.StateQuery; +import io.sphere.sdk.taxcategories.queries.TaxCategoryQuery; import io.sphere.sdk.types.queries.TypeQuery; import io.sphere.sdk.utils.CompletableFutureUtils; import java.time.ZonedDateTime; @@ -182,8 +184,8 @@ void sync_AsProductsDeltaSync_ShouldBuildSyncerAndExecuteSync() { && loggingEvent .getMessage() .contains( - "Summary: 0 products were processed in total (0 created, 0 updated " - + "and 0 failed to sync)."), + "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(syncerTestLogger.getAllLoggingEvents()) @@ -236,8 +238,8 @@ void sync_AsProductsFullSync_ShouldBuildSyncerAndExecuteSync() { && loggingEvent .getMessage() .contains( - "Summary: 0 products were processed in total (0 created, 0 updated " - + "and 0 failed to sync)."), + "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(syncerTestLogger.getAllLoggingEvents()) @@ -300,8 +302,8 @@ void sync_AsProductsFullSync_ShouldBuildSyncerAndExecuteSync() { && loggingEvent .getMessage() .contains( - "Summary: 0 products were processed in total (0 created, 0 updated " - + "and 0 failed to sync)."), + "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(syncerTestLogger.getAllLoggingEvents()) @@ -334,6 +336,8 @@ void sync_AsProductsFullSync_ShouldBuildSyncerAndExecuteSync() { SphereJsonUtils.readObjectFromResource("product-key-1.json", Product.class); final Product product2 = SphereJsonUtils.readObjectFromResource("product-key-2.json", Product.class); + final Product product3 = + SphereJsonUtils.readObjectFromResource("product-key-3.json", Product.class); final List fullPageOfProducts = IntStream.range(0, 500).mapToObj(o -> product1).collect(Collectors.toList()); @@ -341,17 +345,26 @@ void sync_AsProductsFullSync_ShouldBuildSyncerAndExecuteSync() { when(sourceClient.execute(any(ProductQuery.class))) .thenReturn(CompletableFuture.completedFuture(MockPagedQueryResult.of(fullPageOfProducts))) .thenReturn( - CompletableFuture.completedFuture(MockPagedQueryResult.of(asList(product1, product2)))); + CompletableFuture.completedFuture( + MockPagedQueryResult.of(asList(product1, product3, product2)))); + + when(targetClient.execute(any(ProductQuery.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())).thenReturn(CompletableFuture.completedFuture(null)); - final BadGatewayException badGatewayException = new BadGatewayException("Error!"); when(targetClient.execute(any(ProductCreateCommand.class))) .thenReturn(CompletableFuture.completedFuture(product2)); - when(sourceClient.execute(any(CombinedResourceKeysRequest.class))) - .thenReturn(CompletableFutureUtils.failed(badGatewayException)); + final ResultingResourcesContainer productsResult = new ResultingResourcesContainer( - asSet(new ReferenceIdKey("53c4a8b4-754f-4b95-b6f2-3e1e70e3d0c1", "prod1"))); + asSet(new ReferenceIdKey("53c4a8b4-754f-4b95-b6f2-3e1e70e3d0c1", "productKey3"))); final ResultingResourcesContainer productTypesResult = new ResultingResourcesContainer( asSet(new ReferenceIdKey("53c4a8b4-754f-4b95-b6f2-3e1e70e3d0c2", "prodType1"))); @@ -359,7 +372,9 @@ void sync_AsProductsFullSync_ShouldBuildSyncerAndExecuteSync() { new ResultingResourcesContainer( asSet(new ReferenceIdKey("53c4a8b4-754f-4b95-b6f2-3e1e70e3d0c3", "cat1"))); + final BadGatewayException badGatewayException = new BadGatewayException("Error!"); when(sourceClient.execute(any(CombinedResourceKeysRequest.class))) + .thenReturn(CompletableFutureUtils.failed(badGatewayException)) .thenReturn( CompletableFuture.completedFuture( new CombinedResult(productsResult, categoriesResult, productTypesResult))); @@ -373,7 +388,7 @@ void sync_AsProductsFullSync_ShouldBuildSyncerAndExecuteSync() { // assertions verify(sourceClient, times(2)).execute(any(ProductQuery.class)); verify(sourceClient, times(2)).execute(any(CombinedResourceKeysRequest.class)); - verifyInteractionsWithClientAfterSync(sourceClient, 502); + verifyInteractionsWithClientAfterSync(sourceClient, 2); final Condition startLog = new Condition<>( @@ -389,8 +404,8 @@ void sync_AsProductsFullSync_ShouldBuildSyncerAndExecuteSync() { && loggingEvent .getMessage() .contains( - "Summary: 1 products were processed in total (1 created, 0 updated " - + "and 0 failed to sync)."), + "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(syncerTestLogger.getAllLoggingEvents()) @@ -399,9 +414,9 @@ void sync_AsProductsFullSync_ShouldBuildSyncerAndExecuteSync() { .haveExactly(1, statisticsLog); assertThat(productSyncerTestLogger.getAllLoggingEvents()) - .hasSize(501) + .hasSize(2) .containsOnlyElementsOf( - singletonList( + asList( LoggingEvent.warn( "The product with id " + "'ba81a6da-cf83-435b-a89e-2afab579846f' on the source project ('foo') will not be synced because it " @@ -410,7 +425,12 @@ void sync_AsProductsFullSync_ShouldBuildSyncerAndExecuteSync() { + "{\"id\":\"53c4a8b4-754f-4b95-b6f2-3e1e70e3d0c4\",\"typeId\":\"category\"}].\n" + "These references are either pointing to a non-existent resource or to an existing one but with a " + "blank key. Please make sure these referenced resources are existing and have non-blank (i.e. " - + "non-null and non-empty) keys."))); + + "non-null and non-empty) keys."), + LoggingEvent.warn( + badGatewayException, + "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."))); } private static void verifyTimestampGeneratorCustomObjectUpsert( diff --git a/src/test/java/com/commercetools/project/sync/model/request/CombinedResourceKeysRequestTest.java b/src/test/java/com/commercetools/project/sync/model/request/CombinedResourceKeysRequestTest.java index 7141a139..9c1d2211 100644 --- a/src/test/java/com/commercetools/project/sync/model/request/CombinedResourceKeysRequestTest.java +++ b/src/test/java/com/commercetools/project/sync/model/request/CombinedResourceKeysRequestTest.java @@ -104,8 +104,8 @@ void httpRequestIntent_WithOnlyEmptyProductIds_ShouldBuildRequestCorrectly() { assertThat(requestBody.getString()) .isEqualTo( "{\"query\": \"{ " - + "categories(where: \\\"id in (\\\\\\\"foo\\\\\\\", \\\\\\\"bar\\\\\\\")\\\") { results { id key } }, " - + "productTypes(where: \\\"id in (\\\\\\\"foo\\\\\\\", \\\\\\\"bar\\\\\\\")\\\") { results { id key } }" + + "categories(limit: 500, where: \\\"id in (\\\\\\\"foo\\\\\\\", \\\\\\\"bar\\\\\\\")\\\") { results { id key } }, " + + "productTypes(limit: 500, where: \\\"id in (\\\\\\\"foo\\\\\\\", \\\\\\\"bar\\\\\\\")\\\") { results { id key } }" + " }\"}"); } @@ -129,7 +129,7 @@ void httpRequestIntent_WithOnlyCategoryIds_ShouldBuildRequestCorrectly() { assertThat(requestBody.getString()) .isEqualTo( "{\"query\": \"{ " - + "categories(where: \\\"id in (\\\\\\\"foo\\\\\\\", \\\\\\\"bar\\\\\\\")\\\") { results { id key } }" + + "categories(limit: 500, where: \\\"id in (\\\\\\\"foo\\\\\\\", \\\\\\\"bar\\\\\\\")\\\") { results { id key } }" + " }\"}"); } @@ -153,7 +153,7 @@ void httpRequestIntent_WithOnlyProductIds_ShouldBuildRequestCorrectly() { assertThat(requestBody.getString()) .isEqualTo( "{\"query\": \"{ " - + "products(where: \\\"id in (\\\\\\\"foo\\\\\\\", \\\\\\\"bar\\\\\\\")\\\") { results { id key } }" + + "products(limit: 500, where: \\\"id in (\\\\\\\"foo\\\\\\\", \\\\\\\"bar\\\\\\\")\\\") { results { id key } }" + " }\"}"); } @@ -177,9 +177,9 @@ void httpRequestIntent_WithNoEmptyIds_ShouldBuildRequestCorrectly() { assertThat(requestBody.getString()) .isEqualTo( "{\"query\": \"{ " - + "products(where: \\\"id in (\\\\\\\"foo\\\\\\\", \\\\\\\"bar\\\\\\\")\\\") { results { id key } }, " - + "categories(where: \\\"id in (\\\\\\\"foo\\\\\\\", \\\\\\\"bar\\\\\\\")\\\") { results { id key } }, " - + "productTypes(where: \\\"id in (\\\\\\\"foo\\\\\\\", \\\\\\\"bar\\\\\\\")\\\") { results { id key } }" + + "products(limit: 500, where: \\\"id in (\\\\\\\"foo\\\\\\\", \\\\\\\"bar\\\\\\\")\\\") { results { id key } }, " + + "categories(limit: 500, where: \\\"id in (\\\\\\\"foo\\\\\\\", \\\\\\\"bar\\\\\\\")\\\") { results { id key } }, " + + "productTypes(limit: 500, where: \\\"id in (\\\\\\\"foo\\\\\\\", \\\\\\\"bar\\\\\\\")\\\") { results { id key } }" + " }\"}"); } 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 43326182..f6503ac7 100644 --- a/src/test/java/com/commercetools/project/sync/util/TestUtils.java +++ b/src/test/java/com/commercetools/project/sync/util/TestUtils.java @@ -89,8 +89,8 @@ public static void assertProductSyncerLoggingEvents( @Nonnull final TestLogger syncerTestLogger, final int numberOfResources) { final String productStatsSummary = format( - "Summary: %d products were processed in total (%d created, 0 updated " - + "and 0 failed to sync).", + "Summary: %d product(s) were processed in total (%d created, 0 updated, " + + "0 failed to sync and 0 product(s) with missing reference(s)).", numberOfResources, numberOfResources); assertSyncerLoggingEvents(syncerTestLogger, "ProductSync", productStatsSummary); diff --git a/src/test/resources/product-key-3.json b/src/test/resources/product-key-3.json new file mode 100644 index 00000000..6585ae15 --- /dev/null +++ b/src/test/resources/product-key-3.json @@ -0,0 +1,131 @@ +{ + "id": "ba81a6da-cf83-435b-a89e-2afab579846f", + "version": 10, + "key": "productKey3", + "productType": { + "typeId": "product-type", + "id": "cda0dbf7-b42e-40bf-8453-241d5b587f93" + }, + "catalogs": [], + "masterData": { + "staged": { + "name": { + "en": "english name" + }, + "description": { + "en": "english description." + }, + "categories": [ + { + "typeId": "category", + "id": "1dfc8bea-84f2-45bc-b3c2-cdc94bf96f1f" + }, + { + "typeId": "category", + "id": "2dfc8bea-84f2-45bc-b3c2-cdc94bf96f1f" + }, + { + "typeId": "category", + "id": "3dfc8bea-84f2-45bc-b3c2-cdc94bf96f1f" + }, + { + "typeId": "category", + "id": "4dfc8bea-84f2-45bc-b3c2-cdc94bf96f1f" + } + ], + "categoryOrderHints": { + "1dfc8bea-84f2-45bc-b3c2-cdc94bf96f1f": "0.43", + "2dfc8bea-84f2-45bc-b3c2-cdc94bf96f1f": "0.53", + "3dfc8bea-84f2-45bc-b3c2-cdc94bf96f1f": "0.63", + "4dfc8bea-84f2-45bc-b3c2-cdc94bf96f1f": "0.73" + }, + "slug": { + "en": "english-slug" + }, + "masterVariant": { + "id": 1, + "key": "v1", + "sku": "3065833", + "images": [ + { + "url": "https://53346cfbf3c7e017ed3d-6de74c3efa80f1c837c6a988b57abe66.ssl.cf3.rackcdn.com/old-image.png", + "dimensions": { + "w": 0, + "h": 0 + } + } + ], + "attributes": [ + { + "name": "priceInfo", + "value": "64,90/kg" + }, + { + "name": "size", + "value": "ca. 1 x 1000 g" + }, + { + "name": "rinderrasse", + "value": "Reh" + }, + { + "name": "herkunft", + "value": "Deutschland" + }, + { + "name": "fuetterung", + "value": "Gräser, Kräuter, Knospen, Eicheln, Beeren" + }, + { + "name": "teilstueck", + "value": "Reh-Rücken" + }, + { + "name": "reifung", + "value": "wet-aged, mindestens 2 Tage" + }, + { + "name": "haltbarkeit", + "value": "bei < 7°C: 8 Tage ungeöffnet" + }, + { + "name": "verpackung", + "value": "vakuumverpackt" + }, + { + "name": "anlieferung", + "value": "frisch" + }, + { + "name": "zubereitung", + "value": "Grill, Ofen, Niedertemperatur" + }, + { + "name": "localisedText", + "value": { + "fr": "#FR An emergency call is placed automatically in the event of a severe accident", + "it": "#IT An emergency call is placed automatically in the event of a severe accident", + "de": "Audi connect verbindet Ihren Audi mit dem Internet und der Infrastruktur, um Sie unterwegs mit Informationen und Unterhaltung zu versorgen", + "en": "An emergency call is placed automatically in the event of a severe accident" + } + } + ], + "assets": [], + "prices": [] + }, + "variants": [], + "searchKeywords": {} + }, + "published": true, + "hasStagedChanges": true + }, + "catalogData": {}, + "taxCategory": { + "typeId": "tax-category", + "id": "ebbe95fb-2282-4f9a-8747-fbe440e02dc0" + }, + "lastVariantId": 1, + "createdAt": "2016-11-21T09:32:41.261Z", + "lastModifiedAt": "2016-12-05T16:09:03.307Z", + "lastMessageSequenceNumber": 3 +}