From ecbe61abef3f02a8ab2bd66f646ae088c4b50d9d Mon Sep 17 00:00:00 2001 From: Mihir Bhalala <77438541+mihir-4116@users.noreply.github.com> Date: Wed, 13 Mar 2024 12:33:49 +0530 Subject: [PATCH 1/2] chore: refactor intercom proxy tests to new component structure (#3088) * chore: refactor intercom proxy tests to new component structure * chore: code review changes * chore: code review changes * chore: code review changes * chore: code review changes --- .../intercom/dataDelivery/business.ts | 615 ++++++++++++++++++ .../intercom/dataDelivery/data.ts | 96 +-- .../intercom/dataDelivery/other.ts | 384 +++++++++++ .../destinations/intercom/network.ts | 358 +++++++--- 4 files changed, 1271 insertions(+), 182 deletions(-) create mode 100644 test/integrations/destinations/intercom/dataDelivery/business.ts create mode 100644 test/integrations/destinations/intercom/dataDelivery/other.ts diff --git a/test/integrations/destinations/intercom/dataDelivery/business.ts b/test/integrations/destinations/intercom/dataDelivery/business.ts new file mode 100644 index 0000000000..2490041832 --- /dev/null +++ b/test/integrations/destinations/intercom/dataDelivery/business.ts @@ -0,0 +1,615 @@ +import { + generateMetadata, + generateProxyV0Payload, + generateProxyV1Payload, +} from '../../../testUtils'; + +const commonHeaders = { + 'Content-Type': 'application/json', + Authorization: 'Bearer testApiKey', + Accept: 'application/json', + 'Intercom-Version': '1.4', +}; + +const unauthorizedResponseHeaders = { + 'Content-Type': 'application/json', + Authorization: 'Bearer invalidApiKey', + Accept: 'application/json', + 'Intercom-Version': '1.4', +}; + +const createUserPayload = { + email: 'test_1@test.com', + phone: '9876543210', + name: 'Test Name', + signed_up_at: 1601493060, + last_seen_user_agent: 'unknown', + update_last_request_at: true, + user_id: 'test_user_id_1', + custom_attributes: { + 'address.city': 'Kolkata', + 'address.state': 'West Bengal', + }, +}; + +const conflictUserPayload = { + email: 'test_1@test.com', + name: 'Rudder Labs', + signed_up_at: 1601496060, + last_seen_user_agent: 'unknown', + update_last_request_at: true, + user_id: 'test_user_id_2', + custom_attributes: { + 'address.city': 'Kolkata', + 'address.state': 'West Bengal', + }, +}; + +const updateUserPayload = { + email: 'test_1@test.com', + phone: '9876543211', + name: 'Sample Name', + signed_up_at: 1601493060, + update_last_request_at: true, + user_id: 'test_user_id_1', + custom_attributes: { + 'address.city': 'Kolkata', + 'address.state': 'West Bengal', + }, +}; + +const createCompanyPayload = { + company_id: 'rudderlabs', + name: 'RudderStack', + website: 'www.rudderstack.com', + plan: 'enterprise', + size: 500, + industry: 'CDP', + custom_attributes: { isOpenSource: true }, +}; + +const sendMessagePayload = { + from: { + type: 'user', + id: 'id@1', + }, + body: 'heyy, how are you', + referer: 'https://twitter.com/bob', +}; + +const createUserRequestParameters = { + JSON: createUserPayload, + headers: commonHeaders, +}; + +const updateUserRequestParameters = { + JSON: updateUserPayload, + headers: commonHeaders, +}; + +const createCompanyRequestParameters = { + JSON: createCompanyPayload, + headers: commonHeaders, +}; + +const sendMessageRequestParameters = { + JSON: sendMessagePayload, + headers: commonHeaders, +}; + +const metadataArray = [generateMetadata(1)]; + +export const testScenariosForV0API = [ + { + id: 'intercom_v0_other_scenario_1', + name: 'intercom', + description: + '[Proxy v0 API] :: Scenario to test Invalid Credentials Handling during Destination Authentication', + successCriteria: 'Should return 401 status code with error message', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v0', + input: { + request: { + body: generateProxyV0Payload({ + ...createUserRequestParameters, + headers: unauthorizedResponseHeaders, + endpoint: 'https://api.intercom.io/users/test1', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 401, + body: { + output: { + destinationResponse: { + errors: [ + { + code: 'unauthorized', + message: 'Access Token Invalid', + }, + ], + request_id: 'request123', + type: 'error.list', + }, + message: 'Request Processed Successfully', + status: 401, + }, + }, + }, + }, + }, + { + id: 'intercom_v0_other_scenario_2', + name: 'intercom', + description: + '[Proxy v0 API] :: Scenario to test Malformed Payload Response Handling from Destination', + successCriteria: 'Should return 401 status code with error message', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v0', + input: { + request: { + body: generateProxyV0Payload({ + ...createCompanyRequestParameters, + endpoint: 'https://api.eu.intercom.io/companies', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 401, + body: { + output: { + destinationResponse: { + errors: [ + { + code: 'parameter_invalid', + message: "Custom attribute 'isOpenSource' does not exist", + }, + ], + request_id: 'request_1', + type: 'error.list', + }, + message: 'Request Processed Successfully', + status: 401, + }, + }, + }, + }, + }, + { + id: 'intercom_v0_other_scenario_3', + name: 'intercom', + description: + '[Proxy v0 API] :: Scenario to test Plan-Restricted Response Handling from Destination', + successCriteria: 'Should return 403 status code with error message', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v0', + input: { + request: { + body: generateProxyV0Payload({ + ...sendMessageRequestParameters, + endpoint: 'https://api.intercom.io/messages', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 403, + body: { + output: { + destinationResponse: { + errors: [ + { + code: 'api_plan_restricted', + message: 'Active subscription needed.', + }, + ], + request_id: 'request124', + type: 'error.list', + }, + message: 'Request Processed Successfully', + status: 403, + }, + }, + }, + }, + }, + { + id: 'intercom_v0_other_scenario_4', + name: 'intercom', + description: '[Proxy v0 API] :: Scenario to test Rate Limit Exceeded Handling from Destination', + successCriteria: 'Should return 429 status code with error message', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v0', + input: { + request: { + body: generateProxyV0Payload({ + ...updateUserRequestParameters, + endpoint: 'https://api.intercom.io/users/test1', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 429, + body: { + output: { + destinationResponse: { + errors: [ + { + code: 'rate_limit_exceeded', + message: 'The rate limit for the App has been exceeded', + }, + ], + request_id: 'request125', + type: 'error.list', + }, + message: 'Request Processed Successfully', + status: 429, + }, + }, + }, + }, + }, + { + id: 'intercom_v0_other_scenario_5', + name: 'intercom', + description: '[Proxy v0 API] :: Scenario to test Conflict User Handling from Destination', + successCriteria: 'Should return 409 status code with error message', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v0', + input: { + request: { + body: generateProxyV0Payload({ + JSON: conflictUserPayload, + headers: { + ...commonHeaders, + 'Intercom-Version': '2.10', + }, + endpoint: 'https://api.intercom.io/contacts', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 409, + body: { + output: { + destinationResponse: { + errors: [ + { + code: 'conflict', + message: 'A contact matching those details already exists with id=test1', + }, + ], + request_id: 'request126', + type: 'error.list', + }, + message: 'Request Processed Successfully', + status: 409, + }, + }, + }, + }, + }, + { + id: 'intercom_v0_other_scenario_6', + name: 'intercom', + description: '[Proxy v0 API] :: Scenario to test Unsupported Media Handling from Destination', + successCriteria: 'Should return 406 status code with error message', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v0', + input: { + request: { + body: generateProxyV0Payload({ + JSON: createUserPayload, + headers: { + ...commonHeaders, + 'Intercom-Version': '2.10', + }, + endpoint: 'https://api.intercom.io/users', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 406, + body: { + output: { + destinationResponse: { + errors: [ + { + code: 'media_type_not_acceptable', + message: 'The Accept header should send a media type of application/json', + }, + ], + type: 'error.list', + }, + message: 'Request Processed Successfully', + status: 406, + }, + }, + }, + }, + }, +]; + +export const testScenariosForV1API = [ + { + id: 'intercom_v1_other_scenario_1', + name: 'intercom', + description: + '[Proxy v1 API] :: Scenario to test Invalid Credentials Handling during Destination Authentication', + successCriteria: 'Should return 401 status code with error message', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + ...createUserRequestParameters, + headers: unauthorizedResponseHeaders, + endpoint: 'https://api.intercom.io/users/test1', + }, + metadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + message: 'Request Processed Successfully', + response: [ + { + error: + '{"type":"error.list","request_id":"request123","errors":[{"code":"unauthorized","message":"Access Token Invalid"}]}', + metadata: generateMetadata(1), + statusCode: 401, + }, + ], + status: 401, + }, + }, + }, + }, + }, + { + id: 'intercom_v1_other_scenario_2', + name: 'intercom', + description: + '[Proxy v1 API] :: Scenario to test Malformed Payload Response Handling from Destination', + successCriteria: 'Should return 401 status code with error message', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + ...createCompanyRequestParameters, + endpoint: 'https://api.eu.intercom.io/companies', + }, + metadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + message: 'Request Processed Successfully', + response: [ + { + error: + '{"type":"error.list","request_id":"request_1","errors":[{"code":"parameter_invalid","message":"Custom attribute \'isOpenSource\' does not exist"}]}', + metadata: generateMetadata(1), + statusCode: 401, + }, + ], + status: 401, + }, + }, + }, + }, + }, + { + id: 'intercom_v1_other_scenario_3', + name: 'intercom', + description: + '[Proxy v1 API] :: Scenario to test Plan-Restricted Response Handling from Destination', + successCriteria: 'Should return 403 status code with error message', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + ...sendMessageRequestParameters, + endpoint: 'https://api.intercom.io/messages', + }, + metadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + message: 'Request Processed Successfully', + response: [ + { + error: + '{"type":"error.list","request_id":"request124","errors":[{"code":"api_plan_restricted","message":"Active subscription needed."}]}', + metadata: generateMetadata(1), + statusCode: 403, + }, + ], + status: 403, + }, + }, + }, + }, + }, + { + id: 'intercom_v1_other_scenario_4', + name: 'intercom', + description: '[Proxy v1 API] :: Scenario to test Rate Limit Exceeded Handling from Destination', + successCriteria: 'Should return 429 status code with error message', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + ...updateUserRequestParameters, + endpoint: 'https://api.intercom.io/users/test1', + }, + metadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + message: 'Request Processed Successfully', + response: [ + { + error: + '{"type":"error.list","request_id":"request125","errors":[{"code":"rate_limit_exceeded","message":"The rate limit for the App has been exceeded"}]}', + metadata: generateMetadata(1), + statusCode: 429, + }, + ], + status: 429, + }, + }, + }, + }, + }, + { + id: 'intercom_v1_other_scenario_5', + name: 'intercom', + description: '[Proxy v1 API] :: Scenario to test Conflict User Handling from Destination', + successCriteria: 'Should return 409 status code with error message', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + JSON: conflictUserPayload, + headers: { + ...commonHeaders, + 'Intercom-Version': '2.10', + }, + endpoint: 'https://api.intercom.io/contacts', + }, + metadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + message: 'Request Processed Successfully', + response: [ + { + error: + '{"type":"error.list","request_id":"request126","errors":[{"code":"conflict","message":"A contact matching those details already exists with id=test1"}]}', + metadata: generateMetadata(1), + statusCode: 409, + }, + ], + status: 409, + }, + }, + }, + }, + }, + { + id: 'intercom_v1_other_scenario_6', + name: 'intercom', + description: '[Proxy v1 API] :: Scenario to test Unsupported Media Handling from Destination', + successCriteria: 'Should return 406 status code with error message', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + JSON: createUserPayload, + headers: { + ...commonHeaders, + 'Intercom-Version': '2.10', + }, + endpoint: 'https://api.intercom.io/users', + }, + metadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + message: 'Request Processed Successfully', + response: [ + { + error: + '{"errors":[{"code":"media_type_not_acceptable","message":"The Accept header should send a media type of application/json"}],"type":"error.list"}', + metadata: generateMetadata(1), + statusCode: 406, + }, + ], + status: 406, + }, + }, + }, + }, + }, +]; diff --git a/test/integrations/destinations/intercom/dataDelivery/data.ts b/test/integrations/destinations/intercom/dataDelivery/data.ts index db7aafc963..e5d2f4696b 100644 --- a/test/integrations/destinations/intercom/dataDelivery/data.ts +++ b/test/integrations/destinations/intercom/dataDelivery/data.ts @@ -1,91 +1,9 @@ +import { otherScenariosV0, otherScenariosV1 } from './other'; +import { testScenariosForV0API, testScenariosForV1API } from './business'; + export const data = [ - { - name: 'intercom', - description: 'Test 0', - feature: 'dataDelivery', - module: 'destination', - version: 'v0', - input: { - request: { - body: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://api.intercom.io/users/test1', - headers: { - 'Content-Type': 'application/json', - Authorization: 'Bearer intercomApiKey', - Accept: 'application/json', - 'Intercom-Version': '1.4', - }, - params: {}, - body: { - JSON: { - email: 'test_1@test.com', - phone: '9876543210', - name: 'Test Name', - signed_up_at: 1601493060, - last_seen_user_agent: 'unknown', - update_last_request_at: true, - user_id: 'test_user_id_1', - custom_attributes: { - anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - key1: 'value1', - 'address.city': 'Kolkata', - 'address.state': 'West Bengal', - 'originalArray[0].nested_field': 'nested value', - 'originalArray[0].tags[0]': 'tag_1', - 'originalArray[0].tags[1]': 'tag_2', - 'originalArray[0].tags[2]': 'tag_3', - 'originalArray[1].nested_field': 'nested value', - 'originalArray[1].tags[0]': 'tag_1', - 'originalArray[2].nested_field': 'nested value', - }, - }, - XML: {}, - JSON_ARRAY: {}, - FORM: {}, - }, - files: {}, - userId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - }, - method: 'POST', - }, - }, - output: { - response: { - status: 500, - body: { - output: { - status: 500, - message: - '[Intercom Response Handler] Request failed for destination intercom with status: 408', - destinationResponse: { - response: { - type: 'error.list', - request_id: '000on04msi4jpk7d3u60', - errors: [ - { - code: 'Request Timeout', - message: 'The server would not wait any longer for the client', - }, - ], - }, - status: 408, - }, - statTags: { - destType: 'INTERCOM', - errorCategory: 'network', - destinationId: 'Non-determininable', - workspaceId: 'Non-determininable', - errorType: 'retryable', - feature: 'dataDelivery', - implementation: 'native', - module: 'destination', - }, - }, - }, - }, - }, - }, + ...otherScenariosV0, + ...otherScenariosV1, + ...testScenariosForV0API, + ...testScenariosForV1API, ]; diff --git a/test/integrations/destinations/intercom/dataDelivery/other.ts b/test/integrations/destinations/intercom/dataDelivery/other.ts new file mode 100644 index 0000000000..46cdcac468 --- /dev/null +++ b/test/integrations/destinations/intercom/dataDelivery/other.ts @@ -0,0 +1,384 @@ +import { ProxyV1TestData } from '../../../testTypes'; +import { + generateMetadata, + generateProxyV0Payload, + generateProxyV1Payload, +} from '../../../testUtils'; + +const commonHeaders = { + 'Content-Type': 'application/json', + Authorization: 'Bearer testApiKey', + Accept: 'application/json', + 'Intercom-Version': '1.4', +}; + +const createUserPayload = { + email: 'test_1@test.com', + phone: '9876543210', + name: 'Test Name', + signed_up_at: 1601493060, + last_seen_user_agent: 'unknown', + update_last_request_at: true, + user_id: 'test_user_id_1', + custom_attributes: { + 'address.city': 'Kolkata', + 'address.state': 'West Bengal', + }, +}; + +const commonRequestParameters = { + JSON: createUserPayload, + headers: commonHeaders, +}; + +const expectedStatTags = { + destType: 'INTERCOM', + destinationId: 'default-destinationId', + errorCategory: 'network', + errorType: 'retryable', + feature: 'dataDelivery', + implementation: 'native', + module: 'destination', + workspaceId: 'default-workspaceId', +}; + +const metadataArray = [generateMetadata(1)]; + +export const otherScenariosV0 = [ + { + id: 'intercom_v0_other_scenario_1', + name: 'intercom', + description: '[Proxy v0 API] :: Request Timeout Error Handling from Destination', + successCriteria: 'Should return 500 status code with error message', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v0', + input: { + request: { + body: generateProxyV0Payload({ + ...commonRequestParameters, + endpoint: 'https://api.intercom.io/users/test1', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 500, + body: { + output: { + status: 500, + message: + '[Intercom Response Handler] Request failed for destination intercom with status: 408', + destinationResponse: { + response: { + type: 'error.list', + request_id: '000on04msi4jpk7d3u60', + errors: [ + { + code: 'Request Timeout', + message: 'The server would not wait any longer for the client', + }, + ], + }, + status: 408, + }, + statTags: expectedStatTags, + }, + }, + }, + }, + }, + { + id: 'intercom_v0_other_scenario_2', + name: 'intercom', + description: + '[Proxy v0 API] :: Scenario for testing Service Unavailable error from destination', + successCriteria: 'Should return 503 status code with destination response', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v0', + input: { + request: { + body: generateProxyV0Payload({ + ...commonRequestParameters, + endpoint: 'https://api.intercom.io/users/test2', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 503, + body: { + output: { + status: 503, + message: 'Request Processed Successfully', + destinationResponse: { + type: 'error.list', + request_id: 'request127', + errors: [ + { + code: 'service_unavailable', + message: 'Sorry, the API service is temporarily unavailable', + }, + ], + }, + }, + }, + }, + }, + }, + { + id: 'intercom_v0_other_scenario_3', + name: 'intercom', + description: '[Proxy v0 API] :: Scenario for testing Internal Server error from destination', + successCriteria: 'Should return 500 status code with destination response', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v0', + input: { + request: { + body: generateProxyV0Payload({ + ...commonRequestParameters, + endpoint: 'https://api.intercom.io/users/test3', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 500, + body: { + output: { + destinationResponse: { + errors: [ + { + code: 'client_error', + message: 'Unknown server error', + }, + ], + request_id: 'request128', + type: 'error.list', + }, + message: 'Request Processed Successfully', + status: 500, + }, + }, + }, + }, + }, + { + id: 'intercom_v0_other_scenario_4', + name: 'intercom', + description: '[Proxy v0 API] :: Scenario for testing Gateway Time Out error from destination', + successCriteria: 'Should return 504 status code with destination response', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v0', + input: { + request: { + body: generateProxyV0Payload({ + ...commonRequestParameters, + endpoint: 'https://api.intercom.io/users/test4', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 504, + body: { + output: { + destinationResponse: { + errors: [ + { + code: 'server_timeout', + message: 'Server timed out when making request', + }, + ], + request_id: 'request129', + type: 'error.list', + }, + message: 'Request Processed Successfully', + status: 504, + }, + }, + }, + }, + }, +]; + +export const otherScenariosV1: ProxyV1TestData[] = [ + { + id: 'intercom_v1_other_scenario_1', + name: 'intercom', + description: '[Proxy v1 API] :: Request Timeout Error Handling from Destination', + successCriteria: 'Should return 500 status code with error message', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + ...commonRequestParameters, + endpoint: 'https://api.intercom.io/users/test1', + }, + metadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + message: + '[Intercom Response Handler] Request failed for destination intercom with status: 408', + response: [ + { + error: + '{"type":"error.list","request_id":"000on04msi4jpk7d3u60","errors":[{"code":"Request Timeout","message":"The server would not wait any longer for the client"}]}', + metadata: generateMetadata(1), + statusCode: 500, + }, + ], + statTags: expectedStatTags, + status: 500, + }, + }, + }, + }, + }, + { + id: 'intercom_v1_other_scenario_2', + name: 'intercom', + description: + '[Proxy v1 API] :: Scenario for testing Service Unavailable error from destination', + successCriteria: 'Should return 503 status code with destination response', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + ...commonRequestParameters, + endpoint: 'https://api.intercom.io/users/test2', + }, + metadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + message: 'Request Processed Successfully', + response: [ + { + error: + '{"type":"error.list","request_id":"request127","errors":[{"code":"service_unavailable","message":"Sorry, the API service is temporarily unavailable"}]}', + metadata: generateMetadata(1), + statusCode: 503, + }, + ], + status: 503, + }, + }, + }, + }, + }, + { + id: 'intercom_v1_other_scenario_3', + name: 'intercom', + description: '[Proxy v1 API] :: Scenario for testing Internal Server error from destination', + successCriteria: 'Should return 500 status code with destination response', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + ...commonRequestParameters, + endpoint: 'https://api.intercom.io/users/test3', + }, + metadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + message: 'Request Processed Successfully', + response: [ + { + error: + '{"type":"error.list","request_id":"request128","errors":[{"code":"client_error","message":"Unknown server error"}]}', + metadata: generateMetadata(1), + statusCode: 500, + }, + ], + status: 500, + }, + }, + }, + }, + }, + { + id: 'intercom_v1_other_scenario_4', + name: 'intercom', + description: '[Proxy v1 API] :: Scenario for testing Gateway Time Out error from destination', + successCriteria: 'Should return 504 status code with destination response', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + ...commonRequestParameters, + endpoint: 'https://api.intercom.io/users/test4', + }, + metadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + message: 'Request Processed Successfully', + response: [ + { + error: + '{"type":"error.list","request_id":"request129","errors":[{"code":"server_timeout","message":"Server timed out when making request"}]}', + metadata: generateMetadata(1), + statusCode: 504, + }, + ], + status: 504, + }, + }, + }, + }, + }, +]; diff --git a/test/integrations/destinations/intercom/network.ts b/test/integrations/destinations/intercom/network.ts index 74c861259f..5fa8ac6e96 100644 --- a/test/integrations/destinations/intercom/network.ts +++ b/test/integrations/destinations/intercom/network.ts @@ -1,3 +1,48 @@ +const commonHeaders = { + Accept: 'application/json', + Authorization: 'Bearer testApiKey', + 'Content-Type': 'application/json', +}; + +const v0VersionHeaders = { + 'Content-Type': 'application/json', + Authorization: 'Bearer testApiKey', + Accept: 'application/json', + 'Intercom-Version': '1.4', + 'User-Agent': 'RudderLabs', +}; + +const v1VersionHeaders = { + 'Content-Type': 'application/json', + Authorization: 'Bearer testApiKey', + Accept: 'application/json', + 'Intercom-Version': '2.10', + 'User-Agent': 'RudderLabs', +}; + +const userPayload = { + email: 'test_1@test.com', + phone: '9876543210', + name: 'Test Name', + signed_up_at: 1601493060, + last_seen_user_agent: 'unknown', + update_last_request_at: true, + user_id: 'test_user_id_1', + custom_attributes: { + 'address.city': 'Kolkata', + 'address.state': 'West Bengal', + }, +}; + +const companyPayload = { + company_id: 'rudderlabs', + name: 'RudderStack', + website: 'www.rudderstack.com', + plan: 'enterprise', + size: 500, + industry: 'CDP', +}; + const deleteNwData = [ { httpReq: { @@ -6,11 +51,7 @@ const deleteNwData = [ data: { intercom_user_id: '1', }, - headers: { - Accept: 'application/json', - Authorization: 'Bearer testApiKey', - 'Content-Type': 'application/json', - }, + headers: commonHeaders, }, httpRes: { data: { @@ -33,11 +74,7 @@ const deleteNwData = [ data: { intercom_user_id: '12', }, - headers: { - Accept: 'application/json', - Authorization: 'Bearer testApiKey', - 'Content-Type': 'application/json', - }, + headers: commonHeaders, }, httpRes: { status: 200, @@ -54,11 +91,7 @@ const deleteNwData = [ data: { intercom_user_id: '7', }, - headers: { - Accept: 'application/json', - Authorization: 'Bearer testApiKey', - 'Content-Type': 'application/json', - }, + headers: commonHeaders, }, httpRes: { status: 200, @@ -75,11 +108,7 @@ const deleteNwData = [ data: { intercom_user_id: '9', }, - headers: { - Accept: 'application/json', - Authorization: 'Bearer testApiKey', - 'Content-Type': 'application/json', - }, + headers: commonHeaders, }, httpRes: { status: 200, @@ -89,6 +118,8 @@ const deleteNwData = [ }, }, }, +]; +const deliveryCallsData = [ { httpReq: { method: 'post', @@ -99,11 +130,7 @@ const deleteNwData = [ value: [{ field: 'email', operator: '=', value: 'test@rudderlabs.com' }], }, }, - headers: { - Accept: 'application/json', - Authorization: 'Bearer testApiKey', - 'Content-Type': 'application/json', - }, + headers: commonHeaders, }, httpRes: { status: 200, @@ -131,11 +158,7 @@ const deleteNwData = [ value: [{ field: 'email', operator: '=', value: 'test+2@rudderlabs.com' }], }, }, - headers: { - Accept: 'application/json', - Authorization: 'Bearer testApiKey', - 'Content-Type': 'application/json', - }, + headers: commonHeaders, }, httpRes: { status: 200, @@ -172,11 +195,7 @@ const deleteNwData = [ value: [{ field: 'email', operator: '=', value: 'test+5@rudderlabs.com' }], }, }, - headers: { - Accept: 'application/json', - Authorization: 'Bearer testApiKey', - 'Content-Type': 'application/json', - }, + headers: commonHeaders, }, httpRes: { status: 200, @@ -213,11 +232,7 @@ const deleteNwData = [ value: [{ field: 'phone', operator: '=', value: '+91 9299999999' }], }, }, - headers: { - Accept: 'application/json', - Authorization: 'Bearer testApiKey', - 'Content-Type': 'application/json', - }, + headers: commonHeaders, }, httpRes: { status: 200, @@ -254,11 +269,7 @@ const deleteNwData = [ value: [{ field: 'email', operator: '=', value: 'test+4@rudderlabs.com' }], }, }, - headers: { - Accept: 'application/json', - Authorization: 'Bearer testApiKey', - 'Content-Type': 'application/json', - }, + headers: commonHeaders, }, httpRes: { status: 200, @@ -310,19 +321,8 @@ const deleteNwData = [ httpReq: { method: 'post', url: 'https://api.eu.intercom.io/companies', - data: { - company_id: 'rudderlabs', - name: 'RudderStack', - website: 'www.rudderstack.com', - plan: 'enterprise', - size: 500, - industry: 'CDP', - }, - headers: { - Accept: 'application/json', - Authorization: 'Bearer testApiKey', - 'Content-Type': 'application/json', - }, + data: companyPayload, + headers: commonHeaders, }, httpRes: { status: 200, @@ -346,19 +346,10 @@ const deleteNwData = [ method: 'post', url: 'https://api.eu.intercom.io/companies', data: { - company_id: 'rudderlabs', - name: 'RudderStack', - website: 'www.rudderstack.com', - plan: 'enterprise', - size: 500, - industry: 'CDP', + ...companyPayload, custom_attributes: { isOpenSource: true }, }, - headers: { - Accept: 'application/json', - Authorization: 'Bearer testApiKey', - 'Content-Type': 'application/json', - }, + headers: commonHeaders, }, httpRes: { status: 401, @@ -374,55 +365,236 @@ const deleteNwData = [ }, }, }, -]; -const deliveryCallsData = [ + { + httpReq: { + url: 'https://api.intercom.io/users/test1', + data: userPayload, + params: {}, + headers: v0VersionHeaders, + method: 'POST', + }, + httpRes: { + data: { + type: 'error.list', + request_id: '000on04msi4jpk7d3u60', + errors: [ + { + code: 'Request Timeout', + message: 'The server would not wait any longer for the client', + }, + ], + }, + status: 408, + }, + }, + { + httpReq: { + url: 'https://api.intercom.io/users/test1', + data: userPayload, + params: {}, + headers: { + 'Content-Type': 'application/json', + Authorization: 'Bearer invalidApiKey', + Accept: 'application/json', + 'Intercom-Version': '1.4', + 'User-Agent': 'RudderLabs', + }, + method: 'POST', + }, + httpRes: { + data: { + type: 'error.list', + request_id: 'request123', + errors: [ + { + code: 'unauthorized', + message: 'Access Token Invalid', + }, + ], + }, + status: 401, + }, + }, + { + httpReq: { + url: 'https://api.intercom.io/messages', + data: { + from: { + type: 'user', + id: 'id@1', + }, + body: 'heyy, how are you', + referer: 'https://twitter.com/bob', + }, + params: {}, + headers: v0VersionHeaders, + method: 'POST', + }, + httpRes: { + data: { + type: 'error.list', + request_id: 'request124', + errors: [ + { + code: 'api_plan_restricted', + message: 'Active subscription needed.', + }, + ], + }, + status: 403, + }, + }, { httpReq: { url: 'https://api.intercom.io/users/test1', data: { email: 'test_1@test.com', - phone: '9876543210', - name: 'Test Name', + phone: '9876543211', + name: 'Sample Name', signed_up_at: 1601493060, - last_seen_user_agent: 'unknown', update_last_request_at: true, user_id: 'test_user_id_1', custom_attributes: { - anonymousId: '58b21c2d-f8d5-4410-a2d0-b268a26b7e33', - key1: 'value1', 'address.city': 'Kolkata', 'address.state': 'West Bengal', - 'originalArray[0].nested_field': 'nested value', - 'originalArray[0].tags[0]': 'tag_1', - 'originalArray[0].tags[1]': 'tag_2', - 'originalArray[0].tags[2]': 'tag_3', - 'originalArray[1].nested_field': 'nested value', - 'originalArray[1].tags[0]': 'tag_1', - 'originalArray[2].nested_field': 'nested value', }, }, params: {}, - headers: { - 'Content-Type': 'application/json', - Authorization: 'Bearer intercomApiKey', - Accept: 'application/json', - 'Intercom-Version': '1.4', - 'User-Agent': 'RudderLabs', + headers: v0VersionHeaders, + method: 'POST', + }, + httpRes: { + data: { + type: 'error.list', + request_id: 'request125', + errors: [ + { + code: 'rate_limit_exceeded', + message: 'The rate limit for the App has been exceeded', + }, + ], }, + status: 429, + }, + }, + { + httpReq: { + url: 'https://api.intercom.io/contacts', + data: { + email: 'test_1@test.com', + name: 'Rudder Labs', + signed_up_at: 1601496060, + last_seen_user_agent: 'unknown', + update_last_request_at: true, + user_id: 'test_user_id_2', + custom_attributes: { + 'address.city': 'Kolkata', + 'address.state': 'West Bengal', + }, + }, + params: {}, + headers: v1VersionHeaders, method: 'POST', }, httpRes: { data: { type: 'error.list', - request_id: '000on04msi4jpk7d3u60', + request_id: 'request126', errors: [ { - code: 'Request Timeout', - message: 'The server would not wait any longer for the client', + code: 'conflict', + message: 'A contact matching those details already exists with id=test1', }, ], }, - status: 408, + status: 409, + }, + }, + { + httpReq: { + url: 'https://api.intercom.io/users', + data: userPayload, + params: {}, + headers: v1VersionHeaders, + method: 'POST', + }, + httpRes: { + data: { + errors: [ + { + code: 'media_type_not_acceptable', + message: 'The Accept header should send a media type of application/json', + }, + ], + type: 'error.list', + }, + status: 406, + }, + }, + { + httpReq: { + url: 'https://api.intercom.io/users/test2', + data: userPayload, + params: {}, + headers: v0VersionHeaders, + method: 'POST', + }, + httpRes: { + data: { + type: 'error.list', + request_id: 'request127', + errors: [ + { + code: 'service_unavailable', + message: 'Sorry, the API service is temporarily unavailable', + }, + ], + }, + status: 503, + }, + }, + { + httpReq: { + url: 'https://api.intercom.io/users/test3', + data: userPayload, + params: {}, + headers: v0VersionHeaders, + method: 'POST', + }, + httpRes: { + data: { + type: 'error.list', + request_id: 'request128', + errors: [ + { + code: 'client_error', + message: 'Unknown server error', + }, + ], + }, + status: 500, + }, + }, + { + httpReq: { + url: 'https://api.intercom.io/users/test4', + data: userPayload, + params: {}, + headers: v0VersionHeaders, + method: 'POST', + }, + httpRes: { + data: { + type: 'error.list', + request_id: 'request129', + errors: [ + { + code: 'server_timeout', + message: 'Server timed out when making request', + }, + ], + }, + status: 504, }, }, ]; From 9dff6860248c2bd7980140c8e21c4ed3e434ee33 Mon Sep 17 00:00:00 2001 From: Mihir Bhalala <77438541+mihir-4116@users.noreply.github.com> Date: Wed, 13 Mar 2024 12:35:27 +0530 Subject: [PATCH 2/2] chore: gaoc proxy test refactor (#3072) * chore: gaoc proxy test refactor * chore: code review changes * chore: partial failure tests added * chore: code review changes * chore: code review changes * chore: code review changes * chore: code review changes * chore: code review changes * chore: code review changes --- .../dataDelivery/business.ts | 669 +++++++++++++++ .../dataDelivery/data.ts | 773 +----------------- .../dataDelivery/oauth.ts | 263 ++++++ .../network.ts | 154 ++++ 4 files changed, 1093 insertions(+), 766 deletions(-) create mode 100644 test/integrations/destinations/google_adwords_offline_conversions/dataDelivery/business.ts create mode 100644 test/integrations/destinations/google_adwords_offline_conversions/dataDelivery/oauth.ts diff --git a/test/integrations/destinations/google_adwords_offline_conversions/dataDelivery/business.ts b/test/integrations/destinations/google_adwords_offline_conversions/dataDelivery/business.ts new file mode 100644 index 0000000000..fbeaf7f250 --- /dev/null +++ b/test/integrations/destinations/google_adwords_offline_conversions/dataDelivery/business.ts @@ -0,0 +1,669 @@ +import { + generateMetadata, + generateProxyV0Payload, + generateProxyV1Payload, +} from '../../../testUtils'; + +const transactionAttribute = { + CUSTOM_KEY: 'CUSTOM_VALUE', + currency_code: 'INR', + order_id: 'order id', + store_attribute: { + store_code: 'store code', + }, + transaction_amount_micros: '100000000', + transaction_date_time: '2019-10-14 11:15:18+00:00', +}; + +const createJobPayload = { + job: { + storeSalesMetadata: { + custom_key: 'CUSTOM_KEY', + loyaltyFraction: 1, + transaction_upload_fraction: '1', + }, + type: 'STORE_SALES_UPLOAD_FIRST_PARTY', + }, +}; + +const products = [ + { + product_id: '507f1f77bcf86cd799439011', + quantity: '2', + price: '50', + sku: '45790-32', + name: 'Monopoly: 3rd Edition', + position: '1', + category: 'cars', + url: 'https://www.example.com/product/path', + image_url: 'https://www.example.com/product/path.jpg', + }, +]; + +const headers = { + header1: { + Authorization: 'Bearer abcd1234', + 'Content-Type': 'application/json', + 'developer-token': 'ijkl91011', + 'login-customer-id': 'logincustomerid', + }, + header2: { + Authorization: 'Bearer abcd1234', + 'Content-Type': 'application/json', + 'developer-token': 'ijkl91011', + }, +}; + +const params = { + param1: { + customerId: '1112223333', + event: 'Sign-up - click', + }, + param2: { + event: 'Sign-up - click', + customerId: '1234567891', + customVariables: [ + { + from: 'Value', + to: 'revenue', + }, + { + from: 'total', + to: 'cost', + }, + ], + properties: { + gbraid: 'gbraid', + wbraid: 'wbraid', + externalAttributionCredit: 10, + externalAttributionModel: 'externalAttributionModel', + conversionCustomVariable: 'conversionCustomVariable', + Value: 'value', + merchantId: '9876merchantId', + feedCountryCode: 'feedCountryCode', + feedLanguageCode: 'feedLanguageCode', + localTransactionCost: 20, + products, + userIdentifierSource: 'FIRST_PARTY', + conversionEnvironment: 'WEB', + gclid: 'gclid', + conversionDateTime: '2022-01-01 12:32:45-08:00', + conversionValue: '1', + currency: 'GBP', + orderId: 'PL-123QR', + }, + }, + param3: {}, + param4: {}, +}; + +params['param3'] = { ...params.param2, customVariables: [] }; + +params['param4'] = { ...params.param3, customerId: '1234567893', conversionEnvironment: 'APP' }; + +const validRequestPayload1 = { + addConversionPayload: { + enable_partial_failure: false, + enable_warnings: false, + operations: [ + { + create: { + transaction_attribute: transactionAttribute, + userIdentifiers: [ + { + hashedEmail: '6db61e6dcbcf2390e4a46af426f26a133a3bee45021422fc7ae86e9136f14110', + userIdentifierSource: 'UNSPECIFIED', + }, + ], + }, + }, + ], + validate_only: false, + }, + createJobPayload, + event: '1112223333', + executeJobPayload: { + validate_only: false, + }, + isStoreConversion: true, +}; + +const validRequestPayload2 = { + conversions: [ + { + gbraid: 'gbraid', + wbraid: 'wbraid', + externalAttributionData: { + externalAttributionCredit: 10, + externalAttributionModel: 'externalAttributionModel', + }, + cartData: { + merchantId: 9876, + feedCountryCode: 'feedCountryCode', + feedLanguageCode: 'feedLanguageCode', + localTransactionCost: 20, + items: [ + { + productId: '507f1f77bcf86cd799439011', + quantity: 2, + unitPrice: 50, + }, + ], + }, + userIdentifiers: [ + { + userIdentifierSource: 'FIRST_PARTY', + hashedPhoneNumber: '04e1dabb7c1348b72bfa87da179c9697c69af74827649266a5da8cdbb367abcd', + }, + ], + conversionEnvironment: 'WEB', + gclid: 'gclid', + conversionDateTime: '2022-01-01 12:32:45-08:00', + conversionValue: 1, + currencyCode: 'GBP', + orderId: 'PL-123QR', + }, + ], + partialFailure: true, +}; + +const invalidArgumentRequestPayload = { + addConversionPayload: { + enable_partial_failure: false, + enable_warnings: false, + operations: [ + { + create: { + transaction_attribute: transactionAttribute, + userIdentifiers: [ + { + hashedEmail: '6db61e6dcbcf2390e4a46af26f26a133a3bee45021422fc7ae86e9136f14110', + userIdentifierSource: 'UNSPECIFIED', + }, + ], + }, + }, + ], + validate_only: false, + }, + createJobPayload, + event: '1112223333', + executeJobPayload: { + validate_only: false, + }, + isStoreConversion: true, +}; + +const notAllowedToAccessFeatureRequestPayload = { + ...validRequestPayload2, + conversions: [ + { + ...validRequestPayload2.conversions[0], + conversionEnvironment: 'APP', + }, + ], +}; + +const metadataArray = [generateMetadata(1)]; + +const expectedStatTags = { + destType: 'GOOGLE_ADWORDS_OFFLINE_CONVERSIONS', + destinationId: 'default-destinationId', + errorCategory: 'network', + errorType: 'aborted', + feature: 'dataDelivery', + implementation: 'native', + module: 'destination', + workspaceId: 'default-workspaceId', +}; + +export const testScenariosForV0API = [ + { + id: 'gaoc_v0_scenario_1', + name: 'google_adwords_offline_conversions', + description: + '[Proxy v0 API] :: Test for invalid argument - where the destination responds with 400 with invalid argument error', + successCriteria: 'Should return 400 with error with destination response', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v0', + input: { + request: { + body: generateProxyV0Payload({ + headers: headers.header1, + params: params.param1, + JSON: invalidArgumentRequestPayload, + endpoint: + 'https://googleads.googleapis.com/v14/customers/11122233331/offlineUserDataJobs', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 400, + body: { + output: { + status: 400, + message: + '[Google Ads Offline Conversions]:: Request contains an invalid argument. during google_ads_offline_store_conversions Add Conversion', + destinationResponse: { + error: { + code: 400, + details: [ + { + '@type': 'type.googleapis.com/google.ads.googleads.v14.errors.GoogleAdsFailure', + errors: [ + { + errorCode: { + offlineUserDataJobError: 'INVALID_SHA256_FORMAT', + }, + message: 'The SHA256 encoded value is malformed.', + location: { + fieldPathElements: [ + { + fieldName: 'operations', + index: 0, + }, + { + fieldName: 'create', + }, + { + fieldName: 'user_identifiers', + index: 0, + }, + { + fieldName: 'hashed_email', + }, + ], + }, + }, + ], + requestId: '68697987', + }, + ], + message: 'Request contains an invalid argument.', + status: 'INVALID_ARGUMENT', + }, + }, + statTags: expectedStatTags, + }, + }, + }, + }, + }, + { + id: 'gaoc_v0_scenario_2', + name: 'google_adwords_offline_conversions', + description: + '[Proxy v0 API] :: Test for a valid operations request with a successful 200 response from the destination', + successCriteria: 'Should return 200 with no error with destination response', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v0', + input: { + request: { + body: generateProxyV0Payload({ + headers: headers.header1, + params: params.param1, + JSON: validRequestPayload1, + endpoint: 'https://googleads.googleapis.com/v14/customers/1112223333/offlineUserDataJobs', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + status: 200, + message: + '[Google Ads Offline Conversions Response Handler] - Request processed successfully', + destinationResponse: { + response: { + name: 'customers/111-222-3333/operations/abcd=', + }, + status: 200, + }, + }, + }, + }, + }, + }, + { + id: 'gaoc_v0_scenario_3', + name: 'google_adwords_offline_conversions', + description: + '[Proxy v0 API] :: Test for a valid request with a successful 200 response from the destination', + successCriteria: 'Should return 200 with no error with destination response', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v0', + input: { + request: { + body: generateProxyV0Payload({ + headers: headers.header2, + params: params.param2, + JSON: validRequestPayload2, + endpoint: + 'https://googleads.googleapis.com/v14/customers/1234567891:uploadClickConversions', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + status: 200, + message: + '[Google Ads Offline Conversions Response Handler] - Request processed successfully', + destinationResponse: { + response: [ + { + adjustmentType: 'ENHANCEMENT', + conversionAction: 'customers/1234567891/conversionActions/874224905', + adjustmentDateTime: '2021-01-01 12:32:45-08:00', + gclidDateTimePair: { + gclid: '1234', + conversionDateTime: '2021-01-01 12:32:45-08:00', + }, + orderId: '12345', + }, + ], + status: 200, + }, + }, + }, + }, + }, + }, + { + id: 'gaoc_v0_scenario_4', + name: 'google_adwords_offline_conversions', + description: + '[Proxy v0 API] :: Test for a valid conversion action request with a successful 200 response from the destination', + successCriteria: 'Should return 200 with no error with destination response', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v0', + input: { + request: { + body: generateProxyV0Payload({ + headers: headers.header2, + params: params.param3, + JSON: validRequestPayload2, + endpoint: + 'https://googleads.googleapis.com/v14/customers/1234567891:uploadClickConversions', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + destinationResponse: { + response: [ + { + adjustmentDateTime: '2021-01-01 12:32:45-08:00', + adjustmentType: 'ENHANCEMENT', + conversionAction: 'customers/1234567891/conversionActions/874224905', + gclidDateTimePair: { + conversionDateTime: '2021-01-01 12:32:45-08:00', + gclid: '1234', + }, + orderId: '12345', + }, + ], + status: 200, + }, + message: + '[Google Ads Offline Conversions Response Handler] - Request processed successfully', + status: 200, + }, + }, + }, + }, + }, +]; + +export const testScenariosForV1API = [ + { + id: 'gaoc_v1_scenario_1', + name: 'google_adwords_offline_conversions', + description: + '[Proxy v1 API] :: Test for invalid argument - where the destination responds with 400 with invalid argument error', + successCriteria: 'Should return 400 with error with destination response', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + headers: headers.header1, + params: params.param1, + JSON: invalidArgumentRequestPayload, + endpoint: + 'https://googleads.googleapis.com/v14/customers/11122233331/offlineUserDataJobs', + }, + metadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + message: + '[Google Ads Offline Conversions]:: Request contains an invalid argument. during google_ads_offline_store_conversions Add Conversion', + response: [ + { + error: + '[Google Ads Offline Conversions]:: Request contains an invalid argument. during google_ads_offline_store_conversions Add Conversion', + metadata: generateMetadata(1), + statusCode: 400, + }, + ], + statTags: expectedStatTags, + status: 400, + }, + }, + }, + }, + }, + { + id: 'gaoc_v1_scenario_2', + name: 'google_adwords_offline_conversions', + description: + '[Proxy v1 API] :: Test for a valid operations request with a successful 200 response from the destination', + successCriteria: 'Should return 200 with no error with destination response', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + headers: headers.header1, + params: params.param1, + JSON: validRequestPayload1, + endpoint: + 'https://googleads.googleapis.com/v14/customers/1112223333/offlineUserDataJobs', + }, + metadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + message: + '[Google Ads Offline Conversions Response Handler] - Request processed successfully', + response: [ + { + error: '{"name":"customers/111-222-3333/operations/abcd="}', + metadata: generateMetadata(1), + statusCode: 200, + }, + ], + status: 200, + }, + }, + }, + }, + }, + { + id: 'gaoc_v1_scenario_3', + name: 'google_adwords_offline_conversions', + description: + '[Proxy v1 API] :: Test for a valid request with a successful 200 response from the destination', + successCriteria: 'Should return 200 with no error with destination response', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + headers: headers.header2, + params: params.param2, + JSON: validRequestPayload2, + endpoint: + 'https://googleads.googleapis.com/v14/customers/1234567891:uploadClickConversions', + }, + metadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + message: + '[Google Ads Offline Conversions Response Handler] - Request processed successfully', + response: [ + { + error: + '[{"adjustmentType":"ENHANCEMENT","conversionAction":"customers/1234567891/conversionActions/874224905","adjustmentDateTime":"2021-01-01 12:32:45-08:00","gclidDateTimePair":{"gclid":"1234","conversionDateTime":"2021-01-01 12:32:45-08:00"},"orderId":"12345"}]', + metadata: generateMetadata(1), + statusCode: 200, + }, + ], + status: 200, + }, + }, + }, + }, + }, + { + id: 'gaoc_v1_scenario_4', + name: 'google_adwords_offline_conversions', + description: + '[Proxy v1 API] :: Test for a valid conversion action request with a successful 200 response from the destination', + successCriteria: 'Should return 200 with no error with destination response', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + headers: headers.header2, + params: params.param3, + JSON: validRequestPayload2, + endpoint: + 'https://googleads.googleapis.com/v14/customers/1234567891:uploadClickConversions', + }, + metadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + message: + '[Google Ads Offline Conversions Response Handler] - Request processed successfully', + response: [ + { + error: + '[{"adjustmentType":"ENHANCEMENT","conversionAction":"customers/1234567891/conversionActions/874224905","adjustmentDateTime":"2021-01-01 12:32:45-08:00","gclidDateTimePair":{"gclid":"1234","conversionDateTime":"2021-01-01 12:32:45-08:00"},"orderId":"12345"}]', + metadata: generateMetadata(1), + statusCode: 200, + }, + ], + status: 200, + }, + }, + }, + }, + }, + { + id: 'gaoc_v1_scenario_5', + name: 'google_adwords_offline_conversions', + description: + '[Proxy v1 API] :: Test for customer is not allowed Test for a valid request with a successful 200 response from the destinationto access feature partial failure error', + successCriteria: 'Should return 400 with error with destination response', + scenario: 'Business', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + headers: headers.header2, + params: params.param4, + JSON: notAllowedToAccessFeatureRequestPayload, + endpoint: + 'https://googleads.googleapis.com/v14/customers/1234567893:uploadClickConversions', + }, + metadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: { + message: + '[Google Ads Offline Conversions]:: partialFailureError - Customer is not allowlisted for accessing this feature., at conversions[0].conversion_environment', + response: [ + { + error: + '[Google Ads Offline Conversions]:: partialFailureError - Customer is not allowlisted for accessing this feature., at conversions[0].conversion_environment', + metadata: generateMetadata(1), + statusCode: 400, + }, + ], + statTags: expectedStatTags, + status: 400, + }, + }, + }, + }, + }, +]; diff --git a/test/integrations/destinations/google_adwords_offline_conversions/dataDelivery/data.ts b/test/integrations/destinations/google_adwords_offline_conversions/dataDelivery/data.ts index ae75273399..709ab6d2a8 100644 --- a/test/integrations/destinations/google_adwords_offline_conversions/dataDelivery/data.ts +++ b/test/integrations/destinations/google_adwords_offline_conversions/dataDelivery/data.ts @@ -1,768 +1,9 @@ +import { v0oauthScenarios, v1oauthScenarios } from './oauth'; +import { testScenariosForV0API, testScenariosForV1API } from './business'; + export const data = [ - { - name: 'google_adwords_offline_conversions', - description: 'Test 0', - feature: 'dataDelivery', - module: 'destination', - version: 'v0', - input: { - request: { - body: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: - 'https://googleads.googleapis.com/v14/customers/11122233331/offlineUserDataJobs', - headers: { - Authorization: 'Bearer abcd1234', - 'Content-Type': 'application/json', - 'developer-token': 'ijkl91011', - 'login-customer-id': 'logincustomerid', - }, - params: { - customerId: '1112223333', - event: 'Sign-up - click', - }, - body: { - JSON: { - addConversionPayload: { - enable_partial_failure: false, - enable_warnings: false, - operations: [ - { - create: { - transaction_attribute: { - CUSTOM_KEY: 'CUSTOM_VALUE', - currency_code: 'INR', - order_id: 'order id', - store_attribute: { - store_code: 'store code', - }, - transaction_amount_micros: '100000000', - transaction_date_time: '2019-10-14 11:15:18+00:00', - }, - userIdentifiers: [ - { - hashedEmail: - '6db61e6dcbcf2390e4a46af26f26a133a3bee45021422fc7ae86e9136f14110', - userIdentifierSource: 'UNSPECIFIED', - }, - ], - }, - }, - ], - validate_only: false, - }, - createJobPayload: { - job: { - storeSalesMetadata: { - custom_key: 'CUSTOM_KEY', - loyaltyFraction: 1, - transaction_upload_fraction: '1', - }, - type: 'STORE_SALES_UPLOAD_FIRST_PARTY', - }, - }, - event: '1112223333', - executeJobPayload: { - validate_only: false, - }, - isStoreConversion: true, - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - }, - method: 'POST', - }, - }, - output: { - response: { - status: 400, - body: { - output: { - status: 400, - message: - '[Google Ads Offline Conversions]:: Request contains an invalid argument. during google_ads_offline_store_conversions Add Conversion', - destinationResponse: { - error: { - code: 400, - details: [ - { - '@type': 'type.googleapis.com/google.ads.googleads.v14.errors.GoogleAdsFailure', - errors: [ - { - errorCode: { - offlineUserDataJobError: 'INVALID_SHA256_FORMAT', - }, - message: 'The SHA256 encoded value is malformed.', - location: { - fieldPathElements: [ - { - fieldName: 'operations', - index: 0, - }, - { - fieldName: 'create', - }, - { - fieldName: 'user_identifiers', - index: 0, - }, - { - fieldName: 'hashed_email', - }, - ], - }, - }, - ], - requestId: '68697987', - }, - ], - message: 'Request contains an invalid argument.', - status: 'INVALID_ARGUMENT', - }, - }, - statTags: { - destType: 'GOOGLE_ADWORDS_OFFLINE_CONVERSIONS', - destinationId: 'Non-determininable', - errorCategory: 'network', - errorType: 'aborted', - feature: 'dataDelivery', - implementation: 'native', - module: 'destination', - workspaceId: 'Non-determininable', - }, - }, - }, - }, - }, - }, - { - name: 'google_adwords_offline_conversions', - description: 'Test 1', - feature: 'dataDelivery', - module: 'destination', - version: 'v0', - input: { - request: { - body: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://googleads.googleapis.com/v14/customers/1112223333/offlineUserDataJobs', - headers: { - Authorization: 'Bearer abcd1234', - 'Content-Type': 'application/json', - 'developer-token': 'ijkl91011', - 'login-customer-id': 'logincustomerid', - }, - params: { - customerId: '1112223333', - event: 'Sign-up - click', - }, - body: { - JSON: { - addConversionPayload: { - enable_partial_failure: false, - enable_warnings: false, - operations: [ - { - create: { - transaction_attribute: { - CUSTOM_KEY: 'CUSTOM_VALUE', - currency_code: 'INR', - order_id: 'order id', - store_attribute: { - store_code: 'store code', - }, - transaction_amount_micros: '100000000', - transaction_date_time: '2019-10-14 11:15:18+00:00', - }, - userIdentifiers: [ - { - hashedEmail: - '6db61e6dcbcf2390e4a46af426f26a133a3bee45021422fc7ae86e9136f14110', - userIdentifierSource: 'UNSPECIFIED', - }, - ], - }, - }, - ], - validate_only: false, - }, - createJobPayload: { - job: { - storeSalesMetadata: { - custom_key: 'CUSTOM_KEY', - loyaltyFraction: 1, - transaction_upload_fraction: '1', - }, - type: 'STORE_SALES_UPLOAD_FIRST_PARTY', - }, - }, - event: '1112223333', - executeJobPayload: { - validate_only: false, - }, - isStoreConversion: true, - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - }, - }, - }, - output: { - response: { - status: 200, - body: { - output: { - status: 200, - message: - '[Google Ads Offline Conversions Response Handler] - Request processed successfully', - destinationResponse: { - response: { - name: 'customers/111-222-3333/operations/abcd=', - }, - status: 200, - }, - }, - }, - }, - }, - }, - { - name: 'google_adwords_offline_conversions', - description: 'Test 2', - feature: 'dataDelivery', - module: 'destination', - version: 'v0', - input: { - request: { - body: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://googleads.googleapis.com/v14/customers/customerid/offlineUserDataJobs', - headers: { - Authorization: 'Bearer abcd1234', - 'Content-Type': 'application/json', - 'developer-token': 'ijkl91011', - 'login-customer-id': 'logincustomerid', - }, - params: { - customerId: '1112223333', - event: 'Sign-up - click', - }, - body: { - JSON: { - addConversionPayload: { - enable_partial_failure: false, - enable_warnings: false, - operations: [ - { - create: { - transaction_attribute: { - CUSTOM_KEY: 'CUSTOM_VALUE', - currency_code: 'INR', - order_id: 'order id', - store_attribute: { - store_code: 'store code', - }, - transaction_amount_micros: '100000000', - transaction_date_time: '2019-10-14 11:15:18+00:00', - }, - userIdentifiers: [ - { - hashedEmail: - '6db61e6dcbcf2390e4a46af426f26a133a3bee45021422fc7ae86e9136f14110', - userIdentifierSource: 'UNSPECIFIED', - }, - ], - }, - }, - ], - validate_only: false, - }, - createJobPayload: { - job: { - storeSalesMetadata: { - custom_key: 'CUSTOM_KEY', - loyaltyFraction: 1, - transaction_upload_fraction: '1', - }, - type: 'STORE_SALES_UPLOAD_FIRST_PARTY', - }, - }, - event: '1112223333', - executeJobPayload: { - validate_only: false, - }, - isStoreConversion: true, - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - }, - }, - }, - output: { - response: { - status: 401, - body: { - output: { - status: 401, - message: - '[Google Ads Offline Conversions]:: Request had invalid authentication credentials. Expected OAuth 2 access token, login cookie or other valid authentication credential. See https://developers.google.com/identity/sign-in/web/devconsole-project. during google_ads_offline_store_conversions Job Creation', - authErrorCategory: 'REFRESH_TOKEN', - destinationResponse: { - error: { - code: 401, - message: - 'Request had invalid authentication credentials. Expected OAuth 2 access token, login cookie or other valid authentication credential. See https://developers.google.com/identity/sign-in/web/devconsole-project.', - status: 'UNAUTHENTICATED', - }, - }, - statTags: { - destType: 'GOOGLE_ADWORDS_OFFLINE_CONVERSIONS', - destinationId: 'Non-determininable', - errorCategory: 'network', - errorType: 'aborted', - feature: 'dataDelivery', - implementation: 'native', - module: 'destination', - workspaceId: 'Non-determininable', - }, - }, - }, - }, - }, - }, - { - name: 'google_adwords_offline_conversions', - description: 'Test 3', - feature: 'dataDelivery', - module: 'destination', - version: 'v0', - input: { - request: { - body: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: - 'https://googleads.googleapis.com/v14/customers/1234567890:uploadClickConversions', - headers: { - Authorization: 'Bearer abcd1234', - 'Content-Type': 'application/json', - 'developer-token': 'ijkl91011', - }, - params: { - event: 'Sign-up - click', - customerId: '1234567890', - customVariables: [ - { - from: 'value', - to: 'revenue', - }, - { - from: 'total', - to: 'cost', - }, - ], - properties: { - gbraid: 'gbraid', - wbraid: 'wbraid', - externalAttributionCredit: 10, - externalAttributionModel: 'externalAttributionModel', - conversionCustomVariable: 'conversionCustomVariable', - value: 'value', - merchantId: '9876merchantId', - feedCountryCode: 'feedCountryCode', - feedLanguageCode: 'feedLanguageCode', - localTransactionCost: 20, - products: [ - { - product_id: '507f1f77bcf86cd799439011', - quantity: '2', - price: '50', - sku: '45790-32', - name: 'Monopoly: 3rd Edition', - position: '1', - category: 'cars', - url: 'https://www.example.com/product/path', - image_url: 'https://www.example.com/product/path.jpg', - }, - ], - userIdentifierSource: 'FIRST_PARTY', - conversionEnvironment: 'WEB', - gclid: 'gclid', - conversionDateTime: '2022-01-01 12:32:45-08:00', - conversionValue: '1', - currency: 'GBP', - orderId: 'PL-123QR', - }, - }, - body: { - JSON: { - conversions: [ - { - gbraid: 'gbraid', - wbraid: 'wbraid', - externalAttributionData: { - externalAttributionCredit: 10, - externalAttributionModel: 'externalAttributionModel', - }, - cartData: { - merchantId: 9876, - feedCountryCode: 'feedCountryCode', - feedLanguageCode: 'feedLanguageCode', - localTransactionCost: 20, - items: [ - { - productId: '507f1f77bcf86cd799439011', - quantity: 2, - unitPrice: 50, - }, - ], - }, - userIdentifiers: [ - { - userIdentifierSource: 'FIRST_PARTY', - hashedEmail: - '6db61e6dcbcf2390e4a46af426f26a133a3bee45021422fc7ae86e9136f14110', - }, - ], - conversionEnvironment: 'WEB', - gclid: 'gclid', - conversionDateTime: '2022-01-01 12:32:45-08:00', - conversionValue: 1, - currencyCode: 'GBP', - orderId: 'PL-123QR', - }, - ], - partialFailure: true, - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - }, - }, - }, - output: { - response: { - status: 401, - body: { - output: { - status: 401, - message: - '[Google Ads Offline Conversions]:: [{"error":{"code":401,"message":"Request had invalid authentication credentials. Expected OAuth 2 access token, login cookie or other valid authentication credential. See https://developers.google.com/identity/sign-in/web/devconsole-project.","status":"UNAUTHENTICATED"}}] during google_ads_offline_conversions response transformation', - authErrorCategory: 'REFRESH_TOKEN', - destinationResponse: [ - { - error: { - code: 401, - message: - 'Request had invalid authentication credentials. Expected OAuth 2 access token, login cookie or other valid authentication credential. See https://developers.google.com/identity/sign-in/web/devconsole-project.', - status: 'UNAUTHENTICATED', - }, - }, - ], - statTags: { - destType: 'GOOGLE_ADWORDS_OFFLINE_CONVERSIONS', - destinationId: 'Non-determininable', - errorCategory: 'network', - errorType: 'aborted', - feature: 'dataDelivery', - implementation: 'native', - module: 'destination', - workspaceId: 'Non-determininable', - }, - }, - }, - }, - }, - }, - { - name: 'google_adwords_offline_conversions', - description: 'Test 4', - feature: 'dataDelivery', - module: 'destination', - version: 'v0', - input: { - request: { - body: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: - 'https://googleads.googleapis.com/v14/customers/1234567891:uploadClickConversions', - headers: { - Authorization: 'Bearer abcd1234', - 'Content-Type': 'application/json', - 'developer-token': 'ijkl91011', - }, - params: { - event: 'Sign-up - click', - customerId: '1234567891', - customVariables: [ - { - from: 'Value', - to: 'revenue', - }, - { - from: 'total', - to: 'cost', - }, - ], - properties: { - gbraid: 'gbraid', - wbraid: 'wbraid', - externalAttributionCredit: 10, - externalAttributionModel: 'externalAttributionModel', - conversionCustomVariable: 'conversionCustomVariable', - Value: 'value', - merchantId: '9876merchantId', - feedCountryCode: 'feedCountryCode', - feedLanguageCode: 'feedLanguageCode', - localTransactionCost: 20, - products: [ - { - product_id: '507f1f77bcf86cd799439011', - quantity: '2', - price: '50', - sku: '45790-32', - name: 'Monopoly: 3rd Edition', - position: '1', - category: 'cars', - url: 'https://www.example.com/product/path', - image_url: 'https://www.example.com/product/path.jpg', - }, - ], - userIdentifierSource: 'FIRST_PARTY', - conversionEnvironment: 'WEB', - gclid: 'gclid', - conversionDateTime: '2022-01-01 12:32:45-08:00', - conversionValue: '1', - currency: 'GBP', - orderId: 'PL-123QR', - }, - }, - body: { - JSON: { - conversions: [ - { - gbraid: 'gbraid', - wbraid: 'wbraid', - externalAttributionData: { - externalAttributionCredit: 10, - externalAttributionModel: 'externalAttributionModel', - }, - cartData: { - merchantId: 9876, - feedCountryCode: 'feedCountryCode', - feedLanguageCode: 'feedLanguageCode', - localTransactionCost: 20, - items: [ - { - productId: '507f1f77bcf86cd799439011', - quantity: 2, - unitPrice: 50, - }, - ], - }, - userIdentifiers: [ - { - userIdentifierSource: 'FIRST_PARTY', - hashedPhoneNumber: - '04e1dabb7c1348b72bfa87da179c9697c69af74827649266a5da8cdbb367abcd', - }, - ], - conversionEnvironment: 'WEB', - gclid: 'gclid', - conversionDateTime: '2022-01-01 12:32:45-08:00', - conversionValue: 1, - currencyCode: 'GBP', - orderId: 'PL-123QR', - }, - ], - partialFailure: true, - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - }, - }, - }, - output: { - response: { - status: 200, - body: { - output: { - status: 200, - message: - '[Google Ads Offline Conversions Response Handler] - Request processed successfully', - destinationResponse: { - response: [ - { - adjustmentType: 'ENHANCEMENT', - conversionAction: 'customers/1234567891/conversionActions/874224905', - adjustmentDateTime: '2021-01-01 12:32:45-08:00', - gclidDateTimePair: { - gclid: '1234', - conversionDateTime: '2021-01-01 12:32:45-08:00', - }, - orderId: '12345', - }, - ], - status: 200, - }, - }, - }, - }, - }, - }, - { - name: 'google_adwords_offline_conversions', - description: 'Test 5', - feature: 'dataDelivery', - module: 'destination', - version: 'v0', - input: { - request: { - body: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: - 'https://googleads.googleapis.com/v14/customers/1234567891:uploadClickConversions', - headers: { - Authorization: 'Bearer abcd1234', - 'Content-Type': 'application/json', - 'developer-token': 'ijkl91011', - }, - params: { - event: 'Sign-up - click', - customerId: '1234567891', - customVariables: [], - properties: { - gbraid: 'gbraid', - wbraid: 'wbraid', - externalAttributionCredit: 10, - externalAttributionModel: 'externalAttributionModel', - conversionCustomVariable: 'conversionCustomVariable', - value: 'value', - merchantId: '9876merchantId', - feedCountryCode: 'feedCountryCode', - feedLanguageCode: 'feedLanguageCode', - localTransactionCost: 20, - products: [ - { - product_id: '507f1f77bcf86cd799439011', - quantity: '2', - price: '50', - sku: '45790-32', - name: 'Monopoly: 3rd Edition', - position: '1', - category: 'cars', - url: 'https://www.example.com/product/path', - image_url: 'https://www.example.com/product/path.jpg', - }, - ], - userIdentifierSource: 'FIRST_PARTY', - conversionEnvironment: 'WEB', - gclid: 'gclid', - conversionDateTime: '2022-01-01 12:32:45-08:00', - conversionValue: '1', - currency: 'GBP', - orderId: 'PL-123QR', - }, - }, - body: { - JSON: { - conversions: [ - { - gbraid: 'gbraid', - wbraid: 'wbraid', - externalAttributionData: { - externalAttributionCredit: 10, - externalAttributionModel: 'externalAttributionModel', - }, - cartData: { - merchantId: 9876, - feedCountryCode: 'feedCountryCode', - feedLanguageCode: 'feedLanguageCode', - localTransactionCost: 20, - items: [ - { - productId: '507f1f77bcf86cd799439011', - quantity: 2, - unitPrice: 50, - }, - ], - }, - userIdentifiers: [ - { - userIdentifierSource: 'FIRST_PARTY', - hashedPhoneNumber: - '04e1dabb7c1348b72bfa87da179c9697c69af74827649266a5da8cdbb367abcd', - }, - ], - conversionEnvironment: 'WEB', - gclid: 'gclid', - conversionDateTime: '2022-01-01 12:32:45-08:00', - conversionValue: 1, - currencyCode: 'GBP', - orderId: 'PL-123QR', - }, - ], - partialFailure: true, - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - }, - }, - }, - output: { - response: { - status: 200, - body: { - output: { - destinationResponse: { - response: [ - { - adjustmentDateTime: '2021-01-01 12:32:45-08:00', - adjustmentType: 'ENHANCEMENT', - conversionAction: 'customers/1234567891/conversionActions/874224905', - gclidDateTimePair: { - conversionDateTime: '2021-01-01 12:32:45-08:00', - gclid: '1234', - }, - orderId: '12345', - }, - ], - status: 200, - }, - message: - '[Google Ads Offline Conversions Response Handler] - Request processed successfully', - status: 200, - }, - }, - }, - }, - }, + ...v0oauthScenarios, + ...v1oauthScenarios, + ...testScenariosForV0API, + ...testScenariosForV1API, ]; diff --git a/test/integrations/destinations/google_adwords_offline_conversions/dataDelivery/oauth.ts b/test/integrations/destinations/google_adwords_offline_conversions/dataDelivery/oauth.ts new file mode 100644 index 0000000000..15a150d0e5 --- /dev/null +++ b/test/integrations/destinations/google_adwords_offline_conversions/dataDelivery/oauth.ts @@ -0,0 +1,263 @@ +import { + generateMetadata, + generateProxyV1Payload, + generateProxyV0Payload, +} from '../../../testUtils'; + +const commonHeaders = { + Authorization: 'Bearer abcd1234', + 'Content-Type': 'application/json', + 'developer-token': 'ijkl91011', + 'login-customer-id': 'logincustomerid', +}; + +const commonParams = { + customerId: '1112223333', + event: 'Sign-up - click', +}; + +const commonRequestPayload = { + addConversionPayload: { + enable_partial_failure: false, + enable_warnings: false, + operations: [ + { + create: { + transaction_attribute: { + CUSTOM_KEY: 'CUSTOM_VALUE', + currency_code: 'INR', + order_id: 'order id', + store_attribute: { + store_code: 'store code', + }, + transaction_amount_micros: '100000000', + transaction_date_time: '2019-10-14 11:15:18+00:00', + }, + userIdentifiers: [ + { + hashedEmail: '6db61e6dcbcf2390e4a46af426f26a133a3bee45021422fc7ae86e9136f14110', + userIdentifierSource: 'UNSPECIFIED', + }, + ], + }, + }, + ], + validate_only: false, + }, + createJobPayload: { + job: { + storeSalesMetadata: { + custom_key: 'CUSTOM_KEY', + loyaltyFraction: 1, + transaction_upload_fraction: '1', + }, + type: 'STORE_SALES_UPLOAD_FIRST_PARTY', + }, + }, + event: '1112223333', + executeJobPayload: { + validate_only: false, + }, + isStoreConversion: true, +}; + +const commonRequestParameters = { + headers: commonHeaders, + params: commonParams, + JSON: commonRequestPayload, +}; + +const metadataArray = [generateMetadata(1)]; + +const expectedStatTags = { + destType: 'GOOGLE_ADWORDS_OFFLINE_CONVERSIONS', + destinationId: 'default-destinationId', + errorCategory: 'network', + errorType: 'aborted', + feature: 'dataDelivery', + implementation: 'native', + module: 'destination', + workspaceId: 'default-workspaceId', +}; + +export const v0oauthScenarios = [ + { + id: 'gaoc_v0_oauth_scenario_1', + name: 'google_adwords_offline_conversions', + description: + '[Proxy v0 API] :: Oauth where valid credentials are missing as mock response from destination', + successCriteria: 'The proxy should return 401 with authErrorCategory as REFRESH_TOKEN', + scenario: 'Oauth', + feature: 'dataDelivery', + module: 'destination', + version: 'v0', + input: { + request: { + body: generateProxyV0Payload({ + ...commonRequestParameters, + endpoint: 'https://googleads.googleapis.com/v14/customers/customerid/offlineUserDataJobs', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 401, + body: { + output: { + status: 401, + message: + '[Google Ads Offline Conversions]:: Request had invalid authentication credentials. Expected OAuth 2 access token, login cookie or other valid authentication credential. See https://developers.google.com/identity/sign-in/web/devconsole-project. during google_ads_offline_store_conversions Job Creation', + authErrorCategory: 'REFRESH_TOKEN', + destinationResponse: { + error: { + code: 401, + message: + 'Request had invalid authentication credentials. Expected OAuth 2 access token, login cookie or other valid authentication credential. See https://developers.google.com/identity/sign-in/web/devconsole-project.', + status: 'UNAUTHENTICATED', + }, + }, + statTags: expectedStatTags, + }, + }, + }, + }, + }, + { + id: 'gaoc_v0_oauth_scenario_2', + name: 'google_adwords_offline_conversions', + description: + '[Proxy v0 API] :: Oauth where ACCESS_TOKEN_SCOPE_INSUFFICIENT error as mock response from destination', + successCriteria: + 'Since the error from the destination is 403 - the proxy should return 403 with authErrorCategory as AUTH_STATUS_INACTIVE', + scenario: 'Oauth', + feature: 'dataDelivery', + module: 'destination', + version: 'v0', + input: { + request: { + body: generateProxyV0Payload({ + ...commonRequestParameters, + endpoint: 'https://googleads.googleapis.com/v14/customers/1234/offlineUserDataJobs', + }), + method: 'POST', + }, + }, + output: { + response: { + status: 403, + body: { + output: { + authErrorCategory: 'AUTH_STATUS_INACTIVE', + destinationResponse: { + error: { + code: 403, + message: 'Request had insufficient authentication scopes', + status: 'PERMISSION_DENIED', + }, + }, + message: + '[Google Ads Offline Conversions]:: Request had insufficient authentication scopes during google_ads_offline_store_conversions Job Creation', + statTags: expectedStatTags, + status: 403, + }, + }, + }, + }, + }, +]; + +export const v1oauthScenarios = [ + { + id: 'gaoc_v1_oauth_scenario_1', + name: 'google_adwords_offline_conversions', + description: + '[Proxy v1 API] :: Oauth where valid credentials are missing as mock response from destination', + successCriteria: 'The proxy should return 401 with authErrorCategory as REFRESH_TOKEN', + scenario: 'Oauth', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + ...commonRequestParameters, + endpoint: + 'https://googleads.googleapis.com/v14/customers/customerid/offlineUserDataJobs', + }, + metadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 401, + body: { + output: { + authErrorCategory: 'REFRESH_TOKEN', + message: + '[Google Ads Offline Conversions]:: Request had invalid authentication credentials. Expected OAuth 2 access token, login cookie or other valid authentication credential. See https://developers.google.com/identity/sign-in/web/devconsole-project. during google_ads_offline_store_conversions Job Creation', + response: [ + { + error: + '[Google Ads Offline Conversions]:: Request had invalid authentication credentials. Expected OAuth 2 access token, login cookie or other valid authentication credential. See https://developers.google.com/identity/sign-in/web/devconsole-project. during google_ads_offline_store_conversions Job Creation', + metadata: generateMetadata(1), + statusCode: 401, + }, + ], + statTags: expectedStatTags, + status: 401, + }, + }, + }, + }, + }, + { + id: 'gaoc_v1_oauth_scenario_2', + name: 'google_adwords_offline_conversions', + description: + '[Proxy v1 API] :: Oauth where ACCESS_TOKEN_SCOPE_INSUFFICIENT error as mock response from destination', + successCriteria: + 'Since the error from the destination is 403 - the proxy should return 403 with authErrorCategory as AUTH_STATUS_INACTIVE', + scenario: 'Oauth', + feature: 'dataDelivery', + module: 'destination', + version: 'v1', + input: { + request: { + body: generateProxyV1Payload( + { + ...commonRequestParameters, + endpoint: 'https://googleads.googleapis.com/v14/customers/1234/offlineUserDataJobs', + }, + metadataArray, + ), + method: 'POST', + }, + }, + output: { + response: { + status: 403, + body: { + output: { + authErrorCategory: 'AUTH_STATUS_INACTIVE', + message: + '[Google Ads Offline Conversions]:: Request had insufficient authentication scopes during google_ads_offline_store_conversions Job Creation', + response: [ + { + error: + '[Google Ads Offline Conversions]:: Request had insufficient authentication scopes during google_ads_offline_store_conversions Job Creation', + metadata: generateMetadata(1), + statusCode: 403, + }, + ], + statTags: expectedStatTags, + status: 403, + }, + }, + }, + }, + }, +]; diff --git a/test/integrations/destinations/google_adwords_offline_conversions/network.ts b/test/integrations/destinations/google_adwords_offline_conversions/network.ts index 375879727b..7dc7f97933 100644 --- a/test/integrations/destinations/google_adwords_offline_conversions/network.ts +++ b/test/integrations/destinations/google_adwords_offline_conversions/network.ts @@ -235,6 +235,8 @@ export const networkCallsData = [ }, }, { + description: + 'Mock response from destination depicting a request with invalid authentication credentials', httpReq: { url: 'https://googleads.googleapis.com/v14/customers/customerid/offlineUserDataJobs:create', data: { @@ -268,6 +270,41 @@ export const networkCallsData = [ }, }, }, + { + description: + 'Mock response from destination depicting a request with invalid authentication scopes', + httpReq: { + url: 'https://googleads.googleapis.com/v14/customers/1234/offlineUserDataJobs:create', + data: { + job: { + storeSalesMetadata: { + custom_key: 'CUSTOM_KEY', + loyaltyFraction: 1, + transaction_upload_fraction: '1', + }, + type: 'STORE_SALES_UPLOAD_FIRST_PARTY', + }, + }, + params: { destination: 'google_adwords_offline_conversion' }, + headers: { + Authorization: 'Bearer abcd1234', + 'Content-Type': 'application/json', + 'developer-token': 'ijkl91011', + 'login-customer-id': 'logincustomerid', + }, + method: 'POST', + }, + httpRes: { + status: 403, + data: { + error: { + code: 403, + message: 'Request had insufficient authentication scopes', + status: 'PERMISSION_DENIED', + }, + }, + }, + }, { httpReq: { url: 'https://googleads.googleapis.com/v14/customers/1234567890/googleAds:searchStream', @@ -491,4 +528,121 @@ export const networkCallsData = [ status: 200, }, }, + { + httpReq: { + url: 'https://googleads.googleapis.com/v14/customers/1234567893/googleAds:searchStream', + data: { + query: + "SELECT conversion_action.id FROM conversion_action WHERE conversion_action.name = 'Sign-up - click'", + }, + headers: { + Authorization: 'Bearer abcd1234', + 'Content-Type': 'application/json', + 'developer-token': 'ijkl91011', + }, + method: 'POST', + params: { destination: 'google_adwords_offline_conversion' }, + }, + httpRes: { + data: [ + { + results: [ + { + conversionAction: { + resourceName: 'customers/1234567893/conversionActions/848898417', + id: '848898417', + }, + }, + ], + fieldMask: 'conversionAction.id', + requestId: 'dummyRequestId', + }, + ], + status: 200, + }, + }, + { + httpReq: { + url: 'https://googleads.googleapis.com/v14/customers/1234567893:uploadClickConversions', + data: { + conversions: [ + { + gbraid: 'gbraid', + wbraid: 'wbraid', + externalAttributionData: { + externalAttributionCredit: 10, + externalAttributionModel: 'externalAttributionModel', + }, + cartData: { + merchantId: 9876, + feedCountryCode: 'feedCountryCode', + feedLanguageCode: 'feedLanguageCode', + localTransactionCost: 20, + items: [{ productId: '507f1f77bcf86cd799439011', quantity: 2, unitPrice: 50 }], + }, + userIdentifiers: [ + { + userIdentifierSource: 'FIRST_PARTY', + hashedPhoneNumber: + '04e1dabb7c1348b72bfa87da179c9697c69af74827649266a5da8cdbb367abcd', + }, + ], + conversionEnvironment: 'APP', + gclid: 'gclid', + conversionDateTime: '2022-01-01 12:32:45-08:00', + conversionValue: 1, + currencyCode: 'GBP', + orderId: 'PL-123QR', + conversionAction: 'customers/1234567893/conversionActions/848898417', + }, + ], + partialFailure: true, + }, + headers: { + Authorization: 'Bearer abcd1234', + 'Content-Type': 'application/json', + 'developer-token': 'ijkl91011', + }, + method: 'POST', + params: { destination: 'google_adwords_offline_conversion' }, + }, + httpRes: { + status: 200, + data: { + partialFailureError: { + code: 3, + message: + 'Customer is not allowlisted for accessing this feature., at conversions[0].conversion_environment', + details: [ + { + '@type': 'type.googleapis.com/google.ads.googleads.v14.errors.GoogleAdsFailure', + errors: [ + { + errorCode: { + notAllowlistedError: 'CUSTOMER_NOT_ALLOWLISTED_FOR_THIS_FEATURE', + }, + message: 'Customer is not allowlisted for accessing this feature.', + trigger: { + int64Value: '2', + }, + location: { + fieldPathElements: [ + { + fieldName: 'conversions', + index: 0, + }, + { + fieldName: 'conversion_environment', + }, + ], + }, + }, + ], + requestId: 'dummyRequestId', + }, + ], + }, + }, + }, + }, ];