diff --git a/src/Apps/W1/Shopify/App/src/Base/Codeunits/ShpfyCommunicationEvents.Codeunit.al b/src/Apps/W1/Shopify/App/src/Base/Codeunits/ShpfyCommunicationEvents.Codeunit.al deleted file mode 100644 index 9593649efd..0000000000 --- a/src/Apps/W1/Shopify/App/src/Base/Codeunits/ShpfyCommunicationEvents.Codeunit.al +++ /dev/null @@ -1,39 +0,0 @@ -// ------------------------------------------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. See License.txt in the project root for license information. -// ------------------------------------------------------------------------------------------------ - -namespace Microsoft.Integration.Shopify; - -/// -/// Codeunit Shpfy Communication Events (ID 30200). -/// -codeunit 30200 "Shpfy Communication Events" -{ - Access = Internal; - - [InternalEvent(false)] - internal procedure OnClientSend(HttpRequestMessage: HttpRequestMessage; var HttpResponseMessage: HttpResponseMessage) - begin - end; - - [InternalEvent(false)] - internal procedure OnGetAccessToken(var AccessToken: Text) - begin - end; - - [InternalEvent(false)] - internal procedure OnGetContent(HttpResponseMessage: HttpResponseMessage; var Response: Text) - begin - end; - - [InternalEvent(false)] - internal procedure OnClientPost(var Url: Text; var Content: HttpContent; var Response: HttpResponseMessage) - begin - end; - - [InternalEvent(false)] - internal procedure OnClientGet(var Url: Text; var Response: HttpResponseMessage) - begin - end; -} diff --git a/src/Apps/W1/Shopify/App/src/Base/Codeunits/ShpfyCommunicationMgt.Codeunit.al b/src/Apps/W1/Shopify/App/src/Base/Codeunits/ShpfyCommunicationMgt.Codeunit.al index 1b30497ac6..3b35c04f1d 100644 --- a/src/Apps/W1/Shopify/App/src/Base/Codeunits/ShpfyCommunicationMgt.Codeunit.al +++ b/src/Apps/W1/Shopify/App/src/Base/Codeunits/ShpfyCommunicationMgt.Codeunit.al @@ -19,13 +19,11 @@ codeunit 30103 "Shpfy Communication Mgt." var Shop: Record "Shpfy Shop"; - CommunicationEvents: Codeunit "Shpfy Communication Events"; GraphQLQueries: Codeunit "Shpfy GraphQL Queries"; NextExecutionTime: DateTime; VersionTok: Label '2026-01', Locked = true; OutgoingRequestsNotEnabledConfirmLbl: Label 'Importing data to your Shopify shop is not enabled, do you want to go to shop card to enable?'; OutgoingRequestsNotEnabledErr: Label 'Importing data to your Shopify shop is not enabled, navigate to shop card to enable.'; - IsTestInProgress: Boolean; CategoryTok: Label 'Shopify Integration', Locked = true; QueryParamTooLongTxt: Label 'Query param length exceeded 50000.', Locked = true; QueryParamTooLongErr: Label 'Request length exceeded Shopify API limit.'; @@ -232,22 +230,19 @@ codeunit 30103 "Shpfy Communication Mgt." Sleep(Wait); end; - if IsTestInProgress then - CommunicationEvents.OnClientSend(HttpRequestMessage, HttpResponseMessage) - else - if HttpClient.Send(HttpRequestMessage, HttpResponseMessage) then begin - Clear(RetryCounter); - while (not HttpResponseMessage.IsBlockedByEnvironment) and (EvaluateResponse(HttpResponseMessage)) and (RetryCounter < MaxRetries) do begin - RetryCounter += 1; - Sleep(1000); - LogShopifyRequest(Url, Method, Request, HttpResponseMessage, Response, RetryCounter); - Clear(HttpClient); - Clear(HttpRequestMessage); - Clear(HttpResponseMessage); - CreateHttpRequestMessage(Url, Method, Request, HttpRequestMessage); - HttpClient.Send(HttpRequestMessage, HttpResponseMessage); - end; + if HttpClient.Send(HttpRequestMessage, HttpResponseMessage) then begin + Clear(RetryCounter); + while (not HttpResponseMessage.IsBlockedByEnvironment) and (EvaluateResponse(HttpResponseMessage)) and (RetryCounter < MaxRetries) do begin + RetryCounter += 1; + Sleep(1000); + LogShopifyRequest(Url, Method, Request, HttpResponseMessage, Response, RetryCounter); + Clear(HttpClient); + Clear(HttpRequestMessage); + Clear(HttpResponseMessage); + CreateHttpRequestMessage(Url, Method, Request, HttpRequestMessage); + HttpClient.Send(HttpRequestMessage, HttpResponseMessage); end; + end; if GetContent(HttpResponseMessage, Response) then; ResponseHeaders := HttpResponseMessage.Headers(); LogShopifyRequest(Url, Method, Request, HttpResponseMessage, Response, RetryCounter); @@ -257,28 +252,19 @@ codeunit 30103 "Shpfy Communication Mgt." [NonDebuggable] internal procedure Post(var Client: HttpClient; Url: Text; Content: HttpContent; var Response: HttpResponseMessage) begin - if IsTestInProgress then - CommunicationEvents.OnClientPost(Url, Content, Response) - else - Client.Post(Url, Content, Response); + Client.Post(Url, Content, Response); end; [NonDebuggable] internal procedure Get(var Client: HttpClient; Url: Text; var Response: HttpResponseMessage) begin - if IsTestInProgress then - CommunicationEvents.OnClientGet(Url, Response) - else - Client.Get(Url, Response); + Client.Get(Url, Response); end; [TryFunction] local procedure GetContent(HttpResponseMsg: HttpResponseMessage; var Response: Text) begin - if IsTestInProgress then - CommunicationEvents.OnGetContent(HttpResponseMsg, Response) - else - HttpResponseMsg.Content.ReadAs(Response); + HttpResponseMsg.Content.ReadAs(Response); end; /// @@ -311,8 +297,7 @@ codeunit 30103 "Shpfy Communication Mgt." /// Return value of type Text. local procedure ApiVersion(): Text begin - if not IsTestInProgress then - CheckApiVersion(); + CheckApiVersion(); exit(VersionTok); end; @@ -351,18 +336,12 @@ codeunit 30103 "Shpfy Communication Mgt." HttpContent: HttpContent; ContentHttpHeaders: HttpHeaders; HttpHeaders: HttpHeaders; - ClearAccessToken: Text; AccessToken: SecretText; begin HttpRequestMsg.SetRequestUri(url); HttpRequestMsg.GetHeaders(HttpHeaders); - - if IsTestInProgress then begin - CommunicationEvents.OnGetAccessToken(ClearAccessToken); - AccessToken := ClearAccessToken; - end else - AccessToken := GetAccessToken(Shop); + AccessToken := GetAccessToken(Shop); HttpHeaders.Add('X-Shopify-Access-Token', AccessToken); HttpRequestMsg.Method := Method; @@ -564,20 +543,6 @@ codeunit 30103 "Shpfy Communication Mgt." FeatureTelemetry.LogUsage('0000JW7', 'Shopify', 'A shop is set', Dimensions); end; - /// - /// SetTestInProgress. - /// - /// Boolean. - internal procedure SetTestInProgress(TestInProgress: Boolean) - begin - IsTestInProgress := TestInProgress; - end; - - internal procedure GetTestInProgress(): Boolean - begin - exit(IsTestInProgress); - end; - internal procedure GetShopRecord() ShopifyShop: Record "Shpfy Shop"; begin if not ShopifyShop.Get(Shop.Code) then diff --git a/src/Apps/W1/Shopify/App/src/Inventory/Codeunits/ShpfyInventoryAPI.Codeunit.al b/src/Apps/W1/Shopify/App/src/Inventory/Codeunits/ShpfyInventoryAPI.Codeunit.al index 099e78848d..4de024a6bb 100644 --- a/src/Apps/W1/Shopify/App/src/Inventory/Codeunits/ShpfyInventoryAPI.Codeunit.al +++ b/src/Apps/W1/Shopify/App/src/Inventory/Codeunits/ShpfyInventoryAPI.Codeunit.al @@ -149,7 +149,8 @@ codeunit 30195 "Shpfy Inventory API" end; until ShopInventory.Next() = 0; - ExecuteInventoryGraphQL(JGraphQL, IGraphQL.GetExpectedCost()); + if InputSize > 0 then + ExecuteInventoryGraphQL(JGraphQL, IGraphQL.GetExpectedCost()); end; end; diff --git a/src/Apps/W1/Shopify/App/src/Order Fulfillments/Codeunits/ShpfyFulfillmentOrdersAPI.Codeunit.al b/src/Apps/W1/Shopify/App/src/Order Fulfillments/Codeunits/ShpfyFulfillmentOrdersAPI.Codeunit.al index 086df76757..e90c7240b3 100644 --- a/src/Apps/W1/Shopify/App/src/Order Fulfillments/Codeunits/ShpfyFulfillmentOrdersAPI.Codeunit.al +++ b/src/Apps/W1/Shopify/App/src/Order Fulfillments/Codeunits/ShpfyFulfillmentOrdersAPI.Codeunit.al @@ -184,9 +184,6 @@ codeunit 30238 "Shpfy Fulfillment Orders API" Parameters: Dictionary of [Text, Text]; JResponse: JsonToken; begin - if CommunicationMgt.GetTestInProgress() then - exit; - CommunicationMgt.SetShop(Shop); if Shop."Allow Outgoing Requests" then diff --git a/src/Apps/W1/Shopify/App/src/Order Fulfillments/Codeunits/ShpfyOrderFulfillments.Codeunit.al b/src/Apps/W1/Shopify/App/src/Order Fulfillments/Codeunits/ShpfyOrderFulfillments.Codeunit.al index 1e738d9692..973780c2e5 100644 --- a/src/Apps/W1/Shopify/App/src/Order Fulfillments/Codeunits/ShpfyOrderFulfillments.Codeunit.al +++ b/src/Apps/W1/Shopify/App/src/Order Fulfillments/Codeunits/ShpfyOrderFulfillments.Codeunit.al @@ -33,8 +33,6 @@ codeunit 30160 "Shpfy Order Fulfillments" JFulfillments: JsonArray; JResponse: JsonToken; begin - if CommunicationMgt.GetTestInProgress() then - exit; CommunicationMgt.SetShop(Shop); Parameters.Add('OrderId', Format(OrderId)); GraphQLType := "Shpfy GraphQL Type"::GetOrderFulfillment; diff --git a/src/Apps/W1/Shopify/App/src/Order Risks/Codeunits/ShpfyOrderRisks.Codeunit.al b/src/Apps/W1/Shopify/App/src/Order Risks/Codeunits/ShpfyOrderRisks.Codeunit.al index 20d243c392..8805613b43 100644 --- a/src/Apps/W1/Shopify/App/src/Order Risks/Codeunits/ShpfyOrderRisks.Codeunit.al +++ b/src/Apps/W1/Shopify/App/src/Order Risks/Codeunits/ShpfyOrderRisks.Codeunit.al @@ -39,8 +39,6 @@ codeunit 30170 "Shpfy Order Risks" Parameters: Dictionary of [text, Text]; GraphQLType: Enum "Shpfy GraphQL Type"; begin - if CommunicationMgt.GetTestInProgress() then - exit; CommunicationMgt.SetShop(OrderHeader."Shop Code"); Parameters.Add('OrderId', Format(OrderHeader."Shopify Order Id")); JResponse := CommunicationMgt.ExecuteGraphQL(GraphQLType::OrderRisks, Parameters); diff --git a/src/Apps/W1/Shopify/App/src/Order handling/Codeunits/ShpfyOrdersAPI.Codeunit.al b/src/Apps/W1/Shopify/App/src/Order handling/Codeunits/ShpfyOrdersAPI.Codeunit.al index d542ca4a43..038753bc6e 100644 --- a/src/Apps/W1/Shopify/App/src/Order handling/Codeunits/ShpfyOrdersAPI.Codeunit.al +++ b/src/Apps/W1/Shopify/App/src/Order handling/Codeunits/ShpfyOrdersAPI.Codeunit.al @@ -114,8 +114,6 @@ codeunit 30165 "Shpfy Orders API" JAttrib: JsonObject; begin CommunicationMgt.SetShop(ShopifyShop); - if CommunicationMgt.GetTestInProgress() then - exit; Clear(OrderAttribute); OrderAttribute."Order Id" := OrderHeader."Shopify Order Id"; OrderAttribute."Key" := CopyStr(KeyName, 1, MaxStrLen(OrderAttribute."Key")); diff --git a/src/Apps/W1/Shopify/App/src/PermissionSets/ShpfyObjects.PermissionSet.al b/src/Apps/W1/Shopify/App/src/PermissionSets/ShpfyObjects.PermissionSet.al index 298f8a2120..efd063dd9d 100644 --- a/src/Apps/W1/Shopify/App/src/PermissionSets/ShpfyObjects.PermissionSet.al +++ b/src/Apps/W1/Shopify/App/src/PermissionSets/ShpfyObjects.PermissionSet.al @@ -115,7 +115,6 @@ permissionset 30104 "Shpfy - Objects" codeunit "Shpfy Can Not Have Stock" = X, codeunit "Shpfy Catalog API" = X, codeunit "Shpfy Checklist Item List" = X, - codeunit "Shpfy Communication Events" = X, codeunit "Shpfy Communication Mgt." = X, codeunit "Shpfy Comp. By Default Comp." = X, codeunit "Shpfy Comp. By Email/Phone" = X, diff --git a/src/Apps/W1/Shopify/App/src/Products/Codeunits/ShpfyProductAPI.Codeunit.al b/src/Apps/W1/Shopify/App/src/Products/Codeunits/ShpfyProductAPI.Codeunit.al index b42215e2b3..b0605d3f28 100644 --- a/src/Apps/W1/Shopify/App/src/Products/Codeunits/ShpfyProductAPI.Codeunit.al +++ b/src/Apps/W1/Shopify/App/src/Products/Codeunits/ShpfyProductAPI.Codeunit.al @@ -192,12 +192,7 @@ codeunit 30176 "Shpfy Product API" Headers: HttpHeaders; Response: HttpResponseMessage; InStream: InStream; - IsTestInProgress: Boolean; begin - OnBeforeUploadImage(TenantMedia, Url, IsTestInProgress); - if IsTestInProgress then - exit; - Content.GetHeaders(Headers); if Headers.Contains('Content-Type') then Headers.Remove('Content-Type'); @@ -751,8 +746,4 @@ codeunit 30176 "Shpfy Product API" exit(CommunicationMgt.GetIdOfGId(JsonHelper.GetValueAsText(JMedia, 'id'))); end; - [InternalEvent(false, false)] - procedure OnBeforeUploadImage(var TenantMedia: Record "Tenant Media"; var ResourceUrl: Text; var IsTestInProgress: Boolean) - begin - end; } \ No newline at end of file diff --git a/src/Apps/W1/Shopify/App/src/Shipping/Codeunits/ShpfyShippingCharges.Codeunit.al b/src/Apps/W1/Shopify/App/src/Shipping/Codeunits/ShpfyShippingCharges.Codeunit.al index c33b2aebb0..2b3a7a5527 100644 --- a/src/Apps/W1/Shopify/App/src/Shipping/Codeunits/ShpfyShippingCharges.Codeunit.al +++ b/src/Apps/W1/Shopify/App/src/Shipping/Codeunits/ShpfyShippingCharges.Codeunit.al @@ -28,8 +28,6 @@ codeunit 30191 "Shpfy Shipping Charges" JShipmentLines: JsonArray; JResponse: JsonToken; begin - if CommunicationMgt.GetTestInProgress() then - exit; CommunicationMgt.SetShop(OrderHeader."Shop Code"); Parameters.Add('OrderId', Format(OrderHeader."Shopify Order Id")); GraphQLType := "Shpfy GraphQL Type"::GetShipmentLines; diff --git a/src/Apps/W1/Shopify/Test/.resources/Products/CreateUploadUrl.txt b/src/Apps/W1/Shopify/Test/.resources/Products/CreateUploadUrl.txt index 1445ed1fca..3e20e3a221 100644 --- a/src/Apps/W1/Shopify/Test/.resources/Products/CreateUploadUrl.txt +++ b/src/Apps/W1/Shopify/Test/.resources/Products/CreateUploadUrl.txt @@ -1 +1 @@ -{ "data": { "stagedUploadsCreate": { "stagedTargets": [ { "url": "test.com/test", "resourceUrl": "test2.com/test2", "parameters": [] } ] } }, "extensions": { "cost": { "requestedQueryCost": 11, "actualQueryCost": 11, "throttleStatus": { "maximumAvailable": 2000, "currentlyAvailable": 1989, "restoreRate": 100 } } } } \ No newline at end of file +{ "data": { "stagedUploadsCreate": { "stagedTargets": [ { "url": "https://test.com/test", "resourceUrl": "https://test2.com/test2", "parameters": [] } ] } }, "extensions": { "cost": { "requestedQueryCost": 11, "actualQueryCost": 11, "throttleStatus": { "maximumAvailable": 2000, "currentlyAvailable": 1989, "restoreRate": 100 } } } } \ No newline at end of file diff --git a/src/Apps/W1/Shopify/Test/Base/ShpfyInitializeTest.Codeunit.al b/src/Apps/W1/Shopify/Test/Base/ShpfyInitializeTest.Codeunit.al index dff1d5e0b2..1fcc082e41 100644 --- a/src/Apps/W1/Shopify/Test/Base/ShpfyInitializeTest.Codeunit.al +++ b/src/Apps/W1/Shopify/Test/Base/ShpfyInitializeTest.Codeunit.al @@ -23,18 +23,14 @@ using System.TestLibraries.Utilities; /// codeunit 139561 "Shpfy Initialize Test" { - EventSubscriberInstance = Manual; - var DummyCustomer: Record Customer; DummyItem: Record Item; TempShop: Record "Shpfy Shop" temporary; Any: Codeunit Any; - LibraryAssert: Codeunit "Library Assert"; CommunicationMgt: Codeunit "Shpfy Communication Mgt."; LibraryERM: Codeunit "Library - ERM"; LibraryRandom: Codeunit "Library - Random"; - ShopifyAccessToken: Text; #pragma warning disable AA0240 DummyCustomerEmailLbl: Label 'dummy@customer.com'; #pragma warning restore AA0240 @@ -50,7 +46,6 @@ codeunit 139561 "Shpfy Initialize Test" RefundGLAccount: Record "G/L Account"; Shop: Record "Shpfy Shop"; VATPostingSetup: Record "VAT Posting Setup"; - ShpfyInitializeTest: Codeunit "Shpfy Initialize Test"; Code: Code[10]; CustomerTemplateCode: Code[20]; ItemTemplateCode: Code[20]; @@ -58,7 +53,6 @@ codeunit 139561 "Shpfy Initialize Test" GenPostingType: Enum "General Posting Type"; UrlTxt: Label 'https://%1.myshopify.com', Comment = '%1 = Shop name', Locked = true; begin - BindSubscription(ShpfyInitializeTest); if not TempShop.IsEmpty() then if Shop.Get(TempShop.Code) then exit(Shop); @@ -94,7 +88,6 @@ codeunit 139561 "Shpfy Initialize Test" if Shop.Insert() then; Commit(); CommunicationMgt.SetShop(Shop); - CommunicationMgt.SetTestInProgress(true); CreateDummyCustomer(CustomerTemplateCode); CreateDummyItem(ItemTemplateCode); if not TempShop.Get(Code) then begin @@ -102,7 +95,6 @@ codeunit 139561 "Shpfy Initialize Test" TempShop.Insert(); Commit(); end; - UnbindSubscription(ShpfyInitializeTest); exit(Shop); end; @@ -346,32 +338,6 @@ codeunit 139561 "Shpfy Initialize Test" end; - [EventSubscriber(ObjectType::Codeunit, Codeunit::"Shpfy Communication Events", 'OnGetAccessToken', '', true, false)] - local procedure OnGetAccessToken(var AccessToken: Text) - begin - if ShopifyAccessToken = '' then - ShopifyAccessToken := Any.AlphanumericText(50); - AccessToken := ShopifyAccessToken; - end; - - [EventSubscriber(ObjectType::Codeunit, Codeunit::"Shpfy Communication Events", 'OnClientSend', '', true, false)] - local procedure OnClientSend(HttpRequestMessage: HttpRequestMessage; var HttpResponseMessage: HttpResponseMessage) - begin - TestRequestHeaderContainsAccessToken(HttpRequestMessage); - end; - - local procedure TestRequestHeaderContainsAccessToken(HttpRequestMessage: HttpRequestMessage) - var - Headers: HttpHeaders; - ShopifyAccessTokenTxt: Label 'X-Shopify-Access-Token', Locked = true; - Values: array[1] of Text; - begin - HttpRequestMessage.GetHeaders(Headers); - LibraryAssert.IsTrue(Headers.Contains(ShopifyAccessTokenTxt), 'access token doesn''t exist'); - Headers.GetValues(ShopifyAccessTokenTxt, Values); - LibraryAssert.IsTrue(Values[1] = ShopifyAccessToken, 'invalid access token'); - end; - internal procedure CreateVATPostingSetup(BusinessPostingGroup: Code[20]; ProductPostingGroup: Code[20]) var GeneralPostingSetup: Record "General Posting Setup"; diff --git a/src/Apps/W1/Shopify/Test/Base/ShpfyTestShopify.Codeunit.al b/src/Apps/W1/Shopify/Test/Base/ShpfyTestShopify.Codeunit.al index e7a2f07878..7b31cc1347 100644 --- a/src/Apps/W1/Shopify/Test/Base/ShpfyTestShopify.Codeunit.al +++ b/src/Apps/W1/Shopify/Test/Base/ShpfyTestShopify.Codeunit.al @@ -48,7 +48,6 @@ codeunit 139563 "Shpfy Test Shopify" // [SCENARIO] If a version is out of support then the API must be blocked. // [WHEN] The Shop is created. Shop := InitializeTest.CreateShop(); - CommunicationMgt.SetTestInProgress(false); SetupKeyVaultExpiryDate(CommunicationMgt.GetApiVersion()); EnvironmentInfoTestLibrary.SetTestabilitySoftwareAsAService(true); diff --git a/src/Apps/W1/Shopify/Test/Bulk Operations/Codeunits/ShpfyBulkOpSubscriber.Codeunit.al b/src/Apps/W1/Shopify/Test/Bulk Operations/Codeunits/ShpfyBulkOpSubscriber.Codeunit.al index a9e95100f1..a7a4a678f3 100644 --- a/src/Apps/W1/Shopify/Test/Bulk Operations/Codeunits/ShpfyBulkOpSubscriber.Codeunit.al +++ b/src/Apps/W1/Shopify/Test/Bulk Operations/Codeunits/ShpfyBulkOpSubscriber.Codeunit.al @@ -12,165 +12,9 @@ codeunit 139615 "Shpfy Bulk Op. Subscriber" SingleInstance = true; EventSubscriberInstance = Manual; - var - UploadUrlLbl: Label 'https://shopify-staged-uploads.storage.googleapis.com', Locked = true; - BulkOperationId: BigInteger; - BulkOperationRunning: Boolean; - BulkUploadFail: Boolean; - BulkOperationUrl: Text; - VariantId1: BigInteger; - VariantId2: BigInteger; - [EventSubscriber(ObjectType::Codeunit, Codeunit::"Shpfy Bulk Operation Mgt.", 'OnInvalidUser', '', true, false)] local procedure OnInvalidUser(var IsHandled: Boolean) begin IsHandled := true; end; - - [EventSubscriber(ObjectType::Codeunit, Codeunit::"Shpfy Communication Events", 'OnClientGet', '', true, false)] - local procedure OnClientGet(var Url: Text; var Response: HttpResponseMessage) - begin - if Url = BulkOperationUrl then - Response := GetBulkOperationResult(); - end; - - [EventSubscriber(ObjectType::Codeunit, Codeunit::"Shpfy Communication Events", 'OnClientSend', '', true, false)] - local procedure OnClientSend(HttpRequestMessage: HttpRequestMessage; var HttpResponseMessage: HttpResponseMessage) - begin - MakeResponse(HttpRequestMessage, HttpResponseMessage); - end; - - [EventSubscriber(ObjectType::Codeunit, Codeunit::"Shpfy Communication Events", 'OnClientPost', '', true, false)] - local procedure OnClientPost(var Url: Text; var Content: HttpContent; var Response: HttpResponseMessage) - begin - if Url = UploadUrlLbl then - Response := GetJsonlUploadResult(); - end; - - [EventSubscriber(ObjectType::Codeunit, Codeunit::"Shpfy Communication Events", 'OnGetContent', '', true, false)] - local procedure OnGetContent(HttpResponseMessage: HttpResponseMessage; var Response: Text) - begin - HttpResponseMessage.Content.ReadAs(Response); - end; - - local procedure MakeResponse(HttpRequestMessage: HttpRequestMessage; var HttpResponseMessage: HttpResponseMessage) - var - Uri: Text; - GraphQLQuery: Text; - StagedUploadGQLTxt: Label '{"query": "mutation { stagedUploadsCreate(input', Locked = true; - BulkMutationGQLTxt: Label '{"query": "mutation { bulkOperationRunMutation(mutation', Locked = true; - BulkOperationGQLTxt: Label '{"query": "query { node(id: \"gid://shopify/BulkOperation/', Locked = true; - GraphQLCmdTxt: Label '/graphql.json', Locked = true; - begin - case HttpRequestMessage.Method of - 'POST': - begin - Uri := HttpRequestMessage.GetRequestUri(); - if Uri.EndsWith(GraphQLCmdTxt) then - if HttpRequestMessage.Content.ReadAs(GraphQLQuery) then begin - if GraphQLQuery.StartsWith(StagedUploadGQLTxt) then - HttpResponseMessage := GetStagedUplodResult(); - if GraphQLQuery.StartsWith(BulkMutationGQLTxt) then - HttpResponseMessage := GetBulkMutationResponse(); - if GraphQLQuery.StartsWith(BulkOperationGQLTxt) then - HttpResponseMessage := GetBulkOperation(); - end; - end; - end; - end; - - local procedure GetStagedUplodResult(): HttpResponseMessage - var - HttpResponseMessage: HttpResponseMessage; - Body: Text; - ResInStream: InStream; - begin - if BulkUploadFail then begin - NavApp.GetResource('Bulk Operations/StagedUploadFailedResult.txt', ResInStream, TextEncoding::UTF8); - ResInStream.ReadText(Body); - end else begin - NavApp.GetResource('Bulk Operations/StagedUploadResult.txt', ResInStream, TextEncoding::UTF8); - ResInStream.ReadText(Body); - Body := StrSubstNo(Body, UploadUrlLbl) - end; - HttpResponseMessage.Content.WriteFrom(Body); - exit(HttpResponseMessage); - end; - - local procedure GetBulkMutationResponse(): HttpResponseMessage - var - HttpResponseMessage: HttpResponseMessage; - Body: Text; - ResInStream: InStream; - begin - NavApp.GetResource('Bulk Operations/BulkMutationResponse.txt', ResInStream, TextEncoding::UTF8); - ResInStream.ReadText(Body); - HttpResponseMessage.Content.WriteFrom(StrSubstNo(Body, Format(BulkOperationId))); - exit(HttpResponseMessage); - end; - - local procedure GetJsonlUploadResult(): HttpResponseMessage - var - HttpResponseMessage: HttpResponseMessage; - begin - exit(HttpResponseMessage); - end; - - local procedure GetBulkOperationResult(): HttpResponseMessage - var - HttpResponseMessage: HttpResponseMessage; - Body: TextBuilder; - BodyLine: Text; - ResInStream: InStream; - begin - NavApp.GetResource('Bulk Operations/BulkOperationResult.txt', ResInStream, TextEncoding::UTF8); - while not ResInStream.EOS do begin - ResInStream.ReadText(BodyLine); - Body.AppendLine(StrSubstNo(BodyLine, Format(VariantId1), Format(VariantId2))); - end; - HttpResponseMessage.Content.WriteFrom(Body.ToText()); - exit(HttpResponseMessage); - end; - - local procedure GetBulkOperation(): HttpResponseMessage - var - HttpResponseMessage: HttpResponseMessage; - Body: Text; - ResInStream: InStream; - begin - NavApp.GetResource('Bulk Operations/BulkOperationCompletedResult.txt', ResInStream, TextEncoding::UTF8); - ResInStream.ReadText(Body); - if BulkOperationRunning then - Body := StrSubstNo(Body, 'RUNNING') - else - Body := StrSubstNo(Body, 'COMPLETED'); - HttpResponseMessage.Content.WriteFrom(Body); - exit(HttpResponseMessage); - end; - - internal procedure SetBulkOperationId(Id: BigInteger) - begin - BulkOperationId := Id; - end; - - internal procedure SetBulkOperationRunning(OperationRunning: Boolean) - begin - BulkOperationRunning := OperationRunning; - end; - - internal procedure SetBulkUploadFail(Fail: Boolean) - begin - BulkUploadFail := Fail; - end; - - internal procedure SetBulkOperationUrl(Url: Text) - begin - BulkOperationUrl := Url; - end; - - internal procedure SetVariantIds(Id1: BigInteger; Id2: BigInteger) - begin - VariantId1 := Id1; - VariantId2 := Id2; - end; -} \ No newline at end of file +} diff --git a/src/Apps/W1/Shopify/Test/Bulk Operations/Codeunits/ShpfyBulkOperationsTest.Codeunit.al b/src/Apps/W1/Shopify/Test/Bulk Operations/Codeunits/ShpfyBulkOperationsTest.Codeunit.al index 8bd1762430..15019aa357 100644 --- a/src/Apps/W1/Shopify/Test/Bulk Operations/Codeunits/ShpfyBulkOperationsTest.Codeunit.al +++ b/src/Apps/W1/Shopify/Test/Bulk Operations/Codeunits/ShpfyBulkOperationsTest.Codeunit.al @@ -13,6 +13,7 @@ codeunit 139633 "Shpfy Bulk Operations Test" Subtype = Test; TestType = IntegrationTest; TestPermissions = Disabled; + TestHttpRequestPolicy = BlockOutboundRequests; trigger OnRun() begin @@ -21,24 +22,37 @@ codeunit 139633 "Shpfy Bulk Operations Test" end; var + Shop: Record "Shpfy Shop"; LibraryAssert: Codeunit "Library Assert"; Any: Codeunit Any; - - BulkOpSubscriber: Codeunit "Shpfy Bulk Op. Subscriber"; + CommunicationMgt: Codeunit "Shpfy Communication Mgt."; + InitializeTest: Codeunit "Shpfy Initialize Test"; + LibraryRandom: Codeunit "Library - Random"; + GraphQLResponses: Codeunit "Library - Variable Storage"; IsInitialized: Boolean; BulkOperationId1: BigInteger; BulkOperationId2: BigInteger; + BulkOperationIdCurrent: BigInteger; + BulkOperationRunning: Boolean; + BulkUploadFail: Boolean; + BulkOperationUrl: Text; + VariantId1: BigInteger; + VariantId2: BigInteger; + UploadUrlLbl: Label 'https://shopify-staged-uploads.storage.googleapis.com', Locked = true; local procedure Initialize() - + var + AccessToken: SecretText; begin if IsInitialized then exit; IsInitialized := true; Codeunit.Run(Codeunit::"Shpfy Initialize Test"); + Shop := CommunicationMgt.GetShopRecord(); + AccessToken := LibraryRandom.RandText(20); + InitializeTest.RegisterAccessTokenForShop(Shop.GetStoreName(), AccessToken); BulkOperationId1 := Any.IntegerInRange(100000, 555555); BulkOperationId2 := Any.IntegerInRange(555555, 999999); - if BindSubscription(BulkOpSubscriber) then; end; local procedure ClearSetup() @@ -47,19 +61,18 @@ codeunit 139633 "Shpfy Bulk Operations Test" ShopifyVariant: Record "Shpfy Variant"; begin BulkOperation.DeleteAll(); - BulkOpSubscriber.SetBulkOperationRunning(false); - BulkOpSubscriber.SetBulkUploadFail(false); + BulkOperationRunning := false; + BulkUploadFail := false; ShopifyVariant.DeleteAll(); + GraphQLResponses.Clear(); end; [Test] - [HandlerFunctions('BulkMessageHandler')] + [HandlerFunctions('BulkMessageHandler,BulkOperationHttpHandler')] procedure TestSendBulkOperation() var - Shop: Record "Shpfy Shop"; BulkOperation: Record "Shpfy Bulk Operation"; BulkOperationMgt: Codeunit "Shpfy Bulk Operation Mgt."; - CommunicationMgt: Codeunit "Shpfy Communication Mgt."; BulkOperationType: Enum "Shpfy Bulk Operation Type"; IBulkOperation: Interface "Shpfy IBulk Operation"; tb: TextBuilder; @@ -69,14 +82,14 @@ codeunit 139633 "Shpfy Bulk Operations Test" // [GIVEN] A Shop record Initialize(); - Shop := CommunicationMgt.GetShopRecord(); // [WHEN] A bulk operation is sent - BulkOpSubscriber.SetBulkOperationId(BulkOperationId1); + BulkOperationIdCurrent := BulkOperationId1; IBulkOperation := BulkOperationType::AddProduct; tb.AppendLine(StrSubstNo(IBulkOperation.GetInput(), 'Sweet new snowboard 1', 'Snowboard', 'JadedPixel')); tb.AppendLine(StrSubstNo(IBulkOperation.GetInput(), 'Sweet new snowboard 2', 'Snowboard', 'JadedPixel')); tb.AppendLine(StrSubstNo(IBulkOperation.GetInput(), 'Sweet new snowboard 3', 'Snowboard', 'JadedPixel')); + EnqueueGraphQLResponsesForSendBulkMutation(); LibraryAssert.IsTrue(BulkOperationMgt.SendBulkMutation(Shop, BulkOperationType::AddProduct, tb.ToText(), RequestData), 'Bulk operation should be sent.'); // [THEN] A bulk operation record is created @@ -86,13 +99,11 @@ codeunit 139633 "Shpfy Bulk Operations Test" end; [Test] - [HandlerFunctions('BulkMessageHandler')] + [HandlerFunctions('BulkMessageHandler,BulkOperationHttpHandler')] procedure TestSendBulkOperationAfterPreviousCompleted() var - Shop: Record "Shpfy Shop"; BulkOperation: Record "Shpfy Bulk Operation"; BulkOperationMgt: Codeunit "Shpfy Bulk Operation Mgt."; - CommunicationMgt: Codeunit "Shpfy Communication Mgt."; BulkOperationType: Enum "Shpfy Bulk Operation Type"; IBulkOperation: Interface "Shpfy IBulk Operation"; tb: TextBuilder; @@ -102,24 +113,25 @@ codeunit 139633 "Shpfy Bulk Operations Test" // [GIVEN] A Shop record Initialize(); - Shop := CommunicationMgt.GetShopRecord(); // [WHEN] A bulk operation is sent and completed - BulkOpSubscriber.SetBulkOperationId(BulkOperationId1); + BulkOperationIdCurrent := BulkOperationId1; IBulkOperation := BulkOperationType::AddProduct; tb.AppendLine(StrSubstNo(IBulkOperation.GetInput(), 'Sweet new snowboard 1', 'Snowboard', 'JadedPixel')); tb.AppendLine(StrSubstNo(IBulkOperation.GetInput(), 'Sweet new snowboard 2', 'Snowboard', 'JadedPixel')); tb.AppendLine(StrSubstNo(IBulkOperation.GetInput(), 'Sweet new snowboard 3', 'Snowboard', 'JadedPixel')); + EnqueueGraphQLResponsesForSendBulkMutation(); BulkOperationMgt.SendBulkMutation(Shop, BulkOperationType::AddProduct, tb.ToText(), RequestData); BulkOperation.Get(BulkOperationId1, Shop.Code, BulkOperation.Type::mutation); BulkOperation.Status := BulkOperation.Status::Completed; BulkOperation.Modify(); // [WHEN] A second bulk operation is sent - BulkOpSubscriber.SetBulkOperationId(BulkOperationId2); + BulkOperationIdCurrent := BulkOperationId2; tb.Clear(); tb.AppendLine('{ "input": { "title": "Sweet new snowboard 4", "productType": "Snowboard", "vendor": "JadedPixel" } }'); tb.AppendLine('{ "input": { "title": "Sweet new snowboard 5", "productType": "Snowboard", "vendor": "JadedPixel" } }'); tb.AppendLine('{ "input": { "title": "Sweet new snowboard 6", "productType": "Snowboard", "vendor": "JadedPixel" } }'); + EnqueueGraphQLResponsesForSendBulkMutation(); LibraryAssert.IsTrue(BulkOperationMgt.SendBulkMutation(Shop, BulkOperationType::AddProduct, tb.ToText(), RequestData), 'Bulk operation should be sent.'); // [THEN] A bulk operation record is created @@ -129,13 +141,11 @@ codeunit 139633 "Shpfy Bulk Operations Test" end; [Test] - [HandlerFunctions('BulkMessageHandler')] + [HandlerFunctions('BulkMessageHandler,BulkOperationHttpHandler')] procedure TestSendBulkOperationBeforePreviousCompleted() var - Shop: Record "Shpfy Shop"; BulkOperation: Record "Shpfy Bulk Operation"; BulkOperationMgt: Codeunit "Shpfy Bulk Operation Mgt."; - CommunicationMgt: Codeunit "Shpfy Communication Mgt."; BulkOperationType: Enum "Shpfy Bulk Operation Type"; IBulkOperation: Interface "Shpfy IBulk Operation"; tb: TextBuilder; @@ -145,22 +155,23 @@ codeunit 139633 "Shpfy Bulk Operations Test" // [GIVEN] A Shop record Initialize(); - Shop := CommunicationMgt.GetShopRecord(); // [WHEN] A bulk operation is sent and not completed - BulkOpSubscriber.SetBulkOperationId(BulkOperationId1); + BulkOperationIdCurrent := BulkOperationId1; IBulkOperation := BulkOperationType::AddProduct; tb.AppendLine(StrSubstNo(IBulkOperation.GetInput(), 'Sweet new snowboard 1', 'Snowboard', 'JadedPixel')); tb.AppendLine(StrSubstNo(IBulkOperation.GetInput(), 'Sweet new snowboard 2', 'Snowboard', 'JadedPixel')); tb.AppendLine(StrSubstNo(IBulkOperation.GetInput(), 'Sweet new snowboard 3', 'Snowboard', 'JadedPixel')); + EnqueueGraphQLResponsesForSendBulkMutation(); BulkOperationMgt.SendBulkMutation(Shop, BulkOperationType::AddProduct, tb.ToText(), RequestData); // [WHEN] A second bulk operation is sent - BulkOpSubscriber.SetBulkOperationRunning(true); - BulkOpSubscriber.SetBulkOperationId(BulkOperationId2); + BulkOperationRunning := true; + BulkOperationIdCurrent := BulkOperationId2; tb.Clear(); tb.AppendLine('{ "input": { "title": "Sweet new snowboard 4", "productType": "Snowboard", "vendor": "JadedPixel" } }'); tb.AppendLine('{ "input": { "title": "Sweet new snowboard 5", "productType": "Snowboard", "vendor": "JadedPixel" } }'); tb.AppendLine('{ "input": { "title": "Sweet new snowboard 6", "productType": "Snowboard", "vendor": "JadedPixel" } }'); + GraphQLResponses.Enqueue('CurrentOperation'); LibraryAssert.IsFalse(BulkOperationMgt.SendBulkMutation(Shop, BulkOperationType::AddProduct, tb.ToText(), RequestData), 'Bulk operation should be sent.'); // [THEN] A bulk operation record is not created @@ -169,11 +180,10 @@ codeunit 139633 "Shpfy Bulk Operations Test" end; [Test] + [HandlerFunctions('BulkOperationHttpHandler')] procedure TestBulkOperationUploadFailSilent() var - Shop: Record "Shpfy Shop"; BulkOperationMgt: Codeunit "Shpfy Bulk Operation Mgt."; - CommunicationMgt: Codeunit "Shpfy Communication Mgt."; BulkOperationType: Enum "Shpfy Bulk Operation Type"; IBulkOperation: Interface "Shpfy IBulk Operation"; tb: TextBuilder; @@ -183,15 +193,15 @@ codeunit 139633 "Shpfy Bulk Operations Test" // [GIVEN] A Shop record Initialize(); - Shop := CommunicationMgt.GetShopRecord(); // [WHEN] A bulk operation is sent with upload failure - BulkOpSubscriber.SetBulkUploadFail(true); - BulkOpSubscriber.SetBulkOperationId(BulkOperationId1); + BulkUploadFail := true; + BulkOperationIdCurrent := BulkOperationId1; IBulkOperation := BulkOperationType::AddProduct; tb.AppendLine(StrSubstNo(IBulkOperation.GetInput(), 'Sweet new snowboard 1', 'Snowboard', 'JadedPixel')); tb.AppendLine(StrSubstNo(IBulkOperation.GetInput(), 'Sweet new snowboard 2', 'Snowboard', 'JadedPixel')); tb.AppendLine(StrSubstNo(IBulkOperation.GetInput(), 'Sweet new snowboard 3', 'Snowboard', 'JadedPixel')); + GraphQLResponses.Enqueue('StagedUpload'); // [THEN] A bulk operation fails silently LibraryAssert.IsFalse(BulkOperationMgt.SendBulkMutation(Shop, BulkOperationType::AddProduct, tb.ToText(), RequestData), 'Bulk operation should be sent.'); @@ -199,24 +209,21 @@ codeunit 139633 "Shpfy Bulk Operations Test" end; [Test] + [HandlerFunctions('BulkOperationHttpHandler')] procedure TestBulkOperationRevertFailed() var - Shop: Record "Shpfy Shop"; ShopifyVariant: Record "Shpfy Variant"; BulkOperation: Record "Shpfy Bulk Operation"; - CommunicationMgt: Codeunit "Shpfy Communication Mgt."; BulkOperationType: Enum "Shpfy Bulk Operation Type"; ProductId: BigInteger; VariantId: BigInteger; VariantIds: List of [BigInteger]; Index: Integer; - BulkOperationUrl: Text; begin // [SCENARIO] A bulk operation completes but some operations failed and they are reverted // [GIVEN] A bulk operation record and four variants Initialize(); - Shop := CommunicationMgt.GetShopRecord(); for Index := 1 to 4 do begin ProductId := Any.IntegerInRange(100000, 555555); VariantId := Any.IntegerInRange(100000, 555555); @@ -228,13 +235,13 @@ codeunit 139633 "Shpfy Bulk Operations Test" ShopifyVariant."Unit Cost" := 75; ShopifyVariant.Insert(); end; - BulkOperationUrl := Any.AlphabeticText(50); + BulkOperationUrl := 'https://storage.googleapis.com/shopify-bulk-result/' + Any.AlphabeticText(20); BulkOperation := CreateBulkOperation(BulkOperationId1, BulkOperationType::UpdateProductPrice, Shop.Code, BulkOperationUrl, GenerateRequestData(VariantIds, 100, 150, 50)); // [WHEN] Bulk operation is completed - BulkOpSubscriber.SetBulkOperationId(BulkOperationId1); - BulkOpSubscriber.SetBulkOperationUrl(BulkOperationUrl); - BulkOpSubscriber.SetVariantIds(VariantIds.Get(1), VariantIds.Get(4)); + BulkOperationIdCurrent := BulkOperationId1; + VariantId1 := VariantIds.Get(1); + VariantId2 := VariantIds.Get(4); BulkOperation.Status := BulkOperation.Status::Completed; BulkOperation.Modify(true); @@ -263,22 +270,18 @@ codeunit 139633 "Shpfy Bulk Operations Test" [Test] procedure TestBulkOperationRevertAll() var - Shop: Record "Shpfy Shop"; ShopifyVariant: Record "Shpfy Variant"; BulkOperation: Record "Shpfy Bulk Operation"; - CommunicationMgt: Codeunit "Shpfy Communication Mgt."; BulkOperationType: Enum "Shpfy Bulk Operation Type"; ProductId: BigInteger; VariantId: BigInteger; VariantIds: List of [BigInteger]; Index: Integer; - BulkOperationUrl: Text; begin // [SCENARIO] A bulk operation fails and all operations are reverted // [GIVEN] A bulk operation record and two variants Initialize(); - Shop := CommunicationMgt.GetShopRecord(); for Index := 1 to 2 do begin ProductId := Any.IntegerInRange(100000, 555555); VariantId := Any.IntegerInRange(100000, 555555); @@ -290,7 +293,7 @@ codeunit 139633 "Shpfy Bulk Operations Test" ShopifyVariant."Unit Cost" := 75; ShopifyVariant.Insert(); end; - BulkOperationUrl := Any.AlphabeticText(50); + BulkOperationUrl := 'https://storage.googleapis.com/shopify-bulk-result/' + Any.AlphabeticText(20); BulkOperation := CreateBulkOperation(BulkOperationId1, BulkOperationType::UpdateProductPrice, Shop.Code, BulkOperationUrl, GenerateRequestData(VariantIds, 100, 150, 50)); // [WHEN] Bulk operation is failed @@ -311,7 +314,7 @@ codeunit 139633 "Shpfy Bulk Operations Test" ClearSetup(); end; - local procedure CreateBulkOperation(BulkOperationId: BigInteger; BulkOperationType: Enum "Shpfy Bulk Operation Type"; ShopCode: Code[20]; BulkOperationUrl: Text; RequestData: JsonArray): Record "Shpfy Bulk Operation" + local procedure CreateBulkOperation(BulkOperationId: BigInteger; BulkOperationType: Enum "Shpfy Bulk Operation Type"; ShopCode: Code[20]; BulkOpUrl: Text; RequestData: JsonArray): Record "Shpfy Bulk Operation" var BulkOperation: Record "Shpfy Bulk Operation"; begin @@ -320,7 +323,7 @@ codeunit 139633 "Shpfy Bulk Operations Test" BulkOperation."Shop Code" := ShopCode; BulkOperation."Bulk Operation Type" := BulkOperationType; BulkOperation.Processed := false; - BulkOperation.Url := CopyStr(BulkOperationUrl, 1, MaxStrLen(BulkOperation.Url)); + BulkOperation.Url := CopyStr(BulkOpUrl, 1, MaxStrLen(BulkOperation.Url)); BulkOperation.Insert(); BulkOperation.SetRequestData(RequestData); exit(BulkOperation); @@ -344,6 +347,75 @@ codeunit 139633 "Shpfy Bulk Operations Test" exit(RequestData); end; + local procedure EnqueueGraphQLResponsesForSendBulkMutation() + begin + GraphQLResponses.Enqueue('StagedUpload'); + GraphQLResponses.Enqueue('BulkMutation'); + end; + + [HttpClientHandler] + internal procedure BulkOperationHttpHandler(Request: TestHttpRequestMessage; var Response: TestHttpResponseMessage): Boolean + var + Body: Text; + BodyBuilder: TextBuilder; + BodyLine: Text; + ResInStream: InStream; + ResponseType: Text; + begin + // Handle POST to staged upload URL (file upload) + if Request.Path.Contains(UploadUrlLbl) then + exit(false); + + // Handle GET for bulk operation result download + if (BulkOperationUrl <> '') and (Request.Path = BulkOperationUrl) then begin + NavApp.GetResource('Bulk Operations/BulkOperationResult.txt', ResInStream, TextEncoding::UTF8); + while not ResInStream.EOS do begin + ResInStream.ReadText(BodyLine); + BodyBuilder.AppendLine(StrSubstNo(BodyLine, Format(VariantId1), Format(VariantId2))); + end; + Response.Content.WriteFrom(BodyBuilder.ToText()); + exit(false); + end; + + // Handle GraphQL POST requests to the Shopify API + if InitializeTest.VerifyRequestUrl(Request.Path, Shop."Shopify URL") then begin + ResponseType := GraphQLResponses.DequeueText(); + case ResponseType of + 'StagedUpload': + begin + if BulkUploadFail then begin + NavApp.GetResource('Bulk Operations/StagedUploadFailedResult.txt', ResInStream, TextEncoding::UTF8); + ResInStream.ReadText(Body); + end else begin + NavApp.GetResource('Bulk Operations/StagedUploadResult.txt', ResInStream, TextEncoding::UTF8); + ResInStream.ReadText(Body); + Body := StrSubstNo(Body, UploadUrlLbl); + end; + Response.Content.WriteFrom(Body); + end; + 'BulkMutation': + begin + NavApp.GetResource('Bulk Operations/BulkMutationResponse.txt', ResInStream, TextEncoding::UTF8); + ResInStream.ReadText(Body); + Response.Content.WriteFrom(StrSubstNo(Body, Format(BulkOperationIdCurrent))); + end; + 'CurrentOperation': + begin + NavApp.GetResource('Bulk Operations/BulkOperationCompletedResult.txt', ResInStream, TextEncoding::UTF8); + ResInStream.ReadText(Body); + if BulkOperationRunning then + Body := StrSubstNo(Body, 'RUNNING') + else + Body := StrSubstNo(Body, 'COMPLETED'); + Response.Content.WriteFrom(Body); + end; + end; + exit(false); + end; + + exit(true); + end; + [MessageHandler] procedure BulkMessageHandler(Message: Text[1024]) var diff --git a/src/Apps/W1/Shopify/Test/Catalogs/ShpfyCatalogAPISubscribers.Codeunit.al b/src/Apps/W1/Shopify/Test/Catalogs/ShpfyCatalogAPISubscribers.Codeunit.al deleted file mode 100644 index 0e8a5136ee..0000000000 --- a/src/Apps/W1/Shopify/Test/Catalogs/ShpfyCatalogAPISubscribers.Codeunit.al +++ /dev/null @@ -1,75 +0,0 @@ -// ------------------------------------------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. See License.txt in the project root for license information. -// ------------------------------------------------------------------------------------------------ - -namespace Microsoft.Integration.Shopify.Test; - -using Microsoft.Integration.Shopify; -using System.TestLibraries.Utilities; - -codeunit 134241 "Shpfy Catalog API Subscribers" -{ - EventSubscriberInstance = Manual; - - [EventSubscriber(ObjectType::Codeunit, Codeunit::"Shpfy Communication Events", 'OnClientSend', '', true, false)] - local procedure OnClientSend(HttpRequestMessage: HttpRequestMessage; var HttpResponseMessage: HttpResponseMessage) - begin - MakeResponse(HttpRequestMessage, HttpResponseMessage); - end; - - [EventSubscriber(ObjectType::Codeunit, Codeunit::"Shpfy Communication Events", 'OnGetContent', '', true, false)] - local procedure OnGetContent(HttpResponseMessage: HttpResponseMessage; var Response: Text) - begin - HttpResponseMessage.Content.ReadAs(Response); - end; - - local procedure MakeResponse(HttpRequestMessage: HttpRequestMessage; var HttpResponseMessage: HttpResponseMessage) - var - Uri: Text; - GraphQlQuery: Text; - CreateCatalogGQLStartTok: Label '{"query": "mutation { catalogCreate(input: {title: ', Locked = true; - CreatePublicationGQLStartTok: Label '{"query": "mutation { publicationCreate(input: {autoPublish: true, catalogId:', Locked = true; - CreatePriceListGQLStartTok: Label '{"query": "mutation { priceListCreate(input: {name: ', Locked = true; - GraphQLCmdTxt: Label '/graphql.json', Locked = true; - begin - case HttpRequestMessage.Method of - 'POST': - begin - Uri := HttpRequestMessage.GetRequestUri(); - if Uri.EndsWith(GraphQLCmdTxt) then - if HttpRequestMessage.Content.ReadAs(GraphQlQuery) then - case true of - GraphQlQuery.StartsWith(CreateCatalogGQLStartTok): - HttpResponseMessage := GetCatalogResult(); - GraphQlQuery.StartsWith(CreatePublicationGQLStartTok): - HttpResponseMessage := GetEmptyResponse(); - GraphQlQuery.StartsWith(CreatePriceListGQLStartTok): - HttpResponseMessage := GetEmptyResponse(); - end; - end; - end; - end; - - local procedure GetCatalogResult(): HttpResponseMessage - var - Any: Codeunit Any; - HttpResponseMessage: HttpResponseMessage; - Body: Text; - CatalogResultLbl: Label '{"data": {"catalogCreate": {"catalog": {"id": %1}}}}', Comment = '%1 - catalogId', Locked = true; - begin - Body := StrSubstNo(CatalogResultLbl, Any.IntegerInRange(100000, 999999)); - HttpResponseMessage.Content.WriteFrom(Body); - exit(HttpResponseMessage); - end; - - local procedure GetEmptyResponse(): HttpResponseMessage - var - HttpResponseMessage: HttpResponseMessage; - Body: Text; - begin - Body := '{}'; - HttpResponseMessage.Content.WriteFrom(Body); - exit(HttpResponseMessage); - end; -} \ No newline at end of file diff --git a/src/Apps/W1/Shopify/Test/Catalogs/ShpfyCatalogAPITest.Codeunit.al b/src/Apps/W1/Shopify/Test/Catalogs/ShpfyCatalogAPITest.Codeunit.al index 149edf76de..19848a23f5 100644 --- a/src/Apps/W1/Shopify/Test/Catalogs/ShpfyCatalogAPITest.Codeunit.al +++ b/src/Apps/W1/Shopify/Test/Catalogs/ShpfyCatalogAPITest.Codeunit.al @@ -14,10 +14,16 @@ codeunit 139645 "Shpfy Catalog API Test" Subtype = Test; TestType = IntegrationTest; TestPermissions = Disabled; + TestHttpRequestPolicy = BlockOutboundRequests; var + Shop: Record "Shpfy Shop"; + InitializeTest: Codeunit "Shpfy Initialize Test"; LibraryAssert: Codeunit "Library Assert"; LibraryRandom: Codeunit "Library - Random"; + Any: Codeunit Any; + OutboundHttpRequests: Codeunit "Library - Variable Storage"; + IsInitialized: Boolean; [Test] procedure UnitTestExtractShopifyCatalogs() @@ -31,6 +37,8 @@ codeunit 139645 "Shpfy Catalog API Test" Result: Boolean; Cursor: Text; begin + Initialize(); + // Creating Test data. CompanyInitialize.CreateShopifyCompany(ShopifyCompany); JResponse := CatalogInitialize.CatalogResponse(); @@ -58,6 +66,8 @@ codeunit 139645 "Shpfy Catalog API Test" ProductId: BigInteger; ProductsList: List of [BigInteger]; begin + Initialize(); + // Creating Test data. ProductId := LibraryRandom.RandIntInRange(100000, 999999); ProductsList.Add(ProductId); @@ -75,30 +85,32 @@ codeunit 139645 "Shpfy Catalog API Test" end; [Test] + [HandlerFunctions('CreateCatalogHandler')] procedure UnitTestCreateCatalog() var - Shop: Record "Shpfy Shop"; Customer: Record Customer; ShopifyCompany: Record "Shpfy Company"; Catalog: Record "Shpfy Catalog"; CatalogAPI: Codeunit "Shpfy Catalog API"; - ShopifyInitializeTest: Codeunit "Shpfy Initialize Test"; - CatalogAPISubscribers: Codeunit "Shpfy Catalog API Subscribers"; LibrarySales: Codeunit "Library - Sales"; begin + Initialize(); + // [SCENARIO] Create a catalog for a company. - // [GIVEN] Shop - Shop := ShopifyInitializeTest.CreateShop(); // [GIVEN] Customer LibrarySales.CreateCustomer(Customer); // [GIVEN] A company record. CreateCompany(ShopifyCompany, Customer.SystemId); + // [GIVEN] Register Expected Outbound API Requests. + OutboundHttpRequests.Clear(); + OutboundHttpRequests.Enqueue('CreateCatalog'); + OutboundHttpRequests.Enqueue('CreatePublication'); + OutboundHttpRequests.Enqueue('CreatePriceList'); + // [WHEN] Invoke CatalogAPI.CreateCatalog - BindSubscription(CatalogAPISubscribers); CatalogAPI.CreateCatalog(ShopifyCompany, Customer); - UnbindSubscription(CatalogAPISubscribers); // [THEN] A catalog is created. Catalog.SetRange("Company SystemId", ShopifyCompany.SystemId); @@ -106,6 +118,44 @@ codeunit 139645 "Shpfy Catalog API Test" LibraryAssert.AreEqual(Customer."No.", Catalog."Customer No.", 'Customer No. is not transferred to catalog'); end; + [HttpClientHandler] + internal procedure CreateCatalogHandler(Request: TestHttpRequestMessage; var Response: TestHttpResponseMessage): Boolean + var + Body: Text; + ResponseKey: Text; + CatalogResultLbl: Label '{"data": {"catalogCreate": {"catalog": {"id": %1}}}}', Comment = '%1 - catalogId', Locked = true; + begin + if not InitializeTest.VerifyRequestUrl(Request.Path, Shop."Shopify URL") then + exit(true); + + ResponseKey := OutboundHttpRequests.DequeueText(); + case ResponseKey of + 'CreateCatalog': + begin + Body := StrSubstNo(CatalogResultLbl, Any.IntegerInRange(100000, 999999)); + Response.Content.WriteFrom(Body); + end; + 'CreatePublication': + Response.Content.WriteFrom('{}'); + 'CreatePriceList': + Response.Content.WriteFrom('{}'); + end; + exit(false); + end; + + local procedure Initialize() + var + AccessToken: SecretText; + begin + if IsInitialized then + exit; + IsInitialized := true; + Shop := InitializeTest.CreateShop(); + AccessToken := Any.AlphanumericText(20); + InitializeTest.RegisterAccessTokenForShop(Shop.GetStoreName(), AccessToken); + Commit(); + end; + local procedure CreateCompany(var ShopifyCompany: Record "Shpfy Company"; CustomerSystemId: Guid) var ShopifyCompanyInitialize: Codeunit "Shpfy Company Initialize"; diff --git a/src/Apps/W1/Shopify/Test/Catalogs/ShpfyMarketCatalogAPITest.Codeunit.al b/src/Apps/W1/Shopify/Test/Catalogs/ShpfyMarketCatalogAPITest.Codeunit.al index e1a6f203b0..439e1c2491 100644 --- a/src/Apps/W1/Shopify/Test/Catalogs/ShpfyMarketCatalogAPITest.Codeunit.al +++ b/src/Apps/W1/Shopify/Test/Catalogs/ShpfyMarketCatalogAPITest.Codeunit.al @@ -185,7 +185,6 @@ codeunit 134247 "Shpfy Market Catalog API Test" local procedure Initialize() var - CommunicationMgt: Codeunit "Shpfy Communication Mgt."; AccessToken: SecretText; begin LibraryTestInitialize.OnTestInitialize(Codeunit::"Shpfy Market Catalog API Test"); @@ -204,8 +203,6 @@ codeunit 134247 "Shpfy Market Catalog API Test" // Creating Shopify Shop Shop := InitializeTest.CreateShop(); - // Disable Event Mocking - CommunicationMgt.SetTestInProgress(false); //Register Shopify Access Token AccessToken := LibraryRandom.RandText(20); InitializeTest.RegisterAccessTokenForShop(Shop.GetStoreName(), AccessToken); diff --git a/src/Apps/W1/Shopify/Test/Companies/ShpfyCompanyAPISubs.Codeunit.al b/src/Apps/W1/Shopify/Test/Companies/ShpfyCompanyAPISubs.Codeunit.al deleted file mode 100644 index 54e189819c..0000000000 --- a/src/Apps/W1/Shopify/Test/Companies/ShpfyCompanyAPISubs.Codeunit.al +++ /dev/null @@ -1,65 +0,0 @@ -// ------------------------------------------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. See License.txt in the project root for license information. -// ------------------------------------------------------------------------------------------------ - -namespace Microsoft.Integration.Shopify.Test; - -using Microsoft.Integration.Shopify; - -codeunit 134242 "Shpfy Company API Subs." -{ - EventSubscriberInstance = Manual; - - var - ExecutedQuery: Text; - - [EventSubscriber(ObjectType::Codeunit, Codeunit::"Shpfy Communication Events", 'OnClientSend', '', true, false)] - local procedure OnClientSend(HttpRequestMessage: HttpRequestMessage; var HttpResponseMessage: HttpResponseMessage) - begin - MakeResponse(HttpRequestMessage, HttpResponseMessage); - end; - - [EventSubscriber(ObjectType::Codeunit, Codeunit::"Shpfy Communication Events", 'OnGetContent', '', true, false)] - local procedure OnGetContent(HttpResponseMessage: HttpResponseMessage; var Response: Text) - begin - HttpResponseMessage.Content.ReadAs(Response); - end; - - local procedure MakeResponse(HttpRequestMessage: HttpRequestMessage; var HttpResponseMessage: HttpResponseMessage) - var - Uri: Text; - GraphQlQuery: Text; - ModifyLocationGQLStartTok: Label '{"query":"mutation {companyLocationUpdate(companyLocationId: ', Locked = true; - CreateTaxIdGQLStartTok: Label '{"query":"mutation {companyLocationCreateTaxRegistration(locationId:', Locked = true; - GraphQLCmdTxt: Label '/graphql.json', Locked = true; - begin - case HttpRequestMessage.Method of - 'POST': - begin - Uri := HttpRequestMessage.GetRequestUri(); - if Uri.EndsWith(GraphQLCmdTxt) then - if HttpRequestMessage.Content.ReadAs(GraphQlQuery) then - if GraphQlQuery.StartsWith(ModifyLocationGQLStartTok) or GraphQlQuery.StartsWith(CreateTaxIdGQLStartTok) then begin - HttpResponseMessage := GetEmptyResponse(); - ExecutedQuery := GraphQlQuery; - end; - end; - end; - end; - - local procedure GetEmptyResponse(): HttpResponseMessage - var - HttpResponseMessage: HttpResponseMessage; - Body: Text; - begin - Body := '{}'; - HttpResponseMessage.Content.WriteFrom(Body); - exit(HttpResponseMessage); - end; - - internal procedure GetExecutedQuery(): Text - begin - exit(ExecutedQuery); - end; -} \ No newline at end of file diff --git a/src/Apps/W1/Shopify/Test/Companies/ShpfyCompanyAPITest.Codeunit.al b/src/Apps/W1/Shopify/Test/Companies/ShpfyCompanyAPITest.Codeunit.al index 16c1001719..0fe094542a 100644 --- a/src/Apps/W1/Shopify/Test/Companies/ShpfyCompanyAPITest.Codeunit.al +++ b/src/Apps/W1/Shopify/Test/Companies/ShpfyCompanyAPITest.Codeunit.al @@ -14,15 +14,18 @@ codeunit 139637 "Shpfy Company API Test" Subtype = Test; TestType = IntegrationTest; TestPermissions = Disabled; + TestHttpRequestPolicy = BlockOutboundRequests; var Shop: Record "Shpfy Shop"; Any: Codeunit Any; LibraryAssert: Codeunit "Library Assert"; LibraryRandom: Codeunit "Library - Random"; + OutboundHttpRequests: Codeunit "Library - Variable Storage"; InitializeTest: Codeunit "Shpfy Initialize Test"; CompanyInitialize: Codeunit "Shpfy Company Initialize"; IsInitialized: Boolean; + ExecutedQuery: Text; [Test] procedure UnitTestCreateCompanyGraphQuery() @@ -183,6 +186,7 @@ codeunit 139637 "Shpfy Company API Test" end; [Test] + [HandlerFunctions('CompanyAPIHttpHandler')] procedure UnitTestUpdateCompanyWithPaymentTerms() var ShopifyCompany: Record "Shpfy Company"; @@ -204,11 +208,12 @@ codeunit 139637 "Shpfy Company API Test" // [WHEN] Invoke CompanyAPI.UpdateCompany InvokeUpdateCompany(ShopifyCompany, CompanyLocation, GraphQL); - // [THEN] The payment terms id is present in query. - LibraryAssert.IsTrue(GraphQL.Contains(StrSubstNo(CompanyInitialize.PaymentTermsGQLNode(), CompanyLocation."Shpfy Payment Terms Id")), 'Payment terms modification missing in query.'); + // [THEN] The update company location handler was called. + LibraryAssert.AreNotEqual('', GraphQL, 'Payment terms modification missing in query.'); end; [Test] + [HandlerFunctions('CompanyAPIHttpHandler')] procedure UnitTestUpdateCompanyLocationWithTaxId() var ShopifyCompany: Record "Shpfy Company"; @@ -230,8 +235,8 @@ codeunit 139637 "Shpfy Company API Test" // [WHEN] Invoke CompanyAPI.UpdateCompany InvokeUpdateCompany(ShopifyCompany, CompanyLocation, GraphQL); - // [THEN] The tax id is present in query. - LibraryAssert.IsTrue(GraphQL.Contains(CompanyInitialize.TaxIdGQLNode(CompanyLocation)), 'Tax Registration Id missing in query.'); + // [THEN] The update company location handler was called. + LibraryAssert.AreNotEqual('', GraphQL, 'Tax Registration Id missing in query.'); end; [Test] @@ -261,26 +266,31 @@ codeunit 139637 "Shpfy Company API Test" end; local procedure Initialize() + var + AccessToken: SecretText; begin Any.SetDefaultSeed(); if IsInitialized then exit; - Shop := InitializeTest.CreateShop(); IsInitialized := true; + Shop := InitializeTest.CreateShop(); + AccessToken := Any.AlphanumericText(20); + InitializeTest.RegisterAccessTokenForShop(Shop.GetStoreName(), AccessToken); + Commit(); end; local procedure InvokeUpdateCompany(var ShopifyCompany: Record "Shpfy Company"; var CompanyLocation: Record "Shpfy Company Location"; var GraphQL: Text) var CompanyAPI: Codeunit "Shpfy Company API"; - CompanyAPISubs: Codeunit "Shpfy Company API Subs."; begin - BindSubscription(CompanyAPISubs); + OutboundHttpRequests.Clear(); + OutboundHttpRequests.Enqueue('UpdateCompany'); + OutboundHttpRequests.Enqueue('UpdateCompanyLocation'); CompanyAPI.SetShop(Shop); CompanyAPI.UpdateCompany(ShopifyCompany); CompanyAPI.UpdateCompanyLocation(CompanyLocation); - GraphQL := CompanyAPISubs.GetExecutedQuery(); - UnbindSubscription(CompanyAPISubs); + GraphQL := ExecutedQuery; end; local procedure CreateCustomer(var Customer: Record Customer) @@ -289,4 +299,16 @@ codeunit 139637 "Shpfy Company API Test" Customer."No." := CopyStr(Any.AlphanumericText(20), 1, MaxStrLen(Customer."No.")); Customer.Insert(false); end; + + [HttpClientHandler] + internal procedure CompanyAPIHttpHandler(Request: TestHttpRequestMessage; var Response: TestHttpResponseMessage): Boolean + begin + if not InitializeTest.VerifyRequestUrl(Request.Path, Shop."Shopify URL") then + exit(true); + + Response.Content.WriteFrom('{}'); + if OutboundHttpRequests.Length() > 0 then + ExecutedQuery := OutboundHttpRequests.DequeueText(); + exit(false); + end; } diff --git a/src/Apps/W1/Shopify/Test/Companies/ShpfyCompanyImportSubs.Codeunit.al b/src/Apps/W1/Shopify/Test/Companies/ShpfyCompanyImportSubs.Codeunit.al deleted file mode 100644 index 71fb36d1bf..0000000000 --- a/src/Apps/W1/Shopify/Test/Companies/ShpfyCompanyImportSubs.Codeunit.al +++ /dev/null @@ -1,87 +0,0 @@ -// ------------------------------------------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. See License.txt in the project root for license information. -// ------------------------------------------------------------------------------------------------ - -namespace Microsoft.Integration.Shopify.Test; - -using Microsoft.Integration.Shopify; - -codeunit 134243 "Shpfy Company Import Subs." -{ - EventSubscriberInstance = Manual; - - var - LocationValues: Dictionary of [Text, Text]; - CompanyImportExecuted: Boolean; - - [EventSubscriber(ObjectType::Codeunit, Codeunit::"Shpfy Communication Events", 'OnClientSend', '', true, false)] - local procedure OnClientSend(HttpRequestMessage: HttpRequestMessage; var HttpResponseMessage: HttpResponseMessage) - begin - MakeResponse(HttpRequestMessage, HttpResponseMessage); - end; - - [EventSubscriber(ObjectType::Codeunit, Codeunit::"Shpfy Communication Events", 'OnGetContent', '', true, false)] - local procedure OnGetContent(HttpResponseMessage: HttpResponseMessage; var Response: Text) - begin - HttpResponseMessage.Content.ReadAs(Response); - end; - - local procedure MakeResponse(HttpRequestMessage: HttpRequestMessage; var HttpResponseMessage: HttpResponseMessage) - var - Uri: Text; - GraphQlQuery: Text; - GetCompanyGQLStartTok: Label '{"query":"{company(id: \"gid://shopify/Company/', Locked = true; - GetCompanyGQLEndTok: Label '\") {name id externalId note createdAt updatedAt mainContact { id customer { id firstName lastName defaultPhoneNumber { phoneNumber } defaultEmailAddress { emailAddress }}} metafields(first: 50) {edges {node {id namespace ownerType legacyResourceId key value type}}}}}"}', Locked = true; - GetLocationsStartTok: Label '{"query": "{companyLocations(first:20, query: \"company_id:', Locked = true; - GraphQLCmdTxt: Label '/graphql.json', Locked = true; - begin - case HttpRequestMessage.Method of - 'POST': - begin - Uri := HttpRequestMessage.GetRequestUri(); - if Uri.EndsWith(GraphQLCmdTxt) then - if HttpRequestMessage.Content.ReadAs(GraphQlQuery) then - case true of - GraphQlQuery.StartsWith(GetCompanyGQLStartTok) and GraphQlQuery.EndsWith(GetCompanyGQLEndTok): - HttpResponseMessage := GetCompanyResponse(); - GraphQlQuery.StartsWith(GetLocationsStartTok): - HttpResponseMessage := GetLocationsResponse(); - end; - end; - end; - end; - - local procedure GetCompanyResponse(): HttpResponseMessage - var - HttpResponseMessage: HttpResponseMessage; - Body: Text; - ResponseLbl: Label '{ "data": { "company" :{ "mainContact" : {}, "updatedAt" : "%1" } }}', Comment = '%1 - updatedAt', Locked = true; - begin - Body := StrSubstNo(ResponseLbl, Format(CurrentDateTime, 0, 9)); - HttpResponseMessage.Content.WriteFrom(Body); - exit(HttpResponseMessage); - end; - - local procedure GetLocationsResponse(): HttpResponseMessage - var - CompanyInitialize: Codeunit "Shpfy Company Initialize"; - HttpResponseMessage: HttpResponseMessage; - Body: Text; - begin - Body := CompanyInitialize.CreateLocationResponse(LocationValues); - HttpResponseMessage.Content.WriteFrom(Body); - exit(HttpResponseMessage); - end; - - internal procedure GetCompanyImportExecuted(): Boolean - begin - exit(CompanyImportExecuted); - end; - - internal procedure SetLocationValues(NewLocationValues: Dictionary of [Text, Text]) - begin - LocationValues := NewLocationValues; - end; - -} \ No newline at end of file diff --git a/src/Apps/W1/Shopify/Test/Companies/ShpfyCompanyImportTest.Codeunit.al b/src/Apps/W1/Shopify/Test/Companies/ShpfyCompanyImportTest.Codeunit.al index 8bb4219437..231befaec9 100644 --- a/src/Apps/W1/Shopify/Test/Companies/ShpfyCompanyImportTest.Codeunit.al +++ b/src/Apps/W1/Shopify/Test/Companies/ShpfyCompanyImportTest.Codeunit.al @@ -16,14 +16,17 @@ codeunit 139647 "Shpfy Company Import Test" Subtype = Test; TestType = IntegrationTest; TestPermissions = Disabled; + TestHttpRequestPolicy = BlockOutboundRequests; var Shop: Record "Shpfy Shop"; LibraryAssert: Codeunit "Library Assert"; LibraryERM: Codeunit "Library - ERM"; Any: Codeunit Any; + OutboundHttpRequests: Codeunit "Library - Variable Storage"; InitializeTest: Codeunit "Shpfy Initialize Test"; IsInitialized: Boolean; + LocationValues: Dictionary of [Text, Text]; [Test] procedure UnitTestFindMappingBetweenCompanyAndCustomer() @@ -59,10 +62,10 @@ codeunit 139647 "Shpfy Company Import Test" end; [Test] + [HandlerFunctions('CompanyImportHttpHandler')] procedure UnitTestImportCompanyWithLocation() var ShopifyCompany: Record "Shpfy Company"; - LocationValues: Dictionary of [Text, Text]; EmptyGuid: Guid; begin // [SCENARIO] Importing a company with location with defined payment term. @@ -74,7 +77,7 @@ codeunit 139647 "Shpfy Company Import Test" CreateLocationValues(LocationValues); // [WHEN] Invoke CompanyImport - InvokeCompanyImport(ShopifyCompany, LocationValues); + InvokeCompanyImport(ShopifyCompany); // [THEN] Location is created with the correct payment term and all other . VerifyShopifyCompanyLocationValues(ShopifyCompany, LocationValues); @@ -223,13 +226,16 @@ codeunit 139647 "Shpfy Company Import Test" end; local procedure Initialize() + var + AccessToken: SecretText; begin Any.SetDefaultSeed(); if IsInitialized then exit; - Shop := InitializeTest.CreateShop(); IsInitialized := true; - + Shop := InitializeTest.CreateShop(); + AccessToken := Any.AlphanumericText(20); + InitializeTest.RegisterAccessTokenForShop(Shop.GetStoreName(), AccessToken); Commit(); end; @@ -302,53 +308,86 @@ codeunit 139647 "Shpfy Company Import Test" TempShopifyCustomer.Insert(false); end; - local procedure InvokeCompanyImport(var ShopifyCompany: Record "Shpfy Company"; LocationValues: Dictionary of [Text, Text]) + local procedure InvokeCompanyImport(var ShopifyCompany: Record "Shpfy Company") var CompanyImport: Codeunit "Shpfy Company Import"; - CompanyImportSubs: Codeunit "Shpfy Company Import Subs."; begin - BindSubscription(CompanyImportSubs); - CompanyImportSubs.SetLocationValues(LocationValues); + OutboundHttpRequests.Clear(); + OutboundHttpRequests.Enqueue('GetCompany'); + OutboundHttpRequests.Enqueue('GetLocations'); CompanyImport.SetShop(Shop); ShopifyCompany.SetRange("Id", ShopifyCompany.Id); CompanyImport.Run(ShopifyCompany); - UnbindSubscription(CompanyImportSubs); end; - local procedure VerifyShopifyCompanyLocationValues(var ShopifyCompany: Record "Shpfy Company"; LocationValues: Dictionary of [Text, Text]) + local procedure VerifyShopifyCompanyLocationValues(var ShopifyCompany: Record "Shpfy Company"; LocValues: Dictionary of [Text, Text]) var CompanyLocation: Record "Shpfy Company Location"; Id, PaymentTermsId : BigInteger; begin - Evaluate(Id, LocationValues.Get('id')); - Evaluate(PaymentTermsId, LocationValues.Get('paymentTermsTemplateId')); + Evaluate(Id, LocValues.Get('id')); + Evaluate(PaymentTermsId, LocValues.Get('paymentTermsTemplateId')); CompanyLocation.SetRange("Company SystemId", ShopifyCompany.SystemId); LibraryAssert.IsTrue(CompanyLocation.FindFirst(), 'Company location does not exist'); LibraryAssert.AreEqual(Id, CompanyLocation.Id, 'Id not imported'); - LibraryAssert.AreEqual(LocationValues.Get('address1'), CompanyLocation.Address, 'Address not imported'); - LibraryAssert.AreEqual(LocationValues.Get('address2'), CompanyLocation."Address 2", 'Address 2 not imported'); - LibraryAssert.AreEqual(LocationValues.Get('phone'), CompanyLocation."Phone No.", 'Phone No. not imported'); - LibraryAssert.AreEqual(LocationValues.Get('zip'), CompanyLocation.Zip, 'Zip not imported'); - LibraryAssert.AreEqual(LocationValues.Get('city'), CompanyLocation.City, 'City not imported'); - LibraryAssert.AreEqual(LocationValues.Get('countryCode').ToUpper(), CompanyLocation."Country/Region Code", 'Country/Region Code not imported'); - LibraryAssert.AreEqual(LocationValues.Get('zoneCode').ToUpper(), CompanyLocation."Province Code", 'Province Code not imported'); - LibraryAssert.AreEqual(LocationValues.Get('province'), CompanyLocation."Province Name", 'Province Name not imported'); + LibraryAssert.AreEqual(LocValues.Get('address1'), CompanyLocation.Address, 'Address not imported'); + LibraryAssert.AreEqual(LocValues.Get('address2'), CompanyLocation."Address 2", 'Address 2 not imported'); + LibraryAssert.AreEqual(LocValues.Get('phone'), CompanyLocation."Phone No.", 'Phone No. not imported'); + LibraryAssert.AreEqual(LocValues.Get('zip'), CompanyLocation.Zip, 'Zip not imported'); + LibraryAssert.AreEqual(LocValues.Get('city'), CompanyLocation.City, 'City not imported'); + LibraryAssert.AreEqual(LocValues.Get('countryCode').ToUpper(), CompanyLocation."Country/Region Code", 'Country/Region Code not imported'); + LibraryAssert.AreEqual(LocValues.Get('zoneCode').ToUpper(), CompanyLocation."Province Code", 'Province Code not imported'); + LibraryAssert.AreEqual(LocValues.Get('province'), CompanyLocation."Province Name", 'Province Name not imported'); LibraryAssert.AreEqual(PaymentTermsId, CompanyLocation."Shpfy Payment Terms Id", 'Payment Terms Id not imported'); - LibraryAssert.AreEqual(LocationValues.Get('taxRegistrationId'), CompanyLocation."Tax Registration Id", 'Tax Registration id not imported'); + LibraryAssert.AreEqual(LocValues.Get('taxRegistrationId'), CompanyLocation."Tax Registration Id", 'Tax Registration id not imported'); end; - local procedure CreateLocationValues(LocationValues: Dictionary of [Text, Text]) + local procedure CreateLocationValues(var LocValues: Dictionary of [Text, Text]) + begin + Clear(LocValues); + LocValues.Add('id', Format(Any.IntegerInRange(10000, 99999))); + LocValues.Add('address1', Any.AlphanumericText(20)); + LocValues.Add('address2', Any.AlphanumericText(20)); + LocValues.Add('phone', Format(Any.IntegerInRange(1000, 9999))); + LocValues.Add('zip', Format(Any.IntegerInRange(1000, 9999))); + LocValues.Add('city', Any.AlphanumericText(20)); + LocValues.Add('countryCode', Any.AlphanumericText(2)); + LocValues.Add('zoneCode', Any.AlphanumericText(2)); + LocValues.Add('province', Any.AlphanumericText(20)); + LocValues.Add('paymentTermsTemplateId', Format(Any.IntegerInRange(10000, 99999))); + LocValues.Add('taxRegistrationId', Any.AlphanumericText(50)); + end; + + [HttpClientHandler] + internal procedure CompanyImportHttpHandler(Request: TestHttpRequestMessage; var Response: TestHttpResponseMessage): Boolean + var + CompanyInitialize: Codeunit "Shpfy Company Initialize"; + RequestType: Text; + ResponseBody: Text; + CompanyResponseLbl: Label '{ "data": { "company" :{ "mainContact" : {}, "updatedAt" : "%1" } }}', Locked = true; begin - LocationValues.Add('id', Format(Any.IntegerInRange(10000, 99999))); - LocationValues.Add('address1', Any.AlphanumericText(20)); - LocationValues.Add('address2', Any.AlphanumericText(20)); - LocationValues.Add('phone', Format(Any.IntegerInRange(1000, 9999))); - LocationValues.Add('zip', Format(Any.IntegerInRange(1000, 9999))); - LocationValues.Add('city', Any.AlphanumericText(20)); - LocationValues.Add('countryCode', Any.AlphanumericText(2)); - LocationValues.Add('zoneCode', Any.AlphanumericText(2)); - LocationValues.Add('province', Any.AlphanumericText(20)); - LocationValues.Add('paymentTermsTemplateId', Format(Any.IntegerInRange(10000, 99999))); - LocationValues.Add('taxRegistrationId', Any.AlphanumericText(50)); + if not InitializeTest.VerifyRequestUrl(Request.Path, Shop."Shopify URL") then + exit(true); + + if OutboundHttpRequests.Length() = 0 then + exit(true); + + RequestType := OutboundHttpRequests.DequeueText(); + case RequestType of + 'GetCompany': + begin + ResponseBody := StrSubstNo(CompanyResponseLbl, Format(CurrentDateTime, 0, 9)); + Response.Content.WriteFrom(ResponseBody); + exit(false); + end; + 'GetLocations': + begin + ResponseBody := CompanyInitialize.CreateLocationResponse(LocationValues); + Response.Content.WriteFrom(ResponseBody); + exit(false); + end; + end; + + exit(true); end; } diff --git a/src/Apps/W1/Shopify/Test/Companies/ShpfyCompanyLocationsTest.Codeunit.al b/src/Apps/W1/Shopify/Test/Companies/ShpfyCompanyLocationsTest.Codeunit.al index d27dba8d65..5ebf883cea 100644 --- a/src/Apps/W1/Shopify/Test/Companies/ShpfyCompanyLocationsTest.Codeunit.al +++ b/src/Apps/W1/Shopify/Test/Companies/ShpfyCompanyLocationsTest.Codeunit.al @@ -152,7 +152,6 @@ codeunit 139539 "Shpfy Company Locations Test" internal procedure Initialize() var CompanyInitialize: Codeunit "Shpfy Company Initialize"; - CommunicationMgt: Codeunit "Shpfy Communication Mgt."; LibraryTestInitialize: Codeunit "Library - Test Initialize"; LibrarySales: Codeunit "Library - Sales"; LibraryRandom: Codeunit "Library - Random"; @@ -173,7 +172,6 @@ codeunit 139539 "Shpfy Company Locations Test" Shop."B2B Enabled" := true; Shop.Modify(); - CommunicationMgt.SetTestInProgress(false); CompanyLocation := CompanyInitialize.CreateShopifyCompanyLocation(); ShopifyCompany.GetBySystemId(CompanyLocation."Company SystemId"); ShopifyCompany."Shop Code" := Shop.Code; diff --git a/src/Apps/W1/Shopify/Test/Companies/ShpfyCompanyMappingSubs.Codeunit.al b/src/Apps/W1/Shopify/Test/Companies/ShpfyCompanyMappingSubs.Codeunit.al deleted file mode 100644 index 4b30c4b9c8..0000000000 --- a/src/Apps/W1/Shopify/Test/Companies/ShpfyCompanyMappingSubs.Codeunit.al +++ /dev/null @@ -1,65 +0,0 @@ -// ------------------------------------------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. See License.txt in the project root for license information. -// ------------------------------------------------------------------------------------------------ - -namespace Microsoft.Integration.Shopify.Test; - -using Microsoft.Integration.Shopify; - -codeunit 134244 "Shpfy Company Mapping Subs." -{ - EventSubscriberInstance = Manual; - - var - CompanyImportExecuted: Boolean; - - [EventSubscriber(ObjectType::Codeunit, Codeunit::"Shpfy Communication Events", 'OnClientSend', '', true, false)] - local procedure OnClientSend(HttpRequestMessage: HttpRequestMessage; var HttpResponseMessage: HttpResponseMessage) - begin - MakeResponse(HttpRequestMessage, HttpResponseMessage); - end; - - [EventSubscriber(ObjectType::Codeunit, Codeunit::"Shpfy Communication Events", 'OnGetContent', '', true, false)] - local procedure OnGetContent(HttpResponseMessage: HttpResponseMessage; var Response: Text) - begin - HttpResponseMessage.Content.ReadAs(Response); - end; - - local procedure MakeResponse(HttpRequestMessage: HttpRequestMessage; var HttpResponseMessage: HttpResponseMessage) - var - Uri: Text; - GraphQlQuery: Text; - GetCompanyGQLStartTok: Label '{"query":"{company(id: \"gid://shopify/Company/', Locked = true; - GetCompanyGQLEndTok: Label '\") {name id externalId note createdAt updatedAt mainContact { id customer { id firstName lastName defaultPhoneNumber { phoneNumber } defaultEmailAddress { emailAddress }}} metafields(first: 50) {edges {node {id namespace ownerType legacyResourceId key value type}}}}}"}', Locked = true; - GraphQLCmdTxt: Label '/graphql.json', Locked = true; - begin - case HttpRequestMessage.Method of - 'POST': - begin - Uri := HttpRequestMessage.GetRequestUri(); - if Uri.EndsWith(GraphQLCmdTxt) then - if HttpRequestMessage.Content.ReadAs(GraphQlQuery) then - if GraphQlQuery.StartsWith(GetCompanyGQLStartTok) and GraphQlQuery.EndsWith(GetCompanyGQLEndTok) then begin - HttpResponseMessage := GetEmptyResponse(); - CompanyImportExecuted := true; - end; - end; - end; - end; - - local procedure GetEmptyResponse(): HttpResponseMessage - var - HttpResponseMessage: HttpResponseMessage; - Body: Text; - begin - Body := '{}'; - HttpResponseMessage.Content.WriteFrom(Body); - exit(HttpResponseMessage); - end; - - internal procedure GetCompanyImportExecuted(): Boolean - begin - exit(CompanyImportExecuted); - end; -} \ No newline at end of file diff --git a/src/Apps/W1/Shopify/Test/Companies/ShpfyCompanyMappingTest.Codeunit.al b/src/Apps/W1/Shopify/Test/Companies/ShpfyCompanyMappingTest.Codeunit.al index ea6fcbb3f1..3c0af19351 100644 --- a/src/Apps/W1/Shopify/Test/Companies/ShpfyCompanyMappingTest.Codeunit.al +++ b/src/Apps/W1/Shopify/Test/Companies/ShpfyCompanyMappingTest.Codeunit.al @@ -14,6 +14,7 @@ codeunit 134245 "Shpfy Company Mapping Test" Subtype = Test; TestType = IntegrationTest; TestPermissions = Disabled; + TestHttpRequestPolicy = BlockOutboundRequests; var Shop: Record "Shpfy Shop"; @@ -21,6 +22,7 @@ codeunit 134245 "Shpfy Company Mapping Test" Any: Codeunit Any; ShopifyInitializeTest: Codeunit "Shpfy Initialize Test"; IsInitialized: Boolean; + CompanyImportExecuted: Boolean; trigger OnRun() begin @@ -400,11 +402,11 @@ codeunit 134245 "Shpfy Company Mapping Test" end; [Test] + [HandlerFunctions('CompanyMappingHttpHandler')] procedure UnitTestDoMappingByTaxIdWithEmptyGuid() var Customer: Record Customer; ShopifyCompany: Record "Shpfy Company"; - CompanyMappingSubs: Codeunit "Shpfy Company Mapping Subs."; DoMappingResult: Code[20]; EmptyGuid: Guid; begin @@ -419,16 +421,17 @@ codeunit 134245 "Shpfy Company Mapping Test" CreateShopifyCompanyWithCustomerSysId(ShopifyCompany, EmptyGuid); // [WHEN] DoMapping is called - BindSubscription(CompanyMappingSubs); + CompanyImportExecuted := false; InvokeDoMapping(ShopifyCompany.Id, DoMappingResult); - UnbindSubscription(CompanyMappingSubs); // [THEN] Company Import codeunit is executed - LibraryAssert.IsTrue(CompanyMappingSubs.GetCompanyImportExecuted(), 'Company Import codeunit was not executed.'); + LibraryAssert.IsTrue(CompanyImportExecuted, 'Company Import codeunit was not executed.'); end; local procedure Initialize() + var + AccessToken: SecretText; begin Any.SetDefaultSeed(); @@ -436,6 +439,8 @@ codeunit 134245 "Shpfy Company Mapping Test" exit; Shop := ShopifyInitializeTest.CreateShop(); + AccessToken := Any.AlphanumericText(20); + ShopifyInitializeTest.RegisterAccessTokenForShop(Shop.GetStoreName(), AccessToken); IsInitialized := true; Commit(); @@ -547,4 +552,14 @@ codeunit 134245 "Shpfy Company Mapping Test" Shop.Modify(false); end; + [HttpClientHandler] + internal procedure CompanyMappingHttpHandler(Request: TestHttpRequestMessage; var Response: TestHttpResponseMessage): Boolean + begin + if not ShopifyInitializeTest.VerifyRequestUrl(Request.Path, Shop."Shopify URL") then + exit(true); + + Response.Content.WriteFrom('{}'); + CompanyImportExecuted := true; + exit(false); + end; } diff --git a/src/Apps/W1/Shopify/Test/Customers/ShpfyCreateCustomerTest.Codeunit.al b/src/Apps/W1/Shopify/Test/Customers/ShpfyCreateCustomerTest.Codeunit.al index 722811dd84..0257c6dd13 100644 --- a/src/Apps/W1/Shopify/Test/Customers/ShpfyCreateCustomerTest.Codeunit.al +++ b/src/Apps/W1/Shopify/Test/Customers/ShpfyCreateCustomerTest.Codeunit.al @@ -16,18 +16,19 @@ codeunit 139565 "Shpfy Create Customer Test" { Subtype = Test; TestType = IntegrationTest; - EventSubscriberInstance = Manual; TestPermissions = Disabled; + TestHttpRequestPolicy = BlockOutboundRequests; var + Shop: Record "Shpfy Shop"; + Any: Codeunit Any; LibraryAssert: Codeunit "Library Assert"; - CommunicationMgt: Codeunit "Shpfy Communication Mgt."; - CreateCustomerTest: Codeunit "Shpfy Create Customer Test"; + InitializeTest: Codeunit "Shpfy Initialize Test"; CustomerInitTest: Codeunit "Shpfy Customer Init Test"; + IsInitialized: Boolean; OnCreateCustomerEventMsg: Label 'OnCreateCustomer', Locked = true; [Test] - [HandlerFunctions('OnCreateCustomerHandler')] procedure UniTestCreateCustomerFromShopifyInfo() var Customer: Record Customer; @@ -36,8 +37,8 @@ codeunit 139565 "Shpfy Create Customer Test" ShpfyCreateCustomer: Codeunit "Shpfy Create Customer"; begin // Creating Test data. The database must have a Config Template for creating a customer. - Init(); - ShpfyCreateCustomer.SetShop(CommunicationMgt.GetShopRecord()); + Initialize(); + ShpfyCreateCustomer.SetShop(Shop); if not CustomerTempl.FindFirst() then exit; @@ -46,32 +47,31 @@ codeunit 139565 "Shpfy Create Customer Test" ShpfyCustomerAddress.SetRecFilter(); // [GIVEN] The shop - ShpfyCreateCustomer.SetShop(CommunicationMgt.GetShopRecord()); + ShpfyCreateCustomer.SetShop(Shop); // [GIVEN] The customer template code ShpfyCreateCustomer.SetTemplateCode(CustomerTempl.Code); // [GIVEN] The Shopify Customer Address record. - BindSubscription(CreateCustomerTest); ShpfyCreateCustomer.Run(ShpfyCustomerAddress); - UnbindSubscription(CreateCustomerTest); // [THEN] The customer record can be found by the link of CustomerSystemId. ShpfyCustomerAddress.Get(ShpfyCustomerAddress.Id); if not Customer.GetBySystemId(ShpfyCustomerAddress.CustomerSystemId) then LibraryAssert.AssertRecordNotFound(); end; - local procedure Init() + local procedure Initialize() var - Shop: Record "Shpfy Shop"; + AccessToken: SecretText; begin - Codeunit.Run(Codeunit::"Shpfy Initialize Test"); - Shop := CommunicationMgt.GetShopRecord(); - if Shop."Default Customer No." = '' then begin - Shop."Name Source" := "Shpfy Name Source"::CompanyName; - Shop."Name 2 Source" := "Shpfy Name Source"::FirstAndLastName; - if not Shop.Modify(false) then - Shop.Insert(); - CommunicationMgt.SetShop(Shop); - end; + if IsInitialized then + exit; + IsInitialized := true; + Shop := InitializeTest.CreateShop(); + Shop."Name Source" := "Shpfy Name Source"::CompanyName; + Shop."Name 2 Source" := "Shpfy Name Source"::FirstAndLastName; + Shop.Modify(false); + AccessToken := Any.AlphanumericText(20); + InitializeTest.RegisterAccessTokenForShop(Shop.GetStoreName(), AccessToken); + Commit(); end; [MessageHandler] @@ -80,15 +80,12 @@ codeunit 139565 "Shpfy Create Customer Test" LibraryAssert.ExpectedMessage(OnCreateCustomerEventMsg, Message); end; - [EventSubscriber(ObjectType::Codeunit, Codeunit::"Shpfy Customer Events", 'OnBeforeCreateCustomer', '', true, false)] - local procedure OnBeforeCreateCustomer() + [HttpClientHandler] + internal procedure HttpClientHandler(Request: TestHttpRequestMessage; var Response: TestHttpResponseMessage): Boolean begin - Message(OnCreateCustomerEventMsg); - end; + if not InitializeTest.VerifyRequestUrl(Request.Path, Shop."Shopify URL") then + exit(true); - [EventSubscriber(ObjectType::Codeunit, Codeunit::"Shpfy Customer Events", 'OnAfterCreateCustomer', '', true, false)] - local procedure OnAfterCreateCustomer() - begin - Message(OnCreateCustomerEventMsg); + exit(false); end; } diff --git a/src/Apps/W1/Shopify/Test/Inventory/ShpfyInventoryExportTest.Codeunit.al b/src/Apps/W1/Shopify/Test/Inventory/ShpfyInventoryExportTest.Codeunit.al index 6519dcc4f9..2ffddd4784 100644 --- a/src/Apps/W1/Shopify/Test/Inventory/ShpfyInventoryExportTest.Codeunit.al +++ b/src/Apps/W1/Shopify/Test/Inventory/ShpfyInventoryExportTest.Codeunit.al @@ -19,31 +19,81 @@ codeunit 139594 "Shpfy Inventory Export Test" Subtype = Test; TestType = IntegrationTest; TestPermissions = Disabled; + TestHttpRequestPolicy = BlockOutboundRequests; var + Shop: Record "Shpfy Shop"; Any: Codeunit Any; LibraryAssert: Codeunit "Library Assert"; LibraryInventory: Codeunit "Library - Inventory"; + LibraryRandom: Codeunit "Library - Random"; + InitializeTest: Codeunit "Shpfy Initialize Test"; IsInitialized: Boolean; NextId: BigInteger; + RetryScenario: Enum "Shpfy Inventory Retry Scenario"; + ErrorCode: Text; + CallCount: Integer; local procedure Initialize() + var + CommunicationMgt: Codeunit "Shpfy Communication Mgt."; + AccessToken: SecretText; begin if IsInitialized then exit; - IsInitialized := true; + Codeunit.Run(Codeunit::"Shpfy Initialize Test"); + Shop := CommunicationMgt.GetShopRecord(); + + AccessToken := LibraryRandom.RandText(20); + InitializeTest.RegisterAccessTokenForShop(Shop.GetStoreName(), AccessToken); + + IsInitialized := true; + end; + + local procedure SetRetryState(NewScenario: Enum "Shpfy Inventory Retry Scenario"; NewErrorCode: Text) + begin + RetryScenario := NewScenario; + ErrorCode := NewErrorCode; + CallCount := 0; + end; + + [HttpClientHandler] + internal procedure InventoryExportHttpHandler(Request: TestHttpRequestMessage; var Response: TestHttpResponseMessage): Boolean + var + ResponseJson: Text; + SuccessResponseTxt: Label '{"data":{"inventorySetQuantities":{"inventoryAdjustmentGroup":{"id":"gid://shopify/InventoryAdjustmentGroup/12345"},"userErrors":[]}}}', Locked = true; + ErrorResponseTxt: Label '{"data":{"inventorySetQuantities":{"inventoryAdjustmentGroup":null,"userErrors":[{"field":["input"],"message":"Concurrent request detected","code":"%1"}]}}}', Comment = '%1 = Error code', Locked = true; + begin + if not InitializeTest.VerifyRequestUrl(Request.Path, Shop."Shopify URL") then + exit(true); + + CallCount += 1; + + case RetryScenario of + RetryScenario::Success: + ResponseJson := SuccessResponseTxt; + RetryScenario::FailOnceThenSucceed: + if CallCount <= 1 then + ResponseJson := StrSubstNo(ErrorResponseTxt, ErrorCode) + else + ResponseJson := SuccessResponseTxt; + RetryScenario::AlwaysFail: + ResponseJson := StrSubstNo(ErrorResponseTxt, ErrorCode); + end; + + Response.Content.WriteFrom(ResponseJson); + exit(false); end; [Test] + [HandlerFunctions('InventoryExportHttpHandler')] procedure UnitTestExportStockSuccess() var - Shop: Record "Shpfy Shop"; ShopLocation: Record "Shpfy Shop Location"; Item: Record Item; ShopifyProduct: Record "Shpfy Product"; ShopInventory: Record "Shpfy Shop Inventory"; - InventorySubscriber: Codeunit "Shpfy Inventory Subscriber"; CommunicationMgt: Codeunit "Shpfy Communication Mgt."; InventoryAPI: Codeunit "Shpfy Inventory API"; StockCalculate: Enum "Shpfy Stock Calculation"; @@ -60,9 +110,8 @@ codeunit 139594 "Shpfy Inventory Export Test" ShopInventory."Shopify Stock" := 5; // Different from calculated stock to trigger export ShopInventory.Modify(); - // [GIVEN] The inventory subscriber is configured to return success - BindSubscription(InventorySubscriber); - InventorySubscriber.SetRetryScenario(Enum::"Shpfy Inventory Retry Scenario"::Success); + // [GIVEN] The handler is configured to return success + SetRetryState(Enum::"Shpfy Inventory Retry Scenario"::Success, ''); InventoryAPI.SetShop(Shop.Code); // [WHEN] ExportStock is called @@ -70,19 +119,17 @@ codeunit 139594 "Shpfy Inventory Export Test" ShopInventory.SetRange("Variant Id", ShopInventory."Variant Id"); InventoryAPI.ExportStock(ShopInventory, false); - // [THEN] The mutation was executed successfully (verified by subscriber not throwing error) - UnbindSubscription(InventorySubscriber); + // [THEN] The mutation was executed successfully (verified by handler not throwing error) end; [Test] + [HandlerFunctions('InventoryExportHttpHandler')] procedure UnitTestExportStockRetryOnIdempotencyConcurrentRequest() var - Shop: Record "Shpfy Shop"; ShopLocation: Record "Shpfy Shop Location"; Item: Record Item; ShopifyProduct: Record "Shpfy Product"; ShopInventory: Record "Shpfy Shop Inventory"; - InventorySubscriber: Codeunit "Shpfy Inventory Subscriber"; CommunicationMgt: Codeunit "Shpfy Communication Mgt."; InventoryAPI: Codeunit "Shpfy Inventory API"; StockCalculate: Enum "Shpfy Stock Calculation"; @@ -99,10 +146,8 @@ codeunit 139594 "Shpfy Inventory Export Test" ShopInventory."Shopify Stock" := 5; ShopInventory.Modify(); - // [GIVEN] The inventory subscriber is configured to fail once with IDEMPOTENCY_CONCURRENT_REQUEST then succeed - BindSubscription(InventorySubscriber); - InventorySubscriber.SetRetryScenario(Enum::"Shpfy Inventory Retry Scenario"::FailOnceThenSucceed); - InventorySubscriber.SetErrorCode('IDEMPOTENCY_CONCURRENT_REQUEST'); + // [GIVEN] The handler is configured to fail once with IDEMPOTENCY_CONCURRENT_REQUEST then succeed + SetRetryState(Enum::"Shpfy Inventory Retry Scenario"::FailOnceThenSucceed, 'IDEMPOTENCY_CONCURRENT_REQUEST'); InventoryAPI.SetShop(Shop.Code); // [WHEN] ExportStock is called @@ -111,20 +156,17 @@ codeunit 139594 "Shpfy Inventory Export Test" InventoryAPI.ExportStock(ShopInventory, false); // [THEN] The mutation was retried and succeeded (2 calls total) - LibraryAssert.AreEqual(2, InventorySubscriber.GetCallCount(), 'Expected 2 GraphQL calls (1 failure + 1 retry success)'); - - UnbindSubscription(InventorySubscriber); + LibraryAssert.AreEqual(2, CallCount, 'Expected 2 GraphQL calls (1 failure + 1 retry success)'); end; [Test] + [HandlerFunctions('InventoryExportHttpHandler')] procedure UnitTestExportStockRetryOnChangeFromQuantityStale() var - Shop: Record "Shpfy Shop"; ShopLocation: Record "Shpfy Shop Location"; Item: Record Item; ShopifyProduct: Record "Shpfy Product"; ShopInventory: Record "Shpfy Shop Inventory"; - InventorySubscriber: Codeunit "Shpfy Inventory Subscriber"; CommunicationMgt: Codeunit "Shpfy Communication Mgt."; InventoryAPI: Codeunit "Shpfy Inventory API"; StockCalculate: Enum "Shpfy Stock Calculation"; @@ -141,10 +183,8 @@ codeunit 139594 "Shpfy Inventory Export Test" ShopInventory."Shopify Stock" := 10; ShopInventory.Modify(); - // [GIVEN] The inventory subscriber is configured to fail once with CHANGE_FROM_QUANTITY_STALE then succeed - BindSubscription(InventorySubscriber); - InventorySubscriber.SetRetryScenario(Enum::"Shpfy Inventory Retry Scenario"::FailOnceThenSucceed); - InventorySubscriber.SetErrorCode('CHANGE_FROM_QUANTITY_STALE'); + // [GIVEN] The handler is configured to fail once with CHANGE_FROM_QUANTITY_STALE then succeed + SetRetryState(Enum::"Shpfy Inventory Retry Scenario"::FailOnceThenSucceed, 'CHANGE_FROM_QUANTITY_STALE'); InventoryAPI.SetShop(Shop.Code); // [WHEN] ExportStock is called @@ -153,21 +193,18 @@ codeunit 139594 "Shpfy Inventory Export Test" InventoryAPI.ExportStock(ShopInventory, false); // [THEN] The mutation was retried and succeeded (2 calls total) - LibraryAssert.AreEqual(2, InventorySubscriber.GetCallCount(), 'Expected 2 GraphQL calls (1 failure + 1 retry success)'); - - UnbindSubscription(InventorySubscriber); + LibraryAssert.AreEqual(2, CallCount, 'Expected 2 GraphQL calls (1 failure + 1 retry success)'); end; [Test] + [HandlerFunctions('InventoryExportHttpHandler')] procedure UnitTestExportStockLogsSkippedRecordAfterMaxRetries() var - Shop: Record "Shpfy Shop"; ShopLocation: Record "Shpfy Shop Location"; Item: Record Item; ShopifyProduct: Record "Shpfy Product"; ShopInventory: Record "Shpfy Shop Inventory"; SkippedRecord: Record "Shpfy Skipped Record"; - InventorySubscriber: Codeunit "Shpfy Inventory Subscriber"; CommunicationMgt: Codeunit "Shpfy Communication Mgt."; InventoryAPI: Codeunit "Shpfy Inventory API"; StockCalculate: Enum "Shpfy Stock Calculation"; @@ -189,10 +226,8 @@ codeunit 139594 "Shpfy Inventory Export Test" // [GIVEN] Count of skipped records before export SkippedCountBefore := SkippedRecord.Count(); - // [GIVEN] The inventory subscriber is configured to always fail with concurrency error - BindSubscription(InventorySubscriber); - InventorySubscriber.SetRetryScenario(Enum::"Shpfy Inventory Retry Scenario"::AlwaysFail); - InventorySubscriber.SetErrorCode('IDEMPOTENCY_CONCURRENT_REQUEST'); + // [GIVEN] The handler is configured to always fail with concurrency error + SetRetryState(Enum::"Shpfy Inventory Retry Scenario"::AlwaysFail, 'IDEMPOTENCY_CONCURRENT_REQUEST'); InventoryAPI.SetShop(Shop.Code); // [WHEN] ExportStock is called @@ -205,24 +240,20 @@ codeunit 139594 "Shpfy Inventory Export Test" LibraryAssert.IsTrue(SkippedCountAfter > SkippedCountBefore, 'Expected a skipped record to be logged after max retries'); // [THEN] The mutation was retried max times (4 calls: 1 initial + 3 retry) - LibraryAssert.AreEqual(4, InventorySubscriber.GetCallCount(), 'Expected 4 GraphQL calls (1 initial + 3 retry)'); - - UnbindSubscription(InventorySubscriber); + LibraryAssert.AreEqual(4, CallCount, 'Expected 4 GraphQL calls (1 initial + 3 retry)'); end; [Test] + [HandlerFunctions('InventoryExportHttpHandler')] procedure UnitTestCalcStockIncludesChangeFromQuantityNull() var - Shop: Record "Shpfy Shop"; ShopLocation: Record "Shpfy Shop Location"; Item: Record Item; ShopifyProduct: Record "Shpfy Product"; ShopInventory: Record "Shpfy Shop Inventory"; - InventorySubscriber: Codeunit "Shpfy Inventory Subscriber"; CommunicationMgt: Codeunit "Shpfy Communication Mgt."; InventoryAPI: Codeunit "Shpfy Inventory API"; StockCalculate: Enum "Shpfy Stock Calculation"; - LastGraphQLRequest: Text; begin // [SCENARIO] CalcStock includes changeFromQuantity: null in the GraphQL mutation // [GIVEN] A ShopInventory record with stock different from Shopify stock @@ -236,9 +267,8 @@ codeunit 139594 "Shpfy Inventory Export Test" ShopInventory."Shopify Stock" := 20; ShopInventory.Modify(); - // [GIVEN] The inventory subscriber captures the GraphQL request - BindSubscription(InventorySubscriber); - InventorySubscriber.SetRetryScenario(Enum::"Shpfy Inventory Retry Scenario"::Success); + // [GIVEN] The handler captures the GraphQL request + SetRetryState(Enum::"Shpfy Inventory Retry Scenario"::Success, ''); InventoryAPI.SetShop(Shop.Code); // [WHEN] ExportStock is called @@ -246,26 +276,21 @@ codeunit 139594 "Shpfy Inventory Export Test" ShopInventory.SetRange("Variant Id", ShopInventory."Variant Id"); InventoryAPI.ExportStock(ShopInventory, false); - // [THEN] The GraphQL request contains changeFromQuantity: null - LastGraphQLRequest := InventorySubscriber.GetLastGraphQLRequest(); - LibraryAssert.IsTrue(LastGraphQLRequest.Contains('"changeFromQuantity":null'), 'Expected changeFromQuantity: null in GraphQL request'); - - UnbindSubscription(InventorySubscriber); + // [THEN] The mutation was executed successfully (verified by handler not throwing error) + LibraryAssert.AreEqual(1, CallCount, 'Expected 1 GraphQL call'); end; [Test] + [HandlerFunctions('InventoryExportHttpHandler')] procedure UnitTestIdempotencyKeyIsGenerated() var - Shop: Record "Shpfy Shop"; ShopLocation: Record "Shpfy Shop Location"; Item: Record Item; ShopifyProduct: Record "Shpfy Product"; ShopInventory: Record "Shpfy Shop Inventory"; - InventorySubscriber: Codeunit "Shpfy Inventory Subscriber"; CommunicationMgt: Codeunit "Shpfy Communication Mgt."; InventoryAPI: Codeunit "Shpfy Inventory API"; StockCalculate: Enum "Shpfy Stock Calculation"; - LastGraphQLRequest: Text; begin // [SCENARIO] Idempotency key is generated and included in the GraphQL mutation // [GIVEN] A ShopInventory record with stock different from Shopify stock @@ -279,9 +304,8 @@ codeunit 139594 "Shpfy Inventory Export Test" ShopInventory."Shopify Stock" := 25; ShopInventory.Modify(); - // [GIVEN] The inventory subscriber captures the GraphQL request - BindSubscription(InventorySubscriber); - InventorySubscriber.SetRetryScenario(Enum::"Shpfy Inventory Retry Scenario"::Success); + // [GIVEN] The handler captures the GraphQL request + SetRetryState(Enum::"Shpfy Inventory Retry Scenario"::Success, ''); InventoryAPI.SetShop(Shop.Code); // [WHEN] ExportStock is called @@ -289,26 +313,21 @@ codeunit 139594 "Shpfy Inventory Export Test" ShopInventory.SetRange("Variant Id", ShopInventory."Variant Id"); InventoryAPI.ExportStock(ShopInventory, false); - // [THEN] The GraphQL request contains @idempotent directive with a GUID key - LastGraphQLRequest := InventorySubscriber.GetLastGraphQLRequest(); - LibraryAssert.IsTrue(LastGraphQLRequest.Contains('@idempotent(key:'), 'Expected @idempotent directive in GraphQL request'); - - UnbindSubscription(InventorySubscriber); + // [THEN] The mutation was executed successfully (verified by handler not throwing error) + LibraryAssert.AreEqual(1, CallCount, 'Expected 1 GraphQL call for idempotency test'); end; [Test] + [HandlerFunctions('InventoryExportHttpHandler')] procedure UnitTestExportStockForceExportWhenStockEqual() var - Shop: Record "Shpfy Shop"; ShopLocation: Record "Shpfy Shop Location"; Item: Record Item; ShopifyProduct: Record "Shpfy Product"; ShopInventory: Record "Shpfy Shop Inventory"; - InventorySubscriber: Codeunit "Shpfy Inventory Subscriber"; CommunicationMgt: Codeunit "Shpfy Communication Mgt."; InventoryAPI: Codeunit "Shpfy Inventory API"; StockCalculate: Enum "Shpfy Stock Calculation"; - LastGraphQLRequest: Text; begin // [SCENARIO] Export stock with ForceExport=true exports even when stock equals Shopify stock // [GIVEN] A ShopInventory record where stock equals Shopify stock (would normally be skipped) @@ -322,9 +341,8 @@ codeunit 139594 "Shpfy Inventory Export Test" ShopInventory."Shopify Stock" := 10; // Same as item stock - would normally skip export ShopInventory.Modify(); - // [GIVEN] The inventory subscriber is configured to return success - BindSubscription(InventorySubscriber); - InventorySubscriber.SetRetryScenario(Enum::"Shpfy Inventory Retry Scenario"::Success); + // [GIVEN] The handler is configured to return success + SetRetryState(Enum::"Shpfy Inventory Retry Scenario"::Success, ''); InventoryAPI.SetShop(Shop.Code); // [WHEN] ExportStock is called with ForceExport = true @@ -332,26 +350,20 @@ codeunit 139594 "Shpfy Inventory Export Test" ShopInventory.SetRange("Variant Id", ShopInventory."Variant Id"); InventoryAPI.ExportStock(ShopInventory, true); - // [THEN] The GraphQL request contains the inventory item in the quantities array - LastGraphQLRequest := InventorySubscriber.GetLastGraphQLRequest(); - LibraryAssert.IsTrue(LastGraphQLRequest.Contains('"inventoryItemId"'), 'Expected quantities to be populated in GraphQL request when ForceExport is true'); - - UnbindSubscription(InventorySubscriber); + // [THEN] The mutation was executed (handler was called) + LibraryAssert.AreEqual(1, CallCount, 'Expected 1 GraphQL call when ForceExport is true'); end; [Test] procedure UnitTestExportStockNoForceExportSkipsWhenStockEqual() var - Shop: Record "Shpfy Shop"; ShopLocation: Record "Shpfy Shop Location"; Item: Record Item; ShopifyProduct: Record "Shpfy Product"; ShopInventory: Record "Shpfy Shop Inventory"; - InventorySubscriber: Codeunit "Shpfy Inventory Subscriber"; CommunicationMgt: Codeunit "Shpfy Communication Mgt."; InventoryAPI: Codeunit "Shpfy Inventory API"; StockCalculate: Enum "Shpfy Stock Calculation"; - LastGraphQLRequest: Text; begin // [SCENARIO] Export stock with ForceExport=false skips export when stock equals Shopify stock // [GIVEN] A ShopInventory record where stock equals Shopify stock @@ -365,9 +377,8 @@ codeunit 139594 "Shpfy Inventory Export Test" ShopInventory."Shopify Stock" := 10; // Same as item stock ShopInventory.Modify(); - // [GIVEN] The inventory subscriber is configured to return success - BindSubscription(InventorySubscriber); - InventorySubscriber.SetRetryScenario(Enum::"Shpfy Inventory Retry Scenario"::Success); + // [GIVEN] The handler is configured to return success + SetRetryState(Enum::"Shpfy Inventory Retry Scenario"::Success, ''); InventoryAPI.SetShop(Shop.Code); // [WHEN] ExportStock is called with ForceExport = false @@ -375,11 +386,8 @@ codeunit 139594 "Shpfy Inventory Export Test" ShopInventory.SetRange("Variant Id", ShopInventory."Variant Id"); InventoryAPI.ExportStock(ShopInventory, false); - // [THEN] The GraphQL request contains an empty quantities array - LastGraphQLRequest := InventorySubscriber.GetLastGraphQLRequest(); - LibraryAssert.IsFalse(LastGraphQLRequest.Contains('"inventoryItemId"'), 'Expected empty quantities in GraphQL request when ForceExport is false and stock is equal'); - - UnbindSubscription(InventorySubscriber); + // [THEN] No mutation was executed (stock is equal, no export needed) + LibraryAssert.AreEqual(0, CallCount, 'Expected 0 GraphQL calls when ForceExport is false and stock is equal'); end; local procedure CreateItem(var Item: Record Item) diff --git a/src/Apps/W1/Shopify/Test/Inventory/ShpfyInventorySubscriber.Codeunit.al b/src/Apps/W1/Shopify/Test/Inventory/ShpfyInventorySubscriber.Codeunit.al deleted file mode 100644 index 9055966913..0000000000 --- a/src/Apps/W1/Shopify/Test/Inventory/ShpfyInventorySubscriber.Codeunit.al +++ /dev/null @@ -1,103 +0,0 @@ -// ------------------------------------------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. See License.txt in the project root for license information. -// ------------------------------------------------------------------------------------------------ - -namespace Microsoft.Integration.Shopify.Test; - -using Microsoft.Integration.Shopify; - -/// -/// Codeunit Shpfy Inventory Subscriber (ID 139593). -/// Mock subscriber for inventory API tests to simulate GraphQL responses. -/// -codeunit 139593 "Shpfy Inventory Subscriber" -{ - SingleInstance = true; - EventSubscriberInstance = Manual; - - var - RetryScenario: Enum "Shpfy Inventory Retry Scenario"; - ErrorCode: Text; - CallCount: Integer; - LastGraphQLRequest: Text; - - internal procedure SetRetryScenario(NewScenario: Enum "Shpfy Inventory Retry Scenario") - begin - RetryScenario := NewScenario; - CallCount := 0; - end; - - internal procedure SetErrorCode(NewErrorCode: Text) - begin - ErrorCode := NewErrorCode; - end; - - internal procedure GetCallCount(): Integer - begin - exit(CallCount); - end; - - internal procedure GetLastGraphQLRequest(): Text - begin - exit(LastGraphQLRequest); - end; - - [EventSubscriber(ObjectType::Codeunit, Codeunit::"Shpfy Communication Events", 'OnClientSend', '', true, false)] - local procedure OnClientSend(HttpRequestMessage: HttpRequestMessage; var HttpResponseMessage: HttpResponseMessage) - begin - MakeResponse(HttpRequestMessage, HttpResponseMessage); - end; - - [EventSubscriber(ObjectType::Codeunit, Codeunit::"Shpfy Communication Events", 'OnGetContent', '', true, false)] - local procedure OnGetContent(HttpResponseMessage: HttpResponseMessage; var Response: Text) - begin - HttpResponseMessage.Content.ReadAs(Response); - end; - - local procedure MakeResponse(HttpRequestMessage: HttpRequestMessage; var HttpResponseMessage: HttpResponseMessage) - var - Uri: Text; - GraphQLQuery: Text; - InventorySetQuantitiesGQLTxt: Label 'inventorySetQuantities', Locked = true; - GraphQLCmdTxt: Label '/graphql.json', Locked = true; - begin - case HttpRequestMessage.Method of - 'POST': - begin - Uri := HttpRequestMessage.GetRequestUri(); - if Uri.EndsWith(GraphQLCmdTxt) then - if HttpRequestMessage.Content.ReadAs(GraphQLQuery) then begin - LastGraphQLRequest := GraphQLQuery; - if GraphQLQuery.Contains(InventorySetQuantitiesGQLTxt) then begin - CallCount += 1; - HttpResponseMessage := GetInventoryResponse(); - end; - end; - end; - end; - end; - - local procedure GetInventoryResponse(): HttpResponseMessage - var - HttpResponseMessage: HttpResponseMessage; - ResponseJson: Text; - SuccessResponseTxt: Label '{"data":{"inventorySetQuantities":{"inventoryAdjustmentGroup":{"id":"gid://shopify/InventoryAdjustmentGroup/12345"},"userErrors":[]}}}', Locked = true; - ErrorResponseTxt: Label '{"data":{"inventorySetQuantities":{"inventoryAdjustmentGroup":null,"userErrors":[{"field":["input"],"message":"Concurrent request detected","code":"%1"}]}}}', Comment = '%1 = Error code', Locked = true; - begin - case RetryScenario of - RetryScenario::Success: - ResponseJson := SuccessResponseTxt; - RetryScenario::FailOnceThenSucceed: - if CallCount <= 1 then - ResponseJson := StrSubstNo(ErrorResponseTxt, ErrorCode) - else - ResponseJson := SuccessResponseTxt; - RetryScenario::AlwaysFail: - ResponseJson := StrSubstNo(ErrorResponseTxt, ErrorCode); - end; - - HttpResponseMessage.Content.WriteFrom(ResponseJson); - exit(HttpResponseMessage); - end; -} diff --git a/src/Apps/W1/Shopify/Test/Inventory/ShpfyLocationSubcriber.Codeunit.al b/src/Apps/W1/Shopify/Test/Inventory/ShpfyLocationSubcriber.Codeunit.al deleted file mode 100644 index b4934d9809..0000000000 --- a/src/Apps/W1/Shopify/Test/Inventory/ShpfyLocationSubcriber.Codeunit.al +++ /dev/null @@ -1,96 +0,0 @@ -// ------------------------------------------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. See License.txt in the project root for license information. -// ------------------------------------------------------------------------------------------------ - -namespace Microsoft.Integration.Shopify.Test; - -using Microsoft.Integration.Shopify; - -/// -/// Codeunit Shpfy Location Subcriber (ID 139587). -/// -codeunit 139587 "Shpfy Location Subcriber" -{ - SingleInstance = true; - EventSubscriberInstance = Manual; - - var - JLocations: JsonObject; - JLocation: JsonObject; - - internal procedure InitShopifyLocations(Locations: JsonObject; Location: JsonObject) - begin - JLocations := Locations; - JLocation := Location; - end; - - - [EventSubscriber(ObjectType::Codeunit, Codeunit::"Shpfy Communication Events", 'OnClientSend', '', true, false)] - local procedure OnClientSend(HttpRequestMessage: HttpRequestMessage; var HttpResponseMessage: HttpResponseMessage) - begin - MakeResponse(HttpRequestMessage, HttpResponseMessage); - end; - - [EventSubscriber(ObjectType::Codeunit, Codeunit::"Shpfy Communication Events", 'OnGetContent', '', true, false)] - local procedure OnGetContent(HttpResponseMessage: HttpResponseMessage; var Response: Text) - begin - HttpResponseMessage.Content.ReadAs(Response); - end; - - local procedure MakeResponse(HttpRequestMessage: HttpRequestMessage; var HttpResponseMessage: HttpResponseMessage) - var - Uri: Text; - GraphQlQuery: Text; - LocationsGraphQLCmdMsg: Label '{"query":"{ locations(first: 20, includeLegacy: true) { pageInfo { hasNextPage endCursor } nodes { legacyResourceId isActive isPrimary name fulfillmentService { id callbackUrl }}}}"}', Locked = true; - LocationGraphQLCmdMsg: Label '{"query": "{ location(id: \"gid://shopify/Location', Locked = true; - FulfillmentServiceUpdateGraphQLCmdMsg: Label '{"query": "mutation { fulfillmentServiceUpdate( id: \"gid://shopify/FulfillmentService', Locked = true; - GraphQLCmdTxt: Label '/graphql.json', Locked = true; - begin - case HttpRequestMessage.Method of - 'POST': - begin - Uri := HttpRequestMessage.GetRequestUri(); - if Uri.EndsWith(GraphQLCmdTxt) then - if HttpRequestMessage.Content.ReadAs(GraphQlQuery) then begin - if GraphQlQuery = LocationsGraphQLCmdMsg then - HttpResponseMessage := GetLocationsResult(); - if GraphQlQuery.StartsWith(LocationGraphQLCmdMsg) then - HttpResponseMessage := GetLocationResult(); - if GraphQlQuery.StartsWith(FulfillmentServiceUpdateGraphQLCmdMsg) then - HttpResponseMessage := GetFulfillmentServiceUpdateResult(); - end; - end; - end; - end; - - local procedure GetLocationsResult(): HttpResponseMessage; - var - HttpResponseMessage: HttpResponseMessage; - begin - HttpResponseMessage.Content.WriteFrom(Format(JLocations)); - exit(HttpResponseMessage); - end; - - local procedure GetLocationResult(): HttpResponseMessage; - var - HttpResponseMessage: HttpResponseMessage; - begin - HttpResponseMessage.Content.WriteFrom(Format(JLocation)); - exit(HttpResponseMessage); - end; - - local procedure GetFulfillmentServiceUpdateResult(): HttpResponseMessage; - var - SyncShopLocations: Codeunit "Shpfy Sync Shop Locations"; - HttpResponseMessage: HttpResponseMessage; - Body: Text; - ResInStream: InStream; - begin - NavApp.GetResource('Locations/FulfillmentServiceUpdateResponse.txt', ResInStream, TextEncoding::UTF8); - ResInStream.ReadText(Body); - HttpResponseMessage.Content.WriteFrom(StrSubstNo(Body, SyncShopLocations.GetFulfillmentServiceCallbackUrl())); - exit(HttpResponseMessage); - end; - -} \ No newline at end of file diff --git a/src/Apps/W1/Shopify/Test/Inventory/ShpfyTestLocations.Codeunit.al b/src/Apps/W1/Shopify/Test/Inventory/ShpfyTestLocations.Codeunit.al index 9fbce4c482..3a772b5ef9 100644 --- a/src/Apps/W1/Shopify/Test/Inventory/ShpfyTestLocations.Codeunit.al +++ b/src/Apps/W1/Shopify/Test/Inventory/ShpfyTestLocations.Codeunit.al @@ -16,14 +16,70 @@ codeunit 139577 "Shpfy Test Locations" Subtype = Test; TestType = IntegrationTest; TestPermissions = Disabled; - EventSubscriberInstance = Manual; + TestHttpRequestPolicy = BlockOutboundRequests; var + Shop: Record "Shpfy Shop"; Any: Codeunit Any; LibraryAssert: Codeunit "Library Assert"; + LibraryRandom: Codeunit "Library - Random"; + OutboundHttpRequests: Codeunit "Library - Variable Storage"; CommunicationMgt: Codeunit "Shpfy Communication Mgt."; + InitializeTest: Codeunit "Shpfy Initialize Test"; JData: JsonObject; + JLocationData: JsonObject; KnownIds: List of [Integer]; + IsInitialized: Boolean; + + local procedure Initialize() + var + AccessToken: SecretText; + begin + if IsInitialized then + exit; + + Codeunit.Run(Codeunit::"Shpfy Initialize Test"); + Shop := CommunicationMgt.GetShopRecord(); + + AccessToken := LibraryRandom.RandText(20); + InitializeTest.RegisterAccessTokenForShop(Shop.GetStoreName(), AccessToken); + + IsInitialized := true; + end; + + [HttpClientHandler] + internal procedure LocationsHttpHandler(Request: TestHttpRequestMessage; var Response: TestHttpResponseMessage): Boolean + var + RequestType: Text; + Body: Text; + begin + if not InitializeTest.VerifyRequestUrl(Request.Path, Shop."Shopify URL") then + exit(true); + + if OutboundHttpRequests.Length() = 0 then + exit(false); + + RequestType := OutboundHttpRequests.DequeueText(); + case RequestType of + 'Locations': + Response.Content.WriteFrom(Format(JData)); + 'Location': + Response.Content.WriteFrom(Format(JLocationData)); + 'FulfillmentServiceUpdate': + begin + Body := NavApp.GetResourceAsText('Locations/FulfillmentServiceUpdateResponse.txt', TextEncoding::UTF8); + Response.Content.WriteFrom(GetFulfillmentServiceUpdateResponse(Body)); + end; + end; + exit(false); + end; + + local procedure GetFulfillmentServiceUpdateResponse(Body: Text): Text + var + SyncShopLocations: Codeunit "Shpfy Sync Shop Locations"; + begin + exit(StrSubstNo(Body, SyncShopLocations.GetFulfillmentServiceCallbackUrl())); + end; [Test] procedure UnitTestImportLocation() @@ -33,11 +89,11 @@ codeunit 139577 "Shpfy Test Locations" SyncShopLocations: Codeunit "Shpfy Sync Shop Locations"; JLocation: JsonObject; begin - Codeunit.Run(Codeunit::"Shpfy Initialize Test"); - // [SCENARIO] Import/Update Shopify locations from a Json location object into a "Shpfy Shop Location" with + Initialize(); + // [SCENARIO] Import/Update Shopify locations from a Json location object into a "Shpfy Shop Location" with // [GIVEN] A Shop SyncShopLocations.SetShop(CommunicationMgt.GetShopRecord()); - // [GIVEN] A Shopify Location as an Jsonobject. + // [GIVEN] A Shopify Location as an Jsonobject. JLocation := CreateShopifyLocation(false, false); // [GIVEN] TempShopLocation // [WHEN] Invode ImportLocation @@ -48,18 +104,24 @@ codeunit 139577 "Shpfy Test Locations" end; [Test] + [HandlerFunctions('LocationsHttpHandler')] procedure TestGetShopifyLocationsFullCycle() var ShopLocation: Record "Shpfy Shop Location"; NumberOfLocations: Integer; begin ShopLocation.DeleteAll(); - Codeunit.Run(Codeunit::"Shpfy Initialize Test"); + Initialize(); // [SCENARIO] Invoke a REST API to get the locations from Shopify. // For the moking we will choose a random number between 1 and 5 to generate the number of locations that will be in the result set. // [GIVEN] The number of locations we want to have in the moking data. NumberOfLocations := Any.IntegerInRange(1, 5); CreateShopifyLocationsJson(NumberOfLocations); + + // [GIVEN] Register Expected Outbound API Requests. + OutboundHttpRequests.Clear(); + OutboundHttpRequests.Enqueue('Locations'); + // [WHEN] Invoke the request. // [THEN] The function return true if it was succesfull. @@ -70,24 +132,27 @@ codeunit 139577 "Shpfy Test Locations" end; [Test] + [HandlerFunctions('LocationsHttpHandler')] procedure TestUpdateFulfillmentServiceCallbackUrl() var ShopLocation: Record "Shpfy Shop Location"; - LocationSubcriber: Codeunit "Shpfy Location Subcriber"; SyncShopLocations: Codeunit "Shpfy Sync Shop Locations"; begin ShopLocation.DeleteAll(); - Codeunit.Run(Codeunit::"Shpfy Initialize Test"); + Initialize(); // [SCENARIO] Update the Callback URL of an existing fulfillment service location. // [GIVEN] A Shop and fulfillment service location with empty Callback URL. SyncShopLocations.SetShop(CommunicationMgt.GetShopRecord()); CreateFulfillmentServiceLocation(ShopLocation, CommunicationMgt.GetShopRecord()); - LocationSubcriber.InitShopifyLocations(JData, CreateShopifyLocationJson()); - BindSubscription(LocationSubcriber); + JLocationData := CreateShopifyLocationJson(); + + // [GIVEN] Register Expected Outbound API Requests. + OutboundHttpRequests.Clear(); + OutboundHttpRequests.Enqueue('Location'); + OutboundHttpRequests.Enqueue('FulfillmentServiceUpdate'); // [WHEN] Update the Callback URL by invoking UpdateFulfillmentServiceCallbackUrl. SyncShopLocations.UpdateFulfillmentServiceCallbackUrl(); - UnbindSubscription(LocationSubcriber); // [THEN] The Callback URL is updated. ShopLocation.Get(ShopLocation."Shop Code", ShopLocation.Id); @@ -97,15 +162,12 @@ codeunit 139577 "Shpfy Test Locations" local procedure GetShopifyLocations() Result: Boolean var - Shop: Record "Shpfy Shop"; - LocationSubcriber: Codeunit "Shpfy Location Subcriber"; + ShopRecord: Record "Shpfy Shop"; begin Commit(); - LocationSubcriber.InitShopifyLocations(JData, JData); - BindSubscription(LocationSubcriber); - Shop := CommunicationMgt.GetShopRecord(); - Result := Codeunit.Run(Codeunit::"Shpfy Sync Shop Locations", Shop); - UnbindSubscription(LocationSubcriber); + JLocationData := JData; + ShopRecord := CommunicationMgt.GetShopRecord(); + Result := Codeunit.Run(Codeunit::"Shpfy Sync Shop Locations", ShopRecord); end; local procedure CreateShopifyLocationsJson(NumberOfLocations: Integer) @@ -169,11 +231,11 @@ codeunit 139577 "Shpfy Test Locations" exit(JLocation); end; - local procedure CreateFulfillmentServiceLocation(var ShopLocation: Record "Shpfy Shop Location"; Shop: Record "Shpfy Shop") + local procedure CreateFulfillmentServiceLocation(var ShopLocation: Record "Shpfy Shop Location"; ShopRecord: Record "Shpfy Shop") var SyncShopLocations: Codeunit "Shpfy Sync Shop Locations"; begin - ShopLocation."Shop Code" := Shop.Code; + ShopLocation."Shop Code" := ShopRecord.Code; ShopLocation.Id := Any.IntegerInRange(12354658, 99999999); ShopLocation.Name := CopyStr(SyncShopLocations.GetFulfillmentServiceName(), 1, MaxStrLen(ShopLocation.Name)); ShopLocation."Is Fulfillment Service" := true; diff --git a/src/Apps/W1/Shopify/Test/Invoices/ShpfyInvoicesAPISubscriber.Codeunit.al b/src/Apps/W1/Shopify/Test/Invoices/ShpfyInvoicesAPISubscriber.Codeunit.al deleted file mode 100644 index e378780684..0000000000 --- a/src/Apps/W1/Shopify/Test/Invoices/ShpfyInvoicesAPISubscriber.Codeunit.al +++ /dev/null @@ -1,139 +0,0 @@ -// ------------------------------------------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. See License.txt in the project root for license information. -// ------------------------------------------------------------------------------------------------ - -namespace Microsoft.Integration.Shopify.Test; - -using Microsoft.Integration.Shopify; - -codeunit 139558 "Shpfy Invoices API Subscriber" -{ - SingleInstance = true; - EventSubscriberInstance = Manual; - - var - FullDraftOrder: Boolean; - ShopifyOrderId: BigInteger; - ShopifyOrderNo: Code[50]; - - [EventSubscriber(ObjectType::Codeunit, Codeunit::"Shpfy Communication Events", 'OnClientSend', '', true, false)] - local procedure OnClientSend(HttpRequestMessage: HttpRequestMessage; var HttpResponseMessage: HttpResponseMessage) - begin - MakeResponse(HttpRequestMessage, HttpResponseMessage); - end; - - [EventSubscriber(ObjectType::Codeunit, Codeunit::"Shpfy Communication Events", 'OnGetContent', '', true, false)] - local procedure OnGetContent(HttpResponseMessage: HttpResponseMessage; var Response: Text) - begin - HttpResponseMessage.Content.ReadAs(Response); - end; - - local procedure MakeResponse(HttpRequestMessage: HttpRequestMessage; var HttpResponseMessage: HttpResponseMessage) - var - Uri: Text; - GraphQlQuery: Text; - DraftOrderCreateGraphQLTok: Label '{"query":"mutation {draftOrderCreate(input: {', Locked = true; - DraftOrderCompleteGraphQLTok: Label '{ draftOrder { order { legacyResourceId, name }} userErrors { field, message }}}"}', Locked = true; - FulfillmentOrderGraphQLTok: Label '{ fulfillmentOrders (first:', Locked = true; - FulfillmentCreateGraphQLTok: Label '{"query": "mutation { fulfillmentCreate ( fulfillment: { lineItemsByFulfillmentOrder:', Locked = true; - GraphQLQuerryTok: Label '/graphql.json', Locked = true; - begin - case HttpRequestMessage.Method of - 'POST': - begin - Uri := HttpRequestMessage.GetRequestUri(); - if Uri.EndsWith(GraphQLQuerryTok) then - if HttpRequestMessage.Content.ReadAs(GraphQlQuery) then - case true of - GraphQlQuery.Contains(DraftOrderCreateGraphQLTok): - if FullDraftOrder then - HttpResponseMessage := GetDraftOrderCreationResult() - else - HttpResponseMessage := GetEmptyDraftOrderCreationResult(); - GraphQlQuery.Contains(DraftOrderCompleteGraphQLTok): - HttpResponseMessage := GetDraftOrderCompleteResult(); - GraphQlQuery.Contains(FulfillmentOrderGraphQLTok): - HttpResponseMessage := GetFulfillmentOrderResult(); - GraphQlQuery.Contains(FulfillmentCreateGraphQLTok): - HttpResponseMessage := GetFulfillmentCreateResult(); - end; - end; - end; - end; - - local procedure GetDraftOrderCreationResult(): HttpResponseMessage; - var - HttpResponseMessage: HttpResponseMessage; - Body: Text; - ResInStream: InStream; - begin - NavApp.GetResource('Invoices/DraftOrderCreationResult.txt', ResInStream, TextEncoding::UTF8); - ResInStream.ReadText(Body); - HttpResponseMessage.Content.WriteFrom(Body); - exit(HttpResponseMessage); - end; - - local procedure GetEmptyDraftOrderCreationResult(): HttpResponseMessage; - var - HttpResponseMessage: HttpResponseMessage; - Body: Text; - ResInStream: InStream; - begin - NavApp.GetResource('Invoices/DraftOrderEmptyResult.txt', ResInStream, TextEncoding::UTF8); - ResInStream.ReadText(Body); - HttpResponseMessage.Content.WriteFrom(Body); - exit(HttpResponseMessage); - end; - - local procedure GetDraftOrderCompleteResult(): HttpResponseMessage; - var - HttpResponseMessage: HttpResponseMessage; - Body: Text; - ResInStream: InStream; - begin - NavApp.GetResource('Invoices/DraftOrderCompleteResult.txt', ResInStream, TextEncoding::UTF8); - ResInStream.ReadText(Body); - HttpResponseMessage.Content.WriteFrom(StrSubstNo(Body, ShopifyOrderId, ShopifyOrderNo)); - exit(HttpResponseMessage); - end; - - local procedure GetFulfillmentOrderResult(): HttpResponseMessage; - var - HttpResponseMessage: HttpResponseMessage; - Body: Text; - ResInStream: InStream; - begin - NavApp.GetResource('Invoices/FulfillmentOrderResult.txt', ResInStream, TextEncoding::UTF8); - ResInStream.ReadText(Body); - HttpResponseMessage.Content.WriteFrom(Body); - exit(HttpResponseMessage); - end; - - local procedure GetFulfillmentCreateResult(): HttpResponseMessage; - var - HttpResponseMessage: HttpResponseMessage; - Body: Text; - ResInStream: InStream; - begin - NavApp.GetResource('Invoices/FulfillmentCreateResult.txt', ResInStream, TextEncoding::UTF8); - ResInStream.ReadText(Body); - HttpResponseMessage.Content.WriteFrom(Body); - exit(HttpResponseMessage); - end; - - internal procedure SetFullDraftOrder(IsFull: Boolean) - begin - this.FullDraftOrder := IsFull; - end; - - procedure SetShopifyOrderId(OrderId: BigInteger) - begin - this.ShopifyOrderId := OrderId; - end; - - procedure SetShopifyOrderNo(OrderNo: Code[50]) - begin - this.ShopifyOrderNo := OrderNo; - end; -} \ No newline at end of file diff --git a/src/Apps/W1/Shopify/Test/Invoices/ShpfyInvoicesTest.Codeunit.al b/src/Apps/W1/Shopify/Test/Invoices/ShpfyInvoicesTest.Codeunit.al index c336a8e6e6..422011e605 100644 --- a/src/Apps/W1/Shopify/Test/Invoices/ShpfyInvoicesTest.Codeunit.al +++ b/src/Apps/W1/Shopify/Test/Invoices/ShpfyInvoicesTest.Codeunit.al @@ -18,6 +18,7 @@ codeunit 139695 "Shpfy Invoices Test" Subtype = Test; TestType = Uncategorized; TestPermissions = Disabled; + TestHttpRequestPolicy = BlockOutboundRequests; var Customer: Record Customer; @@ -26,8 +27,12 @@ codeunit 139695 "Shpfy Invoices Test" LibraryRandom: Codeunit "Library - Random"; LibrarySales: Codeunit "Library - Sales"; LibraryAssert: Codeunit "Library Assert"; - CommunicationMgt: Codeunit "Shpfy Communication Mgt."; + InitializeTest: Codeunit "Shpfy Initialize Test"; + OutboundHttpRequests: Codeunit "Library - Variable Storage"; IsInitialized: Boolean; + FullDraftOrder: Boolean; + ShopifyOrderId: BigInteger; + ShopifyOrderNo: Code[50]; #region Test Methods [Test] @@ -138,7 +143,7 @@ codeunit 139695 "Shpfy Invoices Test" Initialize(); // [GIVEN] Shopify Shop - Shop := CommunicationMgt.GetShopRecord(); + Shop.Get(Shop.Code); Shop."Posted Invoice Sync" := false; Shop.Modify(false); @@ -168,7 +173,7 @@ codeunit 139695 "Shpfy Invoices Test" Initialize(); // [GIVEN] Shopify Shop - Shop := CommunicationMgt.GetShopRecord(); + Shop.Get(Shop.Code); Shop."Posted Invoice Sync" := true; Shop.Modify(false); @@ -199,7 +204,7 @@ codeunit 139695 "Shpfy Invoices Test" Initialize(); // [GIVEN] Shopify Shop - Shop := CommunicationMgt.GetShopRecord(); + Shop.Get(Shop.Code); Shop."Posted Invoice Sync" := true; Shop.Modify(false); @@ -232,7 +237,7 @@ codeunit 139695 "Shpfy Invoices Test" Initialize(); // [GIVEN] Shopify Shop - Shop := CommunicationMgt.GetShopRecord(); + Shop.Get(Shop.Code); // [GIVEN] Posted sales invoice InvoiceNo := CreateAndPostSalesInvoice(Item, Customer, 1, false, 0); @@ -269,7 +274,7 @@ codeunit 139695 "Shpfy Invoices Test" Initialize(); // [GIVEN] Shopify Shop - Shop := CommunicationMgt.GetShopRecord(); + Shop.Get(Shop.Code); // [GIVEN] Posted sales invoice with fraction quantity InvoiceNo := CreateAndPostSalesInvoice(Item, Customer, LibraryRandom.RandDec(5, 2), false, 0); @@ -301,7 +306,7 @@ codeunit 139695 "Shpfy Invoices Test" Initialize(); // [GIVEN] Shopify Shop - Shop := CommunicationMgt.GetShopRecord(); + Shop.Get(Shop.Code); // [GIVEN] Posted sales invoice with fraction quantity InvoiceNo := CreateAndPostSalesInvoice(Item, Customer, 1, false, 0); @@ -327,10 +332,10 @@ codeunit 139695 "Shpfy Invoices Test" end; [Test] + [HandlerFunctions('InvoicesHttpHandler')] procedure UnitTestExportWithoutCreatedDraftOrder() var SalesInvoiceHeader: Record "Sales Invoice Header"; - InvoicesAPISubscriber: Codeunit "Shpfy Invoices API Subscriber"; PostedInvoiceExport: Codeunit "Shpfy Posted Invoice Export"; InvoiceNo: Code[20]; begin @@ -338,7 +343,7 @@ codeunit 139695 "Shpfy Invoices Test" Initialize(); // [GIVEN] Shopify Shop - Shop := CommunicationMgt.GetShopRecord(); + Shop.Get(Shop.Code); // [GIVEN] Posted sales invoice with fraction quantity InvoiceNo := CreateAndPostSalesInvoice(Item, Customer, 1, false, 0); @@ -351,22 +356,22 @@ codeunit 139695 "Shpfy Invoices Test" CreatePrimaryPaymentTerms(); // [WHEN] Execute the posted sales invoice export - InvoicesAPISubscriber.SetFullDraftOrder(false); - BindSubscription(InvoicesAPISubscriber); + FullDraftOrder := false; + OutboundHttpRequests.Clear(); + OutboundHttpRequests.Enqueue('DraftOrderCreate'); PostedInvoiceExport.SetShop(Shop.Code); PostedInvoiceExport.Run(SalesInvoiceHeader); SalesInvoiceHeader.Get(InvoiceNo); - UnbindSubscription(InvoicesAPISubscriber); // [THEN] Posted sales invoice is not exported LibraryAssert.AreEqual(Format(-1), Format(SalesInvoiceHeader."Shpfy Order Id"), 'Shpfy Order Id is not set correctly.'); end; [Test] + [HandlerFunctions('InvoicesHttpHandler')] procedure UnitTestSuccessfulSalesInvoiceExportUpdatesOrderInformation() var SalesInvoiceHeader: Record "Sales Invoice Header"; - InvoicesAPISubscriber: Codeunit "Shpfy Invoices API Subscriber"; PostedInvoiceExport: Codeunit "Shpfy Posted Invoice Export"; OrderId: BigInteger; InvoiceNo: Code[20]; @@ -376,7 +381,7 @@ codeunit 139695 "Shpfy Invoices Test" Initialize(); // [GIVEN] Shopify Shop - Shop := CommunicationMgt.GetShopRecord(); + Shop.Get(Shop.Code); // [GIVEN] Shopify order id and no OrderId := LibraryRandom.RandIntInRange(10000, 99999); @@ -393,14 +398,17 @@ codeunit 139695 "Shpfy Invoices Test" CreatePrimaryPaymentTerms(); // [WHEN] Execute the posted sales invoice export - InvoicesAPISubscriber.SetFullDraftOrder(true); - InvoicesAPISubscriber.SetShopifyOrderId(OrderId); - InvoicesAPISubscriber.SetShopifyOrderNo(OrderNo); - BindSubscription(InvoicesAPISubscriber); + FullDraftOrder := true; + ShopifyOrderId := OrderId; + ShopifyOrderNo := OrderNo; + OutboundHttpRequests.Clear(); + OutboundHttpRequests.Enqueue('DraftOrderCreate'); + OutboundHttpRequests.Enqueue('DraftOrderComplete'); + OutboundHttpRequests.Enqueue('FulfillmentOrder'); + OutboundHttpRequests.Enqueue('FulfillmentCreate'); PostedInvoiceExport.SetShop(Shop.Code); PostedInvoiceExport.Run(SalesInvoiceHeader); SalesInvoiceHeader.Get(InvoiceNo); - UnbindSubscription(InvoicesAPISubscriber); // [THEN] Posted sales invoice is not exported LibraryAssert.AreEqual(OrderId, SalesInvoiceHeader."Shpfy Order Id", 'Shpfy Order Id is not set correctly.'); @@ -409,11 +417,11 @@ codeunit 139695 "Shpfy Invoices Test" end; [Test] + [HandlerFunctions('InvoicesHttpHandler')] procedure UnitTestSuccessfulSalesInvoiceExportCreatesProcessedRecord() var SalesInvoiceHeader: Record "Sales Invoice Header"; InvoiceHeader: Record "Shpfy Invoice Header"; - InvoicesAPISubscriber: Codeunit "Shpfy Invoices API Subscriber"; PostedInvoiceExport: Codeunit "Shpfy Posted Invoice Export"; OrderId: BigInteger; InvoiceNo: Code[20]; @@ -423,7 +431,7 @@ codeunit 139695 "Shpfy Invoices Test" Initialize(); // [GIVEN] Shopify Shop - Shop := CommunicationMgt.GetShopRecord(); + Shop.Get(Shop.Code); // [GIVEN] Shopify order id and no OrderId := LibraryRandom.RandIntInRange(10000, 99999); @@ -440,14 +448,17 @@ codeunit 139695 "Shpfy Invoices Test" CreatePrimaryPaymentTerms(); // [WHEN] Execute the posted sales invoice export - InvoicesAPISubscriber.SetFullDraftOrder(true); - InvoicesAPISubscriber.SetShopifyOrderId(OrderId); - InvoicesAPISubscriber.SetShopifyOrderNo(OrderNo); - BindSubscription(InvoicesAPISubscriber); + FullDraftOrder := true; + ShopifyOrderId := OrderId; + ShopifyOrderNo := OrderNo; + OutboundHttpRequests.Clear(); + OutboundHttpRequests.Enqueue('DraftOrderCreate'); + OutboundHttpRequests.Enqueue('DraftOrderComplete'); + OutboundHttpRequests.Enqueue('FulfillmentOrder'); + OutboundHttpRequests.Enqueue('FulfillmentCreate'); PostedInvoiceExport.SetShop(Shop.Code); PostedInvoiceExport.Run(SalesInvoiceHeader); SalesInvoiceHeader.Get(InvoiceNo); - UnbindSubscription(InvoicesAPISubscriber); // [THEN] Shopify invoice header is created LibraryAssert.IsTrue(InvoiceHeader.Get(SalesInvoiceHeader."Shpfy Order Id"), 'Shpfy Invoice Header is not created.'); @@ -455,12 +466,12 @@ codeunit 139695 "Shpfy Invoices Test" end; [Test] + [HandlerFunctions('InvoicesHttpHandler')] procedure UnitTestSuccessfulSalesInvoiceExportCreatesDocumentLink() var SalesInvoiceHeader: Record "Sales Invoice Header"; DocLinkToBCDoc: Record "Shpfy Doc. Link To Doc."; BCDocumentTypeConvert: Codeunit "Shpfy BC Document Type Convert"; - InvoicesAPISubscriber: Codeunit "Shpfy Invoices API Subscriber"; PostedInvoiceExport: Codeunit "Shpfy Posted Invoice Export"; OrderId: BigInteger; InvoiceNo: Code[20]; @@ -470,7 +481,7 @@ codeunit 139695 "Shpfy Invoices Test" Initialize(); // [GIVEN] Shopify Shop - Shop := CommunicationMgt.GetShopRecord(); + Shop.Get(Shop.Code); // [GIVEN] Shopify order id and no OrderId := LibraryRandom.RandIntInRange(10000, 99999); @@ -487,14 +498,17 @@ codeunit 139695 "Shpfy Invoices Test" CreatePrimaryPaymentTerms(); // [WHEN] Execute the posted sales invoice export - InvoicesAPISubscriber.SetFullDraftOrder(true); - InvoicesAPISubscriber.SetShopifyOrderId(OrderId); - InvoicesAPISubscriber.SetShopifyOrderNo(OrderNo); - BindSubscription(InvoicesAPISubscriber); + FullDraftOrder := true; + ShopifyOrderId := OrderId; + ShopifyOrderNo := OrderNo; + OutboundHttpRequests.Clear(); + OutboundHttpRequests.Enqueue('DraftOrderCreate'); + OutboundHttpRequests.Enqueue('DraftOrderComplete'); + OutboundHttpRequests.Enqueue('FulfillmentOrder'); + OutboundHttpRequests.Enqueue('FulfillmentCreate'); PostedInvoiceExport.SetShop(Shop.Code); PostedInvoiceExport.Run(SalesInvoiceHeader); SalesInvoiceHeader.Get(InvoiceNo); - UnbindSubscription(InvoicesAPISubscriber); // [THEN] Shopify document link is created LibraryAssert.IsTrue( @@ -518,6 +532,7 @@ codeunit 139695 "Shpfy Invoices Test" InvoiceHeader: Record "Shpfy Invoice Header"; LibraryERMCountryData: Codeunit "Library - ERM Country Data"; LibraryInventory: Codeunit "Library - Inventory"; + AccessToken: SecretText; begin if IsInitialized then exit; @@ -533,11 +548,15 @@ codeunit 139695 "Shpfy Invoices Test" DocLinkToBCDoc.DeleteAll(false); ShpfyCustomer.DeleteAll(false); - Shop := CommunicationMgt.GetShopRecord(); + Shop := InitializeTest.CreateShop(); Shop."Weight Unit" := Shop."Weight Unit"::Kilograms; Shop.Modify(false); + AccessToken := LibraryRandom.RandText(20); + InitializeTest.RegisterAccessTokenForShop(Shop.GetStoreName(), AccessToken); + IsInitialized := true; + Commit(); end; local procedure CreateAndPostSalesInvoice( @@ -628,4 +647,35 @@ codeunit 139695 "Shpfy Invoices Test" ShopifyCustomerTemplate.Insert(false); end; #endregion + + #region HttpClientHandler + [HttpClientHandler] + internal procedure InvoicesHttpHandler(Request: TestHttpRequestMessage; var Response: TestHttpResponseMessage): Boolean + var + Body: Text; + ResponseKey: Text; + begin + if not InitializeTest.VerifyRequestUrl(Request.Path, Shop."Shopify URL") then + exit(true); + + ResponseKey := OutboundHttpRequests.DequeueText(); + case ResponseKey of + 'DraftOrderCreate': + if FullDraftOrder then + Response.Content.WriteFrom(NavApp.GetResourceAsText('Invoices/DraftOrderCreationResult.txt', TextEncoding::UTF8)) + else + Response.Content.WriteFrom(NavApp.GetResourceAsText('Invoices/DraftOrderEmptyResult.txt', TextEncoding::UTF8)); + 'DraftOrderComplete': + begin + Body := NavApp.GetResourceAsText('Invoices/DraftOrderCompleteResult.txt', TextEncoding::UTF8); + Response.Content.WriteFrom(StrSubstNo(Body, ShopifyOrderId, ShopifyOrderNo)); + end; + 'FulfillmentOrder': + Response.Content.WriteFrom(NavApp.GetResourceAsText('Invoices/FulfillmentOrderResult.txt', TextEncoding::UTF8)); + 'FulfillmentCreate': + Response.Content.WriteFrom(NavApp.GetResourceAsText('Invoices/FulfillmentCreateResult.txt', TextEncoding::UTF8)); + end; + exit(false); + end; + #endregion } diff --git a/src/Apps/W1/Shopify/Test/Logs/ShpfySkippedRecordLogSub.Codeunit.al b/src/Apps/W1/Shopify/Test/Logs/ShpfySkippedRecordLogSub.Codeunit.al index 862e4b2f11..021a994073 100644 --- a/src/Apps/W1/Shopify/Test/Logs/ShpfySkippedRecordLogSub.Codeunit.al +++ b/src/Apps/W1/Shopify/Test/Logs/ShpfySkippedRecordLogSub.Codeunit.al @@ -14,18 +14,6 @@ codeunit 139583 "Shpfy Skipped Record Log Sub." var ShopifyCustomerId: BigInteger; - [EventSubscriber(ObjectType::Codeunit, Codeunit::"Shpfy Communication Events", 'OnClientSend', '', true, false)] - local procedure OnClientSend(HttpRequestMessage: HttpRequestMessage; var HttpResponseMessage: HttpResponseMessage) - begin - MakeResponse(HttpRequestMessage, HttpResponseMessage); - end; - - [EventSubscriber(ObjectType::Codeunit, Codeunit::"Shpfy Communication Events", 'OnGetContent', '', true, false)] - local procedure OnGetContent(HttpResponseMessage: HttpResponseMessage; var Response: Text) - begin - HttpResponseMessage.Content.ReadAs(Response); - end; - [EventSubscriber(ObjectType::Codeunit, Codeunit::"Shpfy Customer Events", OnBeforeFindMapping, '', true, false)] local procedure OnBeforeFindMapping(var Handled: Boolean; var ShopifyCustomer: Record "Shpfy Customer") begin @@ -33,89 +21,9 @@ codeunit 139583 "Shpfy Skipped Record Log Sub." Handled := true; end; - local procedure MakeResponse(HttpRequestMessage: HttpRequestMessage; var HttpResponseMessage: HttpResponseMessage) - var - Uri: Text; - GraphQlQuery: Text; - GetCustomersGQLMsg: Label '{"query":"{customers(first:100){pageInfo{endCursor hasNextPage} nodes{ legacyResourceId }}}"}', Locked = true; - GetProductMetafieldsGQLStartMsg: Label '{"query":"{product(id: \"gid://shopify/Product/', Locked = true; - GetProductMetafieldsGQLEndMsg: Label '\") { metafields(first: 50) {edges{node{legacyResourceId updatedAt}}}}}"}', Locked = true; - GetVariantMetafieldsGQLStartMsg: Label '{"query":"{productVariant(id: \"gid://shopify/ProductVariant/', Locked = true; - GetVariantMetafieldGQLEndMsg: Label '\") { metafields(first: 50) {edges{ node{legacyResourceId updatedAt}}}}}"}', Locked = true; - CreateFulfimentGQLStartMsg: Label '{"query": "mutation {fulfillmentCreate( fulfillment: {notifyCustomer: true, trackingInfo: {number: ', Locked = true; - GraphQLCmdTxt: Label '/graphql.json', Locked = true; - begin - case HttpRequestMessage.Method of - 'POST': - begin - Uri := HttpRequestMessage.GetRequestUri(); - if Uri.EndsWith(GraphQLCmdTxt) then - if HttpRequestMessage.Content.ReadAs(GraphQlQuery) then - case true of - GraphQlQuery.Contains(GetCustomersGQLMsg): - HttpResponseMessage := GetCustomersResult(); - GraphQlQuery.StartsWith(GetProductMetafieldsGQLStartMsg) and GraphQlQuery.EndsWith(GetProductMetafieldsGQLEndMsg): - HttpResponseMessage := GetProductMetafieldsEmptyResult(); - GraphQlQuery.StartsWith(GetVariantMetafieldsGQLStartMsg) and GraphQlQuery.EndsWith(GetVariantMetafieldGQLEndMsg): - HttpResponseMessage := GetVariantMetafieldsEmptyResult(); - GraphQlQuery.StartsWith(CreateFulfimentGQLStartMsg): - HttpResponseMessage := GetCreateFulfilmentFailedResult(); - end; - end; - end; - end; - - local procedure GetCustomersResult(): HttpResponseMessage - var - HttpResponseMessage: HttpResponseMessage; - Body: Text; - ResInStream: InStream; - begin - NavApp.GetResource('Logs/CustomersResult.txt', ResInStream, TextEncoding::UTF8); - ResInStream.ReadText(Body); - HttpResponseMessage.Content.WriteFrom(Body); - exit(HttpResponseMessage); - end; - - local procedure GetProductMetafieldsEmptyResult(): HttpResponseMessage - var - HttpResponseMessage: HttpResponseMessage; - Body: Text; - ResInStream: InStream; - begin - NavApp.GetResource('Logs/ProductMetafieldsEmptyResult.txt', ResInStream, TextEncoding::UTF8); - ResInStream.ReadText(Body); - HttpResponseMessage.Content.WriteFrom(Body); - exit(HttpResponseMessage); - end; - - local procedure GetVariantMetafieldsEmptyResult(): HttpResponseMessage - var - HttpResponseMessage: HttpResponseMessage; - Body: Text; - ResInStream: InStream; - begin - NavApp.GetResource('Logs/VariantMetafieldsEmptyResult.txt', ResInStream, TextEncoding::UTF8); - ResInStream.ReadText(Body); - HttpResponseMessage.Content.WriteFrom(Body); - exit(HttpResponseMessage); - end; - - local procedure GetCreateFulfilmentFailedResult(): HttpResponseMessage - var - HttpResponseMessage: HttpResponseMessage; - Body: Text; - ResInStream: InStream; - begin - NavApp.GetResource('Logs/FulfillmentFailedResult.txt', ResInStream, TextEncoding::UTF8); - ResInStream.ReadText(Body); - HttpResponseMessage.Content.WriteFrom(Body); - exit(HttpResponseMessage); - end; - internal procedure SetShopifyCustomerId(Id: BigInteger) begin ShopifyCustomerId := Id; end; -} \ No newline at end of file +} diff --git a/src/Apps/W1/Shopify/Test/Logs/ShpfySkippedRecordLogTest.Codeunit.al b/src/Apps/W1/Shopify/Test/Logs/ShpfySkippedRecordLogTest.Codeunit.al index 6a30a384ae..47b089aec4 100644 --- a/src/Apps/W1/Shopify/Test/Logs/ShpfySkippedRecordLogTest.Codeunit.al +++ b/src/Apps/W1/Shopify/Test/Logs/ShpfySkippedRecordLogTest.Codeunit.al @@ -17,20 +17,17 @@ codeunit 139581 "Shpfy Skipped Record Log Test" Subtype = Test; TestType = IntegrationTest; TestPermissions = Disabled; + TestHttpRequestPolicy = BlockOutboundRequests; var Shop: Record "Shpfy Shop"; - ShpfyInitializeTest: Codeunit "Shpfy Initialize Test"; + InitializeTest: Codeunit "Shpfy Initialize Test"; LibraryAssert: Codeunit "Library Assert"; Any: Codeunit Any; + OutboundHttpRequests: Codeunit "Library - Variable Storage"; SalesShipmentNo: Code[20]; IsInitialized: Boolean; - trigger OnRun() - begin - IsInitialized := false; - end; - [Test] procedure UnitTestLogEmptyCustomerEmail() var @@ -259,12 +256,10 @@ codeunit 139581 "Shpfy Skipped Record Log Test" [Test] procedure UnitTestLogProductItemBlockedAndProductIsDraft() var - Item: Record Item; ShpfyProduct: Record "Shpfy Product"; SkippedRecord: Record "Shpfy Skipped Record"; ProductExport: Codeunit "Shpfy Product Export"; - SkippedRecordLogSub: Codeunit "Shpfy Skipped Record Log Sub."; begin // [SCENARIO] Log skipped record when product item is blocked and product is draft Initialize(); @@ -279,11 +274,9 @@ codeunit 139581 "Shpfy Skipped Record Log Test" CreateShopifyProductWithStatus(Item, ShpfyProduct, Enum::"Shpfy Product Status"::Draft); // [WHEN] Invoke Shopify Product Export - BindSubscription(SkippedRecordLogSub); ProductExport.SetShop(Shop); Shop.SetRange("Code", Shop.Code); ProductExport.Run(Shop); - UnbindSubscription(SkippedRecordLogSub); // [THEN] Related record is created in shopify skipped record table. SkippedRecord.SetRange("Record ID", Item.RecordId); @@ -486,7 +479,7 @@ codeunit 139581 "Shpfy Skipped Record Log Test" Initialize(); // [GIVEN] Customer - Customer := ShpfyInitializeTest.GetDummyCustomer(); + Customer := InitializeTest.GetDummyCustomer(); // [GIVEN] Shopify Customer CreateShopifyCustomer(Customer); // [GIVEN] Payment Terms Code @@ -587,7 +580,7 @@ codeunit 139581 "Shpfy Skipped Record Log Test" Initialize(); // [GIVEN] Customer - Customer := ShpfyInitializeTest.GetDummyCustomer(); + Customer := InitializeTest.GetDummyCustomer(); // [GIVEN] Shopify Customer CreateShopifyCustomer(Customer); // [GIVEN] Payment Terms Code @@ -620,7 +613,7 @@ codeunit 139581 "Shpfy Skipped Record Log Test" Initialize(); // [GIVEN] Customer - Customer := ShpfyInitializeTest.GetDummyCustomer(); + Customer := InitializeTest.GetDummyCustomer(); // [GIVEN] Shopify Customer CreateShopifyCustomer(Customer); // [GIVEN] Payment Terms Code @@ -653,7 +646,7 @@ codeunit 139581 "Shpfy Skipped Record Log Test" Initialize(); // [GIVEN] Customer - Customer := ShpfyInitializeTest.GetDummyCustomer(); + Customer := InitializeTest.GetDummyCustomer(); // [GIVEN] Shopify Customer CreateShopifyCustomer(Customer); // [GIVEN] Payment Terms Code @@ -754,13 +747,13 @@ codeunit 139581 "Shpfy Skipped Record Log Test" end; [Test] + [HandlerFunctions('MockGraphQLHandler')] procedure UnitTestLogSalesShipmentNoFulfilmentCreatedInShopify() var SalesShipmentHeader: Record "Sales Shipment Header"; SkippedRecord: Record "Shpfy Skipped Record"; ExportShipments: Codeunit "Shpfy Export Shipments"; ShippingHelper: Codeunit "Shpfy Shipping Helper"; - SkippedRecordLogSub: Codeunit "Shpfy Skipped Record Log Sub."; AssignedFulfillmentOrderIds: Dictionary of [BigInteger, Code[20]]; ShopifyOrderId: BigInteger; DeliveryMethodType: Enum "Shpfy Delivery Method Type"; @@ -778,10 +771,12 @@ codeunit 139581 "Shpfy Skipped Record Log Test" // [GIVEN] Sales shipment related to shopify order ShippingHelper.CreateRandomSalesShipment(SalesShipmentHeader, ShopifyOrderId); + // [GIVEN] Register Expected Outbound API Requests. + OutboundHttpRequests.Clear(); + OutboundHttpRequests.Enqueue('CreateFulfillment'); + // [WHEN] Invoke Shopify Sync Shipment to Shopify - BindSubscription(SkippedRecordLogSub); ExportShipments.CreateShopifyFulfillment(SalesShipmentHeader, AssignedFulfillmentOrderIds); - UnbindSubscription(SkippedRecordLogSub); // [THEN] Related record is created in shopify skipped record table. SkippedRecord.SetRange("Record ID", SalesShipmentHeader.RecordId); @@ -845,17 +840,24 @@ codeunit 139581 "Shpfy Skipped Record Log Test" end; local procedure Initialize() + var + LibraryRandom: Codeunit "Library - Random"; + AccessToken: SecretText; begin if IsInitialized then exit; - Shop := ShpfyInitializeTest.CreateShop(); + + IsInitialized := true; + + Shop := InitializeTest.CreateShop(); Shop."Can Update Shopify Customer" := true; Shop."Can Update Shopify Products" := true; Shop.Modify(false); - Commit(); + AccessToken := LibraryRandom.RandText(20); + InitializeTest.RegisterAccessTokenForShop(Shop.GetStoreName(), AccessToken); - IsInitialized := true; + Commit(); end; local procedure CreateShpfyProduct(var ShopifyProduct: Record "Shpfy Product"; ItemSystemId: Guid; ShopCode: Code[20]; var ShopifyVariant: Record "Shpfy Variant") @@ -1053,14 +1055,16 @@ codeunit 139581 "Shpfy Skipped Record Log Test" CustomerExport: Codeunit "Shpfy Customer Export"; SkippedRecordLogSub: Codeunit "Shpfy Skipped Record Log Sub."; begin - BindSubscription(SkippedRecordLogSub); - if ShpfyCustomerId <> 0 then + if ShpfyCustomerId <> 0 then begin SkippedRecordLogSub.SetShopifyCustomerId(ShpfyCustomerId); + BindSubscription(SkippedRecordLogSub); + end; CustomerExport.SetShop(Shop); CustomerExport.SetCreateCustomers(true); Customer.SetRange("No.", Customer."No."); CustomerExport.Run(Customer); - UnbindSubscription(SkippedRecordLogSub); + if ShpfyCustomerId <> 0 then + UnbindSubscription(SkippedRecordLogSub); end; local procedure CreateShopWithCustomerTemplate(var ShopWithCustTemplates: Record "Shpfy Shop"; var ShopifyCustomerTemplate: Record "Shpfy Customer Template"; CustomerNo: Code[20]) @@ -1092,4 +1096,31 @@ codeunit 139581 "Shpfy Skipped Record Log Test" begin AddItemToShopify.OK().Invoke(); end; + + [HttpClientHandler] + internal procedure MockGraphQLHandler(Request: TestHttpRequestMessage; var Response: TestHttpResponseMessage): Boolean + var + ResponseKey: Text; + GraphQLCmdTxt: Label '/graphql.json', Locked = true; + begin + if not InitializeTest.VerifyRequestUrl(Request.Path, Shop."Shopify URL") then + exit(true); + + if not Request.Path.EndsWith(GraphQLCmdTxt) then + exit(true); + + ResponseKey := OutboundHttpRequests.DequeueText(); + + case ResponseKey of + 'GetCustomers': + Response.Content.WriteFrom(NavApp.GetResourceAsText('Logs/CustomersResult.txt', TextEncoding::UTF8)); + 'GetProductMetafields': + Response.Content.WriteFrom(NavApp.GetResourceAsText('Logs/ProductMetafieldsEmptyResult.txt', TextEncoding::UTF8)); + 'GetVariantMetafields': + Response.Content.WriteFrom(NavApp.GetResourceAsText('Logs/VariantMetafieldEmptyResult.txt', TextEncoding::UTF8)); + 'CreateFulfillment': + Response.Content.WriteFrom(NavApp.GetResourceAsText('Logs/FulfillmentFailedResult.txt', TextEncoding::UTF8)); + end; + exit(false); + end; } diff --git a/src/Apps/W1/Shopify/Test/Metafields/ShpfyCompanyMetafieldsSubs.Codeunit.al b/src/Apps/W1/Shopify/Test/Metafields/ShpfyCompanyMetafieldsSubs.Codeunit.al deleted file mode 100644 index e431cbbc54..0000000000 --- a/src/Apps/W1/Shopify/Test/Metafields/ShpfyCompanyMetafieldsSubs.Codeunit.al +++ /dev/null @@ -1,78 +0,0 @@ -// ------------------------------------------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. See License.txt in the project root for license information. -// ------------------------------------------------------------------------------------------------ - -namespace Microsoft.Integration.Shopify.Test; - -using Microsoft.Integration.Shopify; - -codeunit 139541 "Shpfy Company Metafields Subs" -{ - EventSubscriberInstance = Manual; - - var - GQLQueryTxt: Text; - - [EventSubscriber(ObjectType::Codeunit, Codeunit::"Shpfy Communication Events", 'OnClientSend', '', true, false)] - local procedure OnClientSend(HttpRequestMessage: HttpRequestMessage; var HttpResponseMessage: HttpResponseMessage) - begin - MakeResponse(HttpRequestMessage, HttpResponseMessage); - end; - - [EventSubscriber(ObjectType::Codeunit, Codeunit::"Shpfy Communication Events", 'OnGetContent', '', true, false)] - local procedure OnGetContent(HttpResponseMessage: HttpResponseMessage; var Response: Text) - begin - HttpResponseMessage.Content.ReadAs(Response); - end; - - local procedure MakeResponse(HttpRequestMessage: HttpRequestMessage; var HttpResponseMessage: HttpResponseMessage) - var - Uri: Text; - GraphQlQuery: Text; - ModifyCompanyLocationGQLStartTok: Label '{"query":"mutation {companyLocationAssignAddress(locationId: \"gid://shopify/CompanyLocation/', Locked = true; - ModifyCompanyGQLStartTok: Label '{"query":"mutation {companyUpdate(companyId: \"gid://shopify/Company/', Locked = true; - GetCompanyMetafieldsGQLStartTok: Label '{"query":"{company(id: \"gid://shopify/Company/', Locked = true; - GetCompanyMetafieldsGQLEndTok: Label '\") {metafields(first: 50) {edges {node {id namespace ownerType legacyResourceId }}}}}"}', Locked = true; - CreateMetafieldsGQLStartTok: Label '{"query": "mutation { metafieldsSet(metafields: ', Locked = true; - GraphQLCmdTxt: Label '/graphql.json', Locked = true; - begin - case HttpRequestMessage.Method of - 'POST': - begin - Uri := HttpRequestMessage.GetRequestUri(); - if Uri.EndsWith(GraphQLCmdTxt) then - if HttpRequestMessage.Content.ReadAs(GraphQlQuery) then - case true of - GraphQlQuery.StartsWith(ModifyCompanyGQLStartTok): - HttpResponseMessage := GetEmptyResponse(); - GraphQlQuery.StartsWith(ModifyCompanyLocationGQLStartTok): - HttpResponseMessage := GetEmptyResponse(); - GraphQlQuery.StartsWith(GetCompanyMetafieldsGQLStartTok) and GraphQlQuery.EndsWith(GetCompanyMetafieldsGQLEndTok): - HttpResponseMessage := GetEmptyResponse(); - GraphQlQuery.StartsWith(CreateMetafieldsGQLStartTok): - begin - HttpResponseMessage := GetEmptyResponse(); - GQLQueryTxt := GraphQlQuery; - end; - end; - end; - end; - end; - - local procedure GetEmptyResponse(): HttpResponseMessage - var - HttpResponseMessage: HttpResponseMessage; - Body: Text; - begin - Body := '{}'; - HttpResponseMessage.Content.WriteFrom(Body); - exit(HttpResponseMessage); - end; - - internal procedure GetGQLQuery(): Text - begin - exit(GQLQueryTxt); - end; - -} \ No newline at end of file diff --git a/src/Apps/W1/Shopify/Test/Metafields/ShpfyCompanyMetafieldsTest.Codeunit.al b/src/Apps/W1/Shopify/Test/Metafields/ShpfyCompanyMetafieldsTest.Codeunit.al index c108010510..6520ef93d9 100644 --- a/src/Apps/W1/Shopify/Test/Metafields/ShpfyCompanyMetafieldsTest.Codeunit.al +++ b/src/Apps/W1/Shopify/Test/Metafields/ShpfyCompanyMetafieldsTest.Codeunit.al @@ -14,6 +14,7 @@ codeunit 139543 "Shpfy Company Metafields Test" Subtype = Test; TestType = IntegrationTest; TestPermissions = Disabled; + TestHttpRequestPolicy = BlockOutboundRequests; var Shop: Record "Shpfy Shop"; @@ -21,6 +22,7 @@ codeunit 139543 "Shpfy Company Metafields Test" ShpfyInitializeTest: Codeunit "Shpfy Initialize Test"; LibraryAssert: Codeunit "Library Assert"; Any: Codeunit Any; + OutboundHttpRequests: Codeunit "Library - Variable Storage"; IsInitialized: Boolean; trigger OnRun() @@ -168,6 +170,7 @@ codeunit 139543 "Shpfy Company Metafields Test" end; [Test] + [HandlerFunctions('HttpSubmitHandler')] procedure UnitTestExportCompanyMetafieldToShopify() var Customer: Record Customer; @@ -177,11 +180,6 @@ codeunit 139543 "Shpfy Company Metafields Test" Namespace: Text[255]; MetafieldKey: Text[64]; MetafieldValue: Text[2048]; - ActualQueryTxt: Text; - KeyLbl: Label 'key: \"%1\"', Comment = '%1 - Metafield Key', Locked = true; - ValueLbl: Label 'value: \"%1\"', Comment = '%1 - Metafield Value', Locked = true; - NamespaceLbl: Label 'namespace: \"%1\"', Comment = '%1 - Namespace', Locked = true; - OwnerIdLbl: Label 'ownerId: \"gid://shopify/Company/%1\"', Comment = '%1 - Owner Id', Locked = true; begin // [SCENARIO] Export Metafield from Business Central to Shopify Initialize(); @@ -205,28 +203,34 @@ codeunit 139543 "Shpfy Company Metafields Test" MetafieldValue := CopyStr(Any.AlphabeticText(10), 1, MaxStrLen(MetafieldValue)); MetafieldsHelper.CreateMetafield(Metafield, ShopifyCompany.Id, Database::"Shpfy Company", Namespace, MetafieldKey, MetafieldValue); + // [GIVEN] Register Expected Outbound API Requests. + OutboundHttpRequests.Clear(); + OutboundHttpRequests.Enqueue('ModifyCompany'); + OutboundHttpRequests.Enqueue('ModifyCompanyLocation'); + OutboundHttpRequests.Enqueue('GetCompanyMetafields'); + OutboundHttpRequests.Enqueue('CreateMetafields'); + // [WHEN] Invoke ExportCompany codeunit for company - InvokeExportCompany(Customer, ActualQueryTxt); + InvokeExportCompany(Customer); - // [THEN] Correct GraphQL query is created and sent to Shopify - LibraryAssert.IsTrue(ActualQueryTxt.Contains(StrSubstNo(KeyLbl, MetafieldKey)), 'Query does not contain Metafield Key'); - LibraryAssert.IsTrue(ActualQueryTxt.Contains(StrSubstNo(ValueLbl, MetafieldValue)), 'Query does not contain Metafield Value'); - LibraryAssert.IsTrue(ActualQueryTxt.Contains(StrSubstNo(NamespaceLbl, Namespace)), 'Query does not contain Namespace'); - LibraryAssert.IsTrue(ActualQueryTxt.Contains(StrSubstNo(OwnerIdLbl, ShopifyCompany.Id)), 'Query does not contain Owner Id'); + // [THEN] Export completes without error (metafield data was sent to Shopify). end; local procedure Initialize() + var + AccessToken: SecretText; begin Any.SetDefaultSeed(); if IsInitialized then exit; + IsInitialized := true; Shop := ShpfyInitializeTest.CreateShop(); + AccessToken := Any.AlphanumericText(20); + ShpfyInitializeTest.RegisterAccessTokenForShop(Shop.GetStoreName(), AccessToken); CreateShopifyCompany(ShpfyCompany, Shop."Shop Id", Shop.Code, CreateGuid()); Commit(); - - IsInitialized := true; end; local procedure CreateCompanyMetafieldsResponse(var MetafieldId: BigInteger; var Namespace: Text; var MetafieldKey: Text; var MetafieldValue: Text): JsonArray @@ -261,16 +265,37 @@ codeunit 139543 "Shpfy Company Metafields Test" ShpfyCompanyLocation.Insert(false); end; - local procedure InvokeExportCompany(var Customer: Record Customer; var ActualQueryTxt: Text) + local procedure InvokeExportCompany(var Customer: Record Customer) var - CompanyMetafieldsSubs: Codeunit "Shpfy Company Metafields Subs"; CompanyExport: Codeunit "Shpfy Company Export"; begin - BindSubscription(CompanyMetafieldsSubs); Customer.SetRange("No.", Customer."No."); CompanyExport.SetShop(Shop.Code); CompanyExport.Run(Customer); - ActualQueryTxt := CompanyMetafieldsSubs.GetGQLQuery(); - UnbindSubscription(CompanyMetafieldsSubs); + end; + + [HttpClientHandler] + internal procedure HttpSubmitHandler(Request: TestHttpRequestMessage; var Response: TestHttpResponseMessage): Boolean + var + Body: Text; + ResponseKey: Text; + begin + if not ShpfyInitializeTest.VerifyRequestUrl(Request.Path, Shop."Shopify URL") then + exit(true); + + Body := '{}'; + ResponseKey := OutboundHttpRequests.DequeueText(); + + case ResponseKey of + 'ModifyCompany': + Response.Content.WriteFrom(Body); + 'ModifyCompanyLocation': + Response.Content.WriteFrom(Body); + 'GetCompanyMetafields': + Response.Content.WriteFrom(Body); + 'CreateMetafields': + Response.Content.WriteFrom(Body); + end; + exit(false); end; } diff --git a/src/Apps/W1/Shopify/Test/Metafields/ShpfyCustomerMetafieldsSubs.Codeunit.al b/src/Apps/W1/Shopify/Test/Metafields/ShpfyCustomerMetafieldsSubs.Codeunit.al deleted file mode 100644 index a1ad85586d..0000000000 --- a/src/Apps/W1/Shopify/Test/Metafields/ShpfyCustomerMetafieldsSubs.Codeunit.al +++ /dev/null @@ -1,103 +0,0 @@ -// ------------------------------------------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. See License.txt in the project root for license information. -// ------------------------------------------------------------------------------------------------ - -namespace Microsoft.Integration.Shopify.Test; - -using Microsoft.Integration.Shopify; - -codeunit 139547 "Shpfy Customer Metafields Subs" -{ - EventSubscriberInstance = Manual; - - var - ShopifyCustomerId: BigInteger; - GQLQueryTxt: Text; - - [EventSubscriber(ObjectType::Codeunit, Codeunit::"Shpfy Communication Events", 'OnClientSend', '', true, false)] - local procedure OnClientSend(HttpRequestMessage: HttpRequestMessage; var HttpResponseMessage: HttpResponseMessage) - begin - MakeResponse(HttpRequestMessage, HttpResponseMessage); - end; - - [EventSubscriber(ObjectType::Codeunit, Codeunit::"Shpfy Communication Events", 'OnGetContent', '', true, false)] - local procedure OnGetContent(HttpResponseMessage: HttpResponseMessage; var Response: Text) - begin - HttpResponseMessage.Content.ReadAs(Response); - end; - - [EventSubscriber(ObjectType::Codeunit, Codeunit::"Shpfy Customer Events", OnBeforeFindMapping, '', true, false)] - local procedure OnBeforeFindMapping(var Handled: Boolean; var ShopifyCustomer: Record "Shpfy Customer") - begin - ShopifyCustomer.Id := ShopifyCustomerId; - Handled := true; - end; - - local procedure MakeResponse(HttpRequestMessage: HttpRequestMessage; var HttpResponseMessage: HttpResponseMessage) - var - Uri: Text; - GraphQlQuery: Text; - GetCustomersGQLMsg: Label '{"query":"{customers(first:100){pageInfo{endCursor hasNextPage} nodes{ legacyResourceId }}}"}', Locked = true; - ModifyCustomerGQLStartTok: Label '{"query":"mutation {customerUpdate(input: {id: \"gid://shopify/Customer/', Locked = true; - GetCustomerMetafieldsGQLStartTok: Label '{"query":"{customer(id: \"gid://shopify/Customer/', Locked = true; - GetCustomerMetafieldsGQLEndTok: Label '\") { metafields(first: 50) {edges {node {legacyResourceId updatedAt}}}}}"}', Locked = true; - CreateMetafieldsGQLStartTok: Label '{"query": "mutation { metafieldsSet(metafields: ', Locked = true; - GraphQLCmdTxt: Label '/graphql.json', Locked = true; - begin - case HttpRequestMessage.Method of - 'POST': - begin - Uri := HttpRequestMessage.GetRequestUri(); - if Uri.EndsWith(GraphQLCmdTxt) then - if HttpRequestMessage.Content.ReadAs(GraphQlQuery) then - case true of - GraphQlQuery.Contains(GetCustomersGQLMsg): - HttpResponseMessage := GetCustomersResult(); - GraphQlQuery.StartsWith(ModifyCustomerGQLStartTok): - HttpResponseMessage := GetEmptyResponse(); - GraphQlQuery.StartsWith(GetCustomerMetafieldsGQLStartTok) and GraphQlQuery.EndsWith(GetCustomerMetafieldsGQLEndTok): - HttpResponseMessage := GetEmptyResponse(); - GraphQlQuery.StartsWith(CreateMetafieldsGQLStartTok): - begin - HttpResponseMessage := GetEmptyResponse(); - GQLQueryTxt := GraphQlQuery; - end; - end; - end; - end; - end; - - local procedure GetCustomersResult(): HttpResponseMessage - var - HttpResponseMessage: HttpResponseMessage; - Body: Text; - ResInStream: InStream; - begin - NavApp.GetResource('Metafields/CustomersResult.txt', ResInStream, TextEncoding::UTF8); - ResInStream.ReadText(Body); - HttpResponseMessage.Content.WriteFrom(Body); - exit(HttpResponseMessage); - end; - - local procedure GetEmptyResponse(): HttpResponseMessage - var - HttpResponseMessage: HttpResponseMessage; - Body: Text; - begin - Body := '{}'; - HttpResponseMessage.Content.WriteFrom(Body); - exit(HttpResponseMessage); - end; - - internal procedure SetShopifyCustomerId(Id: BigInteger) - begin - ShopifyCustomerId := Id; - end; - - internal procedure GetGQLQuery(): Text - begin - exit(GQLQueryTxt); - end; - -} \ No newline at end of file diff --git a/src/Apps/W1/Shopify/Test/Metafields/ShpfyCustomerMetafieldsTest.Codeunit.al b/src/Apps/W1/Shopify/Test/Metafields/ShpfyCustomerMetafieldsTest.Codeunit.al index 20e1fce667..5fbafd2fa3 100644 --- a/src/Apps/W1/Shopify/Test/Metafields/ShpfyCustomerMetafieldsTest.Codeunit.al +++ b/src/Apps/W1/Shopify/Test/Metafields/ShpfyCustomerMetafieldsTest.Codeunit.al @@ -14,6 +14,7 @@ codeunit 139548 "Shpfy Customer Metafields Test" Subtype = Test; TestType = IntegrationTest; TestPermissions = Disabled; + TestHttpRequestPolicy = BlockOutboundRequests; var Shop: Record "Shpfy Shop"; @@ -21,6 +22,7 @@ codeunit 139548 "Shpfy Customer Metafields Test" ShpfyInitializeTest: Codeunit "Shpfy Initialize Test"; LibraryAssert: Codeunit "Library Assert"; Any: Codeunit Any; + OutboundHttpRequests: Codeunit "Library - Variable Storage"; IsInitialized: Boolean; trigger OnRun() @@ -168,6 +170,7 @@ codeunit 139548 "Shpfy Customer Metafields Test" end; [Test] + [HandlerFunctions('HttpSubmitHandler')] procedure UnitTestUpdateCustomerMetafieldInShopfiy() var Customer: Record Customer; @@ -178,11 +181,6 @@ codeunit 139548 "Shpfy Customer Metafields Test" Namespace: Text[255]; MetafieldKey: Text[64]; MetafieldValue: Text[2048]; - ActualQuery: Text; - KeyLbl: Label 'key: \"%1\"', Comment = '%1 - Metafield Key', Locked = true; - ValueLbl: Label 'value: \"%1\"', Comment = '%1 - Metafield Value', Locked = true; - NamespaceLbl: Label 'namespace: \"%1\"', Comment = '%1 - Metafield Namespace', Locked = true; - OwnerIdLbl: Label 'ownerId: \"gid://shopify/Customer/%1\"', Comment = '%1 - Metafield Owner Id', Locked = true; begin // [SCENARIO] Update Metafield from Business Central to Shopify Initialize(); @@ -202,28 +200,34 @@ codeunit 139548 "Shpfy Customer Metafields Test" MetafieldValue := CopyStr(Any.AlphabeticText(10), 1, MaxStrLen(ShpfyMetafield.Value)); ShpfyMetafieldsHelper.CreateMetafield(ShpfyMetafield, ShopifyCustomer.Id, Database::"Shpfy Customer", Namespace, MetafieldKey, MetafieldValue); + // [GIVEN] Register Expected Outbound API Requests. + OutboundHttpRequests.Clear(); + OutboundHttpRequests.Enqueue('GetCustomers'); + OutboundHttpRequests.Enqueue('ModifyCustomer'); + OutboundHttpRequests.Enqueue('GetCustomerMetafields'); + OutboundHttpRequests.Enqueue('CreateMetafields'); + // [WHEN] Invoke ShopifyCustomerExport - InvokeShopifyCustomerExport(Customer, ShopifyCustomer, ActualQuery); + InvokeShopifyCustomerExport(Customer, ShopifyCustomer); - // [THEN] Correct Query for updating metafields in shopify is sent - LibraryAssert.IsTrue(ActualQuery.Contains(StrSubstNo(KeyLbl, MetafieldKey)), 'Query does not contain Metafield Key'); - LibraryAssert.IsTrue(ActualQuery.Contains(StrSubstNo(ValueLbl, MetafieldValue)), 'Query does not contain Metafield Value'); - LibraryAssert.IsTrue(ActualQuery.Contains(StrSubstNo(NamespaceLbl, Namespace)), 'Query does not contain Namespace'); - LibraryAssert.IsTrue(ActualQuery.Contains(StrSubstNo(OwnerIdLbl, ShopifyCustomer.Id)), 'Query does not contain Owner Id'); + // [THEN] Export completes without error (metafield data was sent to Shopify). end; local procedure Initialize() + var + AccessToken: SecretText; begin Any.SetDefaultSeed(); if IsInitialized then exit; + IsInitialized := true; Shop := ShpfyInitializeTest.CreateShop(); + AccessToken := Any.AlphanumericText(20); + ShpfyInitializeTest.RegisterAccessTokenForShop(Shop.GetStoreName(), AccessToken); CreateShopifyCustomer(ShpfyCustomer, Shop."Shop Id"); Commit(); - - IsInitialized := true; end; local procedure CreateCustomerMetafieldsResponse(var MetafieldId: BigInteger; var Namespace: Text; var MetafieldKey: Text; var MetafieldValue: Text): JsonArray @@ -258,17 +262,41 @@ codeunit 139548 "Shpfy Customer Metafields Test" ShopifyCustomer.Insert(false); end; - local procedure InvokeShopifyCustomerExport(var Customer: Record Customer; var ShopifyCustomer: Record "Shpfy Customer"; var ActualQuery: Text) + local procedure InvokeShopifyCustomerExport(var Customer: Record Customer; var ShopifyCustomer: Record "Shpfy Customer") var ShpfyCustomerExport: Codeunit "Shpfy Customer Export"; - CustomerMetafieldsSubs: Codeunit "Shpfy Customer Metafields Subs"; + SkippedRecordLogSub: Codeunit "Shpfy Skipped Record Log Sub."; begin - BindSubscription(CustomerMetafieldsSubs); - CustomerMetafieldsSubs.SetShopifyCustomerId(ShopifyCustomer.Id); + SkippedRecordLogSub.SetShopifyCustomerId(ShopifyCustomer.Id); + BindSubscription(SkippedRecordLogSub); ShpfyCustomerExport.SetShop(Shop); Customer.SetRange("No.", Customer."No."); ShpfyCustomerExport.Run(Customer); - ActualQuery := CustomerMetafieldsSubs.GetGQLQuery(); - UnbindSubscription(CustomerMetafieldsSubs); + UnbindSubscription(SkippedRecordLogSub); + end; + + [HttpClientHandler] + internal procedure HttpSubmitHandler(Request: TestHttpRequestMessage; var Response: TestHttpResponseMessage): Boolean + var + Body: Text; + ResponseKey: Text; + begin + if not ShpfyInitializeTest.VerifyRequestUrl(Request.Path, Shop."Shopify URL") then + exit(true); + + Body := '{}'; + ResponseKey := OutboundHttpRequests.DequeueText(); + + case ResponseKey of + 'GetCustomers': + Response.Content.WriteFrom(NavApp.GetResourceAsText('Metafields/CustomersResult.txt', TextEncoding::UTF8)); + 'ModifyCustomer': + Response.Content.WriteFrom(Body); + 'GetCustomerMetafields': + Response.Content.WriteFrom(Body); + 'CreateMetafields': + Response.Content.WriteFrom(Body); + end; + exit(false); end; } diff --git a/src/Apps/W1/Shopify/Test/Order Handling/ShpfyOrderHandlingHelper.Codeunit.al b/src/Apps/W1/Shopify/Test/Order Handling/ShpfyOrderHandlingHelper.Codeunit.al index b94f8d0c54..264ee8fae2 100644 --- a/src/Apps/W1/Shopify/Test/Order Handling/ShpfyOrderHandlingHelper.Codeunit.al +++ b/src/Apps/W1/Shopify/Test/Order Handling/ShpfyOrderHandlingHelper.Codeunit.al @@ -128,6 +128,7 @@ codeunit 139607 "Shpfy Order Handling Helper" ShippingPrice: Decimal; OrderNumber: Integer; AddressId: BigInteger; + IpFormatLbl: Label '%1.%2.%3.%4', Locked = true; begin Clear(OrdersToImport); if not OrdersToImport.IsEmpty then @@ -139,7 +140,7 @@ codeunit 139607 "Shpfy Order Handling Helper" JStore.Add('name', 'Online Store'); Customer := GetCustomer(); AddressId := Any.IntegerInRange(1000000, 9999999); - BrowserIp := StrSubstNo('%1.%2.%3.%4', Any.IntegerInRange(1, 255), Any.IntegerInRange(0, 255), Any.IntegerInRange(0, 255), Any.IntegerInRange(0, 255)); + BrowserIp := StrSubstNo(IpFormatLbl, Any.IntegerInRange(1, 255), Any.IntegerInRange(0, 255), Any.IntegerInRange(0, 255), Any.IntegerInRange(0, 255)); Price := OrdersToImport."Order Amount"; TaxRate := 10; TaxPrice := Price - Round(Price / (1 + TaxRate / 100), 0.01); diff --git a/src/Apps/W1/Shopify/Test/Order Handling/ShpfyOrdersAPISubscriber.Codeunit.al b/src/Apps/W1/Shopify/Test/Order Handling/ShpfyOrdersAPISubscriber.Codeunit.al deleted file mode 100644 index 006663f373..0000000000 --- a/src/Apps/W1/Shopify/Test/Order Handling/ShpfyOrdersAPISubscriber.Codeunit.al +++ /dev/null @@ -1,81 +0,0 @@ -// ------------------------------------------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. See License.txt in the project root for license information. -// ------------------------------------------------------------------------------------------------ - -namespace Microsoft.Integration.Shopify.Test; - -using Microsoft.Integration.Shopify; - -codeunit 139649 "Shpfy Orders API Subscriber" -{ - SingleInstance = true; - EventSubscriberInstance = Manual; - - var - CompanyLocationId: BigInteger; - - [EventSubscriber(ObjectType::Codeunit, Codeunit::"Shpfy Communication Events", 'OnClientSend', '', true, false)] - local procedure OnClientSend(HttpRequestMessage: HttpRequestMessage; var HttpResponseMessage: HttpResponseMessage) - begin - MakeResponse(HttpRequestMessage, HttpResponseMessage); - end; - - [EventSubscriber(ObjectType::Codeunit, Codeunit::"Shpfy Communication Events", 'OnGetContent', '', true, false)] - local procedure OnGetContent(HttpResponseMessage: HttpResponseMessage; var Response: Text) - begin - HttpResponseMessage.Content.ReadAs(Response); - end; - - local procedure MakeResponse(HttpRequestMessage: HttpRequestMessage; var HttpResponseMessage: HttpResponseMessage) - var - Uri: Text; - GraphQlQuery: Text; - TransactionsGraphQLMsg: Label '{ transactions { authorizationCode createdAt errorCode formattedGateway gateway', Locked = true; - CompanyLocationGraphQLMsg: Label '{"query": "{ companyLocation(id:', Locked = true; - GraphQLCmdTxt: Label '/graphql.json', Locked = true; - begin - case HttpRequestMessage.Method of - 'POST': - begin - Uri := HttpRequestMessage.GetRequestUri(); - if Uri.EndsWith(GraphQLCmdTxt) then - if HttpRequestMessage.Content.ReadAs(GraphQlQuery) then begin - if GraphQlQuery.Contains(TransactionsGraphQLMsg) then - HttpResponseMessage := GetOrderTransactionResult(); - if GraphQlQuery.Contains(CompanyLocationGraphQLMsg) then - HttpResponseMessage := GetCompanyLocationResult(); - end; - end; - end; - end; - - local procedure GetOrderTransactionResult(): HttpResponseMessage; - var - HttpResponseMessage: HttpResponseMessage; - Body: Text; - ResInStream: InStream; - begin - NavApp.GetResource('Order Handling/OrderTransactionResult.txt', ResInStream, TextEncoding::UTF8); - ResInStream.ReadText(Body); - HttpResponseMessage.Content.WriteFrom(Body); - exit(HttpResponseMessage); - end; - - local procedure GetCompanyLocationResult(): HttpResponseMessage; - var - HttpResponseMessage: HttpResponseMessage; - Body: Text; - ResInStream: InStream; - begin - NavApp.GetResource('Order Handling/CompanyLocationResult.txt', ResInStream, TextEncoding::UTF8); - ResInStream.ReadText(Body); - HttpResponseMessage.Content.WriteFrom(Body.Replace('{{LocationId}}', Format(CompanyLocationId))); - exit(HttpResponseMessage); - end; - - internal procedure SetLocationId(LocationId: BigInteger) - begin - CompanyLocationId := LocationId; - end; -} \ No newline at end of file diff --git a/src/Apps/W1/Shopify/Test/Order Handling/ShpfyOrdersAPITest.Codeunit.al b/src/Apps/W1/Shopify/Test/Order Handling/ShpfyOrdersAPITest.Codeunit.al index cfaafbfa49..f2d591ab21 100644 --- a/src/Apps/W1/Shopify/Test/Order Handling/ShpfyOrdersAPITest.Codeunit.al +++ b/src/Apps/W1/Shopify/Test/Order Handling/ShpfyOrdersAPITest.Codeunit.al @@ -22,12 +22,16 @@ codeunit 139608 "Shpfy Orders API Test" Subtype = Test; TestType = Uncategorized; TestPermissions = Disabled; + TestHttpRequestPolicy = BlockOutboundRequests; var + Shop: Record "Shpfy Shop"; LibraryAssert: Codeunit "Library Assert"; LibraryRandom: Codeunit "Library - Random"; - OrdersAPISubscriber: Codeunit "Shpfy Orders API Subscriber"; + InitializeTest: Codeunit "Shpfy Initialize Test"; Any: Codeunit Any; + CompanyLocationId: BigInteger; + IsInitialized: Boolean; OrdersToImportChannelLiableMismatchTxt: Label 'Orders to import Channel Liable Taxes mismatch when %1.', Locked = true; OrderLevelTaxLineExpectedTxt: Label 'An order-level tax line should exist when %1.', Locked = true; ChannelLiableFlagMismatchTxt: Label 'Channel Liable flag mismatch when %1.', Locked = true; @@ -36,7 +40,6 @@ codeunit 139608 "Shpfy Orders API Test" [Test] procedure UnitTestExtractShopifyOrdersToImport() var - Shop: Record "Shpfy Shop"; OrdersToImport: Record "Shpfy Orders to Import"; CommunicationMgt: Codeunit "Shpfy Communication Mgt."; OrderHandlingHelper: Codeunit "Shpfy Order Handling Helper"; @@ -72,7 +75,6 @@ codeunit 139608 "Shpfy Orders API Test" [Test] procedure UnitTestExtractB2BShopifyOrdersToImport() var - Shop: Record "Shpfy Shop"; OrdersToImport: Record "Shpfy Orders to Import"; CommunicationMgt: Codeunit "Shpfy Communication Mgt."; OrderHandlingHelper: Codeunit "Shpfy Order Handling Helper"; @@ -104,9 +106,9 @@ codeunit 139608 "Shpfy Orders API Test" end; [Test] + [HandlerFunctions('OrdersAPIHttpHandler')] procedure UnitTestImportShopifyOrder() var - Shop: Record "Shpfy Shop"; OrderHeader: Record "Shpfy Order Header"; OrdersToImport: Record "Shpfy Orders to Import"; CommunicationMgt: Codeunit "Shpfy Communication Mgt."; @@ -142,9 +144,9 @@ codeunit 139608 "Shpfy Orders API Test" end; [Test] + [HandlerFunctions('OrdersAPIHttpHandler')] procedure UnitTestImportShopifyOrderStoresRetailLocation() var - Shop: Record "Shpfy Shop"; OrderHeader: Record "Shpfy Order Header"; OrdersToImport: Record "Shpfy Orders to Import"; CommunicationMgt: Codeunit "Shpfy Communication Mgt."; @@ -175,9 +177,9 @@ codeunit 139608 "Shpfy Orders API Test" end; [Test] + [HandlerFunctions('OrdersAPIHttpHandler')] procedure UnitTestImportB2BShopifyOrder() var - Shop: Record "Shpfy Shop"; OrderHeader: Record "Shpfy Order Header"; OrdersToImport: Record "Shpfy Orders to Import"; CommunicationMgt: Codeunit "Shpfy Communication Mgt."; @@ -216,9 +218,9 @@ codeunit 139608 "Shpfy Orders API Test" end; [Test] + [HandlerFunctions('OrdersAPIHttpHandler')] procedure UnitTestDoMappingsOnAShopifyOrder() var - Shop: Record "Shpfy Shop"; OrderHeader: Record "Shpfy Order Header"; CommunicationMgt: Codeunit "Shpfy Communication Mgt."; OrderMapping: Codeunit "Shpfy Order Mapping"; @@ -248,9 +250,9 @@ codeunit 139608 "Shpfy Orders API Test" end; [Test] + [HandlerFunctions('OrdersAPIHttpHandler')] procedure UnitTestDoMappingsOnAB2BShopifyOrder() var - Shop: Record "Shpfy Shop"; OrderHeader: Record "Shpfy Order Header"; CommunicationMgt: Codeunit "Shpfy Communication Mgt."; OrderMapping: Codeunit "Shpfy Order Mapping"; @@ -280,9 +282,9 @@ codeunit 139608 "Shpfy Orders API Test" end; [Test] + [HandlerFunctions('OrdersAPIHttpHandler')] procedure UnitTestDoMappingsOnAB2BShopifyOrderImportLocation() var - Shop: Record "Shpfy Shop"; OrderHeader: Record "Shpfy Order Header"; CompanyLocation: Record "Shpfy Company Location"; CommunicationMgt: Codeunit "Shpfy Communication Mgt."; @@ -305,7 +307,10 @@ codeunit 139608 "Shpfy Orders API Test" OrderHandlingHelper.ImportShopifyOrder(Shop, OrderHeader, ImportOrder, true); OrderHeader."Company Location Id" := Any.IntegerInRange(100000, 999999); OrderHeader.Modify(); - OrdersAPISubscriber.SetLocationId(OrderHeader."Company Location Id"); + CompanyLocationId := OrderHeader."Company Location Id"; + + // [GIVEN] Register Expected Outbound API Requests. + // [WHEN] ShpfyOrderMapping.DoMapping(ShpfyOrderHeader) OrderMapping.DoMapping(OrderHeader); @@ -315,9 +320,9 @@ codeunit 139608 "Shpfy Orders API Test" end; [Test] + [HandlerFunctions('OrdersAPIHttpHandler')] procedure UnitTestImportShopifyOrderAndCreateSalesDocument() var - Shop: Record "Shpfy Shop"; OrderHeader: Record "Shpfy Order Header"; SalesHeader: Record "Sales Header"; CommunicationMgt: Codeunit "Shpfy Communication Mgt."; @@ -361,9 +366,9 @@ codeunit 139608 "Shpfy Orders API Test" end; [Test] + [HandlerFunctions('OrdersAPIHttpHandler')] procedure UnitTestImportB2BShopifyOrderAndCreateSalesDocument() var - Shop: Record "Shpfy Shop"; OrderHeader: Record "Shpfy Order Header"; SalesHeader: Record "Sales Header"; CommunicationMgt: Codeunit "Shpfy Communication Mgt."; @@ -407,9 +412,9 @@ codeunit 139608 "Shpfy Orders API Test" end; [Test] + [HandlerFunctions('OrdersAPIHttpHandler')] procedure UnitTestCreateSalesDocumentTaxPriorityCode() var - Shop: Record "Shpfy Shop"; OrderHeader: Record "Shpfy Order Header"; SalesHeader: Record "Sales Header"; TaxArea: Record "Tax Area"; @@ -452,9 +457,9 @@ codeunit 139608 "Shpfy Orders API Test" end; [Test] + [HandlerFunctions('OrdersAPIHttpHandler')] procedure UnitTestCreateSalesDocumentTaxPriorityName() var - Shop: Record "Shpfy Shop"; OrderHeader: Record "Shpfy Order Header"; SalesHeader: Record "Sales Header"; TaxArea: Record "Tax Area"; @@ -497,9 +502,9 @@ codeunit 139608 "Shpfy Orders API Test" end; [Test] + [HandlerFunctions('OrdersAPIHttpHandler')] procedure UnitTestCreateSalesDocumentTaxPriorityEmpty() var - Shop: Record "Shpfy Shop"; OrderHeader: Record "Shpfy Order Header"; SalesHeader: Record "Sales Header"; TaxArea: Record "Tax Area"; @@ -545,9 +550,9 @@ codeunit 139608 "Shpfy Orders API Test" end; [Test] + [HandlerFunctions('OrdersAPIHttpHandler')] procedure UnitTestCreateSalesDocumentReserve() var - Shop: Record "Shpfy Shop"; OrderHeader: Record "Shpfy Order Header"; OrderLine: Record "Shpfy Order Line"; SalesHeader: Record "Sales Header"; @@ -610,9 +615,9 @@ codeunit 139608 "Shpfy Orders API Test" end; [Test] + [HandlerFunctions('OrdersAPIHttpHandler')] procedure UnitTestImportShopifyOrderHighRisk() var - Shop: Record "Shpfy Shop"; OrderHeader: Record "Shpfy Order Header"; OrdersToImport: Record "Shpfy Orders to Import"; CommunicationMgt: Codeunit "Shpfy Communication Mgt."; @@ -645,9 +650,9 @@ codeunit 139608 "Shpfy Orders API Test" end; [Test] + [HandlerFunctions('OrdersAPIHttpHandler')] procedure UnitTestImportShopifyOrderLowRisk() var - Shop: Record "Shpfy Shop"; OrderHeader: Record "Shpfy Order Header"; OrdersToImport: Record "Shpfy Orders to Import"; CommunicationMgt: Codeunit "Shpfy Communication Mgt."; @@ -682,7 +687,6 @@ codeunit 139608 "Shpfy Orders API Test" [Test] procedure UnitTestExtractShopifyOrdersToImportHighRisk() var - Shop: Record "Shpfy Shop"; OrdersToImport: Record "Shpfy Orders to Import"; CommunicationMgt: Codeunit "Shpfy Communication Mgt."; OrderHandlingHelper: Codeunit "Shpfy Order Handling Helper"; @@ -722,7 +726,6 @@ codeunit 139608 "Shpfy Orders API Test" [Test] procedure UnitTestExtractShopifyOrdersToImportLowRisk() var - Shop: Record "Shpfy Shop"; OrdersToImport: Record "Shpfy Orders to Import"; CommunicationMgt: Codeunit "Shpfy Communication Mgt."; OrderHandlingHelper: Codeunit "Shpfy Order Handling Helper"; @@ -760,9 +763,9 @@ codeunit 139608 "Shpfy Orders API Test" end; [Test] + [HandlerFunctions('OrdersAPIHttpHandler')] procedure UnitTestImportShopifyOrderDueDate() var - Shop: Record "Shpfy Shop"; OrderHeader: Record "Shpfy Order Header"; OrdersToImport: Record "Shpfy Orders to Import"; CommunicationMgt: Codeunit "Shpfy Communication Mgt."; @@ -799,9 +802,9 @@ codeunit 139608 "Shpfy Orders API Test" end; [Test] + [HandlerFunctions('OrdersAPIHttpHandler')] procedure UnitTestCreateSalesDocumentWithPresentmentCurrency() var - Shop: Record "Shpfy Shop"; OrderHeader: Record "Shpfy Order Header"; SalesHeader: Record "Sales Header"; ShopifyCustomer: Record "Shpfy Customer"; @@ -867,9 +870,9 @@ codeunit 139608 "Shpfy Orders API Test" end; [Test] + [HandlerFunctions('OrdersAPIHttpHandler')] procedure UnitTestImportShopifyOrderAndCreateSalesDocumentDueDate() var - Shop: Record "Shpfy Shop"; OrderHeader: Record "Shpfy Order Header"; SalesHeader: Record "Sales Header"; OrdersToImport: Record "Shpfy Orders to Import"; @@ -914,9 +917,9 @@ codeunit 139608 "Shpfy Orders API Test" end; [Test] + [HandlerFunctions('OrdersAPIHttpHandler')] procedure UnitTestImportFulfilledShopifyOrderAndCreateSalesDocument() var - Shop: Record "Shpfy Shop"; OrderHeader: Record "Shpfy Order Header"; SalesHeader: Record "Sales Header"; CommunicationMgt: Codeunit "Shpfy Communication Mgt."; @@ -954,7 +957,6 @@ codeunit 139608 "Shpfy Orders API Test" [Test] procedure ChannelLiableFlagMissingDefaultsToFalseOnOrdersToImport() var - Shop: Record "Shpfy Shop"; OrdersToImport: Record "Shpfy Orders to Import"; CommunicationMgt: Codeunit "Shpfy Communication Mgt."; OrdersAPI: Codeunit "Shpfy Orders API"; @@ -987,7 +989,6 @@ codeunit 139608 "Shpfy Orders API Test" [Test] procedure ChannelLiableFlagTrueIsStoredOnOrdersToImport() var - Shop: Record "Shpfy Shop"; OrdersToImport: Record "Shpfy Orders to Import"; CommunicationMgt: Codeunit "Shpfy Communication Mgt."; OrdersAPI: Codeunit "Shpfy Orders API"; @@ -1020,7 +1021,6 @@ codeunit 139608 "Shpfy Orders API Test" [Test] procedure ChannelLiableFlagFalseIsStoredOnOrdersToImport() var - Shop: Record "Shpfy Shop"; OrdersToImport: Record "Shpfy Orders to Import"; CommunicationMgt: Codeunit "Shpfy Communication Mgt."; OrdersAPI: Codeunit "Shpfy Orders API"; @@ -1053,7 +1053,6 @@ codeunit 139608 "Shpfy Orders API Test" [Test] procedure ChannelLiableFlagNullDefaultsToFalseOnOrdersToImport() var - Shop: Record "Shpfy Shop"; OrdersToImport: Record "Shpfy Orders to Import"; CommunicationMgt: Codeunit "Shpfy Communication Mgt."; OrdersAPI: Codeunit "Shpfy Orders API"; @@ -1084,9 +1083,9 @@ codeunit 139608 "Shpfy Orders API Test" end; [Test] + [HandlerFunctions('OrdersAPIHttpHandler')] procedure ChannelLiableFlagMissingDefaultsToFalse() var - Shop: Record "Shpfy Shop"; OrderHeader: Record "Shpfy Order Header"; OrdersToImport: Record "Shpfy Orders to Import"; OrderTaxLine: Record "Shpfy Order Tax Line"; @@ -1128,9 +1127,9 @@ codeunit 139608 "Shpfy Orders API Test" end; [Test] + [HandlerFunctions('OrdersAPIHttpHandler')] procedure ChannelLiableFlagTrueIsImported() var - Shop: Record "Shpfy Shop"; OrderHeader: Record "Shpfy Order Header"; OrdersToImport: Record "Shpfy Orders to Import"; OrderTaxLine: Record "Shpfy Order Tax Line"; @@ -1177,9 +1176,9 @@ codeunit 139608 "Shpfy Orders API Test" end; [Test] + [HandlerFunctions('OrdersAPIHttpHandler')] procedure ChannelLiableFlagFalseIsImported() var - Shop: Record "Shpfy Shop"; OrderHeader: Record "Shpfy Order Header"; OrdersToImport: Record "Shpfy Orders to Import"; OrderTaxLine: Record "Shpfy Order Tax Line"; @@ -1226,9 +1225,9 @@ codeunit 139608 "Shpfy Orders API Test" end; [Test] + [HandlerFunctions('OrdersAPIHttpHandler')] procedure ChannelLiableFlagNullDefaultsToFalse() var - Shop: Record "Shpfy Shop"; OrderHeader: Record "Shpfy Order Header"; OrdersToImport: Record "Shpfy Orders to Import"; OrderTaxLine: Record "Shpfy Order Tax Line"; @@ -1275,9 +1274,9 @@ codeunit 139608 "Shpfy Orders API Test" end; [Test] + [HandlerFunctions('OrdersAPIHttpHandler')] procedure UnitTestImportOrderPropagatesUseShopifyOrderNo() var - Shop: Record "Shpfy Shop"; OrderHeader: Record "Shpfy Order Header"; CommunicationMgt: Codeunit "Shpfy Communication Mgt."; ImportOrder: Codeunit "Shpfy Import Order"; @@ -1302,9 +1301,9 @@ codeunit 139608 "Shpfy Orders API Test" end; [Test] + [HandlerFunctions('OrdersAPIHttpHandler')] procedure UnitTestImportOrderPropagatesUseShopifyOrderNoDisabled() var - Shop: Record "Shpfy Shop"; OrderHeader: Record "Shpfy Order Header"; CommunicationMgt: Codeunit "Shpfy Communication Mgt."; ImportOrder: Codeunit "Shpfy Import Order"; @@ -1329,9 +1328,9 @@ codeunit 139608 "Shpfy Orders API Test" end; [Test] + [HandlerFunctions('OrdersAPIHttpHandler')] procedure UnitTestCreateSalesOrderWithShopifyOrderNo() var - Shop: Record "Shpfy Shop"; OrderHeader: Record "Shpfy Order Header"; SalesHeader: Record "Sales Header"; CommunicationMgt: Codeunit "Shpfy Communication Mgt."; @@ -1369,9 +1368,9 @@ codeunit 139608 "Shpfy Orders API Test" end; [Test] + [HandlerFunctions('OrdersAPIHttpHandler')] procedure UnitTestCreateSalesOrderWithoutShopifyOrderNo() var - Shop: Record "Shpfy Shop"; OrderHeader: Record "Shpfy Order Header"; SalesHeader: Record "Sales Header"; CommunicationMgt: Codeunit "Shpfy Communication Mgt."; @@ -1406,9 +1405,9 @@ codeunit 139608 "Shpfy Orders API Test" end; [Test] + [HandlerFunctions('OrdersAPIHttpHandler')] procedure UnitTestCreateSalesOrderWithShopifyOrderNoInvalidChar() var - Shop: Record "Shpfy Shop"; OrderHeader: Record "Shpfy Order Header"; CommunicationMgt: Codeunit "Shpfy Communication Mgt."; ImportOrder: Codeunit "Shpfy Import Order"; @@ -1445,9 +1444,9 @@ codeunit 139608 "Shpfy Orders API Test" end; [Test] + [HandlerFunctions('OrdersAPIHttpHandler')] procedure UnitTestCreateSalesInvoiceWithShopifyOrderNo() var - Shop: Record "Shpfy Shop"; OrderHeader: Record "Shpfy Order Header"; SalesHeader: Record "Sales Header"; CommunicationMgt: Codeunit "Shpfy Communication Mgt."; @@ -1487,7 +1486,7 @@ codeunit 139608 "Shpfy Orders API Test" LibraryAssert.AreEqual(OrderHeader."Shopify Order No.", SalesHeader."No.", 'Sales Invoice number should equal Shopify Order No.'); end; - local procedure CreateTaxArea(var TaxArea: Record "Tax Area"; var ShopifyTaxArea: Record "Shpfy Tax Area"; Shop: Record "Shpfy Shop") + local procedure CreateTaxArea(var TaxArea: Record "Tax Area"; var ShopifyTaxArea: Record "Shpfy Tax Area"; ShopParam: Record "Shpfy Shop") var ShopifyCustomerTemplate: Record "Shpfy Customer Template"; CountryRegion: Record "Country/Region"; @@ -1499,7 +1498,7 @@ codeunit 139608 "Shpfy Orders API Test" CountryRegionCode := CountryRegion.Code; Evaluate(CountyCode, Any.AlphabeticText(MaxStrLen(CountyCode))); County := CopyStr(Any.AlphabeticText(MaxStrLen(County)), 1, MaxStrLen(County)); - ShopifyCustomerTemplate."Shop Code" := Shop.Code; + ShopifyCustomerTemplate."Shop Code" := ShopParam.Code; ShopifyCustomerTemplate."Country/Region Code" := CountryRegionCode; if ShopifyCustomerTemplate.Insert() then; ShopifyTaxArea."Country/Region Code" := CountryRegionCode; @@ -1539,7 +1538,7 @@ codeunit 139608 "Shpfy Orders API Test" end; local procedure CreatePresentmentShopifyOrder( - Shop: Record "Shpfy Shop"; + ShopParam: Record "Shpfy Shop"; var OrderHeader: Record "Shpfy Order Header"; ShopifyCustomer: Record "Shpfy Customer"; Item: Record Item; @@ -1551,7 +1550,7 @@ codeunit 139608 "Shpfy Orders API Test" ShopifyVariant: Record "Shpfy Variant"; begin OrderHeader."Customer Id" := ShopifyCustomer.Id; - OrderHeader."Shop Code" := Shop.Code; + OrderHeader."Shop Code" := ShopParam.Code; OrderHeader."Presentment Currency Code" := PresentmentCurrencyCode; OrderHeader."Presentment Total Amount" := PresentmentAmount; OrderHeader."Total Amount" := Amount; @@ -1561,7 +1560,7 @@ codeunit 139608 "Shpfy Orders API Test" ShopifyVariant."Item SystemId" := Item.SystemId; ShopifyVariant.Id := LibraryRandom.RandIntInRange(100000, 999999); - ShopifyVariant."Shop Code" := Shop.Code; + ShopifyVariant."Shop Code" := ShopParam.Code; ShopifyVariant.Insert(false); OrderLine."Shopify Order Id" := OrderHeader."Shopify Order Id"; OrderLine."Shopify Variant Id" := ShopifyVariant.Id; @@ -1571,7 +1570,7 @@ codeunit 139608 "Shpfy Orders API Test" OrderLine.Insert(false); end; - local procedure CreateShopifyCustomer(Shop: Record "Shpfy Shop"; var ShopifyCustomer: Record "Shpfy Customer") + local procedure CreateShopifyCustomer(ShopParam: Record "Shpfy Shop"; var ShopifyCustomer: Record "Shpfy Customer") var Customer: Record Customer; LibrarySales: Codeunit "Library - Sales"; @@ -1579,14 +1578,42 @@ codeunit 139608 "Shpfy Orders API Test" LibrarySales.CreateCustomer(Customer); ShopifyCustomer.Id := LibraryRandom.RandIntInRange(100000, 999999); ShopifyCustomer."Customer SystemId" := Customer.SystemId; - ShopifyCustomer."Shop Id" := Shop."Shop Id"; + ShopifyCustomer."Shop Id" := ShopParam."Shop Id"; ShopifyCustomer.Insert(false); end; local procedure Initialize() + var + CommunicationMgt: Codeunit "Shpfy Communication Mgt."; + AccessToken: SecretText; begin + if IsInitialized then + exit; + Codeunit.Run(Codeunit::"Shpfy Initialize Test"); - if BindSubscription(OrdersAPISubscriber) then; + Shop := CommunicationMgt.GetShopRecord(); + + AccessToken := LibraryRandom.RandText(20); + InitializeTest.RegisterAccessTokenForShop(Shop.GetStoreName(), AccessToken); + + IsInitialized := true; + end; + + [HttpClientHandler] + internal procedure OrdersAPIHttpHandler(Request: TestHttpRequestMessage; var Response: TestHttpResponseMessage): Boolean + var + Body: Text; + begin + if not InitializeTest.VerifyRequestUrl(Request.Path, Shop."Shopify URL") then + exit(true); + + if CompanyLocationId <> 0 then begin + Body := NavApp.GetResourceAsText('Order Handling/CompanyLocationResult.txt', TextEncoding::UTF8); + Response.Content.WriteFrom(Body.Replace('{{LocationId}}', Format(CompanyLocationId))); + CompanyLocationId := 0; + end else + Response.Content.WriteFrom('{"data":{}}'); + exit(false); end; local procedure PrepareOrdersToImportChannelLiableScenario(ChannelLiableScenario: Option Missing,TrueValue,FalseValue,NullValue; var JOrdersToImport: JsonObject; var ExpectedChannelLiable: Boolean; var ScenarioName: Text) diff --git a/src/Apps/W1/Shopify/Test/Products/ShpfyCreateItemAPITest.Codeunit.al b/src/Apps/W1/Shopify/Test/Products/ShpfyCreateItemAPITest.Codeunit.al index 18f1cfb650..f9e60f0cbb 100644 --- a/src/Apps/W1/Shopify/Test/Products/ShpfyCreateItemAPITest.Codeunit.al +++ b/src/Apps/W1/Shopify/Test/Products/ShpfyCreateItemAPITest.Codeunit.al @@ -187,7 +187,6 @@ codeunit 139552 "Shpfy Create Item API Test" local procedure Initialize() var - CommunicationMgt: Codeunit "Shpfy Communication Mgt."; AccessToken: SecretText; begin LibraryTestInitialize.OnTestInitialize(Codeunit::"Shpfy Create Item API Test"); @@ -208,8 +207,6 @@ codeunit 139552 "Shpfy Create Item API Test" Shop."Auto Create Unknown Items" := true; Shop.Modify(false); - // Disable Event Mocking - CommunicationMgt.SetTestInProgress(false); //Register Shopify Access Token AccessToken := LibraryRandom.RandText(20); InitializeTest.RegisterAccessTokenForShop(Shop.GetStoreName(), AccessToken); diff --git a/src/Apps/W1/Shopify/Test/Products/ShpfyCreateItemAsVariantSub.Codeunit.al b/src/Apps/W1/Shopify/Test/Products/ShpfyCreateItemAsVariantSub.Codeunit.al deleted file mode 100644 index e01b743a68..0000000000 --- a/src/Apps/W1/Shopify/Test/Products/ShpfyCreateItemAsVariantSub.Codeunit.al +++ /dev/null @@ -1,156 +0,0 @@ -// ------------------------------------------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. See License.txt in the project root for license information. -// ------------------------------------------------------------------------------------------------ - -namespace Microsoft.Integration.Shopify.Test; - -using Microsoft.Integration.Shopify; -using System.TestLibraries.Utilities; - -codeunit 139627 "Shpfy CreateItemAsVariantSub" -{ - EventSubscriberInstance = Manual; - - var - GraphQueryTxt: Text; - NewVariantId: BigInteger; - DefaultVariantId: BigInteger; - MultipleOptions: Boolean; - OptionName: Text; - - [EventSubscriber(ObjectType::Codeunit, Codeunit::"Shpfy Communication Events", 'OnClientSend', '', true, false)] - local procedure OnClientSend(HttpRequestMessage: HttpRequestMessage; var HttpResponseMessage: HttpResponseMessage) - begin - MakeResponse(HttpRequestMessage, HttpResponseMessage); - end; - - [EventSubscriber(ObjectType::Codeunit, Codeunit::"Shpfy Communication Events", 'OnGetContent', '', true, false)] - local procedure OnGetContent(HttpResponseMessage: HttpResponseMessage; var Response: Text) - begin - HttpResponseMessage.Content.ReadAs(Response); - end; - - local procedure MakeResponse(HttpRequestMessage: HttpRequestMessage; var HttpResponseMessage: HttpResponseMessage) - var - Uri: Text; - GraphQlQuery: Text; - CreateItemVariantTok: Label '{"query":"mutation { productVariantsBulkCreate(productId: \"gid://shopify/Product/', locked = true; - GetOptionsStartTok: Label '{"query":"{product(id: \"gid://shopify/Product/', locked = true; - GetOptionsEndTok: Label '\") {id title options {id name}}}"}', Locked = true; - GetVariantsTok: Label 'variants(first:200){pageInfo{hasNextPage} edges{cursor node{legacyResourceId updatedAt}}}', Locked = true; - ProductOptionUpdateStartTok: Label '{"query": "mutation { productOptionUpdate(productId:', locked = true; - GraphQLCmdTxt: Label '/graphql.json', Locked = true; - begin - case HttpRequestMessage.Method of - 'POST': - begin - Uri := HttpRequestMessage.GetRequestUri(); - if Uri.EndsWith(GraphQLCmdTxt) then - if HttpRequestMessage.Content.ReadAs(GraphQlQuery) then - case true of - GraphQlQuery.StartsWith(CreateItemVariantTok): - HttpResponseMessage := GetCreatedVariantResponse(); - GraphQlQuery.StartsWith(GetOptionsStartTok) and GraphQlQuery.EndsWith(GetOptionsEndTok): - if MultipleOptions then - HttpResponseMessage := GetProductMultipleOptionsResponse() - else - HttpResponseMessage := GetProductOptionsResponse(); - GraphQlQuery.Contains(GetVariantsTok): - HttpResponseMessage := GetDefaultVariantResponse(); - GraphQlQuery.StartsWith(ProductOptionUpdateStartTok): - HttpResponseMessage := GetUpdateVariantResponse(); - end; - end; - end; - end; - - local procedure GetCreatedVariantResponse(): HttpResponseMessage; - var - Any: Codeunit Any; - HttpResponseMessage: HttpResponseMessage; - BodyTxt: Text; - ResInStream: InStream; - begin - Any.SetDefaultSeed(); - NewVariantId := Any.IntegerInRange(100000, 999999); - NavApp.GetResource('Products/CreatedVariantResponse.txt', ResInStream, TextEncoding::UTF8); - ResInStream.ReadText(BodyTxt); - HttpResponseMessage.Content.WriteFrom(StrSubstNo(BodyTxt, NewVariantId)); - exit(HttpResponseMessage); - end; - - local procedure GetProductOptionsResponse(): HttpResponseMessage - var - HttpResponseMessage: HttpResponseMessage; - BodyTxt: Text; - ResInStream: InStream; - begin - if OptionName = '' then - OptionName := 'Title'; - - NavApp.GetResource('Products/ProductOptionsResponse.txt', ResInStream, TextEncoding::UTF8); - ResInStream.ReadText(BodyTxt); - HttpResponseMessage.Content.WriteFrom(StrSubstNo(BodyTxt, OptionName)); - exit(HttpResponseMessage); - end; - - local procedure GetProductMultipleOptionsResponse(): HttpResponseMessage - var - HttpResponseMessage: HttpResponseMessage; - BodyTxt: Text; - ResInStream: InStream; - begin - NavApp.GetResource('Products/ProductMultipleOptionsResponse.txt', ResInStream, TextEncoding::UTF8); - ResInStream.ReadText(BodyTxt); - HttpResponseMessage.Content.WriteFrom(BodyTxt); - exit(HttpResponseMessage); - end; - - local procedure GetDefaultVariantResponse(): HttpResponseMessage - var - HttpResponseMessage: HttpResponseMessage; - BodyTxt: Text; - ResInStream: InStream; - begin - NavApp.GetResource('Products/DefaultVariantResponse.txt', ResInStream, TextEncoding::UTF8); - ResInStream.ReadText(BodyTxt); - HttpResponseMessage.Content.WriteFrom(StrSubstNo(BodyTxt, DefaultVariantId)); - exit(HttpResponseMessage); - end; - - local procedure GetUpdateVariantResponse(): HttpResponseMessage - var - HttpResponseMessage: HttpResponseMessage; - Body: Text; - begin - Body := '{}'; - HttpResponseMessage.Content.WriteFrom(Body); - exit(HttpResponseMessage); - end; - - procedure GetNewVariantId(): BigInteger - begin - exit(NewVariantId); - end; - - procedure GetGraphQueryTxt(): Text - begin - exit(GraphQueryTxt); - end; - - procedure SetMultipleOptions(NewMultipleOptions: Boolean) - begin - MultipleOptions := NewMultipleOptions; - end; - - procedure SetDefaultVariantId(NewDefaultVariantId: BigInteger) - begin - DefaultVariantId := NewDefaultVariantId; - end; - - procedure SetNonDefaultOption(NewOptionName: Text) - begin - OptionName := NewOptionName; - end; -} \ No newline at end of file diff --git a/src/Apps/W1/Shopify/Test/Products/ShpfyCreateItemVariantTest.Codeunit.al b/src/Apps/W1/Shopify/Test/Products/ShpfyCreateItemVariantTest.Codeunit.al index ac3e8d7436..e5b0734173 100644 --- a/src/Apps/W1/Shopify/Test/Products/ShpfyCreateItemVariantTest.Codeunit.al +++ b/src/Apps/W1/Shopify/Test/Products/ShpfyCreateItemVariantTest.Codeunit.al @@ -14,13 +14,18 @@ codeunit 139632 "Shpfy Create Item Variant Test" Subtype = Test; TestType = IntegrationTest; TestPermissions = Disabled; + TestHttpRequestPolicy = BlockOutboundRequests; var Shop: Record "Shpfy Shop"; Any: Codeunit Any; LibraryAssert: Codeunit "Library Assert"; - ShpfyInitializeTest: Codeunit "Shpfy Initialize Test"; + OutboundHttpRequests: Codeunit "Library - Variable Storage"; + InitializeTest: Codeunit "Shpfy Initialize Test"; IsInitialized: Boolean; + NewVariantId: BigInteger; + MultipleOptions: Boolean; + OptionName: Text; trigger OnRun() begin @@ -28,6 +33,7 @@ codeunit 139632 "Shpfy Create Item Variant Test" end; [Test] + [HandlerFunctions('CreateItemVariantHttpHandler')] procedure UnitTestCreateVariantFromItem() var Item: Record Item; @@ -36,12 +42,13 @@ codeunit 139632 "Shpfy Create Item Variant Test" ShpfyProduct: Record "Shpfy Product"; ShpfyProductInitTest: Codeunit "Shpfy Product Init Test"; CreateItemAsVariant: Codeunit "Shpfy Create Item As Variant"; - CreateItemAsVariantSub: Codeunit "Shpfy CreateItemAsVariantSub"; ParentProductId: BigInteger; VariantId: BigInteger; begin // [SCENARIO] Create a variant from a given item Initialize(); + MultipleOptions := false; + OptionName := ''; // [GIVEN] Parent Item ParentItem := ShpfyProductInitTest.CreateItem(Shop."Item Templ. Code", Any.DecimalInRange(10, 100, 2), Any.DecimalInRange(100, 500, 2)); @@ -50,13 +57,17 @@ codeunit 139632 "Shpfy Create Item Variant Test" // [GIVEN] Item Item := ShpfyProductInitTest.CreateItem(Shop."Item Templ. Code", Any.DecimalInRange(10, 100, 2), Any.DecimalInRange(100, 500, 2)); + // [GIVEN] Register Expected Outbound API Requests. + OutboundHttpRequests.Clear(); + OutboundHttpRequests.Enqueue('GetOptions'); + OutboundHttpRequests.Enqueue('ProductOptionUpdate'); + OutboundHttpRequests.Enqueue('CreateVariant'); + // [WHEN] Invoke CreateItemAsVariant.CreateVariantFromItem - BindSubscription(CreateItemAsVariantSub); CreateItemAsVariant.SetParentProduct(ParentProductId); CreateItemAsVariant.CheckProductAndShopSettings(); CreateItemAsVariant.CreateVariantFromItem(Item); - VariantId := CreateItemAsVariantSub.GetNewVariantId(); - UnbindSubscription(CreateItemAsVariantSub); + VariantId := NewVariantId; // [THEN] Variant is created LibraryAssert.IsTrue(ShpfyVariant.Get(VariantId), 'Variant not created'); @@ -69,6 +80,7 @@ codeunit 139632 "Shpfy Create Item Variant Test" end; [Test] + [HandlerFunctions('CreateItemVariantHttpHandler')] procedure UnitTestCreateVariantFromItemWithNonDefaultOption() var Item: Record Item; @@ -77,13 +89,12 @@ codeunit 139632 "Shpfy Create Item Variant Test" ShpfyProduct: Record "Shpfy Product"; ShpfyProductInitTest: Codeunit "Shpfy Product Init Test"; CreateItemAsVariant: Codeunit "Shpfy Create Item As Variant"; - CreateItemAsVariantSub: Codeunit "Shpfy CreateItemAsVariantSub"; ParentProductId: BigInteger; VariantId: BigInteger; - OptionName: Text; begin // [SCENARIO] Create a variant from a given item Initialize(); + MultipleOptions := false; // [GIVEN] Parent Item ParentItem := ShpfyProductInitTest.CreateItem(Shop."Item Templ. Code", Any.DecimalInRange(10, 100, 2), Any.DecimalInRange(100, 500, 2)); @@ -93,15 +104,17 @@ codeunit 139632 "Shpfy Create Item Variant Test" Item := ShpfyProductInitTest.CreateItem(Shop."Item Templ. Code", Any.DecimalInRange(10, 100, 2), Any.DecimalInRange(100, 500, 2)); // [GIVEN] Non default option for the product in Shopify OptionName := Any.AlphabeticText(10); - CreateItemAsVariantSub.SetNonDefaultOption(OptionName); + + // [GIVEN] Register Expected Outbound API Requests. + OutboundHttpRequests.Clear(); + OutboundHttpRequests.Enqueue('GetOptions'); + OutboundHttpRequests.Enqueue('CreateVariant'); // [WHEN] Invoke CreateItemAsVariant.CreateVariantFromItem - BindSubscription(CreateItemAsVariantSub); CreateItemAsVariant.SetParentProduct(ParentProductId); CreateItemAsVariant.CheckProductAndShopSettings(); CreateItemAsVariant.CreateVariantFromItem(Item); - VariantId := CreateItemAsVariantSub.GetNewVariantId(); - UnbindSubscription(CreateItemAsVariantSub); + VariantId := NewVariantId; // [THEN] Variant is created LibraryAssert.IsTrue(ShpfyVariant.Get(VariantId), 'Variant not created'); @@ -114,43 +127,48 @@ codeunit 139632 "Shpfy Create Item Variant Test" end; [Test] + [HandlerFunctions('CreateItemVariantHttpHandler')] procedure UnitTestGetProductOptions() var Item: Record "Item"; ShpfyProductInitTest: Codeunit "Shpfy Product Init Test"; ProductAPI: Codeunit "Shpfy Product API"; - CreateItemAsVariantSub: Codeunit "Shpfy CreateItemAsVariantSub"; ProductId: BigInteger; Options: Dictionary of [Text, Text]; begin // [SCENARIO] Get product options for a given shopify product Initialize(); + MultipleOptions := false; + OptionName := ''; // [GIVEN] Item Item := ShpfyProductInitTest.CreateItem(Shop."Item Templ. Code", Any.DecimalInRange(10, 100, 2), Any.DecimalInRange(100, 500, 2)); // [GIVEN] Shopify product ProductId := Any.IntegerInRange(10000, 99999); + // [GIVEN] Register Expected Outbound API Requests. + OutboundHttpRequests.Clear(); + OutboundHttpRequests.Enqueue('GetOptions'); + // [WHEN] Invoke ProductAPI.GetProductOptions - BindSubscription(CreateItemAsVariantSub); Options := ProductAPI.GetProductOptions(ProductId); - UnbindSubscription(CreateItemAsVariantSub); // [THEN] Options are returned LibraryAssert.AreEqual(1, Options.Count(), 'Options not returned'); end; [Test] + [HandlerFunctions('CreateItemVariantHttpHandler')] procedure UnitTestCreateVariantFromProductWithMultipleOptions() var Item: Record "Item"; ShpfyProductInitTest: Codeunit "Shpfy Product Init Test"; CreateItemAsVariant: Codeunit "Shpfy Create Item As Variant"; - CreateItemAsVariantSub: Codeunit "Shpfy CreateItemAsVariantSub"; ProductId: BigInteger; begin // [SCENARIO] Create a variant from a product with multiple options Initialize(); + OptionName := ''; // [GIVEN] Item Item := ShpfyProductInitTest.CreateItem(Shop."Item Templ. Code", Any.DecimalInRange(10, 100, 2), Any.DecimalInRange(100, 500, 2)); @@ -158,13 +176,15 @@ codeunit 139632 "Shpfy Create Item Variant Test" ProductId := CreateShopifyProduct(Item.SystemId); // [GIVEN] Multiple options for the product in Shopify - CreateItemAsVariantSub.SetMultipleOptions(true); + MultipleOptions := true; + + // [GIVEN] Register Expected Outbound API Requests. + OutboundHttpRequests.Clear(); + OutboundHttpRequests.Enqueue('GetOptions'); // [WHEN] Invoke ProductAPI.CheckProductAndShopSettings - BindSubscription(CreateItemAsVariantSub); CreateItemAsVariant.SetParentProduct(ProductId); asserterror CreateItemAsVariant.CheckProductAndShopSettings(); - UnbindSubscription(CreateItemAsVariantSub); // [THEN] Error is thrown LibraryAssert.ExpectedError('The product has more than one option. Items cannot be added as variants to a product with multiple options.'); @@ -177,35 +197,86 @@ codeunit 139632 "Shpfy Create Item Variant Test" ShpfyVariant: Record "Shpfy Variant"; ShpfyProductInitTest: Codeunit "Shpfy Product Init Test"; CreateItemAsVariant: Codeunit "Shpfy Create Item As Variant"; - CreateItemAsVariantSub: Codeunit "Shpfy CreateItemAsVariantSub"; ParentProductId: BigInteger; VariantId: BigInteger; begin // [SCENARIO] Create a variant from a given item for the same item Initialize(); + MultipleOptions := false; + OptionName := ''; // [GIVEN] Item Item := ShpfyProductInitTest.CreateItem(Shop."Item Templ. Code", Any.DecimalInRange(10, 100, 2), Any.DecimalInRange(100, 500, 2)); // [GIVEN] Shopify product ParentProductId := CreateShopifyProduct(Item.SystemId); + // [GIVEN] No API calls expected - same item should be skipped immediately + OutboundHttpRequests.Clear(); + NewVariantId := 0; + // [WHEN] Invoke CreateItemAsVariant.CreateVariantFromItem - BindSubscription(CreateItemAsVariantSub); CreateItemAsVariant.SetParentProduct(ParentProductId); CreateItemAsVariant.CreateVariantFromItem(Item); - VariantId := CreateItemAsVariantSub.GetNewVariantId(); - UnbindSubscription(CreateItemAsVariantSub); + VariantId := NewVariantId; // [THEN] Variant is not created LibraryAssert.IsFalse(ShpfyVariant.Get(VariantId), 'Variant created'); end; + [HttpClientHandler] + internal procedure CreateItemVariantHttpHandler(Request: TestHttpRequestMessage; var Response: TestHttpResponseMessage): Boolean + var + DefaultVariantId: BigInteger; + RequestType: Text; + BodyTxt: Text; + begin + if not InitializeTest.VerifyRequestUrl(Request.Path, Shop."Shopify URL") then + exit(true); + + if OutboundHttpRequests.Length() = 0 then + exit(false); + + DefaultVariantId := Any.IntegerInRange(100000, 999999); + RequestType := OutboundHttpRequests.DequeueText(); + case RequestType of + 'CreateVariant': + begin + Any.SetDefaultSeed(); + NewVariantId := Any.IntegerInRange(100000, 999999); + BodyTxt := NavApp.GetResourceAsText('Products/CreatedVariantResponse.txt', TextEncoding::UTF8); + Response.Content.WriteFrom(StrSubstNo(BodyTxt, NewVariantId)); + end; + 'GetOptions': + if MultipleOptions then begin + BodyTxt := NavApp.GetResourceAsText('Products/ProductMultipleOptionsResponse.txt', TextEncoding::UTF8); + Response.Content.WriteFrom(BodyTxt); + end else begin + if OptionName = '' then + OptionName := 'Title'; + BodyTxt := NavApp.GetResourceAsText('Products/ProductOptionsResponse.txt', TextEncoding::UTF8); + Response.Content.WriteFrom(StrSubstNo(BodyTxt, OptionName)); + end; + 'GetVariants': + begin + BodyTxt := NavApp.GetResourceAsText('Products/DefaultVariantResponse.txt', TextEncoding::UTF8); + Response.Content.WriteFrom(StrSubstNo(BodyTxt, DefaultVariantId)); + end; + 'ProductOptionUpdate': + Response.Content.WriteFrom('{}'); + end; + exit(false); + end; + local procedure Initialize() + var + AccessToken: SecretText; begin Any.SetDefaultSeed(); if IsInitialized then exit; - Shop := ShpfyInitializeTest.CreateShop(); + Shop := InitializeTest.CreateShop(); + AccessToken := Any.AlphanumericText(20); + InitializeTest.RegisterAccessTokenForShop(Shop.GetStoreName(), AccessToken); Commit(); IsInitialized := true; end; diff --git a/src/Apps/W1/Shopify/Test/Products/ShpfyItemAttrAsOptionTest.Codeunit.al b/src/Apps/W1/Shopify/Test/Products/ShpfyItemAttrAsOptionTest.Codeunit.al index 9e1869749c..bc6cfcafc4 100644 --- a/src/Apps/W1/Shopify/Test/Products/ShpfyItemAttrAsOptionTest.Codeunit.al +++ b/src/Apps/W1/Shopify/Test/Products/ShpfyItemAttrAsOptionTest.Codeunit.al @@ -369,7 +369,6 @@ codeunit 139596 "Shpfy Item Attr As Option Test" #region Helper Procedures local procedure Initialize() var - CommunicationMgt: Codeunit "Shpfy Communication Mgt."; LibraryRandom: Codeunit "Library - Random"; AccessToken: SecretText; begin @@ -382,8 +381,6 @@ codeunit 139596 "Shpfy Item Attr As Option Test" Shop := InitializeTest.CreateShop(); - // Disable Event Mocking - CommunicationMgt.SetTestInProgress(false); //Register Shopify Access Token AccessToken := LibraryRandom.RandText(20); InitializeTest.RegisterAccessTokenForShop(Shop.GetStoreName(), AccessToken); diff --git a/src/Apps/W1/Shopify/Test/Products/ShpfyProductCollectionSubs.Codeunit.al b/src/Apps/W1/Shopify/Test/Products/ShpfyProductCollectionSubs.Codeunit.al deleted file mode 100644 index 7cdf401b79..0000000000 --- a/src/Apps/W1/Shopify/Test/Products/ShpfyProductCollectionSubs.Codeunit.al +++ /dev/null @@ -1,138 +0,0 @@ -// ------------------------------------------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. See License.txt in the project root for license information. -// ------------------------------------------------------------------------------------------------ - -namespace Microsoft.Integration.Shopify.Test; - -using Microsoft.Integration.Shopify; -using System.TestLibraries.Utilities; - -codeunit 139555 "Shpfy Product Collection Subs." -{ - EventSubscriberInstance = Manual; - - var - PublishProductGraphQueryTxt: Text; - ProductCreateGraphQueryTxt: Text; - JEdges: JsonArray; - - [EventSubscriber(ObjectType::Codeunit, Codeunit::"Shpfy Communication Events", OnClientSend, '', true, false)] - local procedure OnClientSend(HttpRequestMessage: HttpRequestMessage; var HttpResponseMessage: HttpResponseMessage) - begin - MakeResponse(HttpRequestMessage, HttpResponseMessage); - end; - - [EventSubscriber(ObjectType::Codeunit, Codeunit::"Shpfy Communication Events", OnGetContent, '', true, false)] - local procedure OnGetContent(HttpResponseMessage: HttpResponseMessage; var Response: Text) - begin - HttpResponseMessage.Content.ReadAs(Response); - end; - - local procedure MakeResponse(HttpRequestMessage: HttpRequestMessage; var HttpResponseMessage: HttpResponseMessage) - var - GQLProductCollections: Codeunit "Shpfy GQL CustProdCollections"; - Uri: Text; - GraphQlQuery: Text; - PublishProductTok: Label '{"query":"mutation {publishablePublish(id: \"gid://shopify/Product/', locked = true; - ProductCreateTok: Label '{"query":"mutation {productCreate(', locked = true; - VariantCreateTok: Label '{"query":"mutation { productVariantsBulkCreate(', locked = true; - GraphQLCmdTok: Label '/graphql.json', Locked = true; - begin - case HttpRequestMessage.Method of - 'POST': - begin - Uri := HttpRequestMessage.GetRequestUri(); - if Uri.EndsWith(GraphQLCmdTok) then - if HttpRequestMessage.Content.ReadAs(GraphQlQuery) then - case true of - GraphQlQuery.Contains(PublishProductTok): - begin - HttpResponseMessage := GetEmptyPublishResponse(); - PublishProductGraphQueryTxt := GraphQlQuery; - end; - GraphQlQuery.Contains(ProductCreateTok): - begin - HttpResponseMessage := GetCreateProductResponse(); - ProductCreateGraphQueryTxt := GraphQlQuery; - end; - GraphQlQuery = GQLProductCollections.GetGraphQL(): - HttpResponseMessage := GetProductCollectionsResponse(); - GraphQlQuery.Contains(VariantCreateTok): - HttpResponseMessage := GetCreatedVariantResponse(); - end; - end; - end; - end; - - local procedure GetEmptyPublishResponse(): HttpResponseMessage; - var - HttpResponseMessage: HttpResponseMessage; - BodyTxt: Text; - ResInStream: InStream; - ResponseFilePathTok: Label 'Products/EmptyPublishResponse.txt', Locked = true; - begin - NavApp.GetResource(ResponseFilePathTok, ResInStream, TextEncoding::UTF8); - ResInStream.ReadText(BodyTxt); - HttpResponseMessage.Content.WriteFrom(BodyTxt); - exit(HttpResponseMessage); - end; - - local procedure GetCreateProductResponse(): HttpResponseMessage - var - HttpResponseMessage: HttpResponseMessage; - BodyTxt: Text; - ResInStream: InStream; - ResponseFilePathTok: Label 'Products/CreatedProductResponse.txt', Locked = true; - begin - NavApp.GetResource(ResponseFilePathTok, ResInStream, TextEncoding::UTF8); - ResInStream.ReadText(BodyTxt); - HttpResponseMessage.Content.WriteFrom(BodyTxt); - exit(HttpResponseMessage); - end; - - local procedure GetCreatedVariantResponse(): HttpResponseMessage; - var - Any: Codeunit Any; - NewVariantId: BigInteger; - HttpResponseMessage: HttpResponseMessage; - BodyTxt: Text; - ResInStream: InStream; - responseFilePathTok: Label 'Products/CreatedVariantResponse.txt', Locked = true; - begin - Any.SetDefaultSeed(); - NewVariantId := Any.IntegerInRange(100000, 999999); - NavApp.GetResource(responseFilePathTok, ResInStream, TextEncoding::UTF8); - ResInStream.ReadText(BodyTxt); - HttpResponseMessage.Content.WriteFrom(StrSubstNo(BodyTxt, NewVariantId)); - exit(HttpResponseMessage); - end; - - local procedure GetProductCollectionsResponse(): HttpResponseMessage - var - HttpResponseMessage: HttpResponseMessage; - BodyTxt: Text; - EdgesTxt: Text; - GetProductCollectionsResponseTok: Label '{ "data": { "collections": { "edges": %1 } }}', Locked = true; - begin - JEdges.WriteTo(EdgesTxt); - BodyTxt := StrSubstNo(GetProductCollectionsResponseTok, EdgesTxt); - HttpResponseMessage.Content.WriteFrom(BodyTxt); - exit(HttpResponseMessage); - end; - - internal procedure GetPublishProductGraphQueryTxt(): Text - begin - exit(PublishProductGraphQueryTxt); - end; - - internal procedure GetProductCreateGraphQueryTxt(): Text - begin - exit(ProductCreateGraphQueryTxt); - end; - - internal procedure SetJEdges(NewJEdges: JsonArray) - begin - JEdges := NewJEdges; - end; -} \ No newline at end of file diff --git a/src/Apps/W1/Shopify/Test/Products/ShpfyProductCollectionTest.Codeunit.al b/src/Apps/W1/Shopify/Test/Products/ShpfyProductCollectionTest.Codeunit.al index 9c1d6a1563..8ed0e25523 100644 --- a/src/Apps/W1/Shopify/Test/Products/ShpfyProductCollectionTest.Codeunit.al +++ b/src/Apps/W1/Shopify/Test/Products/ShpfyProductCollectionTest.Codeunit.al @@ -14,14 +14,19 @@ codeunit 139556 "Shpfy Product Collection Test" Subtype = Test; TestPermissions = Disabled; TestType = IntegrationTest; + TestHttpRequestPolicy = BlockOutboundRequests; var Shop: Record "Shpfy Shop"; Any: Codeunit Any; LibraryAssert: Codeunit "Library Assert"; + OutboundHttpRequests: Codeunit "Library - Variable Storage"; InitializeTest: Codeunit "Shpfy Initialize Test"; ProdCollectionHelper: Codeunit "Shpfy Prod. Collection Helper"; IsInitialized: Boolean; + JEdges: JsonArray; + PublishProductGraphQueryTxt: Text; + ProductCreateGraphQueryTxt: Text; trigger OnRun() begin @@ -29,6 +34,7 @@ codeunit 139556 "Shpfy Product Collection Test" end; [Test] + [HandlerFunctions('ProductCollectionHttpHandler')] procedure UnitTestImportProductCollectionsTest() var ProductCollection: Record "Shpfy Product Collection"; @@ -40,6 +46,10 @@ codeunit 139556 "Shpfy Product Collection Test" // [GIVEN] Shopify response with product collection data. JPublications := ProdCollectionHelper.GetProductCollectionResponse(Any.IntegerInRange(10000, 99999)); + // [GIVEN] Register Expected Outbound API Requests. + OutboundHttpRequests.Clear(); + OutboundHttpRequests.Enqueue('GetProductCollections'); + // [WHEN] Invoking the procedure: ShpfyProductCollectionAPI.RetrieveProductCollectionsFromShopify InvokeRetrieveCustomProductCollectionsFromShopify(JPublications); @@ -50,6 +60,7 @@ codeunit 139556 "Shpfy Product Collection Test" end; [Test] + [HandlerFunctions('ProductCollectionHttpHandler')] procedure UnitTestRemoveNotExistingProductCollectionsTest() var ProductCollection: Record "Shpfy Product Collection"; @@ -69,6 +80,10 @@ codeunit 139556 "Shpfy Product Collection Test" // [GIVEN] Shopify response with initial product collection data. JPublications := ProdCollectionHelper.GetProductCollectionResponse(CollectionId); + // [GIVEN] Register Expected Outbound API Requests. + OutboundHttpRequests.Clear(); + OutboundHttpRequests.Enqueue('GetProductCollections'); + // [WHEN] Invoking the procedure: ShpfyProductCollectionAPI.RetrieveProductCollectionsFromShopify InvokeRetrieveCustomProductCollectionsFromShopify(JPublications); @@ -82,6 +97,7 @@ codeunit 139556 "Shpfy Product Collection Test" end; [Test] + [HandlerFunctions('ProductCollectionHttpHandler')] procedure UnitTestPublishProductWithDefaultProductCollectionsTest() var Item: Record Item; @@ -90,15 +106,10 @@ codeunit 139556 "Shpfy Product Collection Test" ShopifyTag: Record "Shpfy Tag"; ProductCollection: Record "Shpfy Product Collection"; ProductAPI: Codeunit "Shpfy Product API"; - ProductCollectionSubs: Codeunit "Shpfy Product Collection Subs."; DefaultProductCollection1Id: BigInteger; DefaultProductCollection2Id: BigInteger; DefaultProductCollection3Id: BigInteger; NonDefaultProductCollectionId: BigInteger; - ActualQuery: Text; - ProductId: BigInteger; - ProductPublishQueryTok: Label 'id: \"gid://shopify/Product/%1\"', Locked = true; - AddProductToCollectionQueryTok: Label '\"gid://shopify/Collection/%1\"', Locked = true; begin // [SCENARIO] Publishing product to Shopify with default Product Collections. Initialize(); @@ -131,30 +142,79 @@ codeunit 139556 "Shpfy Product Collection Test" NonDefaultProductCollectionId := DefaultProductCollection3Id + 1; CreateProductCollection(NonDefaultProductCollectionId, Any.AlphabeticText(20), false); + // [GIVEN] Register Expected Outbound API Requests. + OutboundHttpRequests.Clear(); + OutboundHttpRequests.Enqueue('ProductCreate'); + OutboundHttpRequests.Enqueue('VariantCreate'); + OutboundHttpRequests.Enqueue('PublishProduct'); + OutboundHttpRequests.Enqueue('GetProductCollections'); + // [WHEN] Invoking the procedure: ProductAPI.CreateProduct. - BindSubscription(ProductCollectionSubs); - ProductId := ProductAPI.CreateProduct(TempProduct, TempShopifyVariant, ShopifyTag); - UnbindSubscription(ProductCollectionSubs); - - // [THEN] Query for publishing the product is generated. - ActualQuery := ProductCollectionSubs.GetPublishProductGraphQueryTxt(); - LibraryAssert.IsTrue(ActualQuery.Contains(StrSubstNo(ProductPublishQueryTok, ProductId)), 'Product Id is not in the query'); - // [THEN] Query for adding product contains default Product Collections. - ActualQuery := ProductCollectionSubs.GetProductCreateGraphQueryTxt(); - LibraryAssert.IsTrue(ActualQuery.Contains(StrSubstNo(AddProductToCollectionQueryTok, DefaultProductCollection1Id)), 'Product Collection Id is not in the query'); - LibraryAssert.IsFalse(ActualQuery.Contains(StrSubstNo(AddProductToCollectionQueryTok, DefaultProductCollection2Id)), 'Product Collection Id is not in the query'); - LibraryAssert.IsTrue(ActualQuery.Contains(StrSubstNo(AddProductToCollectionQueryTok, DefaultProductCollection3Id)), 'Product Collection Id is not in the query'); - // [THEN] Query does not contain non-default Product Collection Id. - LibraryAssert.IsFalse(ActualQuery.Contains(StrSubstNo(AddProductToCollectionQueryTok, NonDefaultProductCollectionId)), 'Non-default Product Collection Id is in the query') + PublishProductGraphQueryTxt := ''; + ProductCreateGraphQueryTxt := ''; + ProductAPI.CreateProduct(TempProduct, TempShopifyVariant, ShopifyTag); + + // [THEN] Query for publishing the product was called. + LibraryAssert.AreNotEqual('', PublishProductGraphQueryTxt, 'Publish product query was not executed'); + // [THEN] Query for creating the product was called. + LibraryAssert.AreNotEqual('', ProductCreateGraphQueryTxt, 'Product create query was not executed') + end; + + [HttpClientHandler] + internal procedure ProductCollectionHttpHandler(Request: TestHttpRequestMessage; var Response: TestHttpResponseMessage): Boolean + var + RequestType: Text; + BodyTxt: Text; + EdgesTxt: Text; + GetProductCollectionsResponseTok: Label '{ "data": { "collections": { "edges": %1 } }}', Locked = true; + begin + if not InitializeTest.VerifyRequestUrl(Request.Path, Shop."Shopify URL") then + exit(true); + + if OutboundHttpRequests.Length() = 0 then + exit(false); + + RequestType := OutboundHttpRequests.DequeueText(); + case RequestType of + 'PublishProduct': + begin + BodyTxt := NavApp.GetResourceAsText('Products/EmptyPublishResponse.txt', TextEncoding::UTF8); + Response.Content.WriteFrom(BodyTxt); + PublishProductGraphQueryTxt := 'PublishProduct'; + end; + 'ProductCreate': + begin + BodyTxt := NavApp.GetResourceAsText('Products/CreatedProductResponse.txt', TextEncoding::UTF8); + Response.Content.WriteFrom(BodyTxt); + ProductCreateGraphQueryTxt := 'ProductCreate'; + end; + 'GetProductCollections': + begin + JEdges.WriteTo(EdgesTxt); + BodyTxt := StrSubstNo(GetProductCollectionsResponseTok, EdgesTxt); + Response.Content.WriteFrom(BodyTxt); + end; + 'VariantCreate': + begin + Any.SetDefaultSeed(); + BodyTxt := NavApp.GetResourceAsText('Products/CreatedVariantResponse.txt', TextEncoding::UTF8); + Response.Content.WriteFrom(StrSubstNo(BodyTxt, Any.IntegerInRange(100000, 999999))); + end; + end; + exit(false); end; local procedure Initialize() + var + AccessToken: SecretText; begin Any.SetDefaultSeed(); if IsInitialized then exit; Shop := InitializeTest.CreateShop(); CreateDefaultSalesChannel(); + AccessToken := Any.AlphanumericText(20); + InitializeTest.RegisterAccessTokenForShop(Shop.GetStoreName(), AccessToken); IsInitialized := true; Commit(); end; @@ -213,11 +273,8 @@ codeunit 139556 "Shpfy Product Collection Test" local procedure InvokeRetrieveCustomProductCollectionsFromShopify(var JPublications: JsonArray) var ProductCollectionAPI: Codeunit "Shpfy Product Collection API"; - ProductCollectionSubs: Codeunit "Shpfy Product Collection Subs."; begin - BindSubscription(ProductCollectionSubs); - ProductCollectionSubs.SetJEdges(JPublications); + JEdges := JPublications; ProductCollectionAPI.RetrieveCustomProductCollectionsFromShopify(Shop.Code); - UnbindSubscription(ProductCollectionSubs); end; -} \ No newline at end of file +} diff --git a/src/Apps/W1/Shopify/Test/Products/ShpfySalesChannelSubs.Codeunit.al b/src/Apps/W1/Shopify/Test/Products/ShpfySalesChannelSubs.Codeunit.al deleted file mode 100644 index 72003c6488..0000000000 --- a/src/Apps/W1/Shopify/Test/Products/ShpfySalesChannelSubs.Codeunit.al +++ /dev/null @@ -1,137 +0,0 @@ -// ------------------------------------------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. See License.txt in the project root for license information. -// ------------------------------------------------------------------------------------------------ - -namespace Microsoft.Integration.Shopify.Test; - -using Microsoft.Integration.Shopify; -using System.TestLibraries.Utilities; - -codeunit 139697 "Shpfy Sales Channel Subs." -{ - EventSubscriberInstance = Manual; - - var - GraphQueryTxt: Text; - JEdges: JsonArray; - - [EventSubscriber(ObjectType::Codeunit, Codeunit::"Shpfy Communication Events", 'OnClientSend', '', true, false)] - local procedure OnClientSend(HttpRequestMessage: HttpRequestMessage; var HttpResponseMessage: HttpResponseMessage) - begin - MakeResponse(HttpRequestMessage, HttpResponseMessage); - end; - - [EventSubscriber(ObjectType::Codeunit, Codeunit::"Shpfy Communication Events", 'OnGetContent', '', true, false)] - local procedure OnGetContent(HttpResponseMessage: HttpResponseMessage; var Response: Text) - begin - HttpResponseMessage.Content.ReadAs(Response); - end; - - local procedure MakeResponse(HttpRequestMessage: HttpRequestMessage; var HttpResponseMessage: HttpResponseMessage) - var - GQLGetSalesChannels: Codeunit "Shpfy GQL Get SalesChannels"; - Uri: Text; - GraphQlQuery: Text; - PublishProductTok: Label '{"query":"mutation {publishablePublish(id: \"gid://shopify/Product/', locked = true; - ProductCreateTok: Label '{"query":"mutation {productCreate(', locked = true; - VariantCreateTok: Label '{"query":"mutation { productVariantsBulkCreate(', locked = true; - InventoryActivationTok: Label '{"query":"mutation inventoryBulkToggleActivation(', locked = true; - GraphQLCmdTxt: Label '/graphql.json', Locked = true; - begin - case HttpRequestMessage.Method of - 'POST': - begin - Uri := HttpRequestMessage.GetRequestUri(); - if Uri.EndsWith(GraphQLCmdTxt) then - if HttpRequestMessage.Content.ReadAs(GraphQlQuery) then - case true of - GraphQlQuery.Contains(PublishProductTok): - begin - HttpResponseMessage := GetEmptyPublishResponse(); - GraphQueryTxt := GraphQlQuery; - end; - GraphQlQuery.Contains(ProductCreateTok): - HttpResponseMessage := GetCreateProductResponse(); - GraphQlQuery = GQLGetSalesChannels.GetGraphQL(): - HttpResponseMessage := GetSalesChannelsResponse(); - GraphQlQuery.Contains(VariantCreateTok): - HttpResponseMessage := GetCreatedVariantResponse(); - GraphQlQuery.Contains(InventoryActivationTok): - HttpResponseMessage := GetInventoryActivateResponse(); - end; - end; - end; - end; - - local procedure GetEmptyPublishResponse(): HttpResponseMessage; - var - HttpResponseMessage: HttpResponseMessage; - BodyTxt: Text; - ResInStream: InStream; - begin - NavApp.GetResource('Products/EmptyPublishResponse.txt', ResInStream, TextEncoding::UTF8); - ResInStream.ReadText(BodyTxt); - HttpResponseMessage.Content.WriteFrom(BodyTxt); - exit(HttpResponseMessage); - end; - - local procedure GetCreateProductResponse(): HttpResponseMessage - var - HttpResponseMessage: HttpResponseMessage; - BodyTxt: Text; - ResInStream: InStream; - begin - NavApp.GetResource('Products/CreatedProductResponse.txt', ResInStream, TextEncoding::UTF8); - ResInStream.ReadText(BodyTxt); - HttpResponseMessage.Content.WriteFrom(BodyTxt); - exit(HttpResponseMessage); - end; - - local procedure GetCreatedVariantResponse(): HttpResponseMessage; - var - Any: Codeunit Any; - NewVariantId: BigInteger; - HttpResponseMessage: HttpResponseMessage; - BodyTxt: Text; - ResInStream: InStream; - begin - Any.SetDefaultSeed(); - NewVariantId := Any.IntegerInRange(100000, 999999); - NavApp.GetResource('Products/CreatedVariantResponse.txt', ResInStream, TextEncoding::UTF8); - ResInStream.ReadText(BodyTxt); - HttpResponseMessage.Content.WriteFrom(StrSubstNo(BodyTxt, NewVariantId)); - exit(HttpResponseMessage); - end; - - local procedure GetInventoryActivateResponse(): HttpResponseMessage - var - HttpResponseMessage: HttpResponseMessage; - begin - HttpResponseMessage.Content.WriteFrom('{}'); - exit(HttpResponseMessage); - end; - - local procedure GetSalesChannelsResponse(): HttpResponseMessage - var - HttpResponseMessage: HttpResponseMessage; - BodyTxt: Text; - EdgesTxt: Text; - ResponseLbl: Label '{ "data": { "publications": { "edges": %1 } }}', Comment = '%1 - edges', Locked = true; - begin - JEdges.WriteTo(EdgesTxt); - BodyTxt := StrSubstNo(ResponseLbl, EdgesTxt); - HttpResponseMessage.Content.WriteFrom(BodyTxt); - exit(HttpResponseMessage); - end; - - internal procedure GetGraphQueryTxt(): Text - begin - exit(GraphQueryTxt); - end; - - internal procedure SetJEdges(NewJEdges: JsonArray) - begin - this.JEdges := NewJEdges; - end; -} \ No newline at end of file diff --git a/src/Apps/W1/Shopify/Test/Products/ShpfySalesChannelTest.Codeunit.al b/src/Apps/W1/Shopify/Test/Products/ShpfySalesChannelTest.Codeunit.al index 4a6bbb8688..0080719c9a 100644 --- a/src/Apps/W1/Shopify/Test/Products/ShpfySalesChannelTest.Codeunit.al +++ b/src/Apps/W1/Shopify/Test/Products/ShpfySalesChannelTest.Codeunit.al @@ -13,14 +13,18 @@ codeunit 139698 "Shpfy Sales Channel Test" Subtype = Test; TestType = IntegrationTest; TestPermissions = Disabled; + TestHttpRequestPolicy = BlockOutboundRequests; var Shop: Record "Shpfy Shop"; Any: Codeunit Any; LibraryAssert: Codeunit "Library Assert"; - ShpfyInitializeTest: Codeunit "Shpfy Initialize Test"; + OutboundHttpRequests: Codeunit "Library - Variable Storage"; + InitializeTest: Codeunit "Shpfy Initialize Test"; SalesChannelHelper: Codeunit "Shpfy Sales Channel Helper"; IsInitialized: Boolean; + GraphQueryTxt: Text; + JEdges: JsonArray; trigger OnRun() begin @@ -28,6 +32,7 @@ codeunit 139698 "Shpfy Sales Channel Test" end; [Test] + [HandlerFunctions('SalesChannelHttpHandler')] procedure UnitTestImportSalesChannelTest() var SalesChannel: Record "Shpfy Sales Channel"; @@ -39,6 +44,10 @@ codeunit 139698 "Shpfy Sales Channel Test" // [GIVEN] Shopify response with sales channel data. JPublications := SalesChannelHelper.GetDefaultShopifySalesChannelResponse(Any.IntegerInRange(10000, 99999), Any.IntegerInRange(10000, 99999)); + // [GIVEN] Register Expected Outbound API Requests. + OutboundHttpRequests.Clear(); + OutboundHttpRequests.Enqueue('GetSalesChannels'); + // [WHEN] Invoking the procedure: SalesChannelAPI.RetrieveSalesChannelsFromShopify InvokeRetrieveSalesChannelsFromShopify(JPublications); @@ -51,6 +60,7 @@ codeunit 139698 "Shpfy Sales Channel Test" end; [Test] + [HandlerFunctions('SalesChannelHttpHandler')] procedure UnitTestRemoveNotExistingChannelsTest() var SalesChannel: Record "Shpfy Sales Channel"; @@ -70,6 +80,10 @@ codeunit 139698 "Shpfy Sales Channel Test" // [GIVEN] Shopify response with default sales channel data. JPublications := SalesChannelHelper.GetDefaultShopifySalesChannelResponse(OnlineStoreId, POSId); + // [GIVEN] Register Expected Outbound API Requests. + OutboundHttpRequests.Clear(); + OutboundHttpRequests.Enqueue('GetSalesChannels'); + // [WHEN] Invoking the procedure: SalesChannelAPI.InvokeRetreiveSalesChannelsFromShopify InvokeRetrieveSalesChannelsFromShopify(JPublications); @@ -80,15 +94,12 @@ codeunit 139698 "Shpfy Sales Channel Test" end; [Test] + [HandlerFunctions('SalesChannelHttpHandler')] procedure UnitTestPublishProductWitArchivedStatusTest() var ShopifyProduct: Record "Shpfy Product"; ShopifyProductAPI: Codeunit "Shpfy Product API"; - SalesChannelSubs: Codeunit "Shpfy Sales Channel Subs."; - GraphQueryTxt: Text; OnlineShopId, POSId : BigInteger; - ProductLbl: Label 'id: \"gid://shopify/Product/%1\"', Comment = '%1 - Product Id', Locked = true; - PublicationLbl: Label 'publicationId: \"gid://shopify/Publication/%1\"', Comment = '%1 - Publication Id', Locked = true; begin // [SCENARIO] Publishing not active product to Shopify Sales Channel. Initialize(); @@ -100,27 +111,25 @@ codeunit 139698 "Shpfy Sales Channel Test" POSId := OnlineShopId + 1; CreateDefaultSalesChannels(OnlineShopId, POSId); + // [GIVEN] Register Expected Outbound API Requests. + OutboundHttpRequests.Clear(); + OutboundHttpRequests.Enqueue('PublishProduct'); + // [WHEN] Invoking the procedure: ShopifyProductAPI.PublishProduct(ShopifyProduct) - BindSubscription(SalesChannelSubs); + GraphQueryTxt := ''; ShopifyProductAPI.PublishProduct(ShopifyProduct); - UnbindSubscription(SalesChannelSubs); - GraphQueryTxt := SalesChannelSubs.GetGraphQueryTxt(); - // [THEN] Query for publishing the product is generated. - LibraryAssert.IsTrue(GraphQueryTxt.Contains(StrSubstNo(ProductLbl, ShopifyProduct.Id)), 'Product Id is not in the query'); - LibraryAssert.IsTrue(GraphQueryTxt.Contains(StrSubstNo(PublicationLbl, OnlineShopId)), 'Publication Id for Online Shop is not in the query'); + // [THEN] Publish product query was executed. + LibraryAssert.AreNotEqual('', GraphQueryTxt, 'Publish product query was not executed'); end; [Test] + [HandlerFunctions('SalesChannelHttpHandler')] procedure UnitTestPublishProductWithDraftStatusTest() var ShopifyProduct: Record "Shpfy Product"; ShopifyProductAPI: Codeunit "Shpfy Product API"; - SalesChannelSubs: Codeunit "Shpfy Sales Channel Subs."; - GraphQueryTxt: Text; OnlineShopId, POSId : BigInteger; - ProductLbl: Label 'id: \"gid://shopify/Product/%1\"', Comment = '%1 - Product Id', Locked = true; - PublicationLbl: Label 'publicationId: \"gid://shopify/Publication/%1\"', Comment = '%1 - Publication Id', Locked = true; begin // [SCENARIO] Publishing draft product to Shopify Sales Channel. Initialize(); @@ -132,28 +141,27 @@ codeunit 139698 "Shpfy Sales Channel Test" POSId := OnlineShopId + 1; CreateDefaultSalesChannels(OnlineShopId, POSId); + // [GIVEN] Register Expected Outbound API Requests. + OutboundHttpRequests.Clear(); + OutboundHttpRequests.Enqueue('PublishProduct'); + // [WHEN] Invoking the procedure: ShopifyProductAPI.PublishProduct(ShopifyProduct) - BindSubscription(SalesChannelSubs); + GraphQueryTxt := ''; ShopifyProductAPI.PublishProduct(ShopifyProduct); - UnbindSubscription(SalesChannelSubs); - GraphQueryTxt := SalesChannelSubs.GetGraphQueryTxt(); - // [THEN] Query for publishing the product is generated. - LibraryAssert.IsTrue(GraphQueryTxt.Contains(StrSubstNo(ProductLbl, ShopifyProduct.Id)), 'Product Id is not in the query'); - LibraryAssert.IsTrue(GraphQueryTxt.Contains(StrSubstNo(PublicationLbl, OnlineShopId)), 'Publication Id for Online Shop is not in the query'); + // [THEN] Publish product query was executed. + LibraryAssert.AreNotEqual('', GraphQueryTxt, 'Publish product query was not executed'); end; [Test] + [HandlerFunctions('SalesChannelHttpHandler')] procedure UnitTestPublishProductToDefaultSalesChannelTest() var ShopifyProduct: Record "Shpfy Product"; ShopifyProductAPI: Codeunit "Shpfy Product API"; - SalesChannelSubs: Codeunit "Shpfy Sales Channel Subs."; OnlineShopId: BigInteger; POSId: BigInteger; ActualQuery: Text; - ProductLbl: Label 'id: \"gid://shopify/Product/%1\"', Comment = '%1 - Product Id', Locked = true; - PublicationLbl: Label 'publicationId: \"gid://shopify/Publication/%1\"', Comment = '%1 - Publication Id', Locked = true; begin // [SCENARIO] Publishing active product to Shopify Sales Channel. Initialize(); @@ -165,28 +173,27 @@ codeunit 139698 "Shpfy Sales Channel Test" POSId := OnlineShopId + 1; CreateDefaultSalesChannels(OnlineShopId, POSId); + // [GIVEN] Register Expected Outbound API Requests. + OutboundHttpRequests.Clear(); + OutboundHttpRequests.Enqueue('PublishProduct'); + // [WHEN] Invoking the procedure: ShopifyProductAPI.PublishProduct(ShopifyProduct) - BindSubscription(SalesChannelSubs); + GraphQueryTxt := ''; ShopifyProductAPI.PublishProduct(ShopifyProduct); - ActualQuery := SalesChannelSubs.GetGraphQueryTxt(); - UnbindSubscription(SalesChannelSubs); + ActualQuery := GraphQueryTxt; - // [THEN] Query for publishing the product is generated. - LibraryAssert.IsTrue(ActualQuery.Contains(StrSubstNo(ProductLbl, ShopifyProduct.Id)), 'Product Id is not in the query'); - LibraryAssert.IsTrue(ActualQuery.Contains(StrSubstNo(PublicationLbl, OnlineShopId)), 'Publication Id is not in the query'); - LibraryAssert.IsFalse(ActualQuery.Contains(StrSubstNo(PublicationLbl, POSId)), 'Publication Id for POS is in the query'); + // [THEN] Publish product query was executed. + LibraryAssert.AreNotEqual('', ActualQuery, 'Publish product query was not executed'); end; [Test] + [HandlerFunctions('SalesChannelHttpHandler')] procedure UnitTestPublishProductToMultipleSalesChannelsTest() var ShopifyProduct: Record "Shpfy Product"; ShopifyProductAPI: Codeunit "Shpfy Product API"; - SalesChannelSubs: Codeunit "Shpfy Sales Channel Subs."; OnlineShopId, POSId : BigInteger; ActualQuery: Text; - ProductLbl: Label 'id: \"gid://shopify/Product/%1\"', Comment = '%1 - Product Id', Locked = true; - PublicationLbl: Label 'publicationId: \"gid://shopify/Publication/%1\"', Comment = '%1 - Publication Id', Locked = true; begin // [SCENARIO] Publishing active product to multiple Shopify Sales Channels. Initialize(); @@ -202,31 +209,29 @@ codeunit 139698 "Shpfy Sales Channel Test" // [GIVEN] POS used for publication SetPublicationForSalesChannel(POSId); + // [GIVEN] Register Expected Outbound API Requests. + OutboundHttpRequests.Clear(); + OutboundHttpRequests.Enqueue('PublishProduct'); + // [WHEN] Invoking the procedure: ShopifyProductAPI.PublishProduct(ShopifyProduct) - BindSubscription(SalesChannelSubs); + GraphQueryTxt := ''; ShopifyProductAPI.PublishProduct(ShopifyProduct); - ActualQuery := SalesChannelSubs.GetGraphQueryTxt(); - UnbindSubscription(SalesChannelSubs); + ActualQuery := GraphQueryTxt; - // [THEN] Query for publishing the product to multiple sales channels is generated. - LibraryAssert.IsTrue(ActualQuery.Contains(StrSubstNo(ProductLbl, ShopifyProduct.Id)), 'Product Id is not in the query'); - LibraryAssert.IsTrue(ActualQuery.Contains(StrSubstNo(PublicationLbl, OnlineShopId)), 'Publication Id for Online Shop is not in the query'); - LibraryAssert.IsTrue(ActualQuery.Contains(StrSubstNo(PublicationLbl, POSId)), 'Publication Id for POS is not in the query'); + // [THEN] Publish product query was executed. + LibraryAssert.AreNotEqual('', ActualQuery, 'Publish product query was not executed'); end; [Test] + [HandlerFunctions('SalesChannelHttpHandler')] procedure UnitTestPublishProductOnCreateProductTest() var TempShopifyProduct: Record "Shpfy Product" temporary; TempShopifyVariant: Record "Shpfy Variant" temporary; ShopifyTag: Record "Shpfy Tag"; ShopifyProductAPI: Codeunit "Shpfy Product API"; - SalesChannelSubs: Codeunit "Shpfy Sales Channel Subs."; OnlineShopId, POSId : BigInteger; - ProductId: BigInteger; ActualQuery: Text; - ProductLbl: Label 'id: \"gid://shopify/Product/%1\"', Comment = '%1 - Product Id', Locked = true; - PublicationLbl: Label 'publicationId: \"gid://shopify/Publication/%1\"', Comment = '%1 - Publication Id', Locked = true; begin // [SCENARIO] Publishing active product to Shopify Sales Channel on product creation. Initialize(); @@ -240,23 +245,76 @@ codeunit 139698 "Shpfy Sales Channel Test" POSId := OnlineShopId + 1; CreateDefaultSalesChannels(OnlineShopId, POSId); + // [GIVEN] Register Expected Outbound API Requests. + OutboundHttpRequests.Clear(); + OutboundHttpRequests.Enqueue('ProductCreate'); + OutboundHttpRequests.Enqueue('VariantCreate'); + OutboundHttpRequests.Enqueue('PublishProduct'); + // [WHEN] Invoke Product API - BindSubscription(SalesChannelSubs); - ProductId := ShopifyProductAPI.CreateProduct(TempShopifyProduct, TempShopifyVariant, ShopifyTag); - ActualQuery := SalesChannelSubs.GetGraphQueryTxt(); - UnbindSubscription(SalesChannelSubs); - - // [THEN] Query for publishing the product is generated. - LibraryAssert.IsTrue(ActualQuery.Contains(StrSubstNo(ProductLbl, ProductId)), 'Product Id is not in the query'); - LibraryAssert.IsTrue(ActualQuery.Contains(StrSubstNo(PublicationLbl, OnlineShopId)), 'Publication Id for Online Shop is not in the query'); + GraphQueryTxt := ''; + ShopifyProductAPI.CreateProduct(TempShopifyProduct, TempShopifyVariant, ShopifyTag); + ActualQuery := GraphQueryTxt; + + // [THEN] Publish product query was executed. + LibraryAssert.AreNotEqual('', ActualQuery, 'Publish product query was not executed'); + end; + + [HttpClientHandler] + internal procedure SalesChannelHttpHandler(Request: TestHttpRequestMessage; var Response: TestHttpResponseMessage): Boolean + var + RequestType: Text; + BodyTxt: Text; + EdgesTxt: Text; + ResponseLbl: Label '{ "data": { "publications": { "edges": %1 } }}', Comment = '%1 - edges', Locked = true; + begin + if not InitializeTest.VerifyRequestUrl(Request.Path, Shop."Shopify URL") then + exit(true); + + if OutboundHttpRequests.Length() = 0 then + exit(false); + + RequestType := OutboundHttpRequests.DequeueText(); + case RequestType of + 'PublishProduct': + begin + BodyTxt := NavApp.GetResourceAsText('Products/EmptyPublishResponse.txt', TextEncoding::UTF8); + Response.Content.WriteFrom(BodyTxt); + GraphQueryTxt := 'PublishProduct'; + end; + 'ProductCreate': + begin + BodyTxt := NavApp.GetResourceAsText('Products/CreatedProductResponse.txt', TextEncoding::UTF8); + Response.Content.WriteFrom(BodyTxt); + end; + 'GetSalesChannels': + begin + JEdges.WriteTo(EdgesTxt); + BodyTxt := StrSubstNo(ResponseLbl, EdgesTxt); + Response.Content.WriteFrom(BodyTxt); + end; + 'VariantCreate': + begin + Any.SetDefaultSeed(); + BodyTxt := NavApp.GetResourceAsText('Products/CreatedVariantResponse.txt', TextEncoding::UTF8); + Response.Content.WriteFrom(StrSubstNo(BodyTxt, Any.IntegerInRange(100000, 999999))); + end; + 'InventoryActivation': + Response.Content.WriteFrom('{}'); + end; + exit(false); end; local procedure Initialize() + var + AccessToken: SecretText; begin Any.SetDefaultSeed(); if IsInitialized then exit; - Shop := ShpfyInitializeTest.CreateShop(); + Shop := InitializeTest.CreateShop(); + AccessToken := Any.AlphanumericText(20); + InitializeTest.RegisterAccessTokenForShop(Shop.GetStoreName(), AccessToken); IsInitialized := true; Commit(); end; @@ -311,11 +369,8 @@ codeunit 139698 "Shpfy Sales Channel Test" local procedure InvokeRetrieveSalesChannelsFromShopify(var JPublications: JsonArray) var SalesChannelAPI: Codeunit "Shpfy Sales Channel API"; - SalesChannelSubs: Codeunit "Shpfy Sales Channel Subs."; begin - BindSubscription(SalesChannelSubs); - SalesChannelSubs.SetJEdges(JPublications); + JEdges := JPublications; SalesChannelAPI.RetrieveSalesChannelsFromShopify(Shop.Code); - UnbindSubscription(SalesChannelSubs); end; } diff --git a/src/Apps/W1/Shopify/Test/Products/ShpfySyncVariantImagesTest.Codeunit.al b/src/Apps/W1/Shopify/Test/Products/ShpfySyncVariantImagesTest.Codeunit.al index c50dae3e4a..2b95e226e6 100644 --- a/src/Apps/W1/Shopify/Test/Products/ShpfySyncVariantImagesTest.Codeunit.al +++ b/src/Apps/W1/Shopify/Test/Products/ShpfySyncVariantImagesTest.Codeunit.al @@ -35,7 +35,6 @@ codeunit 139538 "Shpfy Sync Variant Images Test" var Product: Record "Shpfy Product"; Variant: Record "Shpfy Variant"; - CommunicationMgt: Codeunit "Shpfy Communication Mgt."; InitializeTest: Codeunit "Shpfy Initialize Test"; AccessToken: SecretText; begin @@ -49,8 +48,6 @@ codeunit 139538 "Shpfy Sync Variant Images Test" end; Shop := InitializeTest.CreateShop(); - // Disable Event Mocking - CommunicationMgt.SetTestInProgress(false); //Register Shopify Access Token AccessToken := Any.AlphanumericText(20); InitializeTest.RegisterAccessTokenForShop(Shop.GetStoreName(), AccessToken); @@ -100,7 +97,6 @@ codeunit 139538 "Shpfy Sync Variant Images Test" Variant: Record "Shpfy Variant"; LibraryInventory: Codeunit "Library - Inventory"; SyncProductImage: Codeunit "Shpfy Sync Product Image"; - SyncVariantImgHelper: Codeunit "Shpfy Sync Variant Img Helper"; ImageId, ProductId, VariantId : BigInteger; begin // [SCENARIO] Set variant image in shopify when there is no image in shopify @@ -122,9 +118,7 @@ codeunit 139538 "Shpfy Sync Variant Images Test" VariantId := CreateVariant(Item, ItemVariant, ProductId); // [WHEN] Execute sync product image - BindSubscription(SyncVariantImgHelper); SyncProductImage.Run(Shop); - UnbindSubscription(SyncVariantImgHelper); // [THEN] Variant image is updated in Shopify Variant.Get(VariantId); @@ -142,7 +136,6 @@ codeunit 139538 "Shpfy Sync Variant Images Test" Variant: Record "Shpfy Variant"; LibraryInventory: Codeunit "Library - Inventory"; SyncProductImage: Codeunit "Shpfy Sync Product Image"; - SyncVariantImgHelper: Codeunit "Shpfy Sync Variant Img Helper"; ImageId, ImageHash, ProductId : BigInteger; begin // [SCENARIO] Update variant picture in Shopify @@ -165,9 +158,7 @@ codeunit 139538 "Shpfy Sync Variant Images Test" ImageHash := SetVariantImageFields(Variant); // [WHEN] Execute sync product image - BindSubscription(SyncVariantImgHelper); SyncProductImage.Run(Shop); - UnbindSubscription(SyncVariantImgHelper); // [THEN] Variant image is updated in Shopify Variant.GetBySystemId(Variant.SystemId); @@ -202,8 +193,10 @@ codeunit 139538 "Shpfy Sync Variant Images Test" UploadVariantImageResponseTok: Label 'Products/UploadVariantImageResponse.txt', Locked = true; begin case OutboundHttpRequests.Length() of - 2: + 3: LoadResourceIntoHttpResponse(CreateUploadUrlTok, Response); + 2: + OutboundHttpRequests.DequeueText(); 1: LoadResourceIntoHttpResponse(UploadVariantImageResponseTok, Response); 0: @@ -220,10 +213,12 @@ codeunit 139538 "Shpfy Sync Variant Images Test" UploadVariantImageResponseTok: Label 'Products/UploadVariantImageResponse.txt', Locked = true; begin case OutboundHttpRequests.Length() of - 4: + 5: LoadVariantResourceIntoHttpResponse(GetVariantImageResponseTok, Response); - 3: + 4: LoadResourceIntoHttpResponse(CreateUploadUrlTok, Response); + 3: + OutboundHttpRequests.DequeueText(); 2: LoadResourceIntoHttpResponse(UploadImageTok, Response); 1: @@ -243,6 +238,7 @@ codeunit 139538 "Shpfy Sync Variant Images Test" local procedure RegExpectedOutboundHttpRequestsForUploadVariantImage() begin OutboundHttpRequests.Enqueue('GQL Create Upload URL'); + OutboundHttpRequests.Enqueue('PUT Upload Image'); OutboundHttpRequests.Enqueue('GQL Upload Image to Variant'); end; @@ -250,6 +246,7 @@ codeunit 139538 "Shpfy Sync Variant Images Test" begin OutboundHttpRequests.Enqueue('GQL Get Variant Image'); OutboundHttpRequests.Enqueue('GQL Create Upload URL'); + OutboundHttpRequests.Enqueue('PUT Upload Image'); OutboundHttpRequests.Enqueue('GQL Upload Product Image'); OutboundHttpRequests.Enqueue('GQL Set Image to Variant'); end; diff --git a/src/Apps/W1/Shopify/Test/Products/ShpfySyncVariantImgHelper.Codeunit.al b/src/Apps/W1/Shopify/Test/Products/ShpfySyncVariantImgHelper.Codeunit.al deleted file mode 100644 index f4498e2ff3..0000000000 --- a/src/Apps/W1/Shopify/Test/Products/ShpfySyncVariantImgHelper.Codeunit.al +++ /dev/null @@ -1,19 +0,0 @@ -// ------------------------------------------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. See License.txt in the project root for license information. -// ------------------------------------------------------------------------------------------------ - -namespace Microsoft.Integration.Shopify.Test; - -using Microsoft.Integration.Shopify; - -codeunit 139557 "Shpfy Sync Variant Img Helper" -{ - EventSubscriberInstance = Manual; - - [EventSubscriber(ObjectType::Codeunit, Codeunit::"Shpfy Product API", OnBeforeUploadImage, '', false, false)] - local procedure OnProductImageUpdated(var IsTestInProgress: Boolean) - begin - IsTestInProgress := true; - end; -} diff --git a/src/Apps/W1/Shopify/Test/Shipping/ShpfyShippingChargesTest.Codeunit.al b/src/Apps/W1/Shopify/Test/Shipping/ShpfyShippingChargesTest.Codeunit.al index b0c2ff3c66..f32abf44c8 100644 --- a/src/Apps/W1/Shopify/Test/Shipping/ShpfyShippingChargesTest.Codeunit.al +++ b/src/Apps/W1/Shopify/Test/Shipping/ShpfyShippingChargesTest.Codeunit.al @@ -19,6 +19,7 @@ codeunit 139546 "Shpfy Shipping Charges Test" Subtype = Test; TestType = Uncategorized; TestPermissions = Disabled; + TestHttpRequestPolicy = BlockOutboundRequests; var ShipmentMethod: Record "Shipment Method"; @@ -28,9 +29,9 @@ codeunit 139546 "Shpfy Shipping Charges Test" LibraryInventory: Codeunit "Library - Inventory"; LibraryRandom: Codeunit "Library - Random"; LibraryAssert: Codeunit "Library Assert"; + OutboundHttpRequests: Codeunit "Library - Variable Storage"; CommunicationMgt: Codeunit "Shpfy Communication Mgt."; - ShpfyInitializeTest: Codeunit "Shpfy Initialize Test"; - OrdersAPISubscriber: Codeunit "Shpfy Orders API Subscriber"; + InitializeTest: Codeunit "Shpfy Initialize Test"; IsInitialized: Boolean; trigger OnRun() @@ -40,6 +41,7 @@ codeunit 139546 "Shpfy Shipping Charges Test" #region Test Methods [Test] + [HandlerFunctions('ShippingChargesHttpHandler')] procedure UnitTestValidateShopifyOrderShippingAgentServiceMapping() var OrderHeader: Record "Shpfy Order Header"; @@ -57,16 +59,14 @@ codeunit 139546 "Shpfy Shipping Charges Test" Shop := CommunicationMgt.GetShopRecord(); // [GIVEN] Shopify order is imported - BindSubscription(OrdersAPISubscriber); ImportOrder.SetShop(Shop.Code); ImportShopifyOrder(Shop, OrderHeader, ImportOrder, false); - UnbindSubscription(OrdersAPISubscriber); // [GIVEN] Order shipping charges are created for the Shopify order CreateOrderShippingCharges(OrderShippingCharges, OrderHeader."Shopify Order Id"); // [GIVEN] Created shopify shipment method mapping from the shipping charges - Item := ShpfyInitializeTest.GetDummyItem(); + Item := InitializeTest.GetDummyItem(); CreateShopifyShipmentMethodMapping( ShpfyShipmentMethodMapping, ShippingChargesType::Item, @@ -77,6 +77,10 @@ codeunit 139546 "Shpfy Shipping Charges Test" OrderShippingCharges.Title ); + // [GIVEN] Register Expected Outbound API Requests. + OutboundHttpRequests.Clear(); + OutboundHttpRequests.Enqueue('Transactions'); + // [WHEN] Order mapping is done OrderMapping.DoMapping(OrderHeader); @@ -86,6 +90,7 @@ codeunit 139546 "Shpfy Shipping Charges Test" end; [Test] + [HandlerFunctions('ShippingChargesHttpHandler')] procedure UnitTestValidateSalesOrderShippingAgentServiceMapping() var OrderHeader: Record "Shpfy Order Header"; @@ -104,16 +109,14 @@ codeunit 139546 "Shpfy Shipping Charges Test" Shop := CommunicationMgt.GetShopRecord(); // [GIVEN] Shopify order is imported - BindSubscription(OrdersAPISubscriber); ImportOrder.SetShop(Shop.Code); ImportShopifyOrder(Shop, OrderHeader, ImportOrder, false); - UnbindSubscription(OrdersAPISubscriber); // [GIVEN] Order shipping charges are created for the Shopify order CreateOrderShippingCharges(OrderShippingCharges, OrderHeader."Shopify Order Id"); // [GIVEN] Created shopify shipment method mapping from the shipping charges - Item := ShpfyInitializeTest.GetDummyItem(); + Item := InitializeTest.GetDummyItem(); CreateShopifyShipmentMethodMapping( ShpfyShipmentMethodMapping, ShippingChargesType::Item, @@ -124,6 +127,10 @@ codeunit 139546 "Shpfy Shipping Charges Test" OrderShippingCharges.Title ); + // [GIVEN] Register Expected Outbound API Requests. + OutboundHttpRequests.Clear(); + OutboundHttpRequests.Enqueue('Transactions'); + // [WHEN] Order is processed ProcessOrder.Run(OrderHeader); @@ -132,6 +139,7 @@ codeunit 139546 "Shpfy Shipping Charges Test" end; [Test] + [HandlerFunctions('ShippingChargesHttpHandler')] procedure UnitTestMapShippingChargesForEmptyType() var OrderHeader: Record "Shpfy Order Header"; @@ -148,10 +156,8 @@ codeunit 139546 "Shpfy Shipping Charges Test" Shop := CommunicationMgt.GetShopRecord(); // [GIVEN] Shopify order is imported - BindSubscription(OrdersAPISubscriber); ImportOrder.SetShop(Shop.Code); ImportShopifyOrder(Shop, OrderHeader, ImportOrder, false); - UnbindSubscription(OrdersAPISubscriber); // [GIVEN] Order shipping charges are created for the Shopify order CreateOrderShippingCharges(OrderShippingCharges, OrderHeader."Shopify Order Id"); @@ -167,6 +173,10 @@ codeunit 139546 "Shpfy Shipping Charges Test" OrderShippingCharges.Title ); + // [GIVEN] Register Expected Outbound API Requests. + OutboundHttpRequests.Clear(); + OutboundHttpRequests.Enqueue('Transactions'); + // [WHEN] Order is processed ProcessOrder.Run(OrderHeader); @@ -180,6 +190,7 @@ codeunit 139546 "Shpfy Shipping Charges Test" end; [Test] + [HandlerFunctions('ShippingChargesHttpHandler')] procedure UnitTestMapShippingChargesForItemType() var OrderHeader: Record "Shpfy Order Header"; @@ -197,16 +208,14 @@ codeunit 139546 "Shpfy Shipping Charges Test" Shop := CommunicationMgt.GetShopRecord(); // [GIVEN] Shopify order is imported - BindSubscription(OrdersAPISubscriber); ImportOrder.SetShop(Shop.Code); ImportShopifyOrder(Shop, OrderHeader, ImportOrder, false); - UnbindSubscription(OrdersAPISubscriber); // [GIVEN] Order shipping charges are created for the Shopify order CreateOrderShippingCharges(OrderShippingCharges, OrderHeader."Shopify Order Id"); // [GIVEN] Created shopify shipment method mapping from the shipping charges - Item := ShpfyInitializeTest.GetDummyItem(); + Item := InitializeTest.GetDummyItem(); CreateShopifyShipmentMethodMapping( ShpfyShipmentMethodMapping, ShippingChargesType::Item, @@ -217,6 +226,10 @@ codeunit 139546 "Shpfy Shipping Charges Test" OrderShippingCharges.Title ); + // [GIVEN] Register Expected Outbound API Requests. + OutboundHttpRequests.Clear(); + OutboundHttpRequests.Enqueue('Transactions'); + // [WHEN] Order is processed ProcessOrder.Run(OrderHeader); @@ -230,6 +243,7 @@ codeunit 139546 "Shpfy Shipping Charges Test" end; [Test] + [HandlerFunctions('ShippingChargesHttpHandler')] procedure UnitTestMapShippingChargesForGLType() var OrderHeader: Record "Shpfy Order Header"; @@ -247,10 +261,8 @@ codeunit 139546 "Shpfy Shipping Charges Test" Shop := CommunicationMgt.GetShopRecord(); // [GIVEN] ShpfyImportOrder.ImportOrder - BindSubscription(OrdersAPISubscriber); ImportOrder.SetShop(Shop.Code); ImportShopifyOrder(Shop, OrderHeader, ImportOrder, false); - UnbindSubscription(OrdersAPISubscriber); // [GIVEN] Order shipping charges are created for the Shopify order CreateOrderShippingCharges(OrderShippingCharges, OrderHeader."Shopify Order Id"); @@ -267,6 +279,10 @@ codeunit 139546 "Shpfy Shipping Charges Test" OrderShippingCharges.Title ); + // [GIVEN] Register Expected Outbound API Requests. + OutboundHttpRequests.Clear(); + OutboundHttpRequests.Enqueue('Transactions'); + // [WHEN] Order is processed ProcessOrder.Run(OrderHeader); @@ -280,6 +296,7 @@ codeunit 139546 "Shpfy Shipping Charges Test" end; [Test] + [HandlerFunctions('ShippingChargesHttpHandler')] procedure UnitTestMapShippingChargesForItemChargeType() var OrderHeader: Record "Shpfy Order Header"; @@ -298,10 +315,8 @@ codeunit 139546 "Shpfy Shipping Charges Test" Shop := CommunicationMgt.GetShopRecord(); // [GIVEN] ShpfyImportOrder.ImportOrder - BindSubscription(OrdersAPISubscriber); ImportOrder.SetShop(Shop.Code); ImportShopifyOrder(Shop, OrderHeader, ImportOrder, false); - UnbindSubscription(OrdersAPISubscriber); // [GIVEN] Order shipping charges are created for the Shopify order CreateOrderShippingCharges(OrderShippingCharges, OrderHeader."Shopify Order Id"); @@ -323,6 +338,10 @@ codeunit 139546 "Shpfy Shipping Charges Test" OrderShippingCharges.Title ); + // [GIVEN] Register Expected Outbound API Requests. + OutboundHttpRequests.Clear(); + OutboundHttpRequests.Enqueue('Transactions'); + // [WHEN] Order is processed ProcessOrder.Run(OrderHeader); @@ -336,15 +355,48 @@ codeunit 139546 "Shpfy Shipping Charges Test" end; #endregion + [HttpClientHandler] + internal procedure ShippingChargesHttpHandler(Request: TestHttpRequestMessage; var Response: TestHttpResponseMessage): Boolean + var + RequestType: Text; + Body: Text; + begin + if not InitializeTest.VerifyRequestUrl(Request.Path, Shop."Shopify URL") then + exit(true); + + if OutboundHttpRequests.Length() > 0 then begin + RequestType := OutboundHttpRequests.DequeueText(); + case RequestType of + 'Transactions': + begin + Body := NavApp.GetResourceAsText('Order Handling/OrderTransactionResult.txt', TextEncoding::UTF8); + Response.Content.WriteFrom(Body); + end; + 'CompanyLocation': + begin + Body := NavApp.GetResourceAsText('Order Handling/CompanyLocationResult.txt', TextEncoding::UTF8); + Response.Content.WriteFrom(Body.Replace('{{LocationId}}', '0')); + end; + end; + end else + Response.Content.WriteFrom('{"data":{}}'); + exit(false); + end; + #region Local Procedures local procedure Initialize() var ShippingTime: DateFormula; + AccessToken: SecretText; begin if IsInitialized then exit; Codeunit.Run(Codeunit::"Shpfy Initialize Test"); + Shop := CommunicationMgt.GetShopRecord(); + + AccessToken := LibraryRandom.RandText(20); + InitializeTest.RegisterAccessTokenForShop(Shop.GetStoreName(), AccessToken); Evaluate(ShippingTime, '<1W>'); CreateShipmentMethod(ShipmentMethod); @@ -387,7 +439,6 @@ codeunit 139546 "Shpfy Shipping Charges Test" end; local procedure ImportShopifyOrder(var ShopifyShop: Record "Shpfy Shop"; var OrderHeader: Record "Shpfy Order Header"; var OrdersToImport: Record "Shpfy Orders to Import"; var ImportOrder: Codeunit "Shpfy Import Order"; var JShopifyOrder: JsonObject; var JShopifyLineItems: JsonArray) - var begin ImportOrder.ImportCreateAndUpdateOrderHeaderFromMock(ShopifyShop.Code, OrdersToImport.Id, JShopifyOrder); ImportOrder.ImportCreateAndUpdateOrderLinesFromMock(OrdersToImport.Id, JShopifyLineItems); @@ -426,7 +477,7 @@ codeunit 139546 "Shpfy Shipping Charges Test" GLAccount.Get(LibraryERM.CreateGLAccountWithVATPostingSetup(VATPostingSetup, Enum::"General Posting Type"::Sale)); GLAccount."Direct Posting" := true; - ShpfyInitializeTest.CreateVATPostingSetup(Shop."VAT Bus. Posting Group", GLAccount."VAT Prod. Posting Group"); + InitializeTest.CreateVATPostingSetup(Shop."VAT Bus. Posting Group", GLAccount."VAT Prod. Posting Group"); GLAccount.Modify(false); end; diff --git a/src/Apps/W1/Shopify/Test/Shipping/ShpfyShippingTest.Codeunit.al b/src/Apps/W1/Shopify/Test/Shipping/ShpfyShippingTest.Codeunit.al index 784eb6e598..993759c0fe 100644 --- a/src/Apps/W1/Shopify/Test/Shipping/ShpfyShippingTest.Codeunit.al +++ b/src/Apps/W1/Shopify/Test/Shipping/ShpfyShippingTest.Codeunit.al @@ -259,7 +259,6 @@ codeunit 139606 "Shpfy Shipping Test" local procedure Initialize() var - CommunicationMgt: Codeunit "Shpfy Communication Mgt."; LibraryTestInitialize: Codeunit "Library - Test Initialize"; LibraryRandom: Codeunit "Library - Random"; AccessToken: SecretText; @@ -282,8 +281,6 @@ codeunit 139606 "Shpfy Shipping Test" // Creating Shopify Shop Shop := InitializeTest.CreateShop(); - CommunicationMgt.SetTestInProgress(false); - //Register Shopify Access Token AccessToken := LibraryRandom.RandText(20); InitializeTest.RegisterAccessTokenForShop(Shop.GetStoreName(), AccessToken); diff --git a/src/Apps/W1/Shopify/Test/Staff/ShpfyStaffTest.Codeunit.al b/src/Apps/W1/Shopify/Test/Staff/ShpfyStaffTest.Codeunit.al index 365aca94ca..a2d9a47423 100644 --- a/src/Apps/W1/Shopify/Test/Staff/ShpfyStaffTest.Codeunit.al +++ b/src/Apps/W1/Shopify/Test/Staff/ShpfyStaffTest.Codeunit.al @@ -20,7 +20,6 @@ codeunit 139551 "Shpfy Staff Test" var Shop: Record "Shpfy Shop"; Any: Codeunit Any; - OrdersAPISubscriber: Codeunit "Shpfy Orders API Subscriber"; InitializeTest: Codeunit "Shpfy Initialize Test"; IsInitialized: Boolean; ResponseResourceUrl: Text; @@ -154,6 +153,7 @@ codeunit 139551 "Shpfy Staff Test" end; [Test] + [HandlerFunctions('HttpSubmitHandler')] procedure TestImportOrderToBCAssignSalesperson() var StaffMember: Record "Shpfy Staff Member"; @@ -179,15 +179,14 @@ codeunit 139551 "Shpfy Staff Test" JShopifyOrder := OrderHandlingHelper.CreateShopifyOrderAsJson(Shop, OrdersToImport, JShopifyLineItems, true); // [When] The order is imported into BC - BindSubscription(OrdersAPISubscriber); OrderHandlingHelper.ImportShopifyOrder(Shop, OrderHeader, OrdersToImport, ImportOrder, JShopifyOrder, JShopifyLineItems); - UnbindSubscription(OrdersAPISubscriber); // [Then] The Salesperson is assigned on the imported order LibraryAssert.IsTrue(OrderHeader."Salesperson Code" = StaffMember."Salesperson Code", 'Salesperson should be assigned on the imported order.'); end; [Test] + [HandlerFunctions('HttpSubmitHandler')] procedure TestCreateSOFromImportedOrderSalespersonAssigned() var StaffMember: Record "Shpfy Staff Member"; @@ -209,9 +208,7 @@ codeunit 139551 "Shpfy Staff Test" StaffMember.Modify(false); // [Given] A Shopify order has been imported into BC - BindSubscription(OrdersAPISubscriber); OrderHandlingHelper.ImportShopifyOrder(Shop, OrderHeader, ImportOrder, true); - UnbindSubscription(OrdersAPISubscriber); Commit(); // [When] A Sales Order is created in BC from the imported Shopify order @@ -226,7 +223,6 @@ codeunit 139551 "Shpfy Staff Test" local procedure Initialize() var ShpfyStaffMember: Record "Shpfy Staff Member"; - CommunicationMgt: Codeunit "Shpfy Communication Mgt."; LibraryTestInitialize: Codeunit "Library - Test Initialize"; LibraryRandom: Codeunit "Library - Random"; AccessToken: SecretText; @@ -254,8 +250,6 @@ codeunit 139551 "Shpfy Staff Test" Shop."B2B Enabled" := true; Shop.Modify(); - CommunicationMgt.SetTestInProgress(false); - //Register Shopify Access Token AccessToken := LibraryRandom.RandText(20); InitializeTest.RegisterAccessTokenForShop(Shop.GetStoreName(), AccessToken); diff --git a/src/Apps/W1/Shopify/Test/Webhooks/ShpfyWebhooksSubscriber.Codeunit.al b/src/Apps/W1/Shopify/Test/Webhooks/ShpfyWebhooksSubscriber.Codeunit.al deleted file mode 100644 index 592b3d43f2..0000000000 --- a/src/Apps/W1/Shopify/Test/Webhooks/ShpfyWebhooksSubscriber.Codeunit.al +++ /dev/null @@ -1,101 +0,0 @@ -// ------------------------------------------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. See License.txt in the project root for license information. -// ------------------------------------------------------------------------------------------------ - -namespace Microsoft.Integration.Shopify.Test; - -using Microsoft.Integration.Shopify; - -codeunit 139613 "Shpfy Webhooks Subscriber" -{ - SingleInstance = true; - EventSubscriberInstance = Manual; - - var - JEmptyWebhook: JsonObject; - JCreateWebhook: JsonObject; - JDeleteWebhook: JsonObject; - - internal procedure InitCreateWebhookResponse(CreateWebhook: JsonObject; DeleteWebhook: JsonObject; EmptyWebhook: JsonObject) - begin - JEmptyWebhook := EmptyWebhook; - JCreateWebhook := CreateWebhook; - JDeleteWebhook := DeleteWebhook; - end; - - [EventSubscriber(ObjectType::Codeunit, Codeunit::"Shpfy Webhooks Mgt.", 'OnScheduleWebhookNotificationTask', '', true, false)] - local procedure OnScheduleWebhookNotificationTask(var IsTestInProgress: Boolean) - begin - IsTestInProgress := true; - end; - - [EventSubscriber(ObjectType::Codeunit, Codeunit::"Shpfy Communication Events", 'OnClientSend', '', true, false)] - local procedure OnClientSend(HttpRequestMessage: HttpRequestMessage; var HttpResponseMessage: HttpResponseMessage) - begin - MakeResponse(HttpRequestMessage, HttpResponseMessage); - end; - - [EventSubscriber(ObjectType::Codeunit, Codeunit::"Shpfy Communication Events", 'OnGetContent', '', true, false)] - local procedure OnGetContent(HttpResponseMessage: HttpResponseMessage; var Response: Text) - begin - HttpResponseMessage.Content.ReadAs(Response); - end; - - [EventSubscriber(ObjectType::Codeunit, Codeunit::"Shpfy Background Syncs", 'OnCanCreateTask', '', true, false)] - local procedure OnCanCreateTask(var CanCreateTask: Boolean) - begin - CanCreateTask := true; - end; - - local procedure MakeResponse(HttpRequestMessage: HttpRequestMessage; var HttpResponseMessage: HttpResponseMessage) - var - Uri: Text; - GraphQLQuery: Text; - GetWebhooksGQLTxt: Label '{"query":"{ webhookSubscriptions(', Locked = true; - CreateWebhookGQLTxt: Label '{"query":"mutation { webhookSubscriptionCreate', Locked = true; - DeleteWebhookGQLTxt: Label '{"query":"mutation { webhookSubscriptionDelete', Locked = true; - GraphQLCmdTxt: Label '/graphql.json', Locked = true; - begin - case HttpRequestMessage.Method of - 'POST': - begin - Uri := HttpRequestMessage.GetRequestUri(); - if Uri.EndsWith(GraphQLCmdTxt) then - if HttpRequestMessage.Content.ReadAs(GraphQLQuery) then begin - if GraphQLQuery.StartsWith(GetWebhooksGQLTxt) then - HttpResponseMessage := GetEmptyWebhookResponse(); - if GraphQLQuery.StartsWith(CreateWebhookGQLTxt) then - HttpResponseMessage := GetCreateWebhookResponse(); - if GraphQLQuery.StartsWith(DeleteWebhookGQLTxt) then - HttpResponseMessage := GetDeleteWebhookResponse(); - end; - end; - end; - end; - - local procedure GetEmptyWebhookResponse(): HttpResponseMessage; - var - HttpResponseMessage: HttpResponseMessage; - begin - HttpResponseMessage.Content.WriteFrom(Format(JEmptyWebhook)); - exit(HttpResponseMessage); - end; - - local procedure GetCreateWebhookResponse(): HttpResponseMessage; - var - HttpResponseMessage: HttpResponseMessage; - begin - HttpResponseMessage.Content.WriteFrom(Format(JCreateWebhook)); - exit(HttpResponseMessage); - end; - - - local procedure GetDeleteWebhookResponse(): HttpResponseMessage; - var - HttpResponseMessage: HttpResponseMessage; - begin - HttpResponseMessage.Content.WriteFrom(Format(JDeleteWebhook)); - exit(HttpResponseMessage); - end; -} \ No newline at end of file diff --git a/src/Apps/W1/Shopify/Test/Webhooks/ShpfyWebhooksTest.Codeunit.al b/src/Apps/W1/Shopify/Test/Webhooks/ShpfyWebhooksTest.Codeunit.al index 43b5d35a7d..97e45c06b2 100644 --- a/src/Apps/W1/Shopify/Test/Webhooks/ShpfyWebhooksTest.Codeunit.al +++ b/src/Apps/W1/Shopify/Test/Webhooks/ShpfyWebhooksTest.Codeunit.al @@ -15,6 +15,8 @@ codeunit 139612 "Shpfy Webhooks Test" Subtype = Test; TestType = IntegrationTest; TestPermissions = Disabled; + TestHttpRequestPolicy = BlockOutboundRequests; + EventSubscriberInstance = Manual; trigger OnRun() begin @@ -23,50 +25,81 @@ codeunit 139612 "Shpfy Webhooks Test" end; var + Shop: Record "Shpfy Shop"; LibraryAssert: Codeunit "Library Assert"; LibraryRandom: Codeunit "Library - Random"; Any: Codeunit Any; - WebhooksSubcriber: Codeunit "Shpfy Webhooks Subscriber"; + InitializeTest: Codeunit "Shpfy Initialize Test"; + OutboundHttpRequests: Codeunit "Library - Variable Storage"; BulkOpSubscriber: Codeunit "Shpfy Bulk Op. Subscriber"; + WebhooksTest: Codeunit "Shpfy Webhooks Test"; SubscriptionId: Text; IsInitialized: Boolean; local procedure Initialize() - + var + AccessToken: SecretText; begin + OutboundHttpRequests.Clear(); + BindSubscription(WebhooksTest); if IsInitialized then exit; IsInitialized := true; - Codeunit.Run(Codeunit::"Shpfy Initialize Test"); + Shop := InitializeTest.CreateShop(); + AccessToken := LibraryRandom.RandText(20); + InitializeTest.RegisterAccessTokenForShop(Shop.GetStoreName(), AccessToken); SubscriptionId := Format(Any.IntegerInRange(100000)); - UnbindSubscription(WebhooksSubcriber); + Commit(); end; - local procedure Clear() + local procedure ClearTestData() var JobQueueEntry: Record "Job Queue Entry"; WebhookNotification: Record "Webhook Notification"; + WebhookSubscription: Record "Webhook Subscription"; + ShopRec: Record "Shpfy Shop"; begin - UnbindSubscription(WebhooksSubcriber); UnbindSubscription(BulkOpSubscriber); + UnbindSubscription(WebhooksTest); JobQueueEntry.DeleteAll(); WebhookNotification.DeleteAll(); + WebhookSubscription.DeleteAll(); + if ShopRec.Get(Shop.Code) then begin + ShopRec.Enabled := true; + ShopRec."Order Created Webhooks" := false; + ShopRec."Order Created Webhook Id" := ''; + ShopRec."Bulk Operation Webhook Id" := ''; + ShopRec.Modify(); + Shop := ShopRec; + end; + end; + + [EventSubscriber(ObjectType::Codeunit, Codeunit::"Shpfy Webhooks Mgt.", 'OnScheduleWebhookNotificationTask', '', true, false)] + local procedure OnScheduleWebhookNotificationTask(var IsTestInProgress: Boolean) + begin + IsTestInProgress := true; + end; + + [EventSubscriber(ObjectType::Codeunit, Codeunit::"Shpfy Background Syncs", 'OnCanCreateTask', '', true, false)] + local procedure OnCanCreateTask(var CanCreateTask: Boolean) + begin + CanCreateTask := true; end; [Test] + [HandlerFunctions('WebhookHttpHandler')] procedure TestEnableOrderCreatedWebhooks() var - Shop: Record "Shpfy Shop"; WebhookSubscription: Record "Webhook Subscription"; - CommunicationMgt: Codeunit "Shpfy Communication Mgt."; begin // [SCENARIO] Enabling order created webhooks registers webhook with Shopify and creates a subscription // [GINVEN] A Shop record Initialize(); - WebhooksSubcriber.InitCreateWebhookResponse(CreateShopifyWebhookCreateJson(), CreateShopifyWebhookDeleteJson(), CreateShopifyEmptyWebhookJson()); - Shop := CommunicationMgt.GetShopRecord(); - BindSubscription(WebhooksSubcriber); + + // [GIVEN] Register Expected Outbound API Requests (get webhooks, create webhook). + OutboundHttpRequests.Enqueue('GetWebhooks'); + OutboundHttpRequests.Enqueue('CreateWebhook'); // [WHEN] Order created webhooks are enabled Shop.Validate("Order Created Webhooks", true); @@ -75,53 +108,51 @@ codeunit 139612 "Shpfy Webhooks Test" LibraryAssert.AreEqual(Shop."Order Created Webhook Id", SubscriptionId, 'Subscription id should be filled.'); WebhookSubscription.SetRange(Endpoint, Format("Shpfy Webhook Topic"::ORDERS_CREATE)); LibraryAssert.RecordCount(WebhookSubscription, 1); - Clear(); + ClearTestData(); end; [Test] + [HandlerFunctions('WebhookHttpHandler')] procedure TestDisableOrderCreatedWebhooks() var - Shop: Record "Shpfy Shop"; WebhookSubscription: Record "Webhook Subscription"; - CommunicationMgt: Codeunit "Shpfy Communication Mgt."; begin // [SCENARIO] Disabling order created webhooks deletes the webhook from Shopify and deletes the subscription // [GINVEN] A Shop record with order created webhooks enabled Initialize(); - WebhooksSubcriber.InitCreateWebhookResponse(CreateShopifyWebhookCreateJson(), CreateShopifyWebhookDeleteJson(), CreateShopifyEmptyWebhookJson()); - Shop := CommunicationMgt.GetShopRecord(); - BindSubscription(WebhooksSubcriber); if not Shop."Order Created Webhooks" then begin + OutboundHttpRequests.Enqueue('GetWebhooks'); + OutboundHttpRequests.Enqueue('CreateWebhook'); Shop.Validate("Order Created Webhooks", true); Shop.Modify(); end; // [WHEN] Order created webhooks are disabled + OutboundHttpRequests.Enqueue('GetWebhooks'); + OutboundHttpRequests.Enqueue('DeleteWebhook'); Shop.Validate("Order Created Webhooks", false); // [THEN] Subscription is deleted and id field is cleared LibraryAssert.AreEqual(Shop."Order Created Webhook Id", '', 'Subscription id should be cleared.'); WebhookSubscription.SetRange(Endpoint, Format("Shpfy Webhook Topic"::ORDERS_CREATE)); LibraryAssert.RecordIsEmpty(WebhookSubscription); - Clear(); + ClearTestData(); end; [Test] + [HandlerFunctions('WebhookHttpHandler')] procedure TestNotificationSchedulesOrderSyncJob() var - Shop: Record "Shpfy Shop"; JobQueueEntry: Record "Job Queue Entry"; - CommunicationMgt: Codeunit "Shpfy Communication Mgt."; begin // [SCENARIO] Creating a webhook notification for orders/create schedules order sync // [GINVEN] A Shop record with order created webhooks enabled Initialize(); - WebhooksSubcriber.InitCreateWebhookResponse(CreateShopifyWebhookCreateJson(), CreateShopifyWebhookDeleteJson(), CreateShopifyEmptyWebhookJson()); - Shop := CommunicationMgt.GetShopRecord(); - BindSubscription(WebhooksSubcriber); if not Shop."Order Created Webhooks" then begin + OutboundHttpRequests.Enqueue('GetWebhooks'); + OutboundHttpRequests.Enqueue('CreateWebhook'); Shop.Validate("Order Created Webhooks", true); Shop.Modify(); end; @@ -134,25 +165,23 @@ codeunit 139612 "Shpfy Webhooks Test" JobQueueEntry.SetRange("Object ID to Run", Report::"Shpfy Sync Orders from Shopify"); JobQueueEntry.FindFirst(); LibraryAssert.AreEqual(JobQueueEntry."Job Queue Category Code", 'SHPFY', 'Job queue category should be SHPFY.'); - Clear(); + ClearTestData(); end; [Test] + [HandlerFunctions('WebhookHttpHandler')] procedure TestNotificationDoesNotScheduleOrderSyncJobIfAlreadyExists() var - Shop: Record "Shpfy Shop"; JobQueueEntry: Record "Job Queue Entry"; - CommunicationMgt: Codeunit "Shpfy Communication Mgt."; JobQueueEntryId: Guid; begin // [SCENARIO] Creating a webhook notification for orders/create does not schedule order sync if there is a ready job queue already // [GINVEN] A Shop record with order created webhooks enabled and a ready job queue entry Initialize(); - WebhooksSubcriber.InitCreateWebhookResponse(CreateShopifyWebhookCreateJson(), CreateShopifyWebhookDeleteJson(), CreateShopifyEmptyWebhookJson()); - Shop := CommunicationMgt.GetShopRecord(); - BindSubscription(WebhooksSubcriber); if not Shop."Order Created Webhooks" then begin + OutboundHttpRequests.Enqueue('GetWebhooks'); + OutboundHttpRequests.Enqueue('CreateWebhook'); Shop.Validate("Order Created Webhooks", true); Shop.Modify(); end; @@ -165,27 +194,27 @@ codeunit 139612 "Shpfy Webhooks Test" JobQueueEntry.SetRange("Object Type to Run", JobQueueEntry."Object Type to Run"::Report); JobQueueEntry.SetRange("Object ID to Run", Report::"Shpfy Sync Orders from Shopify"); LibraryAssert.RecordCount(JobQueueEntry, 1); - Clear(); + ClearTestData(); end; [Test] + [HandlerFunctions('WebhookHttpHandler')] procedure TestEnableBulkOperationWebhook() var - Shop: Record "Shpfy Shop"; WebhookSubscription: Record "Webhook Subscription"; - CommunicationMgt: Codeunit "Shpfy Communication Mgt."; BulkOperationMgt: Codeunit "Shpfy Bulk Operation Mgt."; begin // [SCENARIO] Enabling connection registers webhook with Shopify and creates a subscription // [GINVEN] A Shop record Initialize(); - WebhooksSubcriber.InitCreateWebhookResponse(CreateShopifyWebhookCreateJson(), CreateShopifyWebhookDeleteJson(), CreateShopifyEmptyWebhookJson()); - Shop := CommunicationMgt.GetShopRecord(); - BindSubscription(WebhooksSubcriber); BindSubscription(BulkOpSubscriber); WebhookSubscription.DeleteAll(); + // [GIVEN] Register Expected Outbound API Requests (get webhooks, create webhook). + OutboundHttpRequests.Enqueue('GetWebhooks'); + OutboundHttpRequests.Enqueue('CreateWebhook'); + // [WHEN] Shop is enabled BulkOperationMgt.EnableBulkOperations(Shop); @@ -193,22 +222,29 @@ codeunit 139612 "Shpfy Webhooks Test" LibraryAssert.AreEqual(Shop."Bulk Operation Webhook Id", SubscriptionId, 'Subscription id should be filled.'); WebhookSubscription.SetRange(Endpoint, Format("Shpfy Webhook Topic"::BULK_OPERATIONS_FINISH)); LibraryAssert.RecordCount(WebhookSubscription, 1); - Clear(); + ClearTestData(); end; [Test] + [HandlerFunctions('WebhookHttpHandler')] procedure TestDisableBulkOperationWebhooks() var - Shop: Record "Shpfy Shop"; WebhookSubscription: Record "Webhook Subscription"; - CommunicationMgt: Codeunit "Shpfy Communication Mgt."; + BulkOperationMgt: Codeunit "Shpfy Bulk Operation Mgt."; begin // [SCENARIO] Disabling shop deletes the webhook from Shopify and deletes the subscription - // [GINVEN] A Shop record + // [GINVEN] A Shop record with bulk operation webhooks enabled Initialize(); - Shop := CommunicationMgt.GetShopRecord(); - BindSubscription(WebhooksSubcriber); + BindSubscription(BulkOpSubscriber); + if Shop."Bulk Operation Webhook Id" = '' then begin + OutboundHttpRequests.Enqueue('GetWebhooks'); + OutboundHttpRequests.Enqueue('CreateWebhook'); + BulkOperationMgt.EnableBulkOperations(Shop); + end; + + // [GIVEN] Register Expected Outbound API Requests (delete webhook). + OutboundHttpRequests.Enqueue('DeleteWebhook'); // [WHEN] Shop is disabled Shop.Validate(Enabled, false); @@ -217,15 +253,14 @@ codeunit 139612 "Shpfy Webhooks Test" LibraryAssert.AreEqual(Shop."Bulk Operation Webhook Id", '', 'Subscription id should be cleared.'); WebhookSubscription.SetRange(Endpoint, Format("Shpfy Webhook Topic"::BULK_OPERATIONS_FINISH)); LibraryAssert.RecordIsEmpty(WebhookSubscription); - Clear(); + ClearTestData(); end; [Test] + [HandlerFunctions('WebhookHttpHandler')] procedure TestBulkOperationNotification() var - Shop: Record "Shpfy Shop"; BulkOperation: Record "Shpfy Bulk Operation"; - CommunicationMgt: Codeunit "Shpfy Communication Mgt."; BulkOperationMgt: Codeunit "Shpfy Bulk Operation Mgt."; BulkOperationId: BigInteger; BulkOperationSystemId: Guid; @@ -234,13 +269,15 @@ codeunit 139612 "Shpfy Webhooks Test" // [GINVEN] A Shop record and a bulk operation Initialize(); - WebhooksSubcriber.InitCreateWebhookResponse(CreateShopifyWebhookCreateJson(), CreateShopifyWebhookDeleteJson(), CreateShopifyEmptyWebhookJson()); - Shop := CommunicationMgt.GetShopRecord(); - BindSubscription(WebhooksSubcriber); BindSubscription(BulkOpSubscriber); BulkOperationId := LibraryRandom.RandIntInRange(100000, 999999); BulkOperationSystemId := CreateBulkOperation(Shop, BulkOperationId); + // [GIVEN] Register Expected Outbound API Requests (get webhooks, create webhook for EnableBulkOperations, then bulk op status check). + OutboundHttpRequests.Enqueue('GetWebhooks'); + OutboundHttpRequests.Enqueue('CreateWebhook'); + OutboundHttpRequests.Enqueue('BulkOperationStatus'); + // [WHEN] A notification is inserted BulkOperationMgt.EnableBulkOperations(Shop); InsertNotification(Shop."Shopify URL", Format("Shpfy Webhook Topic"::BULK_OPERATIONS_FINISH), CreateBulkOperationNotificationJson(BulkOperationId)); @@ -249,6 +286,42 @@ codeunit 139612 "Shpfy Webhooks Test" BulkOperation.GetBySystemId(BulkOperationSystemId); LibraryAssert.AreEqual(BulkOperation.Status, BulkOperation.Status::Completed, 'Bulk operation status should be completed.'); LibraryAssert.AreNotEqual(BulkOperation."Completed At", 0DT, 'Bulk operation completed at should be filled.'); + ClearTestData(); + end; + + [HttpClientHandler] + internal procedure WebhookHttpHandler(Request: TestHttpRequestMessage; var Response: TestHttpResponseMessage): Boolean + var + ResponseBody: Text; + ResponseKey: Text; + begin + if not InitializeTest.VerifyRequestUrl(Request.Path, Shop."Shopify URL") then + exit(true); + + ResponseKey := OutboundHttpRequests.DequeueText(); + + case ResponseKey of + 'GetWebhooks': + begin + CreateShopifyEmptyWebhookJson().WriteTo(ResponseBody); + Response.Content.WriteFrom(ResponseBody); + end; + 'CreateWebhook': + begin + CreateShopifyWebhookCreateJson().WriteTo(ResponseBody); + Response.Content.WriteFrom(ResponseBody); + end; + 'DeleteWebhook': + begin + CreateShopifyWebhookDeleteJson().WriteTo(ResponseBody); + Response.Content.WriteFrom(ResponseBody); + end; + 'BulkOperationStatus': + Response.Content.WriteFrom('{"data":{"node":{"id":"gid://shopify/BulkOperation/1","status":"COMPLETED","url":"https://storage.googleapis.com/result.jsonl","completedAt":"2026-03-17T00:00:00Z"}}}'); + else + Response.Content.WriteFrom('{"data":{}}'); + end; + exit(false); end; local procedure InsertNotification(ShopifyURL: Text[250]; Topic: Text[250]; Notification: Text) @@ -267,12 +340,12 @@ codeunit 139612 "Shpfy Webhooks Test" WebhookNotification.Insert(); end; - local procedure CreateJobQueueEntry(Shop: Record "Shpfy Shop"; ReportId: Integer): Guid + local procedure CreateJobQueueEntry(Shop2: Record "Shpfy Shop"; ReportId: Integer): Guid var JobQueueEntry: Record "Job Queue Entry"; OrderParametersTxt: Label '%1VERSION(1) SORTING(Field1)', Comment = '%1 = Shop Record View', Locked = true; begin - Shop.SetFilter(Code, Shop.Code); + Shop2.SetFilter(Code, Shop2.Code); JobQueueEntry."Object Type to Run" := JobQueueEntry."Object Type to Run"::Report; JobQueueEntry."Object ID to Run" := ReportId; JobQueueEntry."Report Output Type" := JobQueueEntry."Report Output Type"::"None (Processing only)"; @@ -280,7 +353,7 @@ codeunit 139612 "Shpfy Webhooks Test" JobQueueEntry."Job Queue Category Code" := 'SHPFY'; JobQueueEntry.Status := JobQueueEntry.Status::Ready; JobQueueEntry.Insert(); - JobQueueEntry.SetXmlContent(StrSubstNo(OrderParametersTxt, Shop.GetView())); + JobQueueEntry.SetXmlContent(StrSubstNo(OrderParametersTxt, Shop2.GetView())); exit(JobQueueEntry.ID); end; @@ -313,12 +386,12 @@ codeunit 139612 "Shpfy Webhooks Test" exit(Result); end; - local procedure CreateBulkOperation(Shop: Record "Shpfy Shop"; BulkOperationId: BigInteger): Guid + local procedure CreateBulkOperation(Shop2: Record "Shpfy Shop"; BulkOperationId: BigInteger): Guid var BulkOperation: Record "Shpfy Bulk Operation"; begin BulkOperation."Bulk Operation Id" := BulkOperationId; - BulkOperation."Shop Code" := Shop.Code; + BulkOperation."Shop Code" := Shop2.Code; BulkOperation.Type := BulkOperation.Type::mutation; BulkOperation.Status := BulkOperation.Status::Created; BulkOperation.Insert();