diff --git a/.github/workflows/component-test-report.yml b/.github/workflows/component-test-report.yml new file mode 100644 index 00000000000..99040044178 --- /dev/null +++ b/.github/workflows/component-test-report.yml @@ -0,0 +1,68 @@ +name: Component Test Reporter + +on: + pull_request: + types: + - opened + - reopened + - synchronize + +permissions: + id-token: write # allows the JWT to be requested from GitHub's OIDC provider + contents: read # This is required for actions/checkout + +jobs: + test_and_upload: + runs-on: ubuntu-latest + + steps: + - name: Configure AWS Credentials + uses: aws-actions/configure-aws-credentials@v4 + with: + role-to-assume: arn:aws:iam::${{ secrets.AWS_DEV_ACCOUNT_ID }}:role/${{ secrets.AWS_DEV_S3_SYNC_ROLE }} + aws-region: us-east-1 + + - name: Checkout + uses: actions/checkout@v4.1.1 + with: + fetch-depth: 1 + + - name: Setup Node + uses: actions/setup-node@v4.0.1 + with: + node-version-file: '.nvmrc' + cache: 'npm' + + - name: Install Dependencies + run: npm ci + + - name: Run Tests and Generate Report + run: | + npm run test:ts -- component + + + - name: Uplaod Report to S3 + run: | + aws s3 cp ./test_reports/ s3://test-integrations-dev/integrations-test-reports/rudder-transformer/${{ github.event.number }}/ --recursive + + + - name: Comment on PR with S3 Object URL + uses: actions/github-script@v7 + with: + github-token: ${{ secrets.PAT }} + script: | + const { owner, repo } = context.repo; + // Get the pull request number + const prNumber = context.payload.pull_request.number; + const commentBody = `Test report for this run is available at: https://test-integrations-dev.s3.amazonaws.com/integrations-test-reports/rudder-transformer/${prNumber}/test-report.html`; + + + // Comment on the pull request + github.rest.issues.createComment({ + owner, + repo, + issue_number: prNumber, + body: commentBody + }); + + \ No newline at end of file diff --git a/.github/workflows/prepare-for-dev-deploy.yml b/.github/workflows/prepare-for-dev-deploy.yml index cf97772e2ee..9eb705aa524 100644 --- a/.github/workflows/prepare-for-dev-deploy.yml +++ b/.github/workflows/prepare-for-dev-deploy.yml @@ -14,7 +14,7 @@ jobs: report-coverage: name: Report Code Coverage if: github.event_name == 'push' - uses: ./.github/workflows/report-code-coverage.yml + uses: ./.github/workflows/dt-test-and-report-code-coverage.yml secrets: SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} diff --git a/.github/workflows/prepare-for-prod-dt-deploy.yml b/.github/workflows/prepare-for-prod-dt-deploy.yml index 9669e1bc2cc..2af853f6430 100644 --- a/.github/workflows/prepare-for-prod-dt-deploy.yml +++ b/.github/workflows/prepare-for-prod-dt-deploy.yml @@ -14,7 +14,7 @@ jobs: report-coverage: name: Report Code Coverage if: github.event_name == 'push' - uses: ./.github/workflows/report-code-coverage.yml + uses: ./.github/workflows/dt-test-and-report-code-coverage.yml secrets: SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} diff --git a/.github/workflows/prepare-for-prod-ut-deploy.yml b/.github/workflows/prepare-for-prod-ut-deploy.yml index ea5928f3b29..c2900d61da0 100644 --- a/.github/workflows/prepare-for-prod-ut-deploy.yml +++ b/.github/workflows/prepare-for-prod-ut-deploy.yml @@ -14,7 +14,7 @@ jobs: report-coverage: name: Report Code Coverage if: github.event_name == 'push' - uses: ./.github/workflows/report-code-coverage.yml + uses: ./.github/workflows/dt-test-and-report-code-coverage.yml secrets: SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} diff --git a/.gitignore b/.gitignore index 24d37f63544..f96c3ac8077 100644 --- a/.gitignore +++ b/.gitignore @@ -134,4 +134,7 @@ dist **/.DS_Store -.idea \ No newline at end of file +.idea + +# component test report +test_reports/ \ No newline at end of file diff --git a/src/cdk/v2/destinations/ortto/procWorkflow.yaml b/src/cdk/v2/destinations/ortto/procWorkflow.yaml index 4b98f6246f0..dfd7118c41d 100644 --- a/src/cdk/v2/destinations/ortto/procWorkflow.yaml +++ b/src/cdk/v2/destinations/ortto/procWorkflow.yaml @@ -34,6 +34,7 @@ steps: description: | Builds common fields in destination payload. template: | + let phone = .message.().({{{{$.getGenericPaths("phone")}}}}); let commonFields = .message.().({ "fields": { "str::first": {{{{$.getGenericPaths("firstName")}}}}, @@ -46,7 +47,7 @@ steps: "dtz::b": $.getBirthdayObj({{{{$.getGenericPaths("birthday")}}}}), "str::ei": {{{{$.getGenericPaths("userId")}}}}, "str::language": .context.traits.language || .context.locale, - "phn::phone": {"n": {{{{$.getGenericPaths("phone")}}}}}, + "phn::phone": phone ? {"n": phone}, "bol::gdpr": .context.traits.gdpr ?? true, "bol::p": .context.traits.emailConsent || false, "bol::sp": .context.traits.smsConsent || false, diff --git a/src/cdk/v2/destinations/the_trade_desk/utils.js b/src/cdk/v2/destinations/the_trade_desk/utils.js index 0f1c3fb0c1c..632442d74e5 100644 --- a/src/cdk/v2/destinations/the_trade_desk/utils.js +++ b/src/cdk/v2/destinations/the_trade_desk/utils.js @@ -8,6 +8,7 @@ const { getSuccessRespEvents, removeUndefinedAndNullValues, handleRtTfSingleEventError, + isEmptyObject, } = require('../../../../v0/util'); const tradeDeskConfig = require('./config'); @@ -59,34 +60,44 @@ const processRecordInputs = (inputs, destination) => { const successMetadata = []; const errorResponseList = []; - const error = new InstrumentationError('Invalid action type'); + const invalidActionTypeError = new InstrumentationError('Invalid action type'); + const emptyFieldsError = new InstrumentationError('Fields cannot be empty'); inputs.forEach((input) => { const { fields, action } = input.message; const isInsertOrDelete = action === 'insert' || action === 'delete'; - if (isInsertOrDelete) { - successMetadata.push(input.metadata); - const data = [ - { - Name: Config.audienceId, - TTLInMinutes: action === 'insert' ? ttlInMin(Config.ttlInDays) : 0, - }, - ]; - - Object.keys(fields).forEach((id) => { - const value = fields[id]; - if (value) { - // adding only non empty ID's - items.push({ [id]: value, Data: data }); - } - }); - } else { - errorResponseList.push(handleRtTfSingleEventError(input, error, {})); + if (!isInsertOrDelete) { + errorResponseList.push(handleRtTfSingleEventError(input, invalidActionTypeError, {})); + return; } + + if (isEmptyObject(fields)) { + errorResponseList.push(handleRtTfSingleEventError(input, emptyFieldsError, {})); + return; + } + + successMetadata.push(input.metadata); + const data = [ + { + Name: Config.audienceId, + TTLInMinutes: action === 'insert' ? ttlInMin(Config.ttlInDays) : 0, + }, + ]; + + Object.keys(fields).forEach((id) => { + const value = fields[id]; + if (value) { + // adding only non empty ID's + items.push({ [id]: value, Data: data }); + } + }); }); const payloads = batchResponseBuilder(items, Config); + if (payloads.length === 0) { + return errorResponseList; + } const response = getSuccessRespEvents(payloads, successMetadata, destination, true); return [response, ...errorResponseList]; diff --git a/src/v0/destinations/tiktok_ads/config.js b/src/v0/destinations/tiktok_ads/config.js index 641b2e08658..61009d4c31a 100644 --- a/src/v0/destinations/tiktok_ads/config.js +++ b/src/v0/destinations/tiktok_ads/config.js @@ -9,6 +9,10 @@ const ConfigCategory = { type: 'track', name: 'TikTokTrack', }, + TRACK_V2: { + type: 'track', + name: 'TikTokTrackV2', + }, }; const PARTNER_NAME = 'RudderStack'; @@ -32,12 +36,18 @@ const eventNameMapping = { const mappingConfig = getMappingConfig(ConfigCategory, __dirname); +// tiktok docs for max batch size for events 2.0: https://business-api.tiktok.com/portal/docs?id=1771100779668482 +const maxBatchSizeV2 = 1000; +const trackEndpointV2 = 'https://business-api.tiktok.com/open_api/v1.3/event/track/'; module.exports = { TRACK_ENDPOINT, BATCH_ENDPOINT, MAX_BATCH_SIZE, PARTNER_NAME, trackMapping: mappingConfig[ConfigCategory.TRACK.name], + trackMappingV2: mappingConfig[ConfigCategory.TRACK_V2.name], eventNameMapping, DESTINATION: 'TIKTOK_ADS', + trackEndpointV2, + maxBatchSizeV2, }; diff --git a/src/v0/destinations/tiktok_ads/data/TikTokTrack.json b/src/v0/destinations/tiktok_ads/data/TikTokTrack.json index cc5f4886e0b..62688b79528 100644 --- a/src/v0/destinations/tiktok_ads/data/TikTokTrack.json +++ b/src/v0/destinations/tiktok_ads/data/TikTokTrack.json @@ -42,6 +42,22 @@ "type": "toString" } }, + { + "destKey": "properties.shop_id", + "sourceKeys": ["properties.shop_id", "properties.shopId"], + "required": false, + "metadata": { + "type": "toString" + } + }, + { + "destKey": "properties.order_id", + "sourceKeys": ["properties.order_id", "properties.orderId"], + "required": false, + "metadata": { + "type": "toString" + } + }, { "destKey": "properties.content_type", "sourceKeys": ["properties.contentType", "properties.content_type"], diff --git a/src/v0/destinations/tiktok_ads/data/TikTokTrackV2.json b/src/v0/destinations/tiktok_ads/data/TikTokTrackV2.json new file mode 100644 index 00000000000..8a31d67199a --- /dev/null +++ b/src/v0/destinations/tiktok_ads/data/TikTokTrackV2.json @@ -0,0 +1,133 @@ +[ + { + "destKey": "event_id", + "sourceKeys": ["properties.eventId", "properties.event_id", "messageId"], + "required": false + }, + { + "destKey": "event_time", + "sourceKeys": "timestamp", + "sourceFromGenericMap": true, + "required": true, + "metadata": { + "type": "secondTimestamp" + } + }, + { + "destKey": "limited_data_use", + "sourceKeys": "properties.limited_data_use", + "required": false + }, + { + "destKey": "properties.contents", + "sourceKeys": "properties.contents", + "required": false + }, + { + "destKey": "properties.content_type", + "sourceKeys": ["properties.contentType", "properties.content_type"], + "metadata": { + "defaultValue": "product" + } + }, + { + "destKey": "properties.shop_id", + "sourceKeys": ["properties.shop_id", "properties.shopId"], + "required": false, + "metadata": { + "type": "toString" + } + }, + { + "destKey": "properties.order_id", + "sourceKeys": ["properties.order_id", "properties.orderId"], + "required": false, + "metadata": { + "type": "toString" + } + }, + { + "destKey": "properties.currency", + "sourceKeys": "properties.currency", + "required": false + }, + { + "destKey": "properties.value", + "sourceKeys": "properties.value", + "required": false + }, + { + "destKey": "properties.description", + "sourceKeys": "properties.description", + "required": false + }, + { + "destKey": "properties.query", + "sourceKeys": "properties.query", + "required": false + }, + { + "destKey": "page.url", + "sourceKeys": ["properties.context.page.url", "properties.url", "context.page.url"], + "required": true + }, + { + "destKey": "page.referrer", + "sourceKeys": [ + "properties.context.page.referrer", + "properties.referrer", + "context.page.referrer" + ], + "required": false + }, + { + "destKey": "user.locale", + "sourceKeys": ["properties.context.user.locale", "context.locale"], + "required": false + }, + { + "destKey": "user.ttclid", + "sourceKeys": "properties.ttclid", + "required": false + }, + { + "destKey": "user.ttp", + "sourceKeys": ["properties.context.user.ttp", "properties.ttp"], + "required": false + }, + { + "destKey": "user.email", + "sourceKeys": [ + "properties.context.user.email", + "context.user.email", + "traits.email", + "context.traits.email", + "properties.email" + ], + "metadata": { + "type": ["trim", "toLower"] + }, + "required": false + }, + { + "destKey": "user.phone", + "sourceKeys": [ + "properties.context.user.phone", + "traits.phone", + "context.traits.phone", + "properties.phone" + ], + "sourceFromGenericMap": false, + "required": false + }, + { + "destKey": "user.ip", + "sourceKeys": ["properties.context.user.ip", "context.ip", "request_ip"], + "required": false + }, + { + "destKey": "user.user_agent", + "sourceKeys": ["properties.context.user.userAgent", "context.userAgent"], + "required": false + } +] diff --git a/src/v0/destinations/tiktok_ads/transform.js b/src/v0/destinations/tiktok_ads/transform.js index 91e2189170f..bdf3a0defe3 100644 --- a/src/v0/destinations/tiktok_ads/transform.js +++ b/src/v0/destinations/tiktok_ads/transform.js @@ -21,6 +21,7 @@ const { handleRtTfSingleEventError, batchMultiplexedEvents, } = require('../../util'); +const { process: processV2, processRouterDest: processRouterDestV2 } = require('./transformV2'); const { getContents } = require('./util'); const { trackMapping, @@ -165,7 +166,9 @@ const trackResponseBuilder = async (message, { Config }) => { const process = async (event) => { const { message, destination } = event; - + if (destination.Config?.version === 'v2') { + return processV2(event); + } if (!destination.Config.accessToken) { throw new ConfigurationError('Access Token not found. Aborting '); } @@ -240,6 +243,11 @@ function getEventChunks(event, trackResponseList, eventsChunk) { } const processRouterDest = async (inputs, reqMetadata) => { + const { destination } = inputs[0]; + const { Config } = destination; + if (Config?.version === 'v2') { + return processRouterDestV2(inputs, reqMetadata); + } const errorRespEvents = checkInvalidRtTfEvents(inputs); if (errorRespEvents.length > 0) { return errorRespEvents; diff --git a/src/v0/destinations/tiktok_ads/transformV2.js b/src/v0/destinations/tiktok_ads/transformV2.js new file mode 100644 index 00000000000..91078dfe656 --- /dev/null +++ b/src/v0/destinations/tiktok_ads/transformV2.js @@ -0,0 +1,738 @@ +/* eslint-disable camelcase */ +/* eslint-disable @typescript-eslint/naming-convention */ +const set = require('set-value'); +const { ConfigurationError, InstrumentationError } = require('@rudderstack/integrations-lib'); +const { EventType } = require('../../../constants'); +const { + constructPayload, + defaultRequestConfig, + defaultPostRequestConfig, + removeUndefinedAndNullValues, + defaultBatchRequestConfig, + getSuccessRespEvents, + isDefinedAndNotNullAndNotEmpty, + getDestinationExternalID, + getHashFromArrayWithDuplicate, + checkInvalidRtTfEvents, + handleRtTfSingleEventError, +} = require('../../util'); +const { getContents, hashUserField } = require('./util'); +const config = require('./config'); + +const { trackMappingV2, trackEndpointV2, eventNameMapping, PARTNER_NAME } = config; +const { JSON_MIME_TYPE } = require('../../util/constant'); + +/** + * Generated track payload for an event using TikTokTrackV2.json config mapping, + * hashing user properties and + * defining contents from products + * @param {*} message + * @param {*} Config + * @param {*} event + * @returns track payload + */ +const getTrackResponsePayload = (message, destConfig, event) => { + const payload = constructPayload(message, trackMappingV2); + + // if contents is not an array converting it into array + if (payload.properties?.contents && !Array.isArray(payload.properties.contents)) { + payload.properties.contents = [payload.properties.contents]; + } + + // if contents is not present but we have properties.products present which has fields with superset of contents fields + if (payload.properties && !payload.properties.contents && message.properties.products) { + // retreiving data from products only when contents is not present + payload.properties.contents = getContents(message, false); + } + + // getting externalId and hashing it and storing it in + const externalId = getDestinationExternalID(message, 'tiktokExternalId'); + if (isDefinedAndNotNullAndNotEmpty(externalId)) { + set(payload, 'user.external_id', externalId); + } + if (destConfig.hashUserProperties && isDefinedAndNotNullAndNotEmpty(payload.user)) { + payload.user = hashUserField(payload.user); + } + payload.event = event; + // add partner name and return payload + return removeUndefinedAndNullValues(payload); +}; + +const trackResponseBuilder = async (message, { Config }) => { + const { eventsToStandard, sendCustomEvents, accessToken, pixelCode } = Config; + + let event = message.event?.toLowerCase().trim(); + if (!event) { + throw new InstrumentationError('Event name is required'); + } + + const standardEventsMap = getHashFromArrayWithDuplicate(eventsToStandard); + + if (!sendCustomEvents && eventNameMapping[event] === undefined && !standardEventsMap[event]) { + throw new InstrumentationError( + `Event name (${event}) is not valid, must be mapped to one of standard events`, + ); + } + const response = defaultRequestConfig(); + response.headers = { + 'Access-Token': accessToken, + 'Content-Type': JSON_MIME_TYPE, + }; + // tiktok doc for request: https://business-api.tiktok.com/portal/docs?id=1771100865818625 + response.method = defaultPostRequestConfig.requestMethod; + response.endpoint = trackEndpointV2; + const responseList = []; + if (standardEventsMap[event]) { + Object.keys(standardEventsMap).forEach((key) => { + if (key === event) { + standardEventsMap[event].forEach((eventName) => { + responseList.push(getTrackResponsePayload(message, Config, eventName)); + }); + } + }); + } else { + /* + For custom event we do not want to lower case the event or trim it we just want to send those as it is + Doc https://ads.tiktok.com/help/article/standard-events-parameters?lang=en + */ + event = eventNameMapping[event] || message.event; + // if there exists no event mapping we will build payload with custom event recieved + responseList.push(getTrackResponsePayload(message, Config, event)); + } + // set event source and event_source_id + response.body.JSON = { + event_source: 'web', + event_source_id: pixelCode, + partner_name: PARTNER_NAME, + test_event_code: message.properties?.testEventCode, + }; + response.body.JSON.data = responseList; + return response; +}; + +const process = async (event) => { + const { message, destination } = event; + + if (!destination.Config.accessToken) { + throw new ConfigurationError('Access Token not found. Aborting'); + } + + if (!destination.Config.pixelCode) { + throw new ConfigurationError('Pixel Code not found. Aborting'); + } + + if (!message.type) { + throw new InstrumentationError('Event type is required'); + } + + const messageType = message.type.toLowerCase(); + + let response; + if (messageType === EventType.TRACK) { + response = await trackResponseBuilder(message, destination); + } else { + throw new InstrumentationError(`Event type ${messageType} is not supported`); + } + return response; +}; + +/** + * it builds batch response for an event using defaultBatchRequestConfig() utility + * @param {*} eventsChunk + * @returns batchedRequest + * + * Example: + * inputEvent: + *{ + event: { + event_source_id: "dummyPixelCode", + event_source: "web", + partner_name: "RudderStack", + data: [ + { + event_id: "1616318632825_357", + event_time: 1600372167, + properties: { + contents: [ + { + price: 8, + quantity: 2, + content_type: "socks", + content_id: "1077218", + }, + { + price: 30, + quantity: 1, + content_type: "dress", + content_id: "1197218", + }, + ], + content_type: "product", + currency: "USD", + value: 46, + }, + page: { + url: "http://demo.mywebsite.com/purchase", + referrer: "http://demo.mywebsite.com", + }, + user: { + locale: "en-US", + email: "f0e388f53921a51f0bb0fc8a2944109ec188b59172935d8f23020b1614cc44bc", + phone: "2f9d2b4df907e5c9a7b3434351b55700167b998a83dc479b825096486ffcf4ea", + ip: "13.57.97.131", + user_agent: "Mozilla/5.0 (platform; rv:geckoversion) Gecko/geckotrail Firefox/firefoxversion", + external_id: "id5", + }, + event: "CompletePayment", + partner_name: "RudderStack", + }, + { + event_id: "1616318632825_357", + event_time: 1600372167, + properties: { + contents: [ + { + price: 8, + quantity: 2, + content_type: "socks", + content_id: "1077218", + }, + { + price: 30, + quantity: 1, + content_type: "dress", + content_id: "1197218", + }, + ], + content_type: "product", + currency: "USD", + value: 46, + }, + page: { + url: "http://demo.mywebsite.com/purchase", + }, + user: { + locale: "en-US", + email: "f0e388f53921a51f0bb0fc8a2944109ec188b59172935d8f23020b1614cc44bc", + phone: "2f9d2b4df907e5c9a7b3434351b55700167b998a83dc479b825096486ffcf4ea", + ip: "13.57.97.131", + user_agent: "Mozilla/5.0 (platform; rv:geckoversion) Gecko/geckotrail Firefox/firefoxversion", + external_id: "id1", + }, + event: "CompletePayment", + partner_name: "RudderStack", + } + ], + }, + metadata: [ + { + jobId: 5, + }, + { + jobId: 1, + } + ], + destination: { + Config: { + accessToken: "dummyAccessToken", + pixelCode: "dummyPixelCode", + hashUserProperties: false, + version: "v2", + }, + }, +} + * returns: + * + { + version: "1", + type: "REST", + method: "POST", + endpoint: "https://business-api.tiktok.com/open_api/v1.3/event/track/", + headers: { + "Access-Token": "dummyAccessToken", + "Content-Type": "application/json", + }, + params: { + }, + body: { + JSON: { + event_source_id: "asdfg", + event_source: "web", + partner_name: "RudderStack", + data: [ + { + event_id: "1616318632825_357", + event_time: 1600372167, + properties: { + contents: [ + { + price: 8, + quantity: 2, + content_type: "socks", + content_id: "1077218", + }, + { + price: 30, + quantity: 1, + content_type: "dress", + content_id: "1197218", + }, + ], + content_type: "product", + currency: "USD", + value: 46, + }, + page: { + url: "http://demo.mywebsite.com/purchase", + referrer: "http://demo.mywebsite.com", + }, + user: { + locale: "en-US", + email: "f0e388f53921a51f0bb0fc8a2944109ec188b59172935d8f23020b1614cc44bc", + phone: "2f9d2b4df907e5c9a7b3434351b55700167b998a83dc479b825096486ffcf4ea", + ip: "13.57.97.131", + user_agent: "Mozilla/5.0 (platform; rv:geckoversion) Gecko/geckotrail Firefox/firefoxversion", + external_id: "id5", + }, + event: "CompletePayment", + }, + { + event_id: "1616318632825_357", + event_time: 1600372167, + properties: { + contents: [ + { + price: 8, + quantity: 2, + content_type: "socks", + content_id: "1077218", + }, + { + price: 30, + quantity: 1, + content_type: "dress", + content_id: "1197218", + }, + ], + content_type: "product", + currency: "USD", + value: 46, + }, + page: { + url: "http://demo.mywebsite.com/purchase", + }, + user: { + locale: "en-US", + email: "f0e388f53921a51f0bb0fc8a2944109ec188b59172935d8f23020b1614cc44bc", + phone: "2f9d2b4df907e5c9a7b3434351b55700167b998a83dc479b825096486ffcf4ea", + ip: "13.57.97.131", + user_agent: "Mozilla/5.0 (platform; rv:geckoversion) Gecko/geckotrail Firefox/firefoxversion", + external_id: "id1", + }, + event: "CompletePayment", + } + ], + }, + JSON_ARRAY: { + }, + XML: { + }, + FORM: { + }, + }, + files: { + }, +} + */ +const buildBatchResponseForEvent = (inputEvent) => { + const { destination, event } = inputEvent; + const { accessToken } = destination.Config; + const { batchedRequest } = defaultBatchRequestConfig(); + batchedRequest.body.JSON = event; + batchedRequest.endpoint = trackEndpointV2; + batchedRequest.headers = { + 'Access-Token': accessToken, + 'Content-Type': 'application/json', + }; + return batchedRequest; +}; + +const getEventChunks = (event, trackResponseList, eventsChunk) => { + // only for already transformed payload + // eslint-disable-next-line no-param-reassign + event.message = Array.isArray(event.message) ? event.message : [event.message]; + + // not performing batching for test events as it is not supported + if (event.message[0].body.JSON.test_event_code) { + const { metadata, destination, message } = event; + trackResponseList.push(getSuccessRespEvents(message, [metadata], destination)); + } else { + eventsChunk.push({ ...event }); + } +}; + +/** + * This clubs eventsChunk request body and metadat based upon maxBatchSize + * @param {*} eventsChunk + * @param {*} maxBatchSize + * @returns array of objects as + * { + * event, // Batched Event + * metadata, // metadata of all the requests combined to form above event + * destination, // destination object + * } + * + * Example: + * + * eventsChunk:[ + { + message: [ + { + version: "1", + type: "REST", + method: "POST", + endpoint: "https://business-api.tiktok.com/open_api/v1.3/event/track/", + headers: { + "Access-Token": "dummyAccessToken", + "Content-Type": "application/json", + }, + params: { + }, + body: { + JSON: { + event_source: "web", + event_source_id: "pixel_code", + data: [ + { + event_id: "1616318632825_357", + event_time: 1600372167, + properties: { + contents: [ + { + price: 8, + quantity: 2, + content_type: "socks", + content_id: "1077218", + }, + { + price: 30, + quantity: 1, + content_type: "dress", + content_id: "1197218", + }, + ], + content_type: "product", + currency: "USD", + value: 46, + }, + page: { + url: "http://demo.mywebsite.com/purchase", + referrer: "http://demo.mywebsite.com", + }, + user: { + locale: "en-US", + email: "f0e388f53921a51f0bb0fc8a2944109ec188b59172935d8f23020b1614cc44bc", + phone: "2f9d2b4df907e5c9a7b3434351b55700167b998a83dc479b825096486ffcf4ea", + ip: "13.57.97.131", + user_agent: "Mozilla/5.0 (platform; rv:geckoversion) Gecko/geckotrail Firefox/firefoxversion", + external_id: "id5", + }, + event: "CompletePayment", + partner_name: "RudderStack", + }, + ], + }, + JSON_ARRAY: { + }, + XML: { + }, + FORM: { + }, + }, + files: { + }, + }, + ], + metadata: { + jobId: 5, + }, + destination: { + Config: { + accessToken: "dummyAccessToken", + pixelCode: "pixel_code", + hashUserProperties: false, + version: "v2", + }, + }, + }, + { + message: [ + { + version: "1", + type: "REST", + method: "POST", + endpoint: "https://business-api.tiktok.com/open_api/v1.3/event/track/", + headers: { + "Access-Token": "dummyAccessToken", + "Content-Type": "application/json", + }, + params: { + }, + body: { + JSON: { + event_source: "web", + event_source_id: "pixel_code", + data: [ + { + event_id: "1616318632825_357", + event_time: 1600372167, + properties: { + contents: [ + { + price: 8, + quantity: 2, + content_type: "socks", + content_id: "1077218", + }, + { + price: 30, + quantity: 1, + content_type: "dress", + content_id: "1197218", + }, + ], + content_type: "product", + currency: "USD", + value: 46, + }, + page: { + url: "http://demo.mywebsite.com/purchase", + }, + user: { + locale: "en-US", + email: "f0e388f53921a51f0bb0fc8a2944109ec188b59172935d8f23020b1614cc44bc", + phone: "2f9d2b4df907e5c9a7b3434351b55700167b998a83dc479b825096486ffcf4ea", + ip: "13.57.97.131", + user_agent: "Mozilla/5.0 (platform; rv:geckoversion) Gecko/geckotrail Firefox/firefoxversion", + external_id: "id1", + }, + event: "CompletePayment", + partner_name: "RudderStack", + }, + ], + }, + JSON_ARRAY: { + }, + XML: { + }, + FORM: { + }, + }, + files: { + }, + }, + ], + metadata: { + jobId: 1, + }, + destination: { + Config: { + accessToken: "dummyAccessToken", + pixelCode: "pixel_code", + hashUserProperties: false, + version: "v2", + }, + }, + } +] + * maxBatchSize = 1000 + +Returns +[ + { + event: { + event_source_id: "dummyPixelCode", + event_source: "web", + partner_name: "RudderStack", + data: [ + { + event_id: "1616318632825_357", + event_time: 1600372167, + properties: { + contents: [ + { + price: 8, + quantity: 2, + content_type: "socks", + content_id: "1077218", + }, + { + price: 30, + quantity: 1, + content_type: "dress", + content_id: "1197218", + }, + ], + content_type: "product", + currency: "USD", + value: 46, + }, + page: { + url: "http://demo.mywebsite.com/purchase", + referrer: "http://demo.mywebsite.com", + }, + user: { + locale: "en-US", + email: "f0e388f53921a51f0bb0fc8a2944109ec188b59172935d8f23020b1614cc44bc", + phone: "2f9d2b4df907e5c9a7b3434351b55700167b998a83dc479b825096486ffcf4ea", + ip: "13.57.97.131", + user_agent: "Mozilla/5.0 (platform; rv:geckoversion) Gecko/geckotrail Firefox/firefoxversion", + external_id: "id5", + }, + event: "CompletePayment", + partner_name: "RudderStack", + }, + { + event_id: "1616318632825_357", + event_time: 1600372167, + properties: { + contents: [ + { + price: 8, + quantity: 2, + content_type: "socks", + content_id: "1077218", + }, + { + price: 30, + quantity: 1, + content_type: "dress", + content_id: "1197218", + }, + ], + content_type: "product", + currency: "USD", + value: 46, + }, + page: { + url: "http://demo.mywebsite.com/purchase", + }, + user: { + locale: "en-US", + email: "f0e388f53921a51f0bb0fc8a2944109ec188b59172935d8f23020b1614cc44bc", + phone: "2f9d2b4df907e5c9a7b3434351b55700167b998a83dc479b825096486ffcf4ea", + ip: "13.57.97.131", + user_agent: "Mozilla/5.0 (platform; rv:geckoversion) Gecko/geckotrail Firefox/firefoxversion", + external_id: "id1", + }, + event: "CompletePayment", + partner_name: "RudderStack", + } + ], + }, + metadata: [ + { + jobId: 5, + }, + { + jobId: 1, + } + ], + destination: { + Config: { + accessToken: "dummyAccessToken", + pixelCode: "dummyPixelCode", + hashUserProperties: false, + version: "v2", + }, + }, + } +] + */ +const batchEvents = (eventsChunk) => { + const events = []; + let data = []; + let metadata = []; + const { destination } = eventsChunk[0]; + const { pixelCode } = destination.Config; + eventsChunk.forEach((event) => { + const eventData = event.message[0]?.body.JSON.data; + // eslint-disable-next-line unicorn/consistent-destructuring + if (Array.isArray(eventData) && eventData?.length > config.maxBatchSizeV2 - data.length) { + // Partner name must be added above "data": [..]; + events.push({ + event: { + event_source_id: pixelCode, + event_source: 'web', + partner_name: PARTNER_NAME, + data: [...data], + }, + metadata: [...metadata], + destination, + }); + data = []; + metadata = []; + } + data.push(...eventData); + metadata.push(event.metadata); + }); + // Partner name must be added above "data": [..]; + events.push({ + event: { + event_source_id: pixelCode, + event_source: 'web', + partner_name: PARTNER_NAME, + data: [...data], + }, + metadata: [...metadata], + destination, + }); + return events; +}; +const processRouterDest = async (inputs, reqMetadata) => { + const errorRespEvents = checkInvalidRtTfEvents(inputs); + if (errorRespEvents.length > 0) { + return errorRespEvents; + } + const trackResponseList = []; // list containing single track event in batched format + const eventsChunk = []; // temporary variable to divide payload into chunks + const errorRespList = []; + await Promise.all( + inputs.map(async (event) => { + try { + if (event.message.statusCode) { + // already transformed event + getEventChunks(event, trackResponseList, eventsChunk); + } else { + // if not transformed + getEventChunks( + { + message: await process(event), + metadata: event.metadata, + destination: event.destination, + }, + trackResponseList, + eventsChunk, + ); + } + } catch (error) { + const errRespEvent = handleRtTfSingleEventError(event, error, reqMetadata); + errorRespList.push(errRespEvent); + } + }), + ); + + const batchedResponseList = []; + if (eventsChunk.length > 0) { + const batchedEvents = batchEvents(eventsChunk); + batchedEvents.forEach((batch) => { + const batchedRequest = buildBatchResponseForEvent(batch); + batchedResponseList.push( + getSuccessRespEvents(batchedRequest, batch.metadata, batch.destination, true), + ); + }); + } + return [...batchedResponseList.concat(trackResponseList), ...errorRespList]; +}; + +module.exports = { process, processRouterDest }; diff --git a/src/v0/destinations/tiktok_ads/util.js b/src/v0/destinations/tiktok_ads/util.js index dbc8b344fce..5f86193531a 100644 --- a/src/v0/destinations/tiktok_ads/util.js +++ b/src/v0/destinations/tiktok_ads/util.js @@ -1,11 +1,12 @@ -const { removeUndefinedAndNullValues } = require('../../util'); +const { removeUndefinedAndNullValues, hashToSha256 } = require('../../util'); /** * Prepare contents array from products array * @param {*} message + * @param {*} getContentType if true contents.$.content_type is mapped otherwise not * @returns */ -const getContents = (message) => { +const getContents = (message, getContentType = true) => { const contents = []; const { properties } = message; // eslint-disable-next-line @typescript-eslint/naming-convention @@ -13,20 +14,50 @@ const getContents = (message) => { if (products && Array.isArray(products) && products.length > 0) { products.forEach((product) => { const singleProduct = { - content_type: - product.contentType || contentType || product.content_type || content_type || 'product', + content_type: getContentType + ? product.contentType || contentType || product.contentType || content_type || 'product' + : undefined, content_id: String(product.product_id), content_category: product.category, content_name: product.name, price: product.price, quantity: product.quantity, description: product.description, - brand: product.brand + brand: product.brand, }; contents.push(removeUndefinedAndNullValues(singleProduct)); }); } return contents; }; - -module.exports = { getContents }; +const hashString = (input) => hashToSha256(input.toString().trim()); +/* + * Hashing user related detail i.e external_id, email, phone_number + */ +const hashUserField = (user) => { + const updatedUser = { ...user }; + // hashing external_id + const { email, phone, external_id: externalId } = user; + if (externalId) { + // if there are multiple externalId's in form of array that tiktok supports then hashing every + if (Array.isArray(externalId)) { + updatedUser.external_id = externalId.map((extId) => hashString(extId).toString()); + } else { + updatedUser.external_id = hashString(externalId).toString(); + } + } + // hashing email + if (email && email.length > 0) { + updatedUser.email = hashString(email).toString(); + } + // hashing phone + if (phone && phone.length > 0) { + if (Array.isArray(phone)) { + updatedUser.phone = phone.map((num) => hashString(num).toString()); + } else { + updatedUser.phone = hashString(phone).toString(); + } + } + return updatedUser; +}; +module.exports = { getContents, hashUserField }; diff --git a/src/v0/destinations/tiktok_ads/util.test.js b/src/v0/destinations/tiktok_ads/util.test.js index 5a5cc2c5057..1649b94eebc 100644 --- a/src/v0/destinations/tiktok_ads/util.test.js +++ b/src/v0/destinations/tiktok_ads/util.test.js @@ -1,4 +1,4 @@ -const { getContents } = require('./util'); +const { getContents, hashUserField } = require('./util'); describe('getContents utility test', () => { it('product id sent as number', () => { @@ -95,3 +95,80 @@ describe('getContents utility test', () => { expect(expectedOutput).toEqual(getContents(message)); }); }); + +describe('hashUserField utility test', () => { + it('should return the updated user object with hashed fields', () => { + const user = { + external_id: 123, + email: 'test@example.com', + phone: '+1234567890', + }; + + const hashedUser = hashUserField(user); + + expect(hashedUser).toEqual({ + external_id: 'a665a45920422f9d417e4867efdc4fb8a04a1f3fff1fa07e998e86f7f7a27ae3', + email: '973dfe463ec85785f5f95af5ba3906eedb2d931c24e69824a89ea65dba4e813b', + phone: '422ce82c6fc1724ac878042f7d055653ab5e983d186e616826a72d4384b68af8', + }); + }); + it('should hash external_id, email and phone fields when they are defined and not null or empty', () => { + const user = { + external_id: '123', + email: 'test@example.com', + phone: '+1234567890', + }; + + const hashedUser = hashUserField(user); + + expect(hashedUser.external_id).toBeDefined(); + expect(hashedUser.email).toBeDefined(); + expect(hashedUser.phone).toBeDefined(); + }); + + it('should hash external_id field even if it contains multiple values in form of array', () => { + const user = { + external_id: ['123', '456', '789'], + phone: ['+1234130697', '+e211134234'], + }; + + const hashedUser = hashUserField(user); + + expect(Array.isArray(hashedUser.external_id)).toBe(true); + expect(hashedUser.external_id.length).toBe(user.external_id.length); + hashedUser.external_id.forEach((hashedId, index) => { + expect(hashedId).toBeDefined(); + expect(hashedId).not.toBe(user.external_id[index]); + }); + hashedUser.phone.forEach((hashedId, index) => { + expect(hashedId).toBeDefined(); + expect(hashedId).not.toBe(user.phone[index]); + }); + }); + + it('should not hash external_id, email or phone fields when they are undefined, null or empty', () => { + const user = { + external_id: undefined, + email: null, + phone: '', + }; + + const hashedUser = hashUserField(user); + + expect(hashedUser.external_id).toBeUndefined(); + expect(hashedUser.email).toBeNull(); + expect(hashedUser.phone).toBe(''); + }); + + it('should not modify the original user object', () => { + const user = { + external_id: '123', + email: 'test@example.com', + phone: '1234567890', + }; + + const hashedUser = hashUserField(user); + + expect(hashedUser).not.toBe(user); + }); +}); diff --git a/src/v0/util/index.js b/src/v0/util/index.js index 34182a76850..5433529b5e8 100644 --- a/src/v0/util/index.js +++ b/src/v0/util/index.js @@ -2165,6 +2165,7 @@ module.exports = { getValueFromPropertiesOrTraits, getValuesAsArrayFromConfig, handleSourceKeysOperation, + hashToSha256, isAppleFamily, isBlank, isCdkDestination, diff --git a/test/integrations/component.test.ts b/test/integrations/component.test.ts index 847dd4c91fc..d8d7732e12a 100644 --- a/test/integrations/component.test.ts +++ b/test/integrations/component.test.ts @@ -21,6 +21,8 @@ import tags from '../../src/v0/util/tags'; import { Server } from 'http'; import { appendFileSync } from 'fs'; import { responses } from '../testHelper'; +import { generateTestReport, initaliseReport } from '../test_reporter/reporter'; +import _ from 'lodash'; // To run single destination test cases // npm run test:ts -- component --destination=adobe_analytics @@ -37,6 +39,7 @@ command .option('-f, --feature ', 'Enter Feature Name(processor, router)') .option('-i, --index ', 'Enter Test index') .option('-g, --generate ', 'Enter "true" If you want to generate network file') + .option('-id, --id ', 'Enter unique "Id" of the test case you want to run') .parse(); const opts = command.opts(); @@ -50,7 +53,10 @@ if (opts.generate === 'true') { let server: Server; +const REPORT_COMPATIBLE_INTEGRATION = ['klaviyo']; + beforeAll(async () => { + initaliseReport(); const app = new Koa(); app.use( bodyParser({ @@ -124,6 +130,16 @@ const testRoute = async (route, tcData: TestCaseData) => { const outputResp = tcData.output.response || ({} as any); expect(response.status).toEqual(outputResp.status); + if (REPORT_COMPATIBLE_INTEGRATION.includes(tcData.name?.toLocaleLowerCase())) { + const bodyMatched = _.isEqual(response.body, outputResp.body); + const statusMatched = response.status === outputResp.status; + if (bodyMatched && statusMatched) { + generateTestReport(tcData, response.body, 'passed'); + } else { + generateTestReport(tcData, response.body, 'failed'); + } + } + if (outputResp?.body) { expect(response.body).toEqual(outputResp.body); } @@ -181,6 +197,14 @@ describe.each(allTestDataFilePaths)('%s Tests', (testDataPath) => { if (opts.index !== undefined) { testData = [testData[parseInt(opts.index)]]; } + if (opts.id) { + testData = testData.filter((data) => { + if (data['id'] === opts.id) { + return true; + } + return false; + }); + } describe(`${testData[0].name} ${testData[0].module}`, () => { test.each(testData)('$feature -> $description', async (tcData) => { tcData?.mockFns?.(mockAdapter); diff --git a/test/integrations/destinations/klaviyo/processor/data.ts b/test/integrations/destinations/klaviyo/processor/data.ts index 58a10642510..06c4a3e530e 100644 --- a/test/integrations/destinations/klaviyo/processor/data.ts +++ b/test/integrations/destinations/klaviyo/processor/data.ts @@ -1,2500 +1,15 @@ +import { ecomTestData } from './ecomTestData'; +import { groupTestData } from './groupTestData'; +import { identifyData } from './identifyTestData'; +import { screenTestData } from './screenTestData'; +import { trackTestData } from './trackTestData'; +import { validationTestData } from './validationTestData'; + export const data = [ - { - name: 'klaviyo', - description: 'Profile updating call and subscribe user (old transformer)', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - destination: { - Config: { - publicApiKey: 'dummyPublicApiKey', - privateApiKey: 'dummyPrivateApiKey', - }, - }, - message: { - type: 'identify', - sentAt: '2021-01-03T17:02:53.195Z', - userId: 'user@1', - channel: 'web', - context: { - os: { - name: '', - version: '', - }, - app: { - name: 'RudderLabs JavaScript SDK', - build: '1.0.0', - version: '1.1.11', - namespace: 'com.rudderlabs.javascript', - }, - traits: { - firstName: 'Test', - lastName: 'Rudderlabs', - email: 'test@rudderstack.com', - phone: '+12 345 578 900', - userId: 'user@1', - title: 'Developer', - organization: 'Rudder', - city: 'Tokyo', - region: 'Kanto', - country: 'JP', - zip: '100-0001', - Flagged: false, - Residence: 'Shibuya', - properties: { - listId: 'XUepkK', - subscribe: true, - consent: ['email', 'sms'], - }, - }, - locale: 'en-US', - screen: { - density: 2, - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.1.11', - }, - campaign: {}, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.16; rv:84.0) Gecko/20100101 Firefox/84.0', - }, - rudderId: '8f8fa6b5-8e24-489c-8e22-61f23f2e364f', - messageId: '2116ef8c-efc3-4ca4-851b-02ee60dad6ff', - anonymousId: '97c46c81-3140-456d-b2a9-690d70aaca35', - integrations: { - All: true, - }, - originalTimestamp: '2021-01-03T17:02:53.193Z', - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - userId: '', - method: 'PATCH', - endpoint: 'https://a.klaviyo.com/api/profiles/01GW3PHVY0MTCDGS0A1612HARX', - headers: { - Authorization: 'Klaviyo-API-Key dummyPrivateApiKey', - 'Content-Type': 'application/json', - Accept: 'application/json', - revision: '2023-02-22', - }, - params: {}, - body: { - JSON: { - data: { - type: 'profile', - attributes: { - external_id: 'user@1', - email: 'test@rudderstack.com', - first_name: 'Test', - last_name: 'Rudderlabs', - phone_number: '+12 345 578 900', - title: 'Developer', - organization: 'Rudder', - location: { - city: 'Tokyo', - region: 'Kanto', - country: 'JP', - zip: '100-0001', - }, - properties: { - Flagged: false, - Residence: 'Shibuya', - }, - }, - id: '01GW3PHVY0MTCDGS0A1612HARX', - }, - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - }, - statusCode: 200, - }, - { - output: { - version: '1', - type: 'REST', - method: 'POST', - userId: '', - endpoint: 'https://a.klaviyo.com/api/profile-subscription-bulk-create-jobs', - headers: { - 'Content-Type': 'application/json', - Accept: 'application/json', - Authorization: 'Klaviyo-API-Key dummyPrivateApiKey', - revision: '2023-02-22', - }, - params: {}, - body: { - JSON: { - data: { - type: 'profile-subscription-bulk-create-job', - attributes: { - list_id: 'XUepkK', - subscriptions: [ - { - email: 'test@rudderstack.com', - phone_number: '+12 345 578 900', - channels: { - email: ['MARKETING'], - sms: ['MARKETING'], - }, - }, - ], - }, - }, - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'klaviyo', - description: 'Identify call for with flattenProperties enabled (old transformer)', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - destination: { - Config: { - publicApiKey: 'dummyPublicApiKey', - privateApiKey: 'dummyPrivateApiKey', - flattenProperties: true, - }, - }, - message: { - type: 'identify', - sentAt: '2021-01-03T17:02:53.195Z', - userId: 'user@1', - channel: 'web', - context: { - os: { - name: '', - version: '', - }, - app: { - name: 'RudderLabs JavaScript SDK', - build: '1.0.0', - version: '1.1.11', - namespace: 'com.rudderlabs.javascript', - }, - traits: { - firstName: 'Test', - lastName: 'Rudderlabs', - email: 'test@rudderstack.com', - phone: '+12 345 578 900', - userId: 'user@1', - title: 'Developer', - organization: 'Rudder', - city: 'Tokyo', - region: 'Kanto', - country: 'JP', - zip: '100-0001', - Flagged: false, - Residence: 'Shibuya', - friend: { - names: { - first: 'Alice', - last: 'Smith', - }, - age: 25, - }, - properties: { - listId: 'XUepkK', - subscribe: true, - consent: ['email', 'sms'], - }, - }, - locale: 'en-US', - screen: { - density: 2, - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.1.11', - }, - campaign: {}, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.16; rv:84.0) Gecko/20100101 Firefox/84.0', - }, - rudderId: '8f8fa6b5-8e24-489c-8e22-61f23f2e364f', - messageId: '2116ef8c-efc3-4ca4-851b-02ee60dad6ff', - anonymousId: '97c46c81-3140-456d-b2a9-690d70aaca35', - integrations: { - All: true, - }, - originalTimestamp: '2021-01-03T17:02:53.193Z', - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - userId: '', - method: 'PATCH', - endpoint: 'https://a.klaviyo.com/api/profiles/01GW3PHVY0MTCDGS0A1612HARX', - headers: { - Authorization: 'Klaviyo-API-Key dummyPrivateApiKey', - 'Content-Type': 'application/json', - Accept: 'application/json', - revision: '2023-02-22', - }, - params: {}, - body: { - JSON: { - data: { - type: 'profile', - attributes: { - external_id: 'user@1', - email: 'test@rudderstack.com', - first_name: 'Test', - last_name: 'Rudderlabs', - phone_number: '+12 345 578 900', - title: 'Developer', - organization: 'Rudder', - location: { - city: 'Tokyo', - region: 'Kanto', - country: 'JP', - zip: '100-0001', - }, - properties: { - 'friend.age': 25, - 'friend.names.first': 'Alice', - 'friend.names.last': 'Smith', - Flagged: false, - Residence: 'Shibuya', - }, - }, - id: '01GW3PHVY0MTCDGS0A1612HARX', - }, - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - }, - statusCode: 200, - }, - { - output: { - version: '1', - type: 'REST', - userId: '', - method: 'POST', - endpoint: 'https://a.klaviyo.com/api/profile-subscription-bulk-create-jobs', - headers: { - 'Content-Type': 'application/json', - Accept: 'application/json', - Authorization: 'Klaviyo-API-Key dummyPrivateApiKey', - revision: '2023-02-22', - }, - params: {}, - body: { - JSON: { - data: { - type: 'profile-subscription-bulk-create-job', - attributes: { - list_id: 'XUepkK', - subscriptions: [ - { - email: 'test@rudderstack.com', - phone_number: '+12 345 578 900', - channels: { - email: ['MARKETING'], - sms: ['MARKETING'], - }, - }, - ], - }, - }, - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'klaviyo', - description: 'Profile updation call and subcribe user', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - destination: { - Config: { - publicApiKey: 'dummyPublicApiKey', - privateApiKey: 'dummyPrivateApiKeyforfailure', - }, - }, - message: { - type: 'identify', - sentAt: '2021-01-03T17:02:53.195Z', - userId: 'user@1', - channel: 'web', - context: { - os: { - name: '', - version: '', - }, - app: { - name: 'RudderLabs JavaScript SDK', - build: '1.0.0', - version: '1.1.11', - namespace: 'com.rudderlabs.javascript', - }, - traits: { - firstName: 'Test', - lastName: 'Rudderlabs', - email: 'test3@rudderstack.com', - phone: '+12 345 578 900', - userId: 'user@1', - title: 'Developer', - organization: 'Rudder', - city: 'Tokyo', - region: 'Kanto', - country: 'JP', - zip: '100-0001', - Flagged: false, - Residence: 'Shibuya', - properties: { - listId: 'XUepkK', - subscribe: true, - consent: ['email', 'sms'], - }, - }, - locale: 'en-US', - screen: { - density: 2, - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.1.11', - }, - campaign: {}, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.16; rv:84.0) Gecko/20100101 Firefox/84.0', - }, - rudderId: '8f8fa6b5-8e24-489c-8e22-61f23f2e364f', - messageId: '2116ef8c-efc3-4ca4-851b-02ee60dad6ff', - anonymousId: '97c46c81-3140-456d-b2a9-690d70aaca35', - integrations: { - All: true, - }, - originalTimestamp: '2021-01-03T17:02:53.193Z', - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - error: - '{"message":"Failed to create user due to \\"\\"","destinationResponse":"\\"\\""}', - statTags: { - destType: 'KLAVIYO', - errorCategory: 'network', - errorType: 'retryable', - feature: 'processor', - implementation: 'native', - module: 'destination', - }, - statusCode: 500, - }, - ], - }, - }, - }, - { - name: 'klaviyo', - description: 'Profile updation call listId is not provided for subscribing the user', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - destination: { - Config: { - publicApiKey: 'dummyPublicApiKey', - privateApiKey: 'dummyPrivateApiKey', - }, - }, - message: { - type: 'identify', - sentAt: '2021-01-03T17:02:53.195Z', - userId: 'user@1', - channel: 'web', - context: { - os: { - name: '', - version: '', - }, - app: { - name: 'RudderLabs JavaScript SDK', - build: '1.0.0', - version: '1.1.11', - namespace: 'com.rudderlabs.javascript', - }, - traits: { - firstName: 'Test', - lastName: 'Rudderlabs', - email: 'test@rudderstack.com', - phone: '+12 345 578 900', - userId: 'user@1', - title: 'Developer', - organization: 'Rudder', - city: 'Tokyo', - region: 'Kanto', - country: 'JP', - zip: '100-0001', - Flagged: false, - Residence: 'Shibuya', - properties: { - subscribe: false, - consent: ['email', 'sms'], - }, - }, - locale: 'en-US', - screen: { - density: 2, - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.1.11', - }, - campaign: {}, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.16; rv:84.0) Gecko/20100101 Firefox/84.0', - }, - rudderId: '8f8fa6b5-8e24-489c-8e22-61f23f2e364f', - messageId: '2116ef8c-efc3-4ca4-851b-02ee60dad6ff', - anonymousId: '97c46c81-3140-456d-b2a9-690d70aaca35', - integrations: { - All: true, - }, - originalTimestamp: '2021-01-03T17:02:53.193Z', - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'PATCH', - endpoint: 'https://a.klaviyo.com/api/profiles/01GW3PHVY0MTCDGS0A1612HARX', - headers: { - Authorization: 'Klaviyo-API-Key dummyPrivateApiKey', - 'Content-Type': 'application/json', - Accept: 'application/json', - revision: '2023-02-22', - }, - params: {}, - body: { - JSON: { - data: { - type: 'profile', - attributes: { - external_id: 'user@1', - email: 'test@rudderstack.com', - first_name: 'Test', - last_name: 'Rudderlabs', - phone_number: '+12 345 578 900', - title: 'Developer', - organization: 'Rudder', - location: { - city: 'Tokyo', - region: 'Kanto', - country: 'JP', - zip: '100-0001', - }, - properties: { - Flagged: false, - Residence: 'Shibuya', - }, - }, - id: '01GW3PHVY0MTCDGS0A1612HARX', - }, - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'klaviyo', - description: 'Identify call with enforceEmailAsPrimary enabled from UI', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - destination: { - Config: { - publicApiKey: 'dummyPublicApiKey', - privateApiKey: 'dummyPrivateApiKey', - enforceEmailAsPrimary: true, - }, - }, - message: { - type: 'identify', - sentAt: '2021-01-03T17:02:53.195Z', - userId: 'user@1', - channel: 'web', - context: { - os: { - name: '', - version: '', - }, - app: { - name: 'RudderLabs JavaScript SDK', - build: '1.0.0', - version: '1.1.11', - namespace: 'com.rudderlabs.javascript', - }, - traits: { - firstName: 'Test', - lastName: 'Rudderlabs', - email: 'test@rudderstack.com', - phone: '+12 345 578 900', - userId: 'user@1', - title: 'Developer', - organization: 'Rudder', - city: 'Tokyo', - region: 'Kanto', - country: 'JP', - zip: '100-0001', - Flagged: false, - Residence: 'Shibuya', - properties: { - listId: 'XUepkK', - subscribe: true, - consent: ['email', 'sms'], - }, - }, - locale: 'en-US', - screen: { - density: 2, - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.1.11', - }, - campaign: {}, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.16; rv:84.0) Gecko/20100101 Firefox/84.0', - }, - rudderId: '8f8fa6b5-8e24-489c-8e22-61f23f2e364f', - messageId: '2116ef8c-efc3-4ca4-851b-02ee60dad6ff', - anonymousId: '97c46c81-3140-456d-b2a9-690d70aaca35', - integrations: { - All: true, - }, - originalTimestamp: '2021-01-03T17:02:53.193Z', - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - userId: '', - method: 'PATCH', - endpoint: 'https://a.klaviyo.com/api/profiles/01GW3PHVY0MTCDGS0A1612HARX', - headers: { - Authorization: 'Klaviyo-API-Key dummyPrivateApiKey', - 'Content-Type': 'application/json', - Accept: 'application/json', - revision: '2023-02-22', - }, - params: {}, - body: { - JSON: { - data: { - type: 'profile', - attributes: { - email: 'test@rudderstack.com', - first_name: 'Test', - last_name: 'Rudderlabs', - phone_number: '+12 345 578 900', - title: 'Developer', - organization: 'Rudder', - location: { - city: 'Tokyo', - region: 'Kanto', - country: 'JP', - zip: '100-0001', - }, - properties: { - Flagged: false, - Residence: 'Shibuya', - _id: 'user@1', - }, - }, - id: '01GW3PHVY0MTCDGS0A1612HARX', - }, - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - }, - statusCode: 200, - }, - { - output: { - version: '1', - type: 'REST', - userId: '', - method: 'POST', - endpoint: 'https://a.klaviyo.com/api/profile-subscription-bulk-create-jobs', - headers: { - 'Content-Type': 'application/json', - Accept: 'application/json', - Authorization: 'Klaviyo-API-Key dummyPrivateApiKey', - revision: '2023-02-22', - }, - params: {}, - body: { - JSON: { - data: { - type: 'profile-subscription-bulk-create-job', - attributes: { - list_id: 'XUepkK', - subscriptions: [ - { - email: 'test@rudderstack.com', - phone_number: '+12 345 578 900', - channels: { - email: ['MARKETING'], - sms: ['MARKETING'], - }, - }, - ], - }, - }, - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'klaviyo', - description: 'Identify call without user custom Properties', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - destination: { - Config: { - publicApiKey: 'dummyPublicApiKey', - privateApiKey: 'dummyPrivateApiKey', - enforceEmailAsPrimary: false, - }, - }, - message: { - type: 'identify', - sentAt: '2021-01-03T17:02:53.195Z', - userId: 'user@1', - channel: 'web', - context: { - os: { - name: '', - version: '', - }, - app: { - name: 'RudderLabs JavaScript SDK', - build: '1.0.0', - version: '1.1.11', - namespace: 'com.rudderlabs.javascript', - }, - traits: { - firstName: 'Test', - lastName: 'Rudderlabs', - email: 'test@rudderstack.com', - phone: '+12 345 578 900', - userId: 'user@1', - title: 'Developer', - organization: 'Rudder', - city: 'Tokyo', - region: 'Kanto', - country: 'JP', - zip: '100-0001', - properties: { - listId: 'XUepkK', - subscribe: true, - consent: ['email', 'sms'], - }, - }, - locale: 'en-US', - screen: { - density: 2, - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.1.11', - }, - campaign: {}, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.16; rv:84.0) Gecko/20100101 Firefox/84.0', - }, - rudderId: '8f8fa6b5-8e24-489c-8e22-61f23f2e364f', - messageId: '2116ef8c-efc3-4ca4-851b-02ee60dad6ff', - anonymousId: '97c46c81-3140-456d-b2a9-690d70aaca35', - integrations: { - All: true, - }, - originalTimestamp: '2021-01-03T17:02:53.193Z', - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - userId: '', - method: 'PATCH', - endpoint: 'https://a.klaviyo.com/api/profiles/01GW3PHVY0MTCDGS0A1612HARX', - headers: { - Authorization: 'Klaviyo-API-Key dummyPrivateApiKey', - 'Content-Type': 'application/json', - Accept: 'application/json', - revision: '2023-02-22', - }, - params: {}, - body: { - JSON: { - data: { - type: 'profile', - attributes: { - email: 'test@rudderstack.com', - first_name: 'Test', - last_name: 'Rudderlabs', - phone_number: '+12 345 578 900', - external_id: 'user@1', - title: 'Developer', - organization: 'Rudder', - location: { - city: 'Tokyo', - region: 'Kanto', - country: 'JP', - zip: '100-0001', - }, - }, - id: '01GW3PHVY0MTCDGS0A1612HARX', - }, - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - }, - statusCode: 200, - }, - { - output: { - version: '1', - type: 'REST', - userId: '', - method: 'POST', - endpoint: 'https://a.klaviyo.com/api/profile-subscription-bulk-create-jobs', - headers: { - 'Content-Type': 'application/json', - Accept: 'application/json', - Authorization: 'Klaviyo-API-Key dummyPrivateApiKey', - revision: '2023-02-22', - }, - params: {}, - body: { - JSON: { - data: { - type: 'profile-subscription-bulk-create-job', - attributes: { - list_id: 'XUepkK', - subscriptions: [ - { - email: 'test@rudderstack.com', - phone_number: '+12 345 578 900', - channels: { - email: ['MARKETING'], - sms: ['MARKETING'], - }, - }, - ], - }, - }, - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'klaviyo', - description: 'Identify call without email and phone & enforceEmailAsPrimary enabled from UI', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - destination: { - Config: { - publicApiKey: 'dummyPublicApiKey', - privateApiKey: 'dummyPrivateApiKey', - enforceEmailAsPrimary: true, - }, - }, - message: { - type: 'identify', - sentAt: '2021-01-03T17:02:53.195Z', - userId: 'user@1', - channel: 'web', - context: { - os: { - name: '', - version: '', - }, - app: { - name: 'RudderLabs JavaScript SDK', - build: '1.0.0', - version: '1.1.11', - namespace: 'com.rudderlabs.javascript', - }, - traits: { - firstName: 'Test', - lastName: 'Rudderlabs', - userId: 'user@1', - title: 'Developer', - organization: 'Rudder', - city: 'Tokyo', - region: 'Kanto', - country: 'JP', - zip: '100-0001', - Flagged: false, - Residence: 'Shibuya', - properties: { - listId: 'XUepkK', - subscribe: true, - consent: ['email', 'sms'], - }, - }, - locale: 'en-US', - screen: { - density: 2, - }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.1.11', - }, - campaign: {}, - userAgent: - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.16; rv:84.0) Gecko/20100101 Firefox/84.0', - }, - rudderId: '8f8fa6b5-8e24-489c-8e22-61f23f2e364f', - messageId: '2116ef8c-efc3-4ca4-851b-02ee60dad6ff', - anonymousId: '97c46c81-3140-456d-b2a9-690d70aaca35', - integrations: { - All: true, - }, - originalTimestamp: '2021-01-03T17:02:53.193Z', - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - error: 'None of email and phone are present in the payload', - statTags: { - destType: 'KLAVIYO', - errorCategory: 'dataValidation', - errorType: 'instrumentation', - feature: 'processor', - implementation: 'native', - module: 'destination', - }, - statusCode: 400, - }, - ], - }, - }, - }, - { - name: 'klaviyo', - description: 'Screen event call', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - destination: { - Config: { - publicApiKey: 'dummyPublicApiKey', - privateApiKey: 'dummyPrivateApiKey', - }, - }, - message: { - type: 'screen', - event: 'TestEven001', - sentAt: '2021-01-25T16:12:02.048Z', - userId: 'sajal12', - channel: 'mobile', - context: { - os: { - name: 'Android', - version: '10', - }, - app: { - name: 'KlaviyoTest', - build: '1', - version: '1.0', - namespace: 'com.rudderstack.android.rudderstack.sampleAndroidApp', - }, - device: { - id: '9c6bd77ea9da3e68', - name: 'raphaelin', - type: 'android', - model: 'Redmi K20 Pro', - manufacturer: 'Xiaomi', - }, - locale: 'en-IN', - screen: { - width: 1080, - height: 2210, - density: 440, - }, - traits: { - id: 'user@1', - age: '22', - email: 'test@rudderstack.com', - phone: '9112340375', - anonymousId: '9c6bd77ea9da3e68', - }, - library: { - name: 'com.rudderstack.android.sdk.core', - version: '1.0.2', - }, - network: { - wifi: true, - carrier: 'airtel', - cellular: true, - bluetooth: false, - }, - timezone: 'Asia/Kolkata', - userAgent: - 'Dalvik/2.1.0 (Linux; U; Android 10; Redmi K20 Pro MIUI/V12.0.4.0.QFKINXM)', - }, - rudderId: 'b7b24f86-f7bf-46d8-b2b4-ccafc080239c', - messageId: '1611588776408-ee5a3212-fbf9-4cbb-bbad-3ed0f7c6a2ce', - properties: { - PreviouslyVicePresident: true, - YearElected: 1801, - VicePresidents: ['Aaron Burr', 'George Clinton'], - }, - anonymousId: '9c6bd77ea9da3e68', - integrations: { - All: true, - }, - originalTimestamp: '2021-01-25T15:32:56.409Z', - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://a.klaviyo.com/api/events', - headers: { - Accept: 'application/json', - Authorization: 'Klaviyo-API-Key dummyPrivateApiKey', - 'Content-Type': 'application/json', - revision: '2023-02-22', - }, - params: {}, - body: { - JSON: { - data: { - type: 'event', - attributes: { - metric: { - name: 'TestEven001', - }, - properties: { - PreviouslyVicePresident: true, - YearElected: 1801, - VicePresidents: ['Aaron Burr', 'George Clinton'], - }, - profile: { - $email: 'test@rudderstack.com', - $phone_number: '9112340375', - $id: 'sajal12', - age: '22', - }, - }, - }, - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'klaviyo', - description: 'Track event call with flatten properties enabled', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - destination: { - Config: { - publicApiKey: 'dummyPublicApiKey', - privateApiKey: 'dummyPrivateApiKey', - flattenProperties: true, - }, - }, - message: { - type: 'track', - event: 'TestEven001', - sentAt: '2021-01-25T16:12:02.048Z', - userId: 'sajal12', - channel: 'mobile', - context: { - os: { - name: 'Android', - version: '10', - }, - app: { - name: 'KlaviyoTest', - build: '1', - version: '1.0', - namespace: 'com.rudderstack.android.rudderstack.sampleAndroidApp', - }, - device: { - id: '9c6bd77ea9da3e68', - name: 'raphaelin', - type: 'android', - model: 'Redmi K20 Pro', - manufacturer: 'Xiaomi', - }, - locale: 'en-IN', - screen: { - width: 1080, - height: 2210, - density: 440, - }, - traits: { - id: 'user@1', - age: '22', - email: 'test@rudderstack.com', - phone: '9112340375', - anonymousId: '9c6bd77ea9da3e68', - plan_details: { - plan_type: 'gold', - duration: '3 months', - }, - }, - library: { - name: 'com.rudderstack.android.sdk.core', - version: '1.0.2', - }, - network: { - wifi: true, - carrier: 'airtel', - cellular: true, - bluetooth: false, - }, - timezone: 'Asia/Kolkata', - userAgent: - 'Dalvik/2.1.0 (Linux; U; Android 10; Redmi K20 Pro MIUI/V12.0.4.0.QFKINXM)', - }, - rudderId: 'b7b24f86-f7bf-46d8-b2b4-ccafc080239c', - messageId: '1611588776408-ee5a3212-fbf9-4cbb-bbad-3ed0f7c6a2ce', - properties: { - vicePresdentInfo: { - PreviouslVicePresident: true, - YearElected: 1801, - VicePresidents: ['AaronBurr', 'GeorgeClinton'], - }, - }, - anonymousId: '9c6bd77ea9da3e68', - integrations: { - All: true, - }, - originalTimestamp: '2021-01-25T15:32:56.409Z', - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://a.klaviyo.com/api/events', - headers: { - Accept: 'application/json', - Authorization: 'Klaviyo-API-Key dummyPrivateApiKey', - 'Content-Type': 'application/json', - revision: '2023-02-22', - }, - params: {}, - body: { - JSON: { - data: { - type: 'event', - attributes: { - metric: { - name: 'TestEven001', - }, - properties: { - 'vicePresdentInfo.PreviouslVicePresident': true, - 'vicePresdentInfo.VicePresidents': ['AaronBurr', 'GeorgeClinton'], - 'vicePresdentInfo.YearElected': 1801, - }, - profile: { - $email: 'test@rudderstack.com', - $phone_number: '9112340375', - $id: 'sajal12', - age: '22', - 'plan_details.plan_type': 'gold', - 'plan_details.duration': '3 months', - }, - }, - }, - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'klaviyo', - description: 'Track event call', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - destination: { - Config: { - publicApiKey: 'dummyPublicApiKey', - privateApiKey: 'dummyPrivateApiKey', - }, - }, - message: { - type: 'track', - event: 'TestEven002', - sentAt: '2021-01-25T16:12:02.048Z', - userId: 'sajal12', - channel: 'mobile', - context: { - os: { - name: 'Android', - version: '10', - }, - app: { - name: 'KlaviyoTest', - build: '1', - version: '1.0', - namespace: 'com.rudderstack.android.rudderstack.sampleAndroidApp', - }, - device: { - id: '9c6bd77ea9da3e68', - name: 'raphaelin', - type: 'android', - model: 'Redmi K20 Pro', - manufacturer: 'Xiaomi', - }, - locale: 'en-IN', - screen: { - width: 1080, - height: 2210, - density: 440, - }, - traits: { - id: 'user@1', - age: '22', - name: 'Test', - email: 'test@rudderstack.com', - phone: '9112340375', - anonymousId: '9c6bd77ea9da3e68', - description: 'Sample description', - }, - library: { - name: 'com.rudderstack.android.sdk.core', - version: '1.0.2', - }, - network: { - wifi: true, - carrier: 'airtel', - cellular: true, - bluetooth: false, - }, - timezone: 'Asia/Kolkata', - userAgent: - 'Dalvik/2.1.0 (Linux; U; Android 10; Redmi K20 Pro MIUI/V12.0.4.0.QFKINXM)', - }, - rudderId: 'b7b24f86-f7bf-46d8-b2b4-ccafc080239c', - messageId: '1611588776408-ee5a3212-fbf9-4cbb-bbad-3ed0f7c6a2ce', - properties: { - PreviouslyVicePresident: true, - YearElected: 1801, - VicePresidents: ['Aaron Burr', 'George Clinton'], - revenue: 3000, - }, - anonymousId: '9c6bd77ea9da3e68', - integrations: { - All: true, - }, - originalTimestamp: '2021-01-25T15:32:56.409Z', - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://a.klaviyo.com/api/events', - headers: { - Accept: 'application/json', - Authorization: 'Klaviyo-API-Key dummyPrivateApiKey', - 'Content-Type': 'application/json', - revision: '2023-02-22', - }, - params: {}, - body: { - JSON: { - data: { - type: 'event', - attributes: { - metric: { - name: 'TestEven002', - }, - properties: { - PreviouslyVicePresident: true, - YearElected: 1801, - VicePresidents: ['Aaron Burr', 'George Clinton'], - }, - profile: { - $email: 'test@rudderstack.com', - $phone_number: '9112340375', - $id: 'sajal12', - age: '22', - name: 'Test', - description: 'Sample description', - }, - value: 3000, - }, - }, - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'klaviyo', - description: 'Track event call, with make email or phone as primary identifier toggle on', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - destination: { - Config: { - publicApiKey: 'dummyPublicApiKey', - privateApiKey: 'dummyPrivateApiKey', - enforceEmailAsPrimary: true, - }, - }, - message: { - type: 'track', - event: 'TestEven001', - sentAt: '2021-01-25T16:12:02.048Z', - userId: 'sajal12', - channel: 'mobile', - context: { - os: { - name: 'Android', - version: '10', - }, - app: { - name: 'KlaviyoTest', - build: '1', - version: '1.0', - namespace: 'com.rudderstack.android.rudderstack.sampleAndroidApp', - }, - device: { - id: '9c6bd77ea9da3e68', - name: 'raphaelin', - type: 'android', - model: 'Redmi K20 Pro', - manufacturer: 'Xiaomi', - }, - locale: 'en-IN', - screen: { - width: 1080, - height: 2210, - density: 440, - }, - traits: { - id: 'user@1', - age: '22', - email: 'test@rudderstack.com', - phone: '9112340375', - anonymousId: '9c6bd77ea9da3e68', - }, - library: { - name: 'com.rudderstack.android.sdk.core', - version: '1.0.2', - }, - network: { - wifi: true, - carrier: 'airtel', - cellular: true, - bluetooth: false, - }, - timezone: 'Asia/Kolkata', - userAgent: - 'Dalvik/2.1.0 (Linux; U; Android 10; Redmi K20 Pro MIUI/V12.0.4.0.QFKINXM)', - }, - rudderId: 'b7b24f86-f7bf-46d8-b2b4-ccafc080239c', - messageId: '1611588776408-ee5a3212-fbf9-4cbb-bbad-3ed0f7c6a2ce', - properties: { - PreviouslyVicePresident: true, - YearElected: 1801, - VicePresidents: ['Aaron Burr', 'George Clinton'], - }, - anonymousId: '9c6bd77ea9da3e68', - integrations: { - All: true, - }, - originalTimestamp: '2021-01-25T15:32:56.409Z', - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://a.klaviyo.com/api/events', - headers: { - Accept: 'application/json', - Authorization: 'Klaviyo-API-Key dummyPrivateApiKey', - 'Content-Type': 'application/json', - revision: '2023-02-22', - }, - params: {}, - body: { - JSON: { - data: { - type: 'event', - attributes: { - metric: { - name: 'TestEven001', - }, - properties: { - PreviouslyVicePresident: true, - YearElected: 1801, - VicePresidents: ['Aaron Burr', 'George Clinton'], - }, - profile: { - $email: 'test@rudderstack.com', - $phone_number: '9112340375', - age: '22', - _id: 'sajal12', - }, - }, - }, - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'klaviyo', - description: - 'Track event call, without email and phone & with (make email or phone as primary identifier) toggle on', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - destination: { - Config: { - publicApiKey: 'dummyPublicApiKey', - privateApiKey: 'dummyPrivateApiKey', - enforceEmailAsPrimary: true, - }, - }, - message: { - type: 'track', - event: 'TestEven001', - sentAt: '2021-01-25T16:12:02.048Z', - userId: 'sajal12', - channel: 'mobile', - context: { - os: { - name: 'Android', - version: '10', - }, - app: { - name: 'KlaviyoTest', - build: '1', - version: '1.0', - namespace: 'com.rudderstack.android.rudderstack.sampleAndroidApp', - }, - device: { - id: '9c6bd77ea9da3e68', - name: 'raphaelin', - type: 'android', - model: 'Redmi K20 Pro', - manufacturer: 'Xiaomi', - }, - locale: 'en-IN', - screen: { - width: 1080, - height: 2210, - density: 440, - }, - traits: { - id: 'user@1', - age: '22', - anonymousId: '9c6bd77ea9da3e68', - }, - library: { - name: 'com.rudderstack.android.sdk.core', - version: '1.0.2', - }, - network: { - wifi: true, - carrier: 'airtel', - cellular: true, - bluetooth: false, - }, - timezone: 'Asia/Kolkata', - userAgent: - 'Dalvik/2.1.0 (Linux; U; Android 10; Redmi K20 Pro MIUI/V12.0.4.0.QFKINXM)', - }, - rudderId: 'b7b24f86-f7bf-46d8-b2b4-ccafc080239c', - messageId: '1611588776408-ee5a3212-fbf9-4cbb-bbad-3ed0f7c6a2ce', - properties: { - PreviouslyVicePresident: true, - YearElected: 1801, - VicePresidents: ['Aaron Burr', 'George Clinton'], - }, - anonymousId: '9c6bd77ea9da3e68', - integrations: { - All: true, - }, - originalTimestamp: '2021-01-25T15:32:56.409Z', - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - error: 'None of email and phone are present in the payload', - statTags: { - destType: 'KLAVIYO', - errorCategory: 'dataValidation', - errorType: 'instrumentation', - feature: 'processor', - implementation: 'native', - module: 'destination', - }, - statusCode: 400, - }, - ], - }, - }, - }, - { - name: 'klaviyo', - description: 'group call', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - destination: { - Config: { - publicApiKey: 'dummyPublicApiKey', - privateApiKey: 'dummyPrivateApiKey', - }, - }, - message: { - userId: 'user123', - type: 'group', - groupId: 'XUepkK', - traits: { - subscribe: true, - }, - context: { - traits: { - email: 'test@rudderstack.com', - phone: '+12 345 678 900', - consent: ['email'], - }, - ip: '14.5.67.21', - library: { - name: 'http', - }, - }, - timestamp: '2020-01-21T00:21:34.208Z', - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - body: { - FORM: {}, - JSON: { - data: { - attributes: { - list_id: 'XUepkK', - subscriptions: [ - { email: 'test@rudderstack.com', phone_number: '+12 345 678 900' }, - ], - }, - type: 'profile-subscription-bulk-create-job', - }, - }, - JSON_ARRAY: {}, - XML: {}, - }, - endpoint: 'https://a.klaviyo.com/api/profile-subscription-bulk-create-jobs', - files: {}, - headers: { - Accept: 'application/json', - Authorization: 'Klaviyo-API-Key dummyPrivateApiKey', - 'Content-Type': 'application/json', - revision: '2023-02-22', - }, - method: 'POST', - params: {}, - type: 'REST', - userId: '', - version: '1', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'klaviyo', - description: 'group call without groupId', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - destination: { - Config: { - publicApiKey: 'dummyPublicApiKey', - privateApiKey: 'dummyPrivateApiKey', - }, - }, - message: { - userId: 'user123', - type: 'group', - groupId: '', - traits: { - subscribe: true, - }, - context: { - traits: { - email: 'test@rudderstack.com', - phone: '+12 345 678 900', - consent: 'email', - }, - ip: '14.5.67.21', - library: { - name: 'http', - }, - }, - timestamp: '2020-01-21T00:21:34.208Z', - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - error: 'groupId is a required field for group events', - statTags: { - destType: 'KLAVIYO', - errorCategory: 'dataValidation', - errorType: 'instrumentation', - feature: 'processor', - implementation: 'native', - module: 'destination', - }, - statusCode: 400, - }, - ], - }, - }, - }, - { - name: 'klaviyo', - description: '[Error]: Check for unsupported message type', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - destination: { - Config: { - publicApiKey: 'dummyPublicApiKey', - privateApiKey: 'dummyPrivateApiKey', - }, - }, - message: { - userId: 'user123', - type: 'random', - groupId: 'XUepkK', - traits: { - subscribe: true, - }, - context: { - traits: { - email: 'test@rudderstack.com', - phone: '+12 345 678 900', - consent: 'email', - }, - ip: '14.5.67.21', - library: { - name: 'http', - }, - }, - timestamp: '2020-01-21T00:21:34.208Z', - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - error: 'Event type random is not supported', - statTags: { - destType: 'KLAVIYO', - errorCategory: 'dataValidation', - errorType: 'instrumentation', - feature: 'processor', - implementation: 'native', - module: 'destination', - }, - statusCode: 400, - }, - ], - }, - }, - }, - { - name: 'klaviyo', - description: 'Track call with Ecom events (Viewed Product)', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - destination: { - Config: { - publicApiKey: 'dummyPublicApiKey', - privateApiKey: 'dummyPrivateApiKey', - }, - }, - message: { - type: 'track', - event: 'product viewed', - sentAt: '2021-01-25T16:12:02.048Z', - userId: 'sajal12', - channel: 'mobile', - context: { - os: { - name: 'Android', - version: '10', - }, - app: { - name: 'KlaviyoTest', - build: '1', - version: '1.0', - namespace: 'com.rudderstack.android.rudderstack.sampleAndroidApp', - }, - device: { - id: '9c6bd77ea9da3e68', - name: 'raphaelin', - type: 'android', - model: 'Redmi K20 Pro', - manufacturer: 'Xiaomi', - }, - locale: 'en-IN', - screen: { - width: 1080, - height: 2210, - density: 440, - }, - traits: { - id: 'user@1', - age: '22', - name: 'Test', - email: 'test@rudderstack.com', - phone: '9112340375', - anonymousId: '9c6bd77ea9da3e68', - description: 'Sample description', - }, - library: { - name: 'com.rudderstack.android.sdk.core', - version: '1.0.2', - }, - network: { - wifi: true, - carrier: 'airtel', - cellular: true, - bluetooth: false, - }, - timezone: 'Asia/Kolkata', - userAgent: - 'Dalvik/2.1.0 (Linux; U; Android 10; Redmi K20 Pro MIUI/V12.0.4.0.QFKINXM)', - }, - rudderId: 'b7b24f86-f7bf-46d8-b2b4-ccafc080239c', - messageId: '1611588776408-ee5a3212-fbf9-4cbb-bbad-3ed0f7c6a2ce', - properties: { - name: 'test product', - product_id: '1114', - sku: 'WINNIePuh12', - image_url: 'http://www.example.com/path/to/product/image.png', - url: 'http://www.example.com/path/to/product', - brand: 'Not for Kids', - price: 9.9, - categories: ['Fiction', 'Children'], - }, - anonymousId: '9c6bd77ea9da3e68', - integrations: { - All: true, - }, - originalTimestamp: '2021-01-25T15:32:56.409Z', - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://a.klaviyo.com/api/events', - headers: { - Authorization: 'Klaviyo-API-Key dummyPrivateApiKey', - 'Content-Type': 'application/json', - Accept: 'application/json', - revision: '2023-02-22', - }, - params: {}, - body: { - JSON: { - data: { - type: 'event', - attributes: { - metric: { - name: 'Viewed Product', - }, - profile: { - $email: 'test@rudderstack.com', - $phone_number: '9112340375', - $id: 'sajal12', - age: '22', - name: 'Test', - description: 'Sample description', - }, - properties: { - ProductName: 'test product', - ProductID: '1114', - SKU: 'WINNIePuh12', - ImageURL: 'http://www.example.com/path/to/product/image.png', - URL: 'http://www.example.com/path/to/product', - Brand: 'Not for Kids', - Price: 9.9, - Categories: ['Fiction', 'Children'], - }, - }, - }, - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'klaviyo', - description: 'Track event call -> Invalid event i.e. sent as non-string', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - destination: { - Config: { - publicApiKey: 'dummyPublicApiKey', - privateApiKey: 'dummyPrivateApiKey', - }, - }, - message: { - type: 'track', - event: { name: 'TestEven002' }, - sentAt: '2021-01-25T16:12:02.048Z', - userId: 'sajal12', - channel: 'mobile', - context: { - os: { - name: 'Android', - version: '10', - }, - app: { - name: 'KlaviyoTest', - build: '1', - version: '1.0', - namespace: 'com.rudderstack.android.rudderstack.sampleAndroidApp', - }, - device: { - id: '9c6bd77ea9da3e68', - name: 'raphaelin', - type: 'android', - model: 'Redmi K20 Pro', - manufacturer: 'Xiaomi', - }, - locale: 'en-IN', - screen: { - width: 1080, - height: 2210, - density: 440, - }, - traits: { - id: 'user@1', - age: '22', - name: 'Test', - email: 'test@rudderstack.com', - phone: '9112340375', - anonymousId: '9c6bd77ea9da3e68', - description: 'Sample description', - }, - library: { - name: 'com.rudderstack.android.sdk.core', - version: '1.0.2', - }, - network: { - wifi: true, - carrier: 'airtel', - cellular: true, - bluetooth: false, - }, - timezone: 'Asia/Kolkata', - userAgent: - 'Dalvik/2.1.0 (Linux; U; Android 10; Redmi K20 Pro MIUI/V12.0.4.0.QFKINXM)', - }, - rudderId: 'b7b24f86-f7bf-46d8-b2b4-ccafc080239c', - messageId: '1611588776408-ee5a3212-fbf9-4cbb-bbad-3ed0f7c6a2ce', - properties: { - PreviouslyVicePresident: true, - YearElected: 1801, - VicePresidents: ['Aaron Burr', 'George Clinton'], - revenue: 3000, - }, - anonymousId: '9c6bd77ea9da3e68', - integrations: { - All: true, - }, - originalTimestamp: '2021-01-25T15:32:56.409Z', - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - error: 'Event type should be a string', - statTags: { - destType: 'KLAVIYO', - errorCategory: 'dataValidation', - errorType: 'instrumentation', - feature: 'processor', - implementation: 'native', - module: 'destination', - }, - statusCode: 400, - }, - ], - }, - }, - }, - { - name: 'klaviyo', - description: 'Track call with Ecom events (Checkout Started) with enabled flattenProperties', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - destination: { - Config: { - publicApiKey: 'dummyPublicApiKey', - privateApiKey: 'dummyPrivateApiKey', - flattenProperties: true, - }, - }, - message: { - type: 'track', - event: 'checkout started', - sentAt: '2021-01-25T16:12:02.048Z', - userId: 'sajal12', - channel: 'mobile', - context: { - os: { - name: 'Android', - version: '10', - }, - app: { - name: 'KlaviyoTest', - build: '1', - version: '1.0', - namespace: 'com.rudderstack.android.rudderstack.sampleAndroidApp', - }, - device: { - id: '9c6bd77ea9da3e68', - name: 'raphaelin', - type: 'android', - model: 'Redmi K20 Pro', - manufacturer: 'Xiaomi', - }, - locale: 'en-IN', - screen: { - width: 1080, - height: 2210, - density: 440, - }, - traits: { - id: 'user@1', - age: '22', - name: 'Test', - email: 'test@rudderstack.com', - phone: '9112340375', - anonymousId: '9c6bd77ea9da3e68', - description: 'Sample description', - }, - library: { - name: 'com.rudderstack.android.sdk.core', - version: '1.0.2', - }, - network: { - wifi: true, - carrier: 'airtel', - cellular: true, - bluetooth: false, - }, - timezone: 'Asia/Kolkata', - userAgent: - 'Dalvik/2.1.0 (Linux; U; Android 10; Redmi K20 Pro MIUI/V12.0.4.0.QFKINXM)', - }, - rudderId: 'b7b24f86-f7bf-46d8-b2b4-ccafc080239c', - messageId: '1611588776408-ee5a3212-fbf9-4cbb-bbad-3ed0f7c6a2ce', - properties: { - order_id: '1234', - affiliation: 'Apple Store', - value: 20, - revenue: 15, - shipping: 4, - tax: 1, - discount: 1.5, - coupon: 'ImagePro', - currency: 'USD', - products: [ - { - product_id: '123', - sku: 'G-32', - name: 'Monopoly', - price: 14, - quantity: 1, - category: 'Games', - url: 'https://www.website.com/product/path', - image_url: 'https://www.website.com/product/path.jpg', - }, - { - product_id: '345', - sku: 'F-32', - name: 'UNO', - price: 3.45, - quantity: 2, - category: 'Games', - }, - ], - }, - anonymousId: '9c6bd77ea9da3e68', - integrations: { - All: true, - }, - originalTimestamp: '2021-01-25T15:32:56.409Z', - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://a.klaviyo.com/api/events', - headers: { - Authorization: 'Klaviyo-API-Key dummyPrivateApiKey', - 'Content-Type': 'application/json', - Accept: 'application/json', - revision: '2023-02-22', - }, - params: {}, - body: { - JSON: { - data: { - type: 'event', - attributes: { - metric: { - name: 'Started Checkout', - }, - properties: { - $event_id: '1234', - $value: 20, - 'items[0].ProductID': '123', - 'items[0].SKU': 'G-32', - 'items[0].ProductName': 'Monopoly', - 'items[0].Quantity': 1, - 'items[0].ItemPrice': 14, - 'items[0].ProductURL': 'https://www.website.com/product/path', - 'items[0].ImageURL': 'https://www.website.com/product/path.jpg', - 'items[1].ProductID': '345', - 'items[1].SKU': 'F-32', - 'items[1].ProductName': 'UNO', - 'items[1].Quantity': 2, - 'items[1].ItemPrice': 3.45, - }, - profile: { - $email: 'test@rudderstack.com', - $phone_number: '9112340375', - $id: 'sajal12', - age: '22', - name: 'Test', - description: 'Sample description', - }, - }, - }, - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, - { - name: 'klaviyo', - description: 'Track call with Ecom events (Added to Cart) with properties.products', - feature: 'processor', - module: 'destination', - version: 'v0', - input: { - request: { - body: [ - { - destination: { - Config: { - publicApiKey: 'dummyPublicApiKey', - privateApiKey: 'dummyPrivateApiKey', - }, - }, - message: { - type: 'track', - event: 'product added', - sentAt: '2021-01-25T16:12:02.048Z', - userId: 'sajal12', - channel: 'mobile', - context: { - os: { - name: 'Android', - version: '10', - }, - app: { - name: 'KlaviyoTest', - build: '1', - version: '1.0', - namespace: 'com.rudderstack.android.rudderstack.sampleAndroidApp', - }, - device: { - id: '9c6bd77ea9da3e68', - name: 'raphaelin', - type: 'android', - model: 'Redmi K20 Pro', - manufacturer: 'Xiaomi', - }, - locale: 'en-IN', - screen: { - width: 1080, - height: 2210, - density: 440, - }, - traits: { - id: 'user@1', - age: '22', - name: 'Test', - email: 'test@rudderstack.com', - phone: '9112340375', - anonymousId: '9c6bd77ea9da3e68', - description: 'Sample description', - }, - library: { - name: 'com.rudderstack.android.sdk.core', - version: '1.0.2', - }, - network: { - wifi: true, - carrier: 'airtel', - cellular: true, - bluetooth: false, - }, - timezone: 'Asia/Kolkata', - userAgent: - 'Dalvik/2.1.0 (Linux; U; Android 10; Redmi K20 Pro MIUI/V12.0.4.0.QFKINXM)', - }, - rudderId: 'b7b24f86-f7bf-46d8-b2b4-ccafc080239c', - messageId: '1611588776408-ee5a3212-fbf9-4cbb-bbad-3ed0f7c6a2ce', - properties: { - order_id: '1234', - value: 12.12, - categories: ['Fiction3', 'Children3'], - checkout_url: 'http://www.heythere.com', - item_names: ['book1', 'book2'], - products: [ - { - product_id: 'b1pid', - sku: '123x', - name: 'book1', - url: 'heyther.com', - price: 12, - }, - { - product_id: 'b2pid', - sku: '123x', - name: 'book2', - url: 'heyther2.com', - price: 14, - }, - ], - }, - anonymousId: '9c6bd77ea9da3e68', - integrations: { - All: true, - }, - originalTimestamp: '2021-01-25T15:32:56.409Z', - }, - }, - ], - }, - }, - output: { - response: { - status: 200, - body: [ - { - output: { - version: '1', - type: 'REST', - method: 'POST', - endpoint: 'https://a.klaviyo.com/api/events', - headers: { - Authorization: 'Klaviyo-API-Key dummyPrivateApiKey', - 'Content-Type': 'application/json', - Accept: 'application/json', - revision: '2023-02-22', - }, - params: {}, - body: { - JSON: { - data: { - type: 'event', - attributes: { - metric: { - name: 'Added to Cart', - }, - profile: { - $email: 'test@rudderstack.com', - $phone_number: '9112340375', - $id: 'sajal12', - age: '22', - name: 'Test', - description: 'Sample description', - }, - properties: { - $value: 12.12, - AddedItemCategories: ['Fiction3', 'Children3'], - ItemNames: ['book1', 'book2'], - CheckoutURL: 'http://www.heythere.com', - items: [ - { - ProductID: 'b1pid', - SKU: '123x', - ProductName: 'book1', - ItemPrice: 12, - ProductURL: 'heyther.com', - }, - { - ProductID: 'b2pid', - SKU: '123x', - ProductName: 'book2', - ItemPrice: 14, - ProductURL: 'heyther2.com', - }, - ], - }, - }, - }, - }, - JSON_ARRAY: {}, - XML: {}, - FORM: {}, - }, - files: {}, - userId: '', - }, - statusCode: 200, - }, - ], - }, - }, - }, + ...identifyData, + ...trackTestData, + ...screenTestData, + ...groupTestData, + ...ecomTestData, + ...validationTestData, ]; diff --git a/test/integrations/destinations/klaviyo/processor/ecomTestData.ts b/test/integrations/destinations/klaviyo/processor/ecomTestData.ts new file mode 100644 index 00000000000..fab4cf85cec --- /dev/null +++ b/test/integrations/destinations/klaviyo/processor/ecomTestData.ts @@ -0,0 +1,344 @@ +import { overrideDestination, transformResultBuilder } from '../../../testUtils'; + +const destination = { + Config: { + publicApiKey: 'dummyPublicApiKey', + privateApiKey: 'dummyPrivateApiKey', + }, +}; + +const commonTraits = { + id: 'user@1', + age: '22', + name: 'Test', + email: 'test@rudderstack.com', + phone: '9112340375', + anonymousId: '9c6bd77ea9da3e68', + description: 'Sample description', +}; + +const eventsEndpoint = 'https://a.klaviyo.com/api/events'; + +const commonOutputHeaders = { + Authorization: 'Klaviyo-API-Key dummyPrivateApiKey', + 'Content-Type': 'application/json', + Accept: 'application/json', + revision: '2023-02-22', +}; + +export const ecomTestData = [ + { + id: 'klaviyo-ecom-test-1', + name: 'klaviyo', + description: 'Track call with Ecom events (Viewed Product)', + scenario: 'Business', + successCriteria: + 'Response should contain only event payload and status code should be 200, for the event payload should contain contextual traits and properties in the payload the event name should be Viewed Product and the properties should be mapped to the Klaviyo event properties', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + type: 'track', + event: 'product viewed', + sentAt: '2021-01-25T16:12:02.048Z', + userId: 'sajal12', + channel: 'mobile', + context: { + traits: commonTraits, + }, + properties: { + name: 'test product', + product_id: '1114', + sku: 'WINNIePuh12', + image_url: 'http://www.example.com/path/to/product/image.png', + url: 'http://www.example.com/path/to/product', + brand: 'Not for Kids', + price: 9.9, + categories: ['Fiction', 'Children'], + }, + anonymousId: '9c6bd77ea9da3e68', + originalTimestamp: '2021-01-25T15:32:56.409Z', + }, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventsEndpoint, + headers: commonOutputHeaders, + JSON: { + data: { + type: 'event', + attributes: { + metric: { + name: 'Viewed Product', + }, + profile: { + $email: 'test@rudderstack.com', + $phone_number: '9112340375', + $id: 'sajal12', + age: '22', + name: 'Test', + description: 'Sample description', + }, + properties: { + ProductName: 'test product', + ProductID: '1114', + SKU: 'WINNIePuh12', + ImageURL: 'http://www.example.com/path/to/product/image.png', + URL: 'http://www.example.com/path/to/product', + Brand: 'Not for Kids', + Price: 9.9, + Categories: ['Fiction', 'Children'], + }, + }, + }, + }, + userId: '', + }), + statusCode: 200, + }, + ], + }, + }, + }, + { + id: 'klaviyo-ecom-test-2', + name: 'klaviyo', + description: 'Track call with Ecom events (Checkout Started) with enabled flattenProperties', + scenario: 'Business', + successCriteria: + 'Response should contain only event payload and status code should be 200, for the event payload should contain contextual traits and properties in the payload the event name should be Started Checkout and the properties should be mapped to the Klaviyo event properties with flattened properties', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: overrideDestination(destination, { flattenProperties: true }), + message: { + type: 'track', + event: 'checkout started', + sentAt: '2021-01-25T16:12:02.048Z', + userId: 'sajal12', + channel: 'mobile', + context: { + traits: commonTraits, + }, + properties: { + order_id: '1234', + affiliation: 'Apple Store', + value: 20, + revenue: 15, + shipping: 4, + tax: 1, + discount: 1.5, + coupon: 'ImagePro', + currency: 'USD', + products: [ + { + product_id: '123', + sku: 'G-32', + name: 'Monopoly', + price: 14, + quantity: 1, + category: 'Games', + url: 'https://www.website.com/product/path', + image_url: 'https://www.website.com/product/path.jpg', + }, + { + product_id: '345', + sku: 'F-32', + name: 'UNO', + price: 3.45, + quantity: 2, + category: 'Games', + }, + ], + }, + anonymousId: '9c6bd77ea9da3e68', + }, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventsEndpoint, + headers: commonOutputHeaders, + JSON: { + data: { + type: 'event', + attributes: { + metric: { + name: 'Started Checkout', + }, + properties: { + $event_id: '1234', + $value: 20, + 'items[0].ProductID': '123', + 'items[0].SKU': 'G-32', + 'items[0].ProductName': 'Monopoly', + 'items[0].Quantity': 1, + 'items[0].ItemPrice': 14, + 'items[0].ProductURL': 'https://www.website.com/product/path', + 'items[0].ImageURL': 'https://www.website.com/product/path.jpg', + 'items[1].ProductID': '345', + 'items[1].SKU': 'F-32', + 'items[1].ProductName': 'UNO', + 'items[1].Quantity': 2, + 'items[1].ItemPrice': 3.45, + }, + profile: { + $email: 'test@rudderstack.com', + $phone_number: '9112340375', + $id: 'sajal12', + age: '22', + name: 'Test', + description: 'Sample description', + }, + }, + }, + }, + userId: '', + }), + statusCode: 200, + }, + ], + }, + }, + }, + { + id: 'klaviyo-ecom-test-3', + name: 'klaviyo', + description: 'Track call with Ecom events (Added to Cart) with properties.products', + scenario: 'Business', + successCriteria: + 'Response should contain only event payload and status code should be 200, for the event payload should contain contextual traits and properties in the payload the event name should be Added to Cart and the properties should be mapped to the Klaviyo event properties with flattened properties and products array should be mapped to items array in the payload', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: { + type: 'track', + event: 'product added', + sentAt: '2021-01-25T16:12:02.048Z', + userId: 'sajal12', + channel: 'mobile', + context: { + traits: commonTraits, + }, + rudderId: 'b7b24f86-f7bf-46d8-b2b4-ccafc080239c', + messageId: '1611588776408-ee5a3212-fbf9-4cbb-bbad-3ed0f7c6a2ce', + properties: { + order_id: '1234', + value: 12.12, + categories: ['Fiction3', 'Children3'], + checkout_url: 'http://www.heythere.com', + item_names: ['book1', 'book2'], + products: [ + { + product_id: 'b1pid', + sku: '123x', + name: 'book1', + url: 'heyther.com', + price: 12, + }, + { + product_id: 'b2pid', + sku: '123x', + name: 'book2', + url: 'heyther2.com', + price: 14, + }, + ], + }, + anonymousId: '9c6bd77ea9da3e68', + integrations: { + All: true, + }, + originalTimestamp: '2021-01-25T15:32:56.409Z', + }, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: 'https://a.klaviyo.com/api/events', + headers: commonOutputHeaders, + JSON: { + data: { + type: 'event', + attributes: { + metric: { + name: 'Added to Cart', + }, + profile: { + $email: 'test@rudderstack.com', + $phone_number: '9112340375', + $id: 'sajal12', + age: '22', + name: 'Test', + description: 'Sample description', + }, + properties: { + $value: 12.12, + AddedItemCategories: ['Fiction3', 'Children3'], + ItemNames: ['book1', 'book2'], + CheckoutURL: 'http://www.heythere.com', + items: [ + { + ProductID: 'b1pid', + SKU: '123x', + ProductName: 'book1', + ItemPrice: 12, + ProductURL: 'heyther.com', + }, + { + ProductID: 'b2pid', + SKU: '123x', + ProductName: 'book2', + ItemPrice: 14, + ProductURL: 'heyther2.com', + }, + ], + }, + }, + }, + }, + userId: '', + }), + statusCode: 200, + }, + ], + }, + }, + }, +]; diff --git a/test/integrations/destinations/klaviyo/processor/groupTestData.ts b/test/integrations/destinations/klaviyo/processor/groupTestData.ts new file mode 100644 index 00000000000..031c949c4b4 --- /dev/null +++ b/test/integrations/destinations/klaviyo/processor/groupTestData.ts @@ -0,0 +1,136 @@ +import { generateSimplifiedGroupPayload, transformResultBuilder } from '../../../testUtils'; + +const destination = { + Config: { + publicApiKey: 'dummyPublicApiKey', + privateApiKey: 'dummyPrivateApiKey', + }, +}; + +const headers = { + Accept: 'application/json', + Authorization: 'Klaviyo-API-Key dummyPrivateApiKey', + 'Content-Type': 'application/json', + revision: '2023-02-22', +}; + +const commonEndpoint = 'https://a.klaviyo.com/api/profile-subscription-bulk-create-jobs'; + +export const groupTestData = [ + { + id: 'klaviyo-group-test-1', + name: 'klaviyo', + description: 'Simple group call', + scenario: 'Business', + successCriteria: + 'Response should contain only group payload and status code should be 200, for the group payload a subscription payload should be present in the final payload with email and phone', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateSimplifiedGroupPayload({ + userId: 'user123', + groupId: 'XUepkK', + traits: { + subscribe: true, + }, + context: { + traits: { + email: 'test@rudderstack.com', + phone: '+12 345 678 900', + consent: ['email'], + }, + }, + timestamp: '2020-01-21T00:21:34.208Z', + }), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + JSON: { + data: { + attributes: { + list_id: 'XUepkK', + subscriptions: [ + { email: 'test@rudderstack.com', phone_number: '+12 345 678 900' }, + ], + }, + type: 'profile-subscription-bulk-create-job', + }, + }, + endpoint: commonEndpoint, + headers: headers, + method: 'POST', + userId: '', + }), + statusCode: 200, + }, + ], + }, + }, + }, + { + id: 'klaviyo-group-test-2', + name: 'klaviyo', + description: 'Simple group call without groupId', + scenario: 'Business', + successCriteria: + 'Response should contain error message and status code should be 400, as we are not sending groupId in the payload and groupId is a required field for group events', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateSimplifiedGroupPayload({ + userId: 'user123', + groupId: '', + traits: { + subscribe: true, + }, + context: { + traits: { + email: 'test@rudderstack.com', + phone: '+12 345 678 900', + consent: 'email', + }, + }, + timestamp: '2020-01-21T00:21:34.208Z', + }), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: 'groupId is a required field for group events', + statTags: { + destType: 'KLAVIYO', + errorCategory: 'dataValidation', + errorType: 'instrumentation', + feature: 'processor', + implementation: 'native', + module: 'destination', + }, + statusCode: 400, + }, + ], + }, + }, + }, +]; diff --git a/test/integrations/destinations/klaviyo/processor/identifyTestData.ts b/test/integrations/destinations/klaviyo/processor/identifyTestData.ts new file mode 100644 index 00000000000..8b5503fad97 --- /dev/null +++ b/test/integrations/destinations/klaviyo/processor/identifyTestData.ts @@ -0,0 +1,551 @@ +import { removeUndefinedAndNullValues } from '@rudderstack/integrations-lib'; +import { + overrideDestination, + transformResultBuilder, + generateSimplifiedIdentifyPayload, +} from '../../../testUtils'; + +const destination = { + Config: { + publicApiKey: 'dummyPublicApiKey', + privateApiKey: 'dummyPrivateApiKey', + }, +}; + +const commonTraits = { + firstName: 'Test', + lastName: 'Rudderlabs', + email: 'test@rudderstack.com', + phone: '+12 345 578 900', + userId: 'user@1', + title: 'Developer', + organization: 'Rudder', + city: 'Tokyo', + region: 'Kanto', + country: 'JP', + zip: '100-0001', + Flagged: false, + Residence: 'Shibuya', + properties: { + listId: 'XUepkK', + subscribe: true, + consent: ['email', 'sms'], + }, +}; + +const commonOutputUserProps = { + external_id: 'user@1', + email: 'test@rudderstack.com', + first_name: 'Test', + last_name: 'Rudderlabs', + phone_number: '+12 345 578 900', + title: 'Developer', + organization: 'Rudder', + location: { + city: 'Tokyo', + region: 'Kanto', + country: 'JP', + zip: '100-0001', + }, + properties: { + Flagged: false, + Residence: 'Shibuya', + }, +}; + +const commonOutputSubscriptionProps = { + list_id: 'XUepkK', + subscriptions: [ + { + email: 'test@rudderstack.com', + phone_number: '+12 345 578 900', + channels: { + email: ['MARKETING'], + sms: ['MARKETING'], + }, + }, + ], +}; + +const commonOutputHeaders = { + Authorization: 'Klaviyo-API-Key dummyPrivateApiKey', + 'Content-Type': 'application/json', + Accept: 'application/json', + revision: '2023-02-22', +}; + +const anonymousId = '97c46c81-3140-456d-b2a9-690d70aaca35'; +const userId = 'user@1'; +const sentAt = '2021-01-03T17:02:53.195Z'; +const originalTimestamp = '2021-01-03T17:02:53.193Z'; +const commonUserUpdateEndpoint = 'https://a.klaviyo.com/api/profiles/01GW3PHVY0MTCDGS0A1612HARX'; +const subscribeEndpoint = 'https://a.klaviyo.com/api/profile-subscription-bulk-create-jobs'; + +export const identifyData = [ + { + id: 'klaviyo-identify-test-1', + name: 'klaviyo', + description: + 'Identify and Subscribe user where the user doesnot exists[mock] (without suppression status code feature from server)', + scenario: 'Business + Framework', + successCriteria: + 'Response should containt two payloads one for profile updation and other for subscription, response status code should be 200', + comment: + 'Reasoning: As suppression status code feature is not enabled from server, so we would expect 2 responses from transformer one for profile updation (deafult behaviour) and other for subscription', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateSimplifiedIdentifyPayload({ + context: { + traits: commonTraits, + }, + anonymousId, + userId, + sentAt, + }), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + userId: '', + method: 'PATCH', + endpoint: commonUserUpdateEndpoint, + headers: commonOutputHeaders, + JSON: { + data: { + type: 'profile', + attributes: commonOutputUserProps, + id: '01GW3PHVY0MTCDGS0A1612HARX', + }, + }, + }), + statusCode: 200, + }, + { + output: transformResultBuilder({ + method: 'POST', + userId: '', + endpoint: subscribeEndpoint, + headers: commonOutputHeaders, + JSON: { + data: { + type: 'profile-subscription-bulk-create-job', + attributes: commonOutputSubscriptionProps, + }, + }, + }), + statusCode: 200, + }, + ], + }, + }, + }, + { + id: 'klaviyo-identify-test-2', + name: 'klaviyo', + description: 'Identify call for with flattenProperties enabled in destination config', + scenario: 'Business', + successCriteria: + 'The profile updation response should contain the flattened properties of the friend object', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: overrideDestination(destination, { flattenProperties: true }), + message: generateSimplifiedIdentifyPayload({ + sentAt, + userId, + context: { + traits: { + ...commonTraits, + friend: { + names: { + first: 'Alice', + last: 'Smith', + }, + age: 25, + }, + }, + }, + anonymousId, + originalTimestamp, + }), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + userId: '', + method: 'PATCH', + endpoint: commonUserUpdateEndpoint, + headers: commonOutputHeaders, + JSON: { + data: { + type: 'profile', + id: '01GW3PHVY0MTCDGS0A1612HARX', + attributes: { + ...commonOutputUserProps, + properties: { + ...commonOutputUserProps.properties, + 'friend.age': 25, + 'friend.names.first': 'Alice', + 'friend.names.last': 'Smith', + }, + }, + }, + }, + }), + statusCode: 200, + }, + { + output: transformResultBuilder({ + userId: '', + method: 'POST', + endpoint: subscribeEndpoint, + headers: commonOutputHeaders, + JSON: { + data: { + type: 'profile-subscription-bulk-create-job', + attributes: commonOutputSubscriptionProps, + }, + }, + }), + statusCode: 200, + }, + ], + }, + }, + }, + { + id: 'klaviyo-identify-test-3', + name: 'klaviyo', + description: 'Negative Test Case for Profile updation call and subcribe user', + scenario: 'Business + Framework', + successCriteria: + 'Response should contain error message and status code should be 500, as we are getting a network error from klaviyo', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: { + Config: { + publicApiKey: 'dummyPublicApiKey', + privateApiKey: 'dummyPrivateApiKeyforfailure', + }, + }, + message: generateSimplifiedIdentifyPayload({ + sentAt, + userId, + context: { + traits: { + ...commonTraits, + email: 'test3@rudderstack.com', + }, + }, + anonymousId, + originalTimestamp, + }), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: + '{"message":"Failed to create user due to \\"\\"","destinationResponse":"\\"\\""}', + statTags: { + destType: 'KLAVIYO', + errorCategory: 'network', + errorType: 'retryable', + feature: 'processor', + implementation: 'native', + module: 'destination', + }, + statusCode: 500, + }, + ], + }, + }, + }, + { + id: 'klaviyo-identify-test-4', + name: 'klaviyo', + description: 'Profile create update without subscribing the user', + scenario: 'Business', + successCriteria: + 'Response should contain only profile update payload and status code should be 200 as subscribe is set to false in the payload', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateSimplifiedIdentifyPayload({ + sentAt, + userId, + context: { + traits: { + ...commonTraits, + properties: { ...commonTraits.properties, subscribe: false }, + }, + }, + anonymousId, + originalTimestamp, + }), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'PATCH', + endpoint: commonUserUpdateEndpoint, + headers: commonOutputHeaders, + JSON: { + data: { + type: 'profile', + attributes: commonOutputUserProps, + id: '01GW3PHVY0MTCDGS0A1612HARX', + }, + }, + userId: '', + }), + statusCode: 200, + }, + ], + }, + }, + }, + { + id: 'klaviyo-identify-test-5', + name: 'klaviyo', + description: 'Identify call with enforceEmailAsPrimary enabled in destination config', + scenario: 'Business', + successCriteria: + 'Response should contain two payloads one for profile updation and other for subscription, response status code should be 200, for the profile updation payload there should be no external_id field in the payload as enforceEmailAsPrimary is set to true in the destination config and the userId should be mapped to _id field in the properties object', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: overrideDestination(destination, { enforceEmailAsPrimary: true }), + message: generateSimplifiedIdentifyPayload({ + sentAt, + userId, + context: { + traits: commonTraits, + }, + anonymousId, + originalTimestamp, + }), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + userId: '', + method: 'PATCH', + endpoint: commonUserUpdateEndpoint, + headers: commonOutputHeaders, + JSON: { + data: { + type: 'profile', + attributes: removeUndefinedAndNullValues({ + ...commonOutputUserProps, + properties: { + ...commonOutputUserProps.properties, + _id: userId, + }, + // remove external_id from the payload + external_id: undefined, + }), + id: '01GW3PHVY0MTCDGS0A1612HARX', + }, + }, + }), + statusCode: 200, + }, + { + output: transformResultBuilder({ + userId: '', + method: 'POST', + endpoint: subscribeEndpoint, + headers: commonOutputHeaders, + JSON: { + data: { + type: 'profile-subscription-bulk-create-job', + attributes: commonOutputSubscriptionProps, + }, + }, + }), + statusCode: 200, + }, + ], + }, + }, + }, + { + id: 'klaviyo-identify-test-6', + name: 'klaviyo', + description: 'Identify call without user custom Properties', + scenario: 'Business', + successCriteria: + 'Response should contain two payloads one for profile updation and other for subscription, response status code should be 200, for the profile updation payload does not have any custom properties in the payload', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: destination, + message: generateSimplifiedIdentifyPayload({ + sentAt, + userId, + context: { + traits: removeUndefinedAndNullValues({ + ...commonTraits, + Flagged: undefined, + Residence: undefined, + }), + }, + anonymousId, + originalTimestamp, + }), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + userId: '', + method: 'PATCH', + endpoint: commonUserUpdateEndpoint, + headers: commonOutputHeaders, + JSON: { + data: { + type: 'profile', + attributes: removeUndefinedAndNullValues({ + ...commonOutputUserProps, + properties: undefined, + }), + id: '01GW3PHVY0MTCDGS0A1612HARX', + }, + }, + }), + statusCode: 200, + }, + { + output: transformResultBuilder({ + userId: '', + method: 'POST', + endpoint: subscribeEndpoint, + headers: commonOutputHeaders, + JSON: { + data: { + type: 'profile-subscription-bulk-create-job', + attributes: commonOutputSubscriptionProps, + }, + }, + }), + statusCode: 200, + }, + ], + }, + }, + }, + { + id: 'klaviyo-identify-test-7', + name: 'klaviyo', + description: 'Identify call without email and phone & enforceEmailAsPrimary enabled from UI', + scenario: 'Business', + successCriteria: + 'Response should contain error message and status code should be 400, as we are not sending email and phone in the payload and enforceEmailAsPrimary is enabled from UI', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: overrideDestination(destination, { enforceEmailAsPrimary: true }), + message: generateSimplifiedIdentifyPayload({ + sentAt, + userId, + context: { + traits: removeUndefinedAndNullValues({ + ...commonTraits, + email: undefined, + phone: undefined, + }), + }, + anonymousId, + originalTimestamp, + }), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: 'None of email and phone are present in the payload', + statTags: { + destType: 'KLAVIYO', + errorCategory: 'dataValidation', + errorType: 'instrumentation', + feature: 'processor', + implementation: 'native', + module: 'destination', + }, + statusCode: 400, + }, + ], + }, + }, + }, +]; diff --git a/test/integrations/destinations/klaviyo/processor/screenTestData.ts b/test/integrations/destinations/klaviyo/processor/screenTestData.ts new file mode 100644 index 00000000000..3779747a4ee --- /dev/null +++ b/test/integrations/destinations/klaviyo/processor/screenTestData.ts @@ -0,0 +1,97 @@ +import { generateSimplifiedPageOrScreenPayload, transformResultBuilder } from '../../../testUtils'; + +const destination = { + Config: { + publicApiKey: 'dummyPublicApiKey', + privateApiKey: 'dummyPrivateApiKey', + }, +}; + +export const screenTestData = [ + { + id: 'klaviyo-screen-test-1', + name: 'klaviyo', + description: 'Screen event call with properties and contextual traits', + scenario: 'Business', + successCriteria: + 'Response should contain only event payload and status code should be 200, for the event payload should contain properties and contextual traits in the payload', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateSimplifiedPageOrScreenPayload( + { + event: 'TestEven001', + sentAt: '2021-01-25T16:12:02.048Z', + userId: 'sajal12', + context: { + traits: { + id: 'user@1', + age: '22', + email: 'test@rudderstack.com', + phone: '9112340375', + anonymousId: '9c6bd77ea9da3e68', + }, + }, + properties: { + PreviouslyVicePresident: true, + YearElected: 1801, + VicePresidents: ['Aaron Burr', 'George Clinton'], + }, + anonymousId: '9c6bd77ea9da3e68', + originalTimestamp: '2021-01-25T15:32:56.409Z', + }, + 'screen', + ), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: 'https://a.klaviyo.com/api/events', + headers: { + Accept: 'application/json', + Authorization: 'Klaviyo-API-Key dummyPrivateApiKey', + 'Content-Type': 'application/json', + revision: '2023-02-22', + }, + JSON: { + data: { + type: 'event', + attributes: { + metric: { + name: 'TestEven001', + }, + properties: { + PreviouslyVicePresident: true, + YearElected: 1801, + VicePresidents: ['Aaron Burr', 'George Clinton'], + }, + profile: { + $email: 'test@rudderstack.com', + $phone_number: '9112340375', + $id: 'sajal12', + age: '22', + }, + }, + }, + }, + userId: '', + }), + statusCode: 200, + }, + ], + }, + }, + }, +]; diff --git a/test/integrations/destinations/klaviyo/processor/trackTestData.ts b/test/integrations/destinations/klaviyo/processor/trackTestData.ts new file mode 100644 index 00000000000..f3bbfb96b9e --- /dev/null +++ b/test/integrations/destinations/klaviyo/processor/trackTestData.ts @@ -0,0 +1,316 @@ +import { + generateSimplifiedTrackPayload, + generateTrackPayload, + overrideDestination, + transformResultBuilder, +} from '../../../testUtils'; + +const destination = { + Config: { + publicApiKey: 'dummyPublicApiKey', + privateApiKey: 'dummyPrivateApiKey', + }, +}; + +const commonTraits = { + id: 'user@1', + age: '22', + anonymousId: '9c6bd77ea9da3e68', +}; + +const commonProps = { + PreviouslVicePresident: true, + YearElected: 1801, + VicePresidents: ['AaronBurr', 'GeorgeClinton'], +}; + +const commonOutputHeaders = { + Accept: 'application/json', + Authorization: 'Klaviyo-API-Key dummyPrivateApiKey', + 'Content-Type': 'application/json', + revision: '2023-02-22', +}; + +const eventEndPoint = 'https://a.klaviyo.com/api/events'; + +export const trackTestData = [ + { + id: 'klaviyo-track-test-1', + name: 'klaviyo', + description: 'Track event call with flatten properties enabled in destination config', + scenario: 'Business', + successCriteria: + 'Response should contain only event payload and status code should be 200, for the event payload should contain flattened properties in the payload', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: overrideDestination(destination, { flattenProperties: true }), + message: generateSimplifiedTrackPayload({ + type: 'track', + event: 'TestEven001', + sentAt: '2021-01-25T16:12:02.048Z', + userId: 'sajal12', + context: { + traits: { + ...commonTraits, + email: 'test@rudderstack.com', + phone: '9112340375', + plan_details: { + plan_type: 'gold', + duration: '3 months', + }, + }, + }, + properties: { + vicePresdentInfo: commonProps, + }, + anonymousId: '9c6bd77ea9da3e68', + originalTimestamp: '2021-01-25T15:32:56.409Z', + }), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + JSON: { + data: { + type: 'event', + attributes: { + metric: { + name: 'TestEven001', + }, + properties: { + 'vicePresdentInfo.PreviouslVicePresident': true, + 'vicePresdentInfo.VicePresidents': ['AaronBurr', 'GeorgeClinton'], + 'vicePresdentInfo.YearElected': 1801, + }, + profile: { + $email: 'test@rudderstack.com', + $phone_number: '9112340375', + $id: 'sajal12', + age: '22', + 'plan_details.plan_type': 'gold', + 'plan_details.duration': '3 months', + }, + }, + }, + }, + userId: '', + }), + statusCode: 200, + }, + ], + }, + }, + }, + { + id: 'klaviyo-track-test-2', + name: 'klaviyo', + description: 'Simple track event call', + scenario: 'Business', + successCriteria: + 'Response should contain only event payload and status code should be 200, for the event payload should contain contextual traits and properties in the payload', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination, + message: generateSimplifiedTrackPayload({ + type: 'track', + event: 'TestEven002', + sentAt: '2021-01-25T16:12:02.048Z', + userId: 'sajal12', + context: { + traits: { + ...commonTraits, + name: 'Test', + email: 'test@rudderstack.com', + phone: '9112340375', + description: 'Sample description', + }, + }, + properties: { + ...commonProps, + revenue: 3000, + }, + anonymousId: '9c6bd77ea9da3e68', + originalTimestamp: '2021-01-25T15:32:56.409Z', + }), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + JSON: { + data: { + type: 'event', + attributes: { + metric: { + name: 'TestEven002', + }, + properties: commonProps, + profile: { + $email: 'test@rudderstack.com', + $phone_number: '9112340375', + $id: 'sajal12', + age: '22', + name: 'Test', + description: 'Sample description', + }, + value: 3000, + }, + }, + }, + userId: '', + }), + statusCode: 200, + }, + ], + }, + }, + }, + { + id: 'klaviyo-track-test-3', + name: 'klaviyo', + description: 'Track event call, with make email or phone as primary identifier toggle on', + scenario: 'Business', + successCriteria: + 'Response should contain only event payload and status code should be 200, for the event payload should contain contextual traits and properties in the payload, and email should be mapped to $email and userId should be mapped to _id', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: overrideDestination(destination, { enforceEmailAsPrimary: true }), + message: generateSimplifiedTrackPayload({ + type: 'track', + event: 'TestEven001', + sentAt: '2021-01-25T16:12:02.048Z', + userId: 'sajal12', + context: { + traits: { + ...commonTraits, + email: 'test@rudderstack.com', + phone: '9112340375', + }, + }, + properties: commonProps, + anonymousId: '9c6bd77ea9da3e68', + originalTimestamp: '2021-01-25T15:32:56.409Z', + }), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: transformResultBuilder({ + method: 'POST', + endpoint: eventEndPoint, + headers: commonOutputHeaders, + JSON: { + data: { + type: 'event', + attributes: { + metric: { + name: 'TestEven001', + }, + properties: commonProps, + profile: { + $email: 'test@rudderstack.com', + $phone_number: '9112340375', + age: '22', + _id: 'sajal12', + }, + }, + }, + }, + userId: '', + }), + statusCode: 200, + }, + ], + }, + }, + }, + { + id: 'klaviyo-track-test-4', + name: 'klaviyo', + description: + 'Track event call, without email and phone & with (make email or phone as primary identifier) toggle on', + scenario: 'Business', + successCriteria: + 'Response should contain error message and status code should be 400, as we are not sending email and phone in the payload and enforceEmailAsPrimary is enabled from UI', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: overrideDestination(destination, { enforceEmailAsPrimary: true }), + message: generateSimplifiedTrackPayload({ + type: 'track', + event: 'TestEven001', + sentAt: '2021-01-25T16:12:02.048Z', + userId: 'sajal12', + context: { + traits: commonTraits, + }, + properties: commonProps, + anonymousId: '9c6bd77ea9da3e68', + originalTimestamp: '2021-01-25T15:32:56.409Z', + }), + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: 'None of email and phone are present in the payload', + statTags: { + destType: 'KLAVIYO', + errorCategory: 'dataValidation', + errorType: 'instrumentation', + feature: 'processor', + implementation: 'native', + module: 'destination', + }, + statusCode: 400, + }, + ], + }, + }, + }, +]; diff --git a/test/integrations/destinations/klaviyo/processor/validationTestData.ts b/test/integrations/destinations/klaviyo/processor/validationTestData.ts new file mode 100644 index 00000000000..59556cfe5f7 --- /dev/null +++ b/test/integrations/destinations/klaviyo/processor/validationTestData.ts @@ -0,0 +1,62 @@ +export const validationTestData = [ + { + id: 'klaviyo-validation-test-1', + name: 'klaviyo', + description: '[Error]: Check for unsupported message type', + scenario: 'Framework', + successCriteria: + 'Response should contain error message and status code should be 400, as we are sending a message type which is not supported by Klaviyo destination and the error message should be Event type random is not supported', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: { + Config: { + publicApiKey: 'dummyPublicApiKey', + privateApiKey: 'dummyPrivateApiKey', + }, + }, + message: { + userId: 'user123', + type: 'random', + groupId: 'XUepkK', + traits: { + subscribe: true, + }, + context: { + traits: { + email: 'test@rudderstack.com', + phone: '+12 345 678 900', + consent: 'email', + }, + }, + timestamp: '2020-01-21T00:21:34.208Z', + }, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + error: 'Event type random is not supported', + statTags: { + destType: 'KLAVIYO', + errorCategory: 'dataValidation', + errorType: 'instrumentation', + feature: 'processor', + implementation: 'native', + module: 'destination', + }, + statusCode: 400, + }, + ], + }, + }, + }, +]; diff --git a/test/integrations/destinations/klaviyo/router/data.ts b/test/integrations/destinations/klaviyo/router/data.ts index 0fb735d6bfe..83b494199c6 100644 --- a/test/integrations/destinations/klaviyo/router/data.ts +++ b/test/integrations/destinations/klaviyo/router/data.ts @@ -1,7 +1,10 @@ export const data = [ { + id: 'klaviyo-router-test-1', name: 'klaviyo', - description: 'Test 0', + description: 'Basic Router Test to test multiple payloads', + scenario: 'Framework', + successCriteria: 'All the subscription events should be batched', feature: 'router', module: 'destination', version: 'v0', diff --git a/test/integrations/destinations/ortto/processor/data.ts b/test/integrations/destinations/ortto/processor/data.ts index e7c71c73552..715262e4474 100644 --- a/test/integrations/destinations/ortto/processor/data.ts +++ b/test/integrations/destinations/ortto/processor/data.ts @@ -1068,6 +1068,220 @@ export const data = [ }, }, }, + { + name: 'ortto', + description: 'Track call for updating activities with no phone provided', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + destination: { + DestinationDefinition: { + Config: { + cdkV2Enabled: true, + }, + }, + ID: '1pYpzzvcn7AQ2W9GGIAZSsN6Mfq', + Name: 'ORTTO', + Config: { + privateApiKey: 'dummyApiKey', + instanceRegion: 'other', + orttoEventsMapping: [ + { + rsEventName: 'RudderEvent', + orttoEventName: 'Ortto Event', + eventProperties: [ + { + rudderProperty: 'RudderProp', + orttoProperty: 'OrttoProp', + type: 'text', + }, + { + rudderProperty: 'RudderProp', + orttoProperty: 'OrttoProp', + type: 'longText', + }, + ], + }, + ], + orttoPersonAttributes: [ + { + rudderTraits: 'ruddertrait0', + orttoAttribute: 'ortto attirbute0', + type: 'email', + }, + { + rudderTraits: 'ruddertrait1', + orttoAttribute: 'ortto attirbute1', + type: 'email', + }, + ], + }, + Enabled: true, + Transformations: [], + }, + metadata: { destintionId: '1pYpzzvcn7AQ2W9GGIAZSsN6Mfq', jobId: 2 }, + message: { + anonymousId: '8d872292709c6fbe', + channel: 'mobile', + context: { + app: { + build: '1', + name: 'AMTestProject', + namespace: 'com.rudderstack.android.rudderstack.sampleAndroidApp', + version: '1.0', + }, + device: { + id: '8d872292709c6fbe', + manufacturer: 'Google', + model: 'AOSPonIAEmulator', + name: 'generic_x86_arm', + type: 'android', + }, + library: { + name: 'com.rudderstack.android.sdk.core', + version: '1.0.2', + }, + locale: 'en-US', + network: { + carrier: 'Android', + bluetooth: false, + cellular: true, + wifi: true, + }, + os: { + name: 'Android', + version: '9', + }, + screen: { + density: 420, + height: 1794, + width: 1080, + }, + timezone: 'Asia/Kolkata', + traits: { + ruddertrait0: 'abc', + ruddertrait1: 'def', + address: { + city: 'Kolkata', + country: 'India', + postalcode: '700096', + state: 'West bengal', + street: 'Park Street', + }, + age: '30', + anonymousId: '8d872292709c6fbe', + birthday: '2020-05-26', + createDate: '18th March 2020', + description: 'Premium User for 3 years', + email: 'identify@test.com', + firstname: 'John', + gdpr: false, + userId: 'sample_user_id', + lastname: 'Sparrow', + name: 'John Sparrow', + id: 'sample_user_id', + username: 'john_sparrow', + }, + userAgent: + 'Dalvik/2.1.0 (Linux; U; Android 9; AOSP on IA Emulator Build/PSR1.180720.117)', + }, + event: 'RudderEvent', + integrations: { + All: true, + }, + messageId: '1590431830915-73bed370-5889-436d-9a9e-0c0e0c809d06', + properties: { + revenue: '30', + RudderProp: 'USD', + quantity: '5', + test_key_2: { + test_child_key_1: 'test_child_value_1', + }, + price: '58.0', + }, + originalTimestamp: '2020-05-25T18:37:10.917Z', + type: 'track', + userId: 'sample_user_id', + }, + }, + ], + method: 'POST', + }, + pathSuffix: '', + }, + output: { + response: { + status: 200, + body: [ + { + output: { + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://api.ap3api.com/v1/activities/create', + headers: { + 'X-Api-Key': 'dummyApiKey', + 'Content-Type': 'application/json', + }, + params: {}, + body: { + JSON: { + activities: [ + { + fields: { + 'str::first': 'John', + 'str::last': 'Sparrow', + 'str::email': 'identify@test.com', + 'geo::city': { + name: 'Kolkata', + }, + 'geo::country': {}, + 'geo::region': {}, + 'dtz::b': { + day: 26, + month: 5, + year: 2020, + }, + 'str::postal': '700096', + 'str::language': 'en-US', + 'str::ei': 'sample_user_id', + 'bol::gdpr': false, + 'bol::p': false, + 'bol::sp': false, + 'str:cm:ortto-attirbute0': 'abc', + 'str:cm:ortto-attirbute1': 'def', + }, + activity_id: 'act:cm:ortto-event', + attributes: { + 'str:cm:orttoprop': 'USD', + 'txt:cm:orttoprop': 'USD', + }, + location: {}, + }, + ], + merge_by: ['str::ei', 'str::email'], + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + userId: '', + }, + metadata: { + destintionId: '1pYpzzvcn7AQ2W9GGIAZSsN6Mfq', + jobId: 2, + }, + statusCode: 200, + }, + ], + }, + }, + }, { name: 'ortto', description: 'Track call for updating activities', diff --git a/test/integrations/destinations/the_trade_desk/router/data.ts b/test/integrations/destinations/the_trade_desk/router/data.ts index 3c9cb1cc704..8aa16612fc1 100644 --- a/test/integrations/destinations/the_trade_desk/router/data.ts +++ b/test/integrations/destinations/the_trade_desk/router/data.ts @@ -533,4 +533,298 @@ export const data = [ }, }, }, + { + name: destType, + description: 'Invalid action type', + feature: 'router', + module: 'destination', + version: 'v0', + input: { + request: { + body: { + input: [ + { + message: { + type: 'record', + action: 'insert', + fields: { + DAID: 'test-daid-1', + UID2: 'test-uid2-1', + }, + channel: 'sources', + context: sampleContext, + recordId: '1', + }, + destination: sampleDestination, + metadata: { + jobId: 1, + }, + }, + { + message: { + type: 'record', + action: 'update', + fields: { + DAID: 'test-daid-2', + UID2: null, + }, + channel: 'sources', + context: sampleContext, + recordId: '2', + }, + destination: sampleDestination, + metadata: { + jobId: 2, + }, + }, + ], + destType, + }, + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: [ + { + batchedRequest: [ + { + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://sin-data.adsrvr.org/data/advertiser', + headers: {}, + params: {}, + body: { + JSON: { + DataProviderId: dataProviderId, + AdvertiserId: advertiserId, + Items: [ + { + DAID: 'test-daid-1', + Data: [ + { + Name: segmentName, + TTLInMinutes: 43200, + }, + ], + }, + { + UID2: 'test-uid2-1', + Data: [ + { + Name: segmentName, + TTLInMinutes: 43200, + }, + ], + }, + ], + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + }, + ], + metadata: [ + { + jobId: 1, + }, + ], + batched: true, + statusCode: 200, + destination: sampleDestination, + }, + { + batched: false, + metadata: [{ jobId: 2 }], + statusCode: 400, + error: 'Invalid action type', + statTags: { + destType: destTypeInUpperCase, + implementation: 'cdkV2', + feature: 'router', + module: 'destination', + errorCategory: 'dataValidation', + errorType: 'instrumentation', + }, + destination: sampleDestination, + }, + ], + }, + }, + }, + mockFns: defaultMockFns, + }, + { + name: destType, + description: 'Empty fields in the message', + feature: 'router', + module: 'destination', + version: 'v0', + input: { + request: { + body: { + input: [ + { + message: { + type: 'record', + action: 'insert', + fields: {}, + channel: 'sources', + context: sampleContext, + recordId: '1', + }, + destination: sampleDestination, + metadata: { + jobId: 1, + }, + }, + { + message: { + type: 'record', + action: 'insert', + fields: { + DAID: 'test-daid-1', + }, + channel: 'sources', + context: sampleContext, + recordId: '2', + }, + destination: sampleDestination, + metadata: { + jobId: 2, + }, + }, + ], + destType, + }, + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: [ + { + batchedRequest: [ + { + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://sin-data.adsrvr.org/data/advertiser', + headers: {}, + params: {}, + body: { + JSON: { + DataProviderId: dataProviderId, + AdvertiserId: advertiserId, + Items: [ + { + DAID: 'test-daid-1', + Data: [ + { + Name: segmentName, + TTLInMinutes: 43200, + }, + ], + }, + ], + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + }, + ], + metadata: [ + { + jobId: 2, + }, + ], + batched: true, + statusCode: 200, + destination: sampleDestination, + }, + { + batched: false, + metadata: [{ jobId: 1 }], + statusCode: 400, + error: 'Fields cannot be empty', + statTags: { + destType: destTypeInUpperCase, + implementation: 'cdkV2', + feature: 'router', + module: 'destination', + errorCategory: 'dataValidation', + errorType: 'instrumentation', + }, + destination: sampleDestination, + }, + ], + }, + }, + }, + mockFns: defaultMockFns, + }, + { + name: destType, + description: '`fields` is missing', + feature: 'router', + module: 'destination', + version: 'v0', + input: { + request: { + body: { + input: [ + { + message: { + type: 'record', + action: 'insert', + channel: 'sources', + context: sampleContext, + recordId: '1', + }, + destination: sampleDestination, + metadata: { + jobId: 1, + }, + }, + ], + destType, + }, + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: [ + { + batched: false, + metadata: [{ jobId: 1 }], + statusCode: 400, + error: 'Fields cannot be empty', + statTags: { + destType: destTypeInUpperCase, + implementation: 'cdkV2', + feature: 'router', + module: 'destination', + errorCategory: 'dataValidation', + errorType: 'instrumentation', + }, + destination: sampleDestination, + }, + ], + }, + }, + }, + mockFns: defaultMockFns, + }, ]; diff --git a/test/integrations/destinations/tiktok_ads/mocks.ts b/test/integrations/destinations/tiktok_ads/mocks.ts new file mode 100644 index 00000000000..a5d8fc4a638 --- /dev/null +++ b/test/integrations/destinations/tiktok_ads/mocks.ts @@ -0,0 +1,5 @@ +import config from '../../../../src/v0/destinations/tiktok_ads/config'; + +export const defaultMockFns = () => { + jest.replaceProperty(config, 'maxBatchSizeV2', 3); +}; diff --git a/test/integrations/destinations/tiktok_ads/processor/data.ts b/test/integrations/destinations/tiktok_ads/processor/data.ts index 76a4a1ca891..334dba9440a 100644 --- a/test/integrations/destinations/tiktok_ads/processor/data.ts +++ b/test/integrations/destinations/tiktok_ads/processor/data.ts @@ -89,7 +89,7 @@ export const data = [ destination: { Config: { accessToken: 'dummyAccessToken', - pixelCode: 'A1T8T4UYGVIQA8ORZMX9', + pixelCode: '{{PIXEL-CODE}}', hashUserProperties: false, }, }, @@ -114,7 +114,7 @@ export const data = [ params: {}, body: { JSON: { - pixel_code: 'A1T8T4UYGVIQA8ORZMX9', + pixel_code: '{{PIXEL-CODE}}', event: 'CompletePayment', event_id: '1616318632825_357', timestamp: '2020-09-17T19:49:27Z', @@ -256,7 +256,7 @@ export const data = [ destination: { Config: { accessToken: 'dummyAccessToken', - pixelCode: 'A1T8T4UYGVIQA8ORZMX9', + pixelCode: '{{PIXEL-CODE}}', hashUserProperties: false, }, }, @@ -281,7 +281,7 @@ export const data = [ params: {}, body: { JSON: { - pixel_code: 'A1T8T4UYGVIQA8ORZMX9', + pixel_code: '{{PIXEL-CODE}}', event: 'InitiateCheckout', event_id: '1616318632825_357', timestamp: '2020-09-17T19:49:27Z', @@ -418,7 +418,7 @@ export const data = [ destination: { Config: { accessToken: 'dummyAccessToken', - pixelCode: 'A1T8T4UYGVIQA8ORZMX9', + pixelCode: '{{PIXEL-CODE}}', hashUserProperties: false, }, }, @@ -443,7 +443,7 @@ export const data = [ params: {}, body: { JSON: { - pixel_code: 'A1T8T4UYGVIQA8ORZMX9', + pixel_code: '{{PIXEL-CODE}}', event: 'AddToWishlist', event_id: '1616318632825_357', timestamp: '2020-09-17T19:49:27Z', @@ -581,7 +581,7 @@ export const data = [ destination: { Config: { accessToken: 'dummyAccessToken', - pixelCode: 'A1T8T4UYGVIQA8ORZMX9', + pixelCode: '{{PIXEL-CODE}}', hashUserProperties: false, }, }, @@ -697,7 +697,7 @@ export const data = [ destination: { Config: { accessToken: 'dummyAccessToken', - pixelCode: 'A1T8T4UYGVIQA8ORZMX9', + pixelCode: '{{PIXEL-CODE}}', hashUserProperties: false, }, }, @@ -722,7 +722,7 @@ export const data = [ params: {}, body: { JSON: { - pixel_code: 'A1T8T4UYGVIQA8ORZMX9', + pixel_code: '{{PIXEL-CODE}}', event: 'AddToWishlist', event_id: '1616318632825_357', timestamp: '2020-09-17T19:49:27Z', @@ -863,7 +863,7 @@ export const data = [ destination: { Config: { accessToken: 'dummyAccessToken', - pixelCode: 'A1T8T4UYGVIQA8ORZMX9', + pixelCode: '{{PIXEL-CODE}}', hashUserProperties: false, }, }, @@ -888,7 +888,7 @@ export const data = [ params: {}, body: { JSON: { - pixel_code: 'A1T8T4UYGVIQA8ORZMX9', + pixel_code: '{{PIXEL-CODE}}', event: 'AddToWishlist', event_id: '1616318632825_357', timestamp: '2020-09-17T19:49:27Z', @@ -1028,7 +1028,7 @@ export const data = [ destination: { Config: { accessToken: 'dummyAccessToken', - pixelCode: 'A1T8T4UYGVIQA8ORZMX9', + pixelCode: '{{PIXEL-CODE}}', hashUserProperties: true, }, }, @@ -1053,7 +1053,7 @@ export const data = [ params: {}, body: { JSON: { - pixel_code: 'A1T8T4UYGVIQA8ORZMX9', + pixel_code: '{{PIXEL-CODE}}', event: 'Subscribe', event_id: '1616318632825_357', timestamp: '2020-09-17T19:49:27Z', @@ -1192,7 +1192,7 @@ export const data = [ destination: { Config: { accessToken: 'dummyAccessToken', - pixelCode: 'A1T8T4UYGVIQA8ORZMX9', + pixelCode: '{{PIXEL-CODE}}', hashUserProperties: false, }, }, @@ -1217,7 +1217,7 @@ export const data = [ params: {}, body: { JSON: { - pixel_code: 'A1T8T4UYGVIQA8ORZMX9', + pixel_code: '{{PIXEL-CODE}}', event: 'AddPaymentInfo', event_id: '1616318632825_357', timestamp: '2020-09-17T19:49:27Z', @@ -1355,7 +1355,7 @@ export const data = [ destination: { Config: { accessToken: 'dummyAccessToken', - pixelCode: 'A1T8T4UYGVIQA8ORZMX9', + pixelCode: '{{PIXEL-CODE}}', hashUserProperties: false, }, }, @@ -1467,7 +1467,7 @@ export const data = [ destination: { Config: { accessToken: 'dummyAccessToken', - pixelCode: 'A1T8T4UYGVIQA8ORZMX9', + pixelCode: '{{PIXEL-CODE}}', hashUserProperties: false, }, }, @@ -1580,7 +1580,7 @@ export const data = [ destination: { Config: { accessToken: 'dummyAccessToken', - pixelCode: 'A1T8T4UYGVIQA8ORZMX9', + pixelCode: '{{PIXEL-CODE}}', hashUserProperties: false, }, }, @@ -1605,7 +1605,7 @@ export const data = [ params: {}, body: { JSON: { - pixel_code: 'A1T8T4UYGVIQA8ORZMX9', + pixel_code: '{{PIXEL-CODE}}', event: 'AddPaymentInfo', event_id: '1616318632825_357', properties: { @@ -1743,7 +1743,7 @@ export const data = [ destination: { Config: { accessToken: 'dummyAccessToken', - pixelCode: 'A1T8T4UYGVIQA8ORZMX9', + pixelCode: '{{PIXEL-CODE}}', hashUserProperties: false, }, }, @@ -1768,7 +1768,7 @@ export const data = [ params: {}, body: { JSON: { - pixel_code: 'A1T8T4UYGVIQA8ORZMX9', + pixel_code: '{{PIXEL-CODE}}', event: 'SubmitForm', event_id: '16163186328257', timestamp: '2020-09-17T19:49:27Z', @@ -1894,7 +1894,7 @@ export const data = [ destination: { Config: { accessToken: 'dummyAccessToken', - pixelCode: 'A1T8T4UYGVIQA8ORZMX9', + pixelCode: '{{PIXEL-CODE}}', hashUserProperties: false, }, }, @@ -1919,7 +1919,7 @@ export const data = [ params: {}, body: { JSON: { - pixel_code: 'A1T8T4UYGVIQA8ORZMX9', + pixel_code: '{{PIXEL-CODE}}', event: 'SubmitForm', event_id: '16163186328257', timestamp: '2020-09-17T19:49:27Z', @@ -2040,7 +2040,7 @@ export const data = [ destination: { Config: { accessToken: 'dummyAccessToken', - pixelCode: 'A1T8T4UYGVIQA8ORZMX9', + pixelCode: '{{PIXEL-CODE}}', hashUserProperties: false, }, }, @@ -2065,7 +2065,7 @@ export const data = [ params: {}, body: { JSON: { - pixel_code: 'A1T8T4UYGVIQA8ORZMX9', + pixel_code: '{{PIXEL-CODE}}', event: 'Contact', event_id: '16163186328257', timestamp: '2020-09-17T19:49:27Z', @@ -2204,7 +2204,7 @@ export const data = [ destination: { Config: { accessToken: 'dummyAccessToken', - pixelCode: 'A1T8T4UYGVIQA8ORZMX9', + pixelCode: '{{PIXEL-CODE}}', hashUserProperties: false, }, }, @@ -2318,7 +2318,7 @@ export const data = [ destination: { Config: { accessToken: 'dummyAccessToken', - pixelCode: 'A1T8T4UYGVIQA8ORZMX9', + pixelCode: '{{PIXEL-CODE}}', hashUserProperties: false, }, }, @@ -2343,7 +2343,7 @@ export const data = [ params: {}, body: { JSON: { - pixel_code: 'A1T8T4UYGVIQA8ORZMX9', + pixel_code: '{{PIXEL-CODE}}', event: 'CompletePayment', event_id: '1616318632825_357', timestamp: '2020-09-17T19:49:27Z', @@ -2482,7 +2482,7 @@ export const data = [ destination: { Config: { accessToken: 'dummyAccessToken', - pixelCode: 'A1T8T4UYGVIQA8ORZMX9', + pixelCode: '{{PIXEL-CODE}}', hashUserProperties: false, }, }, @@ -2507,7 +2507,7 @@ export const data = [ params: {}, body: { JSON: { - pixel_code: 'A1T8T4UYGVIQA8ORZMX9', + pixel_code: '{{PIXEL-CODE}}', event: 'PlaceAnOrder', event_id: '1616318632825_357', timestamp: '2020-09-17T19:49:27Z', @@ -2650,7 +2650,7 @@ export const data = [ destination: { Config: { accessToken: 'dummyAccessToken', - pixelCode: 'A1T8T4UYGVIQA8ORZMX9', + pixelCode: '{{PIXEL-CODE}}', hashUserProperties: true, }, }, @@ -2675,7 +2675,7 @@ export const data = [ params: {}, body: { JSON: { - pixel_code: 'A1T8T4UYGVIQA8ORZMX9', + pixel_code: '{{PIXEL-CODE}}', event: 'Subscribe', event_id: '1616318632825_357', timestamp: '2020-09-17T19:49:27Z', @@ -2819,7 +2819,7 @@ export const data = [ destination: { Config: { accessToken: 'dummyAccessToken', - pixelCode: 'A1T8T4UYGVIQA8ORZMX9', + pixelCode: '{{PIXEL-CODE}}', hashUserProperties: true, }, }, @@ -2844,7 +2844,7 @@ export const data = [ params: {}, body: { JSON: { - pixel_code: 'A1T8T4UYGVIQA8ORZMX9', + pixel_code: '{{PIXEL-CODE}}', event: 'Subscribe', event_id: '1616318632825_357', timestamp: '2020-09-17T19:49:27Z', @@ -2984,7 +2984,7 @@ export const data = [ destination: { Config: { accessToken: 'dummyAccessToken', - pixelCode: 'A1T8T4UYGVIQA8ORZMX9', + pixelCode: '{{PIXEL-CODE}}', hashUserProperties: false, }, }, @@ -3009,7 +3009,7 @@ export const data = [ params: {}, body: { JSON: { - pixel_code: 'A1T8T4UYGVIQA8ORZMX9', + pixel_code: '{{PIXEL-CODE}}', event: 'CompletePayment', event_id: '1616318632825_357', timestamp: '2020-09-17T19:49:27Z', @@ -3148,7 +3148,7 @@ export const data = [ destination: { Config: { accessToken: 'dummyAccessToken', - pixelCode: 'A1T8T4UYGVIQA8ORZMX9', + pixelCode: '{{PIXEL-CODE}}', hashUserProperties: false, eventsToStandard: [ { @@ -3187,7 +3187,7 @@ export const data = [ params: {}, body: { JSON: { - pixel_code: 'A1T8T4UYGVIQA8ORZMX9', + pixel_code: '{{PIXEL-CODE}}', event: 'download', event_id: '1616318632825_357', timestamp: '2020-09-17T19:49:27Z', @@ -3249,7 +3249,7 @@ export const data = [ params: {}, body: { JSON: { - pixel_code: 'A1T8T4UYGVIQA8ORZMX9', + pixel_code: '{{PIXEL-CODE}}', event: 'search', event_id: '1616318632825_357', timestamp: '2020-09-17T19:49:27Z', @@ -3388,7 +3388,7 @@ export const data = [ destination: { Config: { accessToken: 'dummyAccessToken', - pixelCode: 'A1T8T4UYGVIQA8ORZMX9', + pixelCode: '{{PIXEL-CODE}}', hashUserProperties: false, eventsToStandard: [ { @@ -3508,7 +3508,7 @@ export const data = [ destination: { Config: { accessToken: 'dummyAccessToken', - pixelCode: 'A1T8T4UYGVIQA8ORZMX9', + pixelCode: '{{PIXEL-CODE}}', hashUserProperties: false, eventsToStandard: [ { @@ -3543,7 +3543,7 @@ export const data = [ params: {}, body: { JSON: { - pixel_code: 'A1T8T4UYGVIQA8ORZMX9', + pixel_code: '{{PIXEL-CODE}}', event: 'download', event_id: '1616318632825_357', timestamp: '2020-09-17T19:49:27Z', @@ -3681,7 +3681,7 @@ export const data = [ destination: { Config: { accessToken: 'dummyAccessToken', - pixelCode: 'A1T8T4UYGVIQA8ORZMX9', + pixelCode: '{{PIXEL-CODE}}', hashUserProperties: true, eventsToStandard: [ { @@ -3716,7 +3716,7 @@ export const data = [ params: {}, body: { JSON: { - pixel_code: 'A1T8T4UYGVIQA8ORZMX9', + pixel_code: '{{PIXEL-CODE}}', event: 'download', event_id: '1616318632825_357', timestamp: '2020-09-17T19:49:27Z', @@ -3854,7 +3854,7 @@ export const data = [ destination: { Config: { accessToken: 'dummyAccessToken', - pixelCode: 'A1T8T4UYGVIQA8ORZMX9', + pixelCode: '{{PIXEL-CODE}}', hashUserProperties: true, eventsToStandard: [ { @@ -3889,7 +3889,7 @@ export const data = [ params: {}, body: { JSON: { - pixel_code: 'A1T8T4UYGVIQA8ORZMX9', + pixel_code: '{{PIXEL-CODE}}', event: 'download', event_id: '1616318632825_357', timestamp: '2020-09-17T19:49:27Z', @@ -4048,7 +4048,7 @@ export const data = [ destination: { Config: { accessToken: 'dummyAccessToken', - pixelCode: 'A1T8T4UYGVIQA8ORZMX9', + pixelCode: '{{PIXEL-CODE}}', hashUserProperties: false, }, }, @@ -4073,7 +4073,7 @@ export const data = [ params: {}, body: { JSON: { - pixel_code: 'A1T8T4UYGVIQA8ORZMX9', + pixel_code: '{{PIXEL-CODE}}', event: 'CompletePayment', event_id: '1616318632825_357', timestamp: '2020-09-17T19:49:27Z', @@ -4219,7 +4219,7 @@ export const data = [ destination: { Config: { accessToken: 'dummyAccessToken', - pixelCode: 'A1T8T4UYGVIQA8ORZMX9', + pixelCode: '{{PIXEL-CODE}}', hashUserProperties: false, }, }, @@ -4244,7 +4244,7 @@ export const data = [ params: {}, body: { JSON: { - pixel_code: 'A1T8T4UYGVIQA8ORZMX9', + pixel_code: '{{PIXEL-CODE}}', event: 'CompletePayment', event_id: '1616318632825_357', timestamp: '2020-09-17T19:49:27Z', @@ -4394,7 +4394,7 @@ export const data = [ destination: { Config: { accessToken: 'dummyAccessToken', - pixelCode: 'A1T8T4UYGVIQA8ORZMX9', + pixelCode: '{{PIXEL-CODE}}', hashUserProperties: false, }, }, @@ -4419,7 +4419,7 @@ export const data = [ params: {}, body: { JSON: { - pixel_code: 'A1T8T4UYGVIQA8ORZMX9', + pixel_code: '{{PIXEL-CODE}}', event: 'CompletePayment', event_id: '1616318632825_357', timestamp: '2020-09-17T19:49:27Z', @@ -4541,7 +4541,7 @@ export const data = [ category: 'Games', url: 'https://www.website.com/product/path', image_url: 'https://www.website.com/product/path.jpg', - brand:"brand_name" + brand: 'brand_name', }, { product_id: '345', @@ -4574,7 +4574,7 @@ export const data = [ destination: { Config: { accessToken: 'dummyAccessToken', - pixelCode: 'A1T8T4UYGVIQA8ORZMX9', + pixelCode: '{{PIXEL-CODE}}', hashUserProperties: false, }, }, @@ -4599,7 +4599,7 @@ export const data = [ params: {}, body: { JSON: { - pixel_code: 'A1T8T4UYGVIQA8ORZMX9', + pixel_code: '{{PIXEL-CODE}}', event: 'CompletePayment', event_id: '1616318632825_357', timestamp: '2020-09-17T19:49:27Z', @@ -4619,7 +4619,7 @@ export const data = [ content_name: 'Monopoly', price: 14, quantity: 1, - brand:"brand_name" + brand: 'brand_name', }, { content_type: 'product_group', @@ -4752,7 +4752,7 @@ export const data = [ destination: { Config: { accessToken: 'dummyAccessToken', - pixelCode: 'A1T8T4UYGVIQA8ORZMX9', + pixelCode: '{{PIXEL-CODE}}', hashUserProperties: false, sendCustomEvents: true, }, @@ -4778,7 +4778,7 @@ export const data = [ params: {}, body: { JSON: { - pixel_code: 'A1T8T4UYGVIQA8ORZMX9', + pixel_code: '{{PIXEL-CODE}}', event: 'custom_event', event_id: '1616318632825_357', timestamp: '2020-09-17T19:49:27Z', @@ -4924,7 +4924,7 @@ export const data = [ destination: { Config: { accessToken: 'dummyAccessToken', - pixelCode: 'A1T8T4UYGVIQA8ORZMX9', + pixelCode: '{{PIXEL-CODE}}', hashUserProperties: false, sendCustomEvents: false, }, @@ -4956,7 +4956,7 @@ export const data = [ }, { name: 'tiktok_ads', - description: 'Test 29 -> Camel Case Custom Event Pass', + description: 'Test 31 -> Camel Case Custom Event Pass', feature: 'processor', module: 'destination', version: 'v0', @@ -5044,7 +5044,7 @@ export const data = [ destination: { Config: { accessToken: 'dummyAccessToken', - pixelCode: 'A1T8T4UYGVIQA8ORZMX9', + pixelCode: '{{PIXEL-CODE}}', hashUserProperties: false, sendCustomEvents: true, }, @@ -5070,7 +5070,7 @@ export const data = [ params: {}, body: { JSON: { - pixel_code: 'A1T8T4UYGVIQA8ORZMX9', + pixel_code: '{{PIXEL-CODE}}', event: 'customEvent', event_id: '1616318632825_357', timestamp: '2020-09-17T19:49:27Z', @@ -5126,4 +5126,1750 @@ export const data = [ }, }, }, + { + name: 'tiktok_ads', + description: 'Test 32 -> V2 -> Camel Case Custom Event Pass', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: { + anonymousId: '21e13f4bc7ceddad', + channel: 'web', + context: { + traits: { + email: 'dd6ff77f54e2106661089bae4d40cdb600979bf7edc9eb65c0942ba55c7c2d7f', + }, + userAgent: + 'Mozilla/5.0 (platform; rv:geckoversion) Gecko/geckotrail Firefox/firefoxversion', + ip: '13.57.97.131', + locale: 'en-US', + externalId: [ + { + type: 'tiktokExternalId', + id: 'f0e388f53921a51f0bb0fc8a2944109ec188b59172935d8f23020b1614cc44bc', + }, + ], + }, + messageId: '84e26acc-56a5-4835-8233-591137fca468', + session_id: '3049dc4c-5a95-4ccd-a3e7-d74a7e411f22', + originalTimestamp: '2019-10-14T09:03:17.562Z', + timestamp: '2020-09-17T19:49:27Z', + type: 'track', + event: 'customEvent', + properties: { + eventId: '1616318632825_357', + contents: [ + { + price: 8, + quantity: 2, + content_type: 'socks', + content_id: '1077218', + }, + { + price: 30, + quantity: 1, + content_type: 'dress', + content_id: '1197218', + }, + ], + url: 'http://demo.mywebsite.com/purchase', + clickId: 'dummyclickId', + currency: 'USD', + value: 46, + }, + integrations: { + All: true, + }, + sentAt: '2019-10-14T09:03:22.563Z', + }, + destination: { + Config: { + version: 'v2', + accessToken: 'dummyAccessToken', + pixelCode: '{{PIXEL-CODE}}', + hashUserProperties: false, + sendCustomEvents: true, + }, + }, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: { + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://business-api.tiktok.com/open_api/v1.3/event/track/', + headers: { + 'Access-Token': 'dummyAccessToken', + 'Content-Type': 'application/json', + }, + params: {}, + body: { + JSON: { + event_source: 'web', + event_source_id: '{{PIXEL-CODE}}', + partner_name: 'RudderStack', + data: [ + { + event: 'customEvent', + event_id: '1616318632825_357', + event_time: 1600372167, + properties: { + content_type: 'product', + contents: [ + { + price: 8, + quantity: 2, + content_type: 'socks', + content_id: '1077218', + }, + { + price: 30, + quantity: 1, + content_type: 'dress', + content_id: '1197218', + }, + ], + currency: 'USD', + value: 46, + }, + page: { + url: 'http://demo.mywebsite.com/purchase', + }, + user: { + locale: 'en-US', + email: 'dd6ff77f54e2106661089bae4d40cdb600979bf7edc9eb65c0942ba55c7c2d7f', + external_id: + 'f0e388f53921a51f0bb0fc8a2944109ec188b59172935d8f23020b1614cc44bc', + ip: '13.57.97.131', + user_agent: + 'Mozilla/5.0 (platform; rv:geckoversion) Gecko/geckotrail Firefox/firefoxversion', + }, + }, + ], + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + userId: '', + }, + statusCode: 200, + }, + ], + }, + }, + }, + { + name: 'tiktok_ads', + description: + 'Test 33 -> V2 -> Event mapped to one standard event with contents present as it is in properties', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: { + anonymousId: '21e13f4bc7ceddad', + channel: 'web', + context: { + traits: { + email: 'dd6ff77f54e2106661089bae4d40cdb600979bf7edc9eb65c0942ba55c7c2d7f', + }, + page: { + url: 'http://demo.mywebsite.com/purchase', + referrer: 'http://demo.mywebsite.com', + }, + userAgent: + 'Mozilla/5.0 (platform; rv:geckoversion) Gecko/geckotrail Firefox/firefoxversion', + ip: '13.57.97.131', + locale: 'en-US', + os: { + name: '', + version: '', + }, + screen: { + density: 2, + }, + externalId: [ + { + type: 'tiktokExternalId', + id: 'f0e388f53921a51f0bb0fc8a2944109ec188b59172935d8f23020b1614cc44bc', + }, + ], + }, + messageId: '84e26acc-56a5-4835-8233-591137fca468', + session_id: '3049dc4c-5a95-4ccd-a3e7-d74a7e411f22', + originalTimestamp: '2019-10-14T09:03:17.562Z', + timestamp: '2020-09-17T19:49:27Z', + type: 'track', + event: 'addToCart', + properties: { + eventId: '1616318632825_357', + contents: [ + { + price: 8, + quantity: 2, + content_type: 'socks', + content_id: '1077218', + }, + { + price: 30, + quantity: 1, + content_type: 'dress', + content_id: '1197218', + }, + ], + clickId: 'dummyclickId', + currency: 'USD', + value: 46, + }, + integrations: { + All: true, + }, + sentAt: '2019-10-14T09:03:22.563Z', + }, + destination: { + Config: { + version: 'v2', + accessToken: 'dummyAccessToken', + pixelCode: '{{PIXEL-CODE}}', + hashUserProperties: false, + sendCustomEvents: true, + eventsToStandard: [ + { + from: 'addToCart', + to: 'download', + }, + ], + }, + }, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: { + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://business-api.tiktok.com/open_api/v1.3/event/track/', + headers: { + 'Access-Token': 'dummyAccessToken', + 'Content-Type': 'application/json', + }, + params: {}, + body: { + JSON: { + event_source: 'web', + event_source_id: '{{PIXEL-CODE}}', + partner_name: 'RudderStack', + data: [ + { + event: 'download', + event_id: '1616318632825_357', + event_time: 1600372167, + properties: { + content_type: 'product', + contents: [ + { + price: 8, + quantity: 2, + content_type: 'socks', + content_id: '1077218', + }, + { + price: 30, + quantity: 1, + content_type: 'dress', + content_id: '1197218', + }, + ], + currency: 'USD', + value: 46, + }, + page: { + url: 'http://demo.mywebsite.com/purchase', + referrer: 'http://demo.mywebsite.com', + }, + user: { + locale: 'en-US', + email: 'dd6ff77f54e2106661089bae4d40cdb600979bf7edc9eb65c0942ba55c7c2d7f', + external_id: + 'f0e388f53921a51f0bb0fc8a2944109ec188b59172935d8f23020b1614cc44bc', + ip: '13.57.97.131', + user_agent: + 'Mozilla/5.0 (platform; rv:geckoversion) Gecko/geckotrail Firefox/firefoxversion', + }, + }, + ], + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + userId: '', + }, + statusCode: 200, + }, + ], + }, + }, + }, + { + name: 'tiktok_ads', + description: 'Test 34 -> V2 -> Event mapped to multiple standard events and no phone', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: { + anonymousId: '21e13f4bc7ceddad', + channel: 'web', + context: { + traits: { + email: 'dd6ff77f54e2106661089bae4d40cdb600979bf7edc9eb65c0942ba55c7c2d7f', + }, + page: { + url: 'http://demo.mywebsite.com/purchase', + }, + userAgent: + 'Mozilla/5.0 (platform; rv:geckoversion) Gecko/geckotrail Firefox/firefoxversion', + ip: '13.57.97.131', + locale: 'en-US', + os: { + name: '', + version: '', + }, + screen: { + density: 2, + }, + externalId: [ + { + type: 'tiktokExternalId', + id: 'f0e388f53921a51f0bb0fc8a2944109ec188b59172935d8f23020b1614cc44bc', + }, + ], + }, + messageId: '84e26acc-56a5-4835-8233-591137fca468', + session_id: '3049dc4c-5a95-4ccd-a3e7-d74a7e411f22', + originalTimestamp: '2019-10-14T09:03:17.562Z', + timestamp: '2020-09-17T19:49:27Z', + type: 'track', + event: 'addToCart', + properties: { + eventId: '1616318632825_357', + contents: [ + { + price: 8, + quantity: 2, + content_type: 'socks', + content_id: '1077218', + }, + { + price: 30, + quantity: 1, + content_type: 'dress', + content_id: '1197218', + }, + ], + clickId: 'dummyclickId', + currency: 'USD', + value: 46, + }, + integrations: { + All: true, + }, + sentAt: '2019-10-14T09:03:22.563Z', + }, + destination: { + Config: { + version: 'v2', + accessToken: 'dummyAccessToken', + pixelCode: '{{PIXEL-CODE}}', + hashUserProperties: false, + sendCustomEvents: true, + eventsToStandard: [ + { + from: 'addToCart', + to: 'download', + }, + { + from: 'AddToCart', + to: 'AddToWishlist', + }, + ], + }, + }, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: { + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://business-api.tiktok.com/open_api/v1.3/event/track/', + headers: { + 'Access-Token': 'dummyAccessToken', + 'Content-Type': 'application/json', + }, + params: {}, + body: { + JSON: { + event_source: 'web', + event_source_id: '{{PIXEL-CODE}}', + partner_name: 'RudderStack', + data: [ + { + event: 'download', + event_id: '1616318632825_357', + event_time: 1600372167, + properties: { + content_type: 'product', + contents: [ + { + price: 8, + quantity: 2, + content_type: 'socks', + content_id: '1077218', + }, + { + price: 30, + quantity: 1, + content_type: 'dress', + content_id: '1197218', + }, + ], + currency: 'USD', + value: 46, + }, + page: { + url: 'http://demo.mywebsite.com/purchase', + }, + user: { + locale: 'en-US', + email: 'dd6ff77f54e2106661089bae4d40cdb600979bf7edc9eb65c0942ba55c7c2d7f', + external_id: + 'f0e388f53921a51f0bb0fc8a2944109ec188b59172935d8f23020b1614cc44bc', + ip: '13.57.97.131', + user_agent: + 'Mozilla/5.0 (platform; rv:geckoversion) Gecko/geckotrail Firefox/firefoxversion', + }, + }, + { + event: 'AddToWishlist', + event_id: '1616318632825_357', + event_time: 1600372167, + properties: { + content_type: 'product', + contents: [ + { + price: 8, + quantity: 2, + content_type: 'socks', + content_id: '1077218', + }, + { + price: 30, + quantity: 1, + content_type: 'dress', + content_id: '1197218', + }, + ], + currency: 'USD', + value: 46, + }, + page: { + url: 'http://demo.mywebsite.com/purchase', + }, + user: { + locale: 'en-US', + email: 'dd6ff77f54e2106661089bae4d40cdb600979bf7edc9eb65c0942ba55c7c2d7f', + external_id: + 'f0e388f53921a51f0bb0fc8a2944109ec188b59172935d8f23020b1614cc44bc', + ip: '13.57.97.131', + user_agent: + 'Mozilla/5.0 (platform; rv:geckoversion) Gecko/geckotrail Firefox/firefoxversion', + }, + }, + ], + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + userId: '', + }, + statusCode: 200, + }, + ], + }, + }, + }, + { + name: 'tiktok_ads', + description: 'Test 35 -> V2 -> array of external_id and phone number', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: { + anonymousId: '21e13f4bc7ceddad', + channel: 'web', + context: { + page: { + url: 'http://demo.mywebsite.com/purchase', + referrer: 'http://demo.mywebsite.com', + }, + userAgent: + 'Mozilla/5.0 (platform; rv:geckoversion) Gecko/geckotrail Firefox/firefoxversion', + ip: '13.57.97.131', + externalId: [ + { + type: 'tiktokExternalId', + id: [ + 'f0e388f53921a51f0bb0fc8a2944109ec188b5', + '1f0bb0fc8a2944109ec188b59172935d8f23020b1614cc44bc', + ], + }, + ], + }, + messageId: '84e26acc-56a5-4835-8233-591137fca468', + session_id: '3049dc4c-5a95-4ccd-a3e7-d74a7e411f22', + originalTimestamp: '2019-10-14T09:03:17.562Z', + timestamp: '2020-09-17T19:49:27Z', + type: 'track', + event: 'addToCart', + properties: { + eventId: '1616318632825_357', + contents: [ + { + price: 8, + quantity: 2, + content_type: 'socks', + content_id: '1077218', + }, + { + price: 30, + quantity: 1, + content_type: 'dress', + content_id: '1197218', + }, + ], + clickId: 'dummyclickId', + currency: 'USD', + value: 46, + phone: ['+12345432', '+134234325'], + email: 'dd6ff77f54e2106661089bae4d40cdb600979bf7edc9eb65c0942ba55c7c2d7f', + }, + integrations: { + All: true, + }, + sentAt: '2019-10-14T09:03:22.563Z', + }, + destination: { + Config: { + version: 'v2', + accessToken: 'dummyAccessToken', + pixelCode: '{{PIXEL-CODE}}', + hashUserProperties: false, + sendCustomEvents: true, + eventsToStandard: [ + { + from: 'addToCart', + to: 'download', + }, + { + from: 'AddToCart', + to: 'AddToWishlist', + }, + ], + }, + }, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: { + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://business-api.tiktok.com/open_api/v1.3/event/track/', + headers: { + 'Access-Token': 'dummyAccessToken', + 'Content-Type': 'application/json', + }, + params: {}, + body: { + JSON: { + event_source: 'web', + event_source_id: '{{PIXEL-CODE}}', + partner_name: 'RudderStack', + data: [ + { + event: 'download', + event_id: '1616318632825_357', + event_time: 1600372167, + properties: { + content_type: 'product', + contents: [ + { + price: 8, + quantity: 2, + content_type: 'socks', + content_id: '1077218', + }, + { + price: 30, + quantity: 1, + content_type: 'dress', + content_id: '1197218', + }, + ], + currency: 'USD', + value: 46, + }, + page: { + url: 'http://demo.mywebsite.com/purchase', + referrer: 'http://demo.mywebsite.com', + }, + user: { + phone: ['+12345432', '+134234325'], + email: 'dd6ff77f54e2106661089bae4d40cdb600979bf7edc9eb65c0942ba55c7c2d7f', + external_id: [ + 'f0e388f53921a51f0bb0fc8a2944109ec188b5', + '1f0bb0fc8a2944109ec188b59172935d8f23020b1614cc44bc', + ], + ip: '13.57.97.131', + user_agent: + 'Mozilla/5.0 (platform; rv:geckoversion) Gecko/geckotrail Firefox/firefoxversion', + }, + }, + { + event: 'AddToWishlist', + event_id: '1616318632825_357', + event_time: 1600372167, + properties: { + content_type: 'product', + contents: [ + { + price: 8, + quantity: 2, + content_type: 'socks', + content_id: '1077218', + }, + { + price: 30, + quantity: 1, + content_type: 'dress', + content_id: '1197218', + }, + ], + currency: 'USD', + value: 46, + }, + page: { + url: 'http://demo.mywebsite.com/purchase', + referrer: 'http://demo.mywebsite.com', + }, + user: { + phone: ['+12345432', '+134234325'], + email: 'dd6ff77f54e2106661089bae4d40cdb600979bf7edc9eb65c0942ba55c7c2d7f', + external_id: [ + 'f0e388f53921a51f0bb0fc8a2944109ec188b5', + '1f0bb0fc8a2944109ec188b59172935d8f23020b1614cc44bc', + ], + ip: '13.57.97.131', + user_agent: + 'Mozilla/5.0 (platform; rv:geckoversion) Gecko/geckotrail Firefox/firefoxversion', + }, + }, + ], + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + userId: '', + }, + statusCode: 200, + }, + ], + }, + }, + }, + { + name: 'tiktok_ads', + description: 'Test 36-> V2 -> Event not standard and no custom events allowed', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: { + anonymousId: '21e13f4bc7ceddad', + channel: 'web', + context: { + traits: { + email: 'dd6ff77f54e2106661089bae4d40cdb600979bf7edc9eb65c0942ba55c7c2d7f', + }, + page: { + url: 'http://demo.mywebsite.com/purchase', + referrer: 'http://demo.mywebsite.com', + }, + app: { + build: '1.0.0', + name: 'RudderLabs JavaScript SDK', + namespace: 'com.rudderlabs.javascript', + version: '1.0.0', + }, + library: { + name: 'RudderLabs JavaScript SDK', + version: '1.0.0', + }, + userAgent: + 'Mozilla/5.0 (platform; rv:geckoversion) Gecko/geckotrail Firefox/firefoxversion', + locale: 'en-US', + ip: '13.57.97.131', + os: { + name: '', + version: '', + }, + screen: { + density: 2, + }, + externalId: [ + { + type: 'tiktokExternalId', + id: 'f0e388f53921a51f0bb0fc8a2944109ec188b59172935d8f23020b1614cc44bc', + }, + ], + }, + messageId: '84e26acc-56a5-4835-8233-591137fca468', + session_id: '3049dc4c-5a95-4ccd-a3e7-d74a7e411f22', + originalTimestamp: '2019-10-14T09:03:17.562Z', + timestamp: '2020-09-17T19:49:27Z', + type: 'track', + event: 'Product Added to Wishlist1', + properties: { + eventId: '1616318632825_357', + testEventCode: 'sample rudder test_event_code', + context: { + page: { + url: 'http://demo.mywebsite.com/purchase', + referrer: 'http://demo.mywebsite.com', + }, + user: { + phone_number: + '2f9d2b4df907e5c9a7b3434351b55700167b998a83dc479b825096486ffcf4ea', + email: 'dd6ff77f54e2106661089bae4d40cdb600979bf7edc9eb65c0942ba55c7c2d7f', + }, + }, + contents: [ + { + price: 8, + quantity: 2, + content_type: 'socks', + content_id: '1077218', + }, + { + price: 30, + quantity: 1, + content_type: 'dress', + content_id: '1197218', + }, + ], + currency: 'USD', + value: 46, + }, + integrations: { + All: true, + }, + sentAt: '2019-10-14T09:03:22.563Z', + }, + destination: { + Config: { + accessToken: 'dummyAccessToken', + pixelCode: '{{PIXEL-CODE}}', + hashUserProperties: false, + version: 'v2', + }, + }, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + statusCode: 400, + error: + 'Event name (product added to wishlist1) is not valid, must be mapped to one of standard events', + statTags: { + errorCategory: 'dataValidation', + errorType: 'instrumentation', + destType: 'TIKTOK_ADS', + module: 'destination', + implementation: 'native', + feature: 'processor', + }, + }, + ], + }, + }, + }, + { + name: 'tiktok_ads', + description: 'Test 37-> V2 -> No Message type', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: { + anonymousId: '21e13f4bc7ceddad', + channel: 'web', + + messageId: '84e26acc-56a5-4835-8233-591137fca468', + session_id: '3049dc4c-5a95-4ccd-a3e7-d74a7e411f22', + originalTimestamp: '2019-10-14T09:03:17.562Z', + timestamp: '2020-09-17T19:49:27Z', + event: 'Product Added to Wishlist1', + integrations: { + All: true, + }, + sentAt: '2019-10-14T09:03:22.563Z', + }, + destination: { + Config: { + accessToken: 'dummyAccessToken', + pixelCode: '{{PIXEL-CODE}}', + hashUserProperties: false, + version: 'v2', + }, + }, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + statusCode: 400, + error: 'Event type is required', + statTags: { + errorCategory: 'dataValidation', + errorType: 'instrumentation', + destType: 'TIKTOK_ADS', + module: 'destination', + implementation: 'native', + feature: 'processor', + }, + }, + ], + }, + }, + }, + { + name: 'tiktok_ads', + description: 'Test 38-> V2 -> Event not found', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: { + anonymousId: '21e13f4bc7ceddad', + channel: 'web', + messageId: '84e26acc-56a5-4835-8233-591137fca468', + session_id: '3049dc4c-5a95-4ccd-a3e7-d74a7e411f22', + originalTimestamp: '2019-10-14T09:03:17.562Z', + timestamp: '2020-09-17T19:49:27Z', + type: 'track', + integrations: { + All: true, + }, + sentAt: '2019-10-14T09:03:22.563Z', + }, + destination: { + Config: { + accessToken: 'dummyAccessToken', + pixelCode: '{{PIXEL-CODE}}', + hashUserProperties: false, + version: 'v2', + }, + }, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + statusCode: 400, + error: 'Event name is required', + statTags: { + errorCategory: 'dataValidation', + errorType: 'instrumentation', + destType: 'TIKTOK_ADS', + module: 'destination', + implementation: 'native', + feature: 'processor', + }, + }, + ], + }, + }, + }, + { + name: 'tiktok_ads', + description: 'Test 39-> V2 -> Access Token not found', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: { + anonymousId: '21e13f4bc7ceddad', + channel: 'web', + messageId: '84e26acc-56a5-4835-8233-591137fca468', + session_id: '3049dc4c-5a95-4ccd-a3e7-d74a7e411f22', + originalTimestamp: '2019-10-14T09:03:17.562Z', + timestamp: '2020-09-17T19:49:27Z', + type: 'track', + integrations: { + All: true, + }, + sentAt: '2019-10-14T09:03:22.563Z', + }, + destination: { + Config: { + pixelCode: 'configuration', + hashUserProperties: false, + version: 'v2', + }, + }, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + statusCode: 400, + error: 'Access Token not found. Aborting', + statTags: { + errorCategory: 'dataValidation', + errorType: 'configuration', + destType: 'TIKTOK_ADS', + module: 'destination', + implementation: 'native', + feature: 'processor', + }, + }, + ], + }, + }, + }, + { + name: 'tiktok_ads', + description: 'Test 40-> V2 -> Pixel Code not found. Aborting', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: { + anonymousId: '21e13f4bc7ceddad', + channel: 'web', + messageId: '84e26acc-56a5-4835-8233-591137fca468', + session_id: '3049dc4c-5a95-4ccd-a3e7-d74a7e411f22', + originalTimestamp: '2019-10-14T09:03:17.562Z', + timestamp: '2020-09-17T19:49:27Z', + type: 'track', + integrations: { + All: true, + }, + sentAt: '2019-10-14T09:03:22.563Z', + }, + destination: { + Config: { + accessToken: 'dummyAccessToken', + hashUserProperties: false, + version: 'v2', + }, + }, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + statusCode: 400, + error: 'Pixel Code not found. Aborting', + statTags: { + errorCategory: 'dataValidation', + errorType: 'configuration', + destType: 'TIKTOK_ADS', + module: 'destination', + implementation: 'native', + feature: 'processor', + }, + }, + ], + }, + }, + }, + { + name: 'tiktok_ads', + description: 'Test 41 -> V2 -> One of standard event', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: { + anonymousId: '21e13f4bc7ceddad', + channel: 'web', + context: { + traits: { + email: 'abc@xyz.com', + }, + page: { + url: 'http://demo.mywebsite.com/purchase', + referrer: 'http://demo.mywebsite.com', + }, + userAgent: + 'Mozilla/5.0 (platform; rv:geckoversion) Gecko/geckotrail Firefox/firefoxversion', + ip: '13.57.97.131', + externalId: [ + { + type: 'tiktokExternalId', + id: [ + 'f0e388f53921a51f0bb0fc8a2944109ec188b5', + '1f0bb0fc8a2944109ec188b59172935d8f23020b1614cc44bc', + ], + }, + ], + }, + messageId: '84e26acc-56a5-4835-8233-591137fca468', + session_id: '3049dc4c-5a95-4ccd-a3e7-d74a7e411f22', + originalTimestamp: '2019-10-14T09:03:17.562Z', + timestamp: '2020-09-17T19:49:27Z', + type: 'track', + event: 'viewcontent', + properties: { + eventId: '1616318632825_357', + contents: [ + { + price: 8, + quantity: 2, + content_type: 'socks', + content_id: '1077218', + }, + { + price: 30, + quantity: 1, + content_type: 'dress', + content_id: '1197218', + }, + ], + clickId: 'dummyclickId', + currency: 'USD', + value: 46, + phone: ['+12345432', '+134234325'], + }, + integrations: { + All: true, + }, + sentAt: '2019-10-14T09:03:22.563Z', + }, + destination: { + Config: { + version: 'v2', + accessToken: 'dummyAccessToken', + pixelCode: '{{PIXEL-CODE}}', + hashUserProperties: true, + sendCustomEvents: false, + eventsToStandard: [ + { + from: 'addToCart', + to: 'download', + }, + { + from: 'AddToCart', + to: 'AddToWishlist', + }, + ], + }, + }, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: { + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://business-api.tiktok.com/open_api/v1.3/event/track/', + headers: { + 'Access-Token': 'dummyAccessToken', + 'Content-Type': 'application/json', + }, + params: {}, + body: { + JSON: { + event_source: 'web', + event_source_id: '{{PIXEL-CODE}}', + partner_name: 'RudderStack', + data: [ + { + event: 'ViewContent', + event_id: '1616318632825_357', + event_time: 1600372167, + properties: { + content_type: 'product', + contents: [ + { + price: 8, + quantity: 2, + content_type: 'socks', + content_id: '1077218', + }, + { + price: 30, + quantity: 1, + content_type: 'dress', + content_id: '1197218', + }, + ], + currency: 'USD', + value: 46, + }, + page: { + url: 'http://demo.mywebsite.com/purchase', + referrer: 'http://demo.mywebsite.com', + }, + user: { + email: 'ee278943de84e5d6243578ee1a1057bcce0e50daad9755f45dfa64b60b13bc5d', + external_id: [ + '3e0c7a51acd326b87f29596e38c22cbeb732df37bc5c8f5f524c14b55d3472db', + 'f8be04e62f5a3eba31c8b9380843666f28f3ab5f44a380f47fac04e9ce7b2168', + ], + ip: '13.57.97.131', + phone: [ + '49a15e38bdc2572d362a924c2ddd100baed0fe29db44270d3700fcef03b18c39', + '5a6a7a09b18278e220312ce26d711ff7c8263d0965ee3b1d26b1b6f0ac7e71b3', + ], + user_agent: + 'Mozilla/5.0 (platform; rv:geckoversion) Gecko/geckotrail Firefox/firefoxversion', + }, + }, + ], + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + userId: '', + }, + statusCode: 200, + }, + ], + }, + }, + }, + { + name: 'tiktok_ads', + description: 'Test 42 -> V2 -> One of standard event and contents from products', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: { + anonymousId: '21e13f4bc7ceddad', + channel: 'web', + context: { + traits: { + email: 'abc@xyz.com', + }, + page: { + url: 'http://demo.mywebsite.com/purchase', + referrer: 'http://demo.mywebsite.com', + }, + userAgent: + 'Mozilla/5.0 (platform; rv:geckoversion) Gecko/geckotrail Firefox/firefoxversion', + ip: '13.57.97.131', + externalId: [ + { + type: 'tiktokExternalId', + id: [ + 'f0e388f53921a51f0bb0fc8a2944109ec188b5', + '1f0bb0fc8a2944109ec188b59172935d8f23020b1614cc44bc', + ], + }, + ], + }, + messageId: '84e26acc-56a5-4835-8233-591137fca468', + session_id: '3049dc4c-5a95-4ccd-a3e7-d74a7e411f22', + originalTimestamp: '2019-10-14T09:03:17.562Z', + timestamp: '2020-09-17T19:49:27Z', + type: 'track', + event: 'viewcontent', + properties: { + order_id: 1234, + shop_id: 4567, + description: 'Viewed games', + query: 'New age games', + contentType: 'product_group', + eventId: '1616318632825_357', + products: [ + { + product_id: 123, + sku: 'G-32', + name: 'Monopoly', + price: 14, + quantity: 1, + contentType: 'product_group', + category: 'Games', + brand: 'adidas', + image_url: 'https://www.website.com/product/path.jpg', + }, + { + product_id: 345, + sku: 'F-32', + name: 'UNO', + price: 3.45, + contentType: 'product_group', + quantity: 2, + }, + ], + clickId: 'dummyclickId', + currency: 'USD', + value: 46, + phone: ['+12345432', '+134234325'], + }, + integrations: { + All: true, + }, + sentAt: '2019-10-14T09:03:22.563Z', + }, + destination: { + Config: { + version: 'v2', + accessToken: 'dummyAccessToken', + pixelCode: '{{PIXEL-CODE}}', + hashUserProperties: true, + sendCustomEvents: false, + eventsToStandard: [ + { + from: 'addToCart', + to: 'download', + }, + { + from: 'AddToCart', + to: 'AddToWishlist', + }, + ], + }, + }, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: { + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://business-api.tiktok.com/open_api/v1.3/event/track/', + headers: { + 'Access-Token': 'dummyAccessToken', + 'Content-Type': 'application/json', + }, + params: {}, + body: { + JSON: { + event_source: 'web', + event_source_id: '{{PIXEL-CODE}}', + partner_name: 'RudderStack', + data: [ + { + event: 'ViewContent', + event_id: '1616318632825_357', + event_time: 1600372167, + properties: { + order_id: '1234', + shop_id: '4567', + description: 'Viewed games', + query: 'New age games', + content_type: 'product_group', + contents: [ + { + price: 14, + quantity: 1, + content_category: 'Games', + content_id: '123', + content_name: 'Monopoly', + brand: 'adidas', + }, + { + price: 3.45, + quantity: 2, + content_id: '345', + content_name: 'UNO', + }, + ], + currency: 'USD', + value: 46, + }, + page: { + url: 'http://demo.mywebsite.com/purchase', + referrer: 'http://demo.mywebsite.com', + }, + user: { + email: 'ee278943de84e5d6243578ee1a1057bcce0e50daad9755f45dfa64b60b13bc5d', + external_id: [ + '3e0c7a51acd326b87f29596e38c22cbeb732df37bc5c8f5f524c14b55d3472db', + 'f8be04e62f5a3eba31c8b9380843666f28f3ab5f44a380f47fac04e9ce7b2168', + ], + ip: '13.57.97.131', + phone: [ + '49a15e38bdc2572d362a924c2ddd100baed0fe29db44270d3700fcef03b18c39', + '5a6a7a09b18278e220312ce26d711ff7c8263d0965ee3b1d26b1b6f0ac7e71b3', + ], + user_agent: + 'Mozilla/5.0 (platform; rv:geckoversion) Gecko/geckotrail Firefox/firefoxversion', + }, + }, + ], + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + userId: '', + }, + statusCode: 200, + }, + ], + }, + }, + }, + { + name: 'tiktok_ads', + description: 'Test 43 -> V2 -> Contents present as object in properties', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: { + anonymousId: '21e13f4bc7ceddad', + channel: 'web', + context: { + traits: { + email: 'dd6ff77f54e2106661089bae4d40cdb600979bf7edc9eb65c0942ba55c7c2d7f', + }, + page: { + url: 'http://demo.mywebsite.com/purchase', + referrer: 'http://demo.mywebsite.com', + }, + userAgent: + 'Mozilla/5.0 (platform; rv:geckoversion) Gecko/geckotrail Firefox/firefoxversion', + ip: '13.57.97.131', + locale: 'en-US', + externalId: [ + { + type: 'tiktokExternalId', + id: 'f0e388f53921a51f0bb0fc8a2944109ec188b59172935d8f23020b1614cc44bc', + }, + ], + }, + messageId: '84e26acc-56a5-4835-8233-591137fca468', + session_id: '3049dc4c-5a95-4ccd-a3e7-d74a7e411f22', + originalTimestamp: '2019-10-14T09:03:17.562Z', + timestamp: '2020-09-17T19:49:27Z', + type: 'track', + event: 'addToCart', + properties: { + eventId: '1616318632825_357', + contents: { + price: 8, + quantity: 2, + content_type: 'socks', + content_id: '1077218', + }, + clickId: 'dummyclickId', + currency: 'USD', + value: 46, + }, + integrations: { + All: true, + }, + sentAt: '2019-10-14T09:03:22.563Z', + }, + destination: { + Config: { + version: 'v2', + accessToken: 'dummyAccessToken', + pixelCode: '{{PIXEL-CODE}}', + hashUserProperties: false, + sendCustomEvents: true, + eventsToStandard: [ + { + from: 'addToCart', + to: 'download', + }, + ], + }, + }, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: { + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://business-api.tiktok.com/open_api/v1.3/event/track/', + headers: { + 'Access-Token': 'dummyAccessToken', + 'Content-Type': 'application/json', + }, + params: {}, + body: { + JSON: { + event_source: 'web', + event_source_id: '{{PIXEL-CODE}}', + partner_name: 'RudderStack', + data: [ + { + event: 'download', + event_id: '1616318632825_357', + event_time: 1600372167, + properties: { + content_type: 'product', + contents: [ + { + price: 8, + quantity: 2, + content_type: 'socks', + content_id: '1077218', + }, + ], + currency: 'USD', + value: 46, + }, + page: { + url: 'http://demo.mywebsite.com/purchase', + referrer: 'http://demo.mywebsite.com', + }, + user: { + locale: 'en-US', + email: 'dd6ff77f54e2106661089bae4d40cdb600979bf7edc9eb65c0942ba55c7c2d7f', + external_id: + 'f0e388f53921a51f0bb0fc8a2944109ec188b59172935d8f23020b1614cc44bc', + ip: '13.57.97.131', + user_agent: + 'Mozilla/5.0 (platform; rv:geckoversion) Gecko/geckotrail Firefox/firefoxversion', + }, + }, + ], + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + userId: '', + }, + statusCode: 200, + }, + ], + }, + }, + }, + { + name: 'tiktok_ads', + description: 'Test 44 -> Events 2.0 Event type identify not suported', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: { + anonymousId: '21e13f4bc7ceddad', + channel: 'web', + context: { + externalId: [ + { + type: 'tiktokExternalId', + id: 'f0e388f53921a51f0bb0fc8a2944109ec188b59172935d8f23020b1614cc44bc', + }, + ], + }, + timestamp: '2020-09-17T19:49:27Z', + type: 'identify', + event: 'contact', + properties: {}, + integrations: { + All: true, + }, + sentAt: '2019-10-14T09:03:22.563Z', + }, + destination: { + Config: { + accessToken: 'dummyAccessToken', + pixelCode: '{{PIXEL-CODE}}', + hashUserProperties: false, + version: 'v2', + }, + }, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + statusCode: 400, + error: 'Event type identify is not supported', + statTags: { + errorCategory: 'dataValidation', + errorType: 'instrumentation', + destType: 'TIKTOK_ADS', + module: 'destination', + implementation: 'native', + feature: 'processor', + }, + }, + ], + }, + }, + }, + { + name: 'tiktok_ads', + description: + 'Test 45-> events 1.0 build contents from properties.product.$ where length of prodicts is 0', + feature: 'processor', + module: 'destination', + version: 'v0', + input: { + request: { + body: [ + { + message: { + anonymousId: '21e13f4bc7ceddad', + channel: 'web', + context: { + app: { + build: '1.0.0', + name: 'RudderLabs JavaScript SDK', + namespace: 'com.rudderlabs.javascript', + version: '1.0.0', + }, + library: { + name: 'RudderLabs JavaScript SDK', + version: '1.0.0', + }, + userAgent: + 'Mozilla/5.0 (platform; rv:geckoversion) Gecko/geckotrail Firefox/firefoxversion', + ip: '13.57.97.131', + locale: 'en-US', + os: { + name: '', + version: '', + }, + screen: { + density: 2, + }, + externalId: [ + { + type: 'tiktokExternalId', + id: 'f0e388f53921a51f0bb0fc8a2944109ec188b59172935d8f23020b1614cc44bc', + }, + ], + }, + messageId: '84e26acc-56a5-4835-8233-591137fca468', + session_id: '3049dc4c-5a95-4ccd-a3e7-d74a7e411f22', + originalTimestamp: '2019-10-14T09:03:17.562Z', + timestamp: '2020-09-17T19:49:27Z', + type: 'track', + event: 'checkout step completed', + properties: { + eventId: '1616318632825_357', + products: [], + currency: 'USD', + value: 46, + context: { + page: { + url: 'http://demo.mywebsite.com/purchase', + referrer: 'http://demo.mywebsite.com', + }, + user: { + phone_number: + '2f9d2b4df907e5c9a7b3434351b55700167b998a83dc479b825096486ffcf4ea', + email: 'dd6ff77f54e2106661089bae4d40cdb600979bf7edc9eb65c0942ba55c7c2d7f', + }, + }, + }, + integrations: { + All: true, + }, + sentAt: '2019-10-14T09:03:22.563Z', + }, + destination: { + Config: { + accessToken: 'dummyAccessToken', + pixelCode: '{{PIXEL-CODE}}', + hashUserProperties: false, + }, + }, + }, + ], + }, + }, + output: { + response: { + status: 200, + body: [ + { + output: { + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://business-api.tiktok.com/open_api/v1.3/pixel/track/', + headers: { + 'Access-Token': 'dummyAccessToken', + 'Content-Type': 'application/json', + }, + params: {}, + body: { + JSON: { + pixel_code: '{{PIXEL-CODE}}', + event: 'CompletePayment', + event_id: '1616318632825_357', + timestamp: '2020-09-17T19:49:27Z', + properties: { + currency: 'USD', + value: 46, + contents: [], + }, + context: { + page: { + url: 'http://demo.mywebsite.com/purchase', + referrer: 'http://demo.mywebsite.com', + }, + user: { + phone_number: + '2f9d2b4df907e5c9a7b3434351b55700167b998a83dc479b825096486ffcf4ea', + email: 'dd6ff77f54e2106661089bae4d40cdb600979bf7edc9eb65c0942ba55c7c2d7f', + external_id: + 'f0e388f53921a51f0bb0fc8a2944109ec188b59172935d8f23020b1614cc44bc', + }, + ip: '13.57.97.131', + user_agent: + 'Mozilla/5.0 (platform; rv:geckoversion) Gecko/geckotrail Firefox/firefoxversion', + }, + partner_name: 'RudderStack', + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + userId: '', + }, + statusCode: 200, + }, + ], + }, + }, + }, ]; diff --git a/test/integrations/destinations/tiktok_ads/router/data.ts b/test/integrations/destinations/tiktok_ads/router/data.ts index a8c233c7a82..02989792121 100644 --- a/test/integrations/destinations/tiktok_ads/router/data.ts +++ b/test/integrations/destinations/tiktok_ads/router/data.ts @@ -1,9 +1,9 @@ import { FEATURES } from '../../../../../src/v0/util/tags'; - +import { defaultMockFns } from '../mocks'; export const data = [ { name: 'tiktok_ads', - description: 'Test 0', + description: 'Test 0 -> Events 1.0 Multiple events in single batch', feature: FEATURES.ROUTER, module: 'destination', version: 'v0', @@ -22,21 +22,13 @@ export const data = [ namespace: 'com.rudderlabs.javascript', version: '1.0.0', }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, + library: { name: 'RudderLabs JavaScript SDK', version: '1.0.0' }, userAgent: 'Mozilla/5.0 (platform; rv:geckoversion) Gecko/geckotrail Firefox/firefoxversion', ip: '13.57.97.131', locale: 'en-US', - os: { - name: '', - version: '', - }, - screen: { - density: 2, - }, + os: { name: '', version: '' }, + screen: { density: 2 }, externalId: [ { type: 'tiktokExternalId', @@ -54,18 +46,8 @@ export const data = [ eventId: '1616318632825_357', clickId: 'dummyClickId', contents: [ - { - price: 8, - quantity: 2, - content_type: 'socks', - content_id: '1077218', - }, - { - price: 30, - quantity: 1, - content_type: 'dress', - content_id: '1197218', - }, + { price: 8, quantity: 2, content_type: 'socks', content_id: '1077218' }, + { price: 30, quantity: 1, content_type: 'dress', content_id: '1197218' }, ], currency: 'USD', value: 46.0, @@ -92,7 +74,7 @@ export const data = [ destination: { Config: { accessToken: 'dummyAccessToken', - pixelCode: 'A1T8T4UYGVIQA8ORZMX9', + pixelCode: 'dummyPixelCode', hashUserProperties: false, }, }, @@ -108,21 +90,13 @@ export const data = [ namespace: 'com.rudderlabs.javascript', version: '1.0.0', }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, + library: { name: 'RudderLabs JavaScript SDK', version: '1.0.0' }, userAgent: 'Mozilla/5.0 (platform; rv:geckoversion) Gecko/geckotrail Firefox/firefoxversion', locale: 'en-US', ip: '13.57.97.131', - os: { - name: '', - version: '', - }, - screen: { - density: 2, - }, + os: { name: '', version: '' }, + screen: { density: 2 }, externalId: [ { type: 'tiktokExternalId', @@ -150,18 +124,8 @@ export const data = [ }, }, contents: [ - { - price: 8, - quantity: 2, - content_type: 'socks', - content_id: '1077218', - }, - { - price: 30, - quantity: 1, - content_type: 'dress', - content_id: '1197218', - }, + { price: 8, quantity: 2, content_type: 'socks', content_id: '1077218' }, + { price: 30, quantity: 1, content_type: 'dress', content_id: '1197218' }, ], currency: 'USD', value: 46.0, @@ -177,7 +141,7 @@ export const data = [ destination: { Config: { accessToken: 'dummyAccessToken', - pixelCode: 'A1T8T4UYGVIQA8ORZMX9', + pixelCode: 'dummyPixelCode', hashUserProperties: false, }, }, @@ -193,21 +157,13 @@ export const data = [ namespace: 'com.rudderlabs.javascript', version: '1.0.0', }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, + library: { name: 'RudderLabs JavaScript SDK', version: '1.0.0' }, userAgent: 'Mozilla/5.0 (platform; rv:geckoversion) Gecko/geckotrail Firefox/firefoxversion', locale: 'en-US', ip: '13.57.97.131', - os: { - name: '', - version: '', - }, - screen: { - density: 2, - }, + os: { name: '', version: '' }, + screen: { density: 2 }, externalId: [ { type: 'tiktokExternalId', @@ -238,18 +194,8 @@ export const data = [ ip: '13.57.97.131', }, contents: [ - { - price: 8, - quantity: 2, - content_type: 'socks', - content_id: '1077218', - }, - { - price: 30, - quantity: 1, - content_type: 'dress', - content_id: '1197218', - }, + { price: 8, quantity: 2, content_type: 'socks', content_id: '1077218' }, + { price: 30, quantity: 1, content_type: 'dress', content_id: '1197218' }, ], currency: 'USD', value: 46.0, @@ -265,7 +211,7 @@ export const data = [ destination: { Config: { accessToken: 'dummyAccessToken', - pixelCode: 'A1T8T4UYGVIQA8ORZMX9', + pixelCode: 'dummyPixelCode', hashUserProperties: false, }, }, @@ -281,21 +227,13 @@ export const data = [ namespace: 'com.rudderlabs.javascript', version: '1.0.0', }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, + library: { name: 'RudderLabs JavaScript SDK', version: '1.0.0' }, userAgent: 'Mozilla/5.0 (platform; rv:geckoversion) Gecko/geckotrail Firefox/firefoxversion', locale: 'en-US', ip: '13.57.97.131', - os: { - name: '', - version: '', - }, - screen: { - density: 2, - }, + os: { name: '', version: '' }, + screen: { density: 2 }, externalId: [ { type: 'tiktokExternalId', @@ -326,18 +264,8 @@ export const data = [ ip: '13.57.97.131', }, contents: [ - { - price: 8, - quantity: 2, - content_type: 'socks', - content_id: '1077218', - }, - { - price: 30, - quantity: 1, - content_type: 'dress', - content_id: '1197218', - }, + { price: 8, quantity: 2, content_type: 'socks', content_id: '1077218' }, + { price: 30, quantity: 1, content_type: 'dress', content_id: '1197218' }, ], currency: 'USD', value: 46.0, @@ -353,7 +281,7 @@ export const data = [ destination: { Config: { accessToken: 'dummyAccessToken', - pixelCode: 'A1T8T4UYGVIQA8ORZMX9', + pixelCode: 'dummyPixelCode', hashUserProperties: false, }, }, @@ -382,7 +310,7 @@ export const data = [ params: {}, body: { JSON: { - pixel_code: 'A1T8T4UYGVIQA8ORZMX9', + pixel_code: 'dummyPixelCode', partner_name: 'RudderStack', batch: [ { @@ -392,12 +320,7 @@ export const data = [ timestamp: '2020-09-17T19:49:27Z', properties: { contents: [ - { - price: 8, - quantity: 2, - content_type: 'socks', - content_id: '1077218', - }, + { price: 8, quantity: 2, content_type: 'socks', content_id: '1077218' }, { price: 30, quantity: 1, @@ -409,9 +332,7 @@ export const data = [ value: 46, }, context: { - ad: { - callback: 'dummyClickId', - }, + ad: { callback: 'dummyClickId' }, page: { url: 'http://demo.mywebsite.com/purchase', referrer: 'http://demo.mywebsite.com', @@ -454,12 +375,7 @@ export const data = [ properties: { value: 46, contents: [ - { - price: 8, - quantity: 2, - content_id: '1077218', - content_type: 'socks', - }, + { price: 8, quantity: 2, content_id: '1077218', content_type: 'socks' }, { price: 30, quantity: 1, @@ -495,12 +411,7 @@ export const data = [ properties: { value: 46, contents: [ - { - price: 8, - quantity: 2, - content_id: '1077218', - content_type: 'socks', - }, + { price: 8, quantity: 2, content_id: '1077218', content_type: 'socks' }, { price: 30, quantity: 1, @@ -536,12 +447,7 @@ export const data = [ properties: { value: 46, contents: [ - { - price: 8, - quantity: 2, - content_id: '1077218', - content_type: 'socks', - }, + { price: 8, quantity: 2, content_id: '1077218', content_type: 'socks' }, { price: 30, quantity: 1, @@ -579,7 +485,7 @@ export const data = [ destination: { Config: { accessToken: 'dummyAccessToken', - pixelCode: 'A1T8T4UYGVIQA8ORZMX9', + pixelCode: 'dummyPixelCode', hashUserProperties: false, }, }, @@ -591,7 +497,7 @@ export const data = [ }, { name: 'tiktok_ads', - description: 'Test 1', + description: 'Test 1 -> Events 1.0 Single Event inside batch', feature: FEATURES.ROUTER, module: 'destination', version: 'v0', @@ -610,27 +516,14 @@ export const data = [ namespace: 'com.rudderlabs.javascript', version: '1.0.0', }, - library: { - name: 'RudderLabs JavaScript SDK', - version: '1.0.0', - }, + library: { name: 'RudderLabs JavaScript SDK', version: '1.0.0' }, userAgent: 'Mozilla/5.0 (platform; rv:geckoversion) Gecko/geckotrail Firefox/firefoxversion', locale: 'en-US', ip: '13.57.97.131', - os: { - name: '', - version: '', - }, - screen: { - density: 2, - }, - externalId: [ - { - type: 'tiktokExternalId', - id: '1234', - }, - ], + os: { name: '', version: '' }, + screen: { density: 2 }, + externalId: [{ type: 'tiktokExternalId', id: '1234' }], }, messageId: '84e26acc-56a5-4835-8233-591137fca468', session_id: '3049dc4c-5a95-4ccd-a3e7-d74a7e411f22', @@ -646,27 +539,14 @@ export const data = [ url: 'http://demo.mywebsite.com/purchase', referrer: 'http://demo.mywebsite.com', }, - user: { - phone_number: '+858987675687', - email: 'sample@sample.com', - }, + user: { phone_number: '+858987675687', email: 'sample@sample.com' }, userAgent: 'Mozilla/5.0 (platform; rv:geckoversion) Gecko/geckotrail Firefox/firefoxversion', ip: '13.57.97.131', }, contents: [ - { - price: 8, - quantity: 2, - content_type: 'socks', - content_id: '1077218', - }, - { - price: 30, - quantity: 1, - content_type: 'dress', - content_id: '1197218', - }, + { price: 8, quantity: 2, content_type: 'socks', content_id: '1077218' }, + { price: 30, quantity: 1, content_type: 'dress', content_id: '1197218' }, ], currency: 'USD', value: 46.0, @@ -682,7 +562,7 @@ export const data = [ destination: { Config: { accessToken: 'dummyAccessToken', - pixelCode: 'A1T8T4UYGVIQA8ORZMX9', + pixelCode: 'dummyPixelCode', hashUserProperties: true, }, }, @@ -723,22 +603,12 @@ export const data = [ }, event: 'AddToWishlist', event_id: '1616318632825_357', - pixel_code: 'A1T8T4UYGVIQA8ORZMX9', + pixel_code: 'dummyPixelCode', partner_name: 'RudderStack', properties: { contents: [ - { - content_id: '1077218', - content_type: 'socks', - price: 8, - quantity: 2, - }, - { - content_id: '1197218', - content_type: 'dress', - price: 30, - quantity: 1, - }, + { content_id: '1077218', content_type: 'socks', price: 8, quantity: 2 }, + { content_id: '1197218', content_type: 'dress', price: 30, quantity: 1 }, ], currency: 'USD', value: 46, @@ -764,7 +634,7 @@ export const data = [ destination: { Config: { accessToken: 'dummyAccessToken', - pixelCode: 'A1T8T4UYGVIQA8ORZMX9', + pixelCode: 'dummyPixelCode', hashUserProperties: true, }, }, @@ -780,4 +650,2108 @@ export const data = [ }, }, }, + { + name: 'tiktok_ads', + description: + 'Test 2 -> Events 2.0 Single event is mapped to multiple tiktok event in config and exceeding max batch size', + feature: FEATURES.ROUTER, + module: 'destination', + version: 'v0', + input: { + request: { + body: { + input: [ + { + message: { + anonymousId: '21e13f4bc7ceddad', + channel: 'web', + context: { + page: { + url: 'http://demo.mywebsite.com/purchase', + referrer: 'http://demo.mywebsite.com', + }, + userAgent: + 'Mozilla/5.0 (platform; rv:geckoversion) Gecko/geckotrail Firefox/firefoxversion', + ip: '13.57.97.131', + locale: 'en-US', + externalId: [{ type: 'tiktokExternalId', id: 'id5' }], + }, + messageId: '84e26acc-56a5-4835-8233-591137fca468', + session_id: '3049dc4c-5a95-4ccd-a3e7-d74a7e411f22', + originalTimestamp: '2019-10-14T09:03:17.562Z', + timestamp: '2020-09-17T19:49:27Z', + type: 'track', + event: 'checkout step completed', + properties: { + eventId: '1616318632825_357', + clickId: 'dummyClickId', + contents: [ + { price: 8, quantity: 2, content_type: 'socks', content_id: '1077218' }, + { price: 30, quantity: 1, content_type: 'dress', content_id: '1197218' }, + ], + currency: 'USD', + value: 46, + phone: '2f9d2b4df907e5c9a7b3434351b55700167b998a83dc479b825096486ffcf4ea', + email: 'f0e388f53921a51f0bb0fc8a2944109ec188b59172935d8f23020b1614cc44bc', + }, + integrations: { + All: true, + }, + sentAt: '2019-10-14T09:03:22.563Z', + }, + metadata: { + jobId: 5, + }, + destination: { + Config: { + accessToken: 'dummyAccessToken', + pixelCode: 'dummyPixelCode', + hashUserProperties: false, + version: 'v2', + eventsToStandard: [ + { from: 'addToCart', to: 'CompletePayment' }, + { from: 'addToCart', to: 'download' }, + ], + }, + }, + }, + { + message: { + anonymousId: '21e13f4bc7ceddad', + channel: 'web', + context: { + userAgent: + 'Mozilla/5.0 (platform; rv:geckoversion) Gecko/geckotrail Firefox/firefoxversion', + ip: '13.57.97.131', + locale: 'en-US', + externalId: [{ type: 'tiktokExternalId', id: 'id1' }], + }, + messageId: '84e26acc-56a5-4835-8233-591137fca468', + session_id: '3049dc4c-5a95-4ccd-a3e7-d74a7e411f22', + originalTimestamp: '2019-10-14T09:03:17.562Z', + timestamp: '2020-09-17T19:49:27Z', + type: 'track', + event: 'addToCart', + properties: { + eventId: '1616318632825_357', + clickId: 'dummyClickId', + contents: [ + { price: 8, quantity: 2, content_type: 'socks', content_id: '1077218' }, + { price: 30, quantity: 1, content_type: 'dress', content_id: '1197218' }, + ], + currency: 'USD', + value: 46, + url: 'http://demo.mywebsite.com/purchase', + phone: '2f9d2b4df907e5c9a7b3434351b55700167b998a83dc479b825096486ffcf4ea', + email: 'f0e388f53921a51f0bb0fc8a2944109ec188b59172935d8f23020b1614cc44bc', + }, + integrations: { + All: true, + }, + sentAt: '2019-10-14T09:03:22.563Z', + }, + metadata: { + jobId: 1, + }, + destination: { + Config: { + accessToken: 'dummyAccessToken', + pixelCode: 'dummyPixelCode', + hashUserProperties: false, + version: 'v2', + eventsToStandard: [ + { from: 'addToCart', to: 'CompletePayment' }, + { from: 'addToCart', to: 'download' }, + ], + }, + }, + }, + { + message: { + anonymousId: '21e13f4bc7ceddad', + channel: 'web', + context: { + userAgent: + 'Mozilla/5.0 (platform; rv:geckoversion) Gecko/geckotrail Firefox/firefoxversion', + ip: '13.57.97.131', + locale: 'en-US', + externalId: [{ type: 'tiktokExternalId', id: 'id2' }], + }, + messageId: '84e26acc-56a5-4835-8233-591137fca468', + session_id: '3049dc4c-5a95-4ccd-a3e7-d74a7e411f22', + originalTimestamp: '2019-10-14T09:03:17.562Z', + timestamp: '2020-09-17T19:49:27Z', + type: 'track', + event: 'checkout step completed', + properties: { + eventId: '1616318632825_357', + clickId: 'dummyClickId', + contents: [ + { price: 8, quantity: 2, content_type: 'socks', content_id: '1077218' }, + { price: 30, quantity: 1, content_type: 'dress', content_id: '1197218' }, + ], + currency: 'USD', + value: 46, + url: 'http://demo.mywebsite.com/purchase', + phone: '2f9d2b4df907e5c9a7b3434351b55700167b998a83dc479b825096486ffcf4ea', + email: 'f0e388f53921a51f0bb0fc8a2944109ec188b59172935d8f23020b1614cc44bc', + }, + integrations: { + All: true, + }, + sentAt: '2019-10-14T09:03:22.563Z', + }, + metadata: { + jobId: 2, + }, + destination: { + Config: { + accessToken: 'dummyAccessToken', + pixelCode: 'dummyPixelCode', + hashUserProperties: false, + version: 'v2', + }, + }, + }, + { + message: { + anonymousId: '21e13f4bc7ceddad', + channel: 'web', + context: { + userAgent: + 'Mozilla/5.0 (platform; rv:geckoversion) Gecko/geckotrail Firefox/firefoxversion', + ip: '13.57.97.131', + locale: 'en-US', + externalId: [{ type: 'tiktokExternalId', id: 'id4' }], + }, + messageId: '84e26acc-56a5-4835-8233-591137fca468', + session_id: '3049dc4c-5a95-4ccd-a3e7-d74a7e411f22', + originalTimestamp: '2019-10-14T09:03:17.562Z', + timestamp: '2020-09-17T19:49:27Z', + type: 'track', + event: 'checkout step completed', + properties: { + eventId: '1616318632825_357', + clickId: 'dummyClickId', + contents: [ + { price: 8, quantity: 2, content_type: 'socks', content_id: '1077218' }, + { price: 30, quantity: 1, content_type: 'dress', content_id: '1197218' }, + ], + currency: 'USD', + value: 46, + url: 'http://demo.mywebsite.com/purchase', + phone: '2f9d2b4df907e5c9a7b3434351b55700167b998a83dc479b825096486ffcf4ea', + email: 'f0e388f53921a51f0bb0fc8a2944109ec188b59172935d8f23020b1614cc44bc', + }, + integrations: { + All: true, + }, + sentAt: '2019-10-14T09:03:22.563Z', + }, + metadata: { + jobId: 4, + }, + destination: { + Config: { + accessToken: 'dummyAccessToken', + pixelCode: 'dummyPixelCode', + hashUserProperties: false, + version: 'v2', + }, + }, + }, + ], + destType: 'tiktok_ads', + }, + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: [ + { + batchedRequest: { + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://business-api.tiktok.com/open_api/v1.3/event/track/', + headers: { + 'Access-Token': 'dummyAccessToken', + 'Content-Type': 'application/json', + }, + params: {}, + body: { + JSON: { + event_source_id: 'dummyPixelCode', + event_source: 'web', + partner_name: 'RudderStack', + data: [ + { + event_id: '1616318632825_357', + event_time: 1600372167, + properties: { + contents: [ + { price: 8, quantity: 2, content_type: 'socks', content_id: '1077218' }, + { + price: 30, + quantity: 1, + content_type: 'dress', + content_id: '1197218', + }, + ], + content_type: 'product', + currency: 'USD', + value: 46, + }, + page: { + url: 'http://demo.mywebsite.com/purchase', + referrer: 'http://demo.mywebsite.com', + }, + user: { + locale: 'en-US', + email: 'f0e388f53921a51f0bb0fc8a2944109ec188b59172935d8f23020b1614cc44bc', + phone: '2f9d2b4df907e5c9a7b3434351b55700167b998a83dc479b825096486ffcf4ea', + ip: '13.57.97.131', + user_agent: + 'Mozilla/5.0 (platform; rv:geckoversion) Gecko/geckotrail Firefox/firefoxversion', + external_id: 'id5', + }, + event: 'CompletePayment', + }, + { + event_id: '1616318632825_357', + event_time: 1600372167, + properties: { + contents: [ + { price: 8, quantity: 2, content_type: 'socks', content_id: '1077218' }, + { + price: 30, + quantity: 1, + content_type: 'dress', + content_id: '1197218', + }, + ], + content_type: 'product', + currency: 'USD', + value: 46, + }, + page: { url: 'http://demo.mywebsite.com/purchase' }, + user: { + locale: 'en-US', + email: 'f0e388f53921a51f0bb0fc8a2944109ec188b59172935d8f23020b1614cc44bc', + phone: '2f9d2b4df907e5c9a7b3434351b55700167b998a83dc479b825096486ffcf4ea', + ip: '13.57.97.131', + user_agent: + 'Mozilla/5.0 (platform; rv:geckoversion) Gecko/geckotrail Firefox/firefoxversion', + external_id: 'id1', + }, + event: 'CompletePayment', + }, + { + event_id: '1616318632825_357', + event_time: 1600372167, + properties: { + contents: [ + { price: 8, quantity: 2, content_type: 'socks', content_id: '1077218' }, + { + price: 30, + quantity: 1, + content_type: 'dress', + content_id: '1197218', + }, + ], + content_type: 'product', + currency: 'USD', + value: 46, + }, + page: { url: 'http://demo.mywebsite.com/purchase' }, + user: { + locale: 'en-US', + email: 'f0e388f53921a51f0bb0fc8a2944109ec188b59172935d8f23020b1614cc44bc', + phone: '2f9d2b4df907e5c9a7b3434351b55700167b998a83dc479b825096486ffcf4ea', + ip: '13.57.97.131', + user_agent: + 'Mozilla/5.0 (platform; rv:geckoversion) Gecko/geckotrail Firefox/firefoxversion', + external_id: 'id1', + }, + event: 'download', + }, + ], + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + }, + metadata: [ + { + jobId: 5, + }, + { + jobId: 1, + }, + ], + batched: true, + statusCode: 200, + destination: { + Config: { + accessToken: 'dummyAccessToken', + pixelCode: 'dummyPixelCode', + hashUserProperties: false, + version: 'v2', + eventsToStandard: [ + { from: 'addToCart', to: 'CompletePayment' }, + { from: 'addToCart', to: 'download' }, + ], + }, + }, + }, + { + batchedRequest: { + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://business-api.tiktok.com/open_api/v1.3/event/track/', + headers: { + 'Access-Token': 'dummyAccessToken', + 'Content-Type': 'application/json', + }, + params: {}, + body: { + JSON: { + event_source_id: 'dummyPixelCode', + event_source: 'web', + partner_name: 'RudderStack', + data: [ + { + event_id: '1616318632825_357', + event_time: 1600372167, + properties: { + contents: [ + { price: 8, quantity: 2, content_type: 'socks', content_id: '1077218' }, + { + price: 30, + quantity: 1, + content_type: 'dress', + content_id: '1197218', + }, + ], + content_type: 'product', + currency: 'USD', + value: 46, + }, + page: { url: 'http://demo.mywebsite.com/purchase' }, + user: { + locale: 'en-US', + email: 'f0e388f53921a51f0bb0fc8a2944109ec188b59172935d8f23020b1614cc44bc', + phone: '2f9d2b4df907e5c9a7b3434351b55700167b998a83dc479b825096486ffcf4ea', + ip: '13.57.97.131', + user_agent: + 'Mozilla/5.0 (platform; rv:geckoversion) Gecko/geckotrail Firefox/firefoxversion', + external_id: 'id2', + }, + event: 'CompletePayment', + }, + { + event_id: '1616318632825_357', + event_time: 1600372167, + properties: { + contents: [ + { price: 8, quantity: 2, content_type: 'socks', content_id: '1077218' }, + { + price: 30, + quantity: 1, + content_type: 'dress', + content_id: '1197218', + }, + ], + content_type: 'product', + currency: 'USD', + value: 46, + }, + page: { url: 'http://demo.mywebsite.com/purchase' }, + user: { + locale: 'en-US', + email: 'f0e388f53921a51f0bb0fc8a2944109ec188b59172935d8f23020b1614cc44bc', + phone: '2f9d2b4df907e5c9a7b3434351b55700167b998a83dc479b825096486ffcf4ea', + ip: '13.57.97.131', + user_agent: + 'Mozilla/5.0 (platform; rv:geckoversion) Gecko/geckotrail Firefox/firefoxversion', + external_id: 'id4', + }, + event: 'CompletePayment', + }, + ], + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + }, + metadata: [ + { + jobId: 2, + }, + { + jobId: 4, + }, + ], + batched: true, + statusCode: 200, + destination: { + Config: { + accessToken: 'dummyAccessToken', + pixelCode: 'dummyPixelCode', + hashUserProperties: false, + version: 'v2', + eventsToStandard: [ + { from: 'addToCart', to: 'CompletePayment' }, + { from: 'addToCart', to: 'download' }, + ], + }, + }, + }, + ], + }, + }, + }, + mockFns: defaultMockFns, + }, + { + name: 'tiktok_ads', + description: + 'Test 3 -> Events 2.0 Single event is mapped to single tiktok event in config and over the max batch limit', + feature: FEATURES.ROUTER, + module: 'destination', + version: 'v0', + input: { + request: { + body: { + input: [ + { + message: { + anonymousId: '21e13f4bc7ceddad', + channel: 'web', + context: { + page: { + url: 'http://demo.mywebsite.com/purchase', + referrer: 'http://demo.mywebsite.com', + }, + userAgent: + 'Mozilla/5.0 (platform; rv:geckoversion) Gecko/geckotrail Firefox/firefoxversion', + ip: '13.57.97.131', + locale: 'en-US', + externalId: [{ type: 'tiktokExternalId', id: 'id5' }], + }, + messageId: '84e26acc-56a5-4835-8233-591137fca468', + session_id: '3049dc4c-5a95-4ccd-a3e7-d74a7e411f22', + originalTimestamp: '2019-10-14T09:03:17.562Z', + timestamp: '2020-09-17T19:49:27Z', + type: 'track', + event: 'checkout step completed', + properties: { + eventId: '1616318632825_357', + clickId: 'dummyClickId', + contents: [ + { price: 8, quantity: 2, content_type: 'socks', content_id: '1077218' }, + { price: 30, quantity: 1, content_type: 'dress', content_id: '1197218' }, + ], + currency: 'USD', + value: 46, + phone: '2f9d2b4df907e5c9a7b3434351b55700167b998a83dc479b825096486ffcf4ea', + email: 'f0e388f53921a51f0bb0fc8a2944109ec188b59172935d8f23020b1614cc44bc', + }, + integrations: { + All: true, + }, + sentAt: '2019-10-14T09:03:22.563Z', + }, + metadata: { + jobId: 5, + }, + destination: { + Config: { + accessToken: 'dummyAccessToken', + pixelCode: 'dummyPixelCode', + hashUserProperties: false, + version: 'v2', + }, + }, + }, + { + message: { + anonymousId: '21e13f4bc7ceddad', + channel: 'web', + context: { + userAgent: + 'Mozilla/5.0 (platform; rv:geckoversion) Gecko/geckotrail Firefox/firefoxversion', + ip: '13.57.97.131', + locale: 'en-US', + externalId: [{ type: 'tiktokExternalId', id: 'id1' }], + }, + messageId: '84e26acc-56a5-4835-8233-591137fca468', + session_id: '3049dc4c-5a95-4ccd-a3e7-d74a7e411f22', + originalTimestamp: '2019-10-14T09:03:17.562Z', + timestamp: '2020-09-17T19:49:27Z', + type: 'track', + event: 'checkout step completed', + properties: { + eventId: '1616318632825_357', + clickId: 'dummyClickId', + contents: [ + { price: 8, quantity: 2, content_type: 'socks', content_id: '1077218' }, + { price: 30, quantity: 1, content_type: 'dress', content_id: '1197218' }, + ], + currency: 'USD', + value: 46, + url: 'http://demo.mywebsite.com/purchase', + phone: '2f9d2b4df907e5c9a7b3434351b55700167b998a83dc479b825096486ffcf4ea', + email: 'f0e388f53921a51f0bb0fc8a2944109ec188b59172935d8f23020b1614cc44bc', + }, + integrations: { + All: true, + }, + sentAt: '2019-10-14T09:03:22.563Z', + }, + metadata: { + jobId: 1, + }, + destination: { + Config: { + accessToken: 'dummyAccessToken', + pixelCode: 'dummyPixelCode', + hashUserProperties: false, + version: 'v2', + }, + }, + }, + { + message: { + anonymousId: '21e13f4bc7ceddad', + channel: 'web', + context: { + userAgent: + 'Mozilla/5.0 (platform; rv:geckoversion) Gecko/geckotrail Firefox/firefoxversion', + ip: '13.57.97.131', + locale: 'en-US', + externalId: [{ type: 'tiktokExternalId', id: 'id2' }], + }, + messageId: '84e26acc-56a5-4835-8233-591137fca468', + session_id: '3049dc4c-5a95-4ccd-a3e7-d74a7e411f22', + originalTimestamp: '2019-10-14T09:03:17.562Z', + timestamp: '2020-09-17T19:49:27Z', + type: 'track', + event: 'checkout step completed', + properties: { + eventId: '1616318632825_357', + clickId: 'dummyClickId', + contents: [ + { price: 8, quantity: 2, content_type: 'socks', content_id: '1077218' }, + { price: 30, quantity: 1, content_type: 'dress', content_id: '1197218' }, + ], + currency: 'USD', + value: 46, + url: 'http://demo.mywebsite.com/purchase', + phone: '2f9d2b4df907e5c9a7b3434351b55700167b998a83dc479b825096486ffcf4ea', + email: 'f0e388f53921a51f0bb0fc8a2944109ec188b59172935d8f23020b1614cc44bc', + }, + integrations: { + All: true, + }, + sentAt: '2019-10-14T09:03:22.563Z', + }, + metadata: { + jobId: 2, + }, + destination: { + Config: { + accessToken: 'dummyAccessToken', + pixelCode: 'dummyPixelCode', + hashUserProperties: false, + version: 'v2', + }, + }, + }, + { + message: { + anonymousId: '21e13f4bc7ceddad', + channel: 'web', + context: { + userAgent: + 'Mozilla/5.0 (platform; rv:geckoversion) Gecko/geckotrail Firefox/firefoxversion', + ip: '13.57.97.131', + locale: 'en-US', + externalId: [{ type: 'tiktokExternalId', id: 'id4' }], + }, + messageId: '84e26acc-56a5-4835-8233-591137fca468', + session_id: '3049dc4c-5a95-4ccd-a3e7-d74a7e411f22', + originalTimestamp: '2019-10-14T09:03:17.562Z', + timestamp: '2020-09-17T19:49:27Z', + type: 'track', + event: 'checkout step completed', + properties: { + eventId: '1616318632825_357', + clickId: 'dummyClickId', + contents: [ + { price: 8, quantity: 2, content_type: 'socks', content_id: '1077218' }, + { price: 30, quantity: 1, content_type: 'dress', content_id: '1197218' }, + ], + currency: 'USD', + value: 46, + url: 'http://demo.mywebsite.com/purchase', + phone: '2f9d2b4df907e5c9a7b3434351b55700167b998a83dc479b825096486ffcf4ea', + email: 'f0e388f53921a51f0bb0fc8a2944109ec188b59172935d8f23020b1614cc44bc', + }, + integrations: { + All: true, + }, + sentAt: '2019-10-14T09:03:22.563Z', + }, + metadata: { + jobId: 4, + }, + destination: { + Config: { + accessToken: 'dummyAccessToken', + pixelCode: 'dummyPixelCode', + hashUserProperties: false, + version: 'v2', + }, + }, + }, + ], + destType: 'tiktok_ads', + }, + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: [ + { + batchedRequest: { + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://business-api.tiktok.com/open_api/v1.3/event/track/', + headers: { + 'Access-Token': 'dummyAccessToken', + 'Content-Type': 'application/json', + }, + params: {}, + body: { + JSON: { + event_source_id: 'dummyPixelCode', + event_source: 'web', + partner_name: 'RudderStack', + data: [ + { + event_id: '1616318632825_357', + event_time: 1600372167, + properties: { + contents: [ + { price: 8, quantity: 2, content_type: 'socks', content_id: '1077218' }, + { + price: 30, + quantity: 1, + content_type: 'dress', + content_id: '1197218', + }, + ], + content_type: 'product', + currency: 'USD', + value: 46, + }, + page: { + url: 'http://demo.mywebsite.com/purchase', + referrer: 'http://demo.mywebsite.com', + }, + user: { + locale: 'en-US', + email: 'f0e388f53921a51f0bb0fc8a2944109ec188b59172935d8f23020b1614cc44bc', + phone: '2f9d2b4df907e5c9a7b3434351b55700167b998a83dc479b825096486ffcf4ea', + ip: '13.57.97.131', + user_agent: + 'Mozilla/5.0 (platform; rv:geckoversion) Gecko/geckotrail Firefox/firefoxversion', + external_id: 'id5', + }, + event: 'CompletePayment', + }, + { + event_id: '1616318632825_357', + event_time: 1600372167, + properties: { + contents: [ + { price: 8, quantity: 2, content_type: 'socks', content_id: '1077218' }, + { + price: 30, + quantity: 1, + content_type: 'dress', + content_id: '1197218', + }, + ], + content_type: 'product', + currency: 'USD', + value: 46, + }, + page: { url: 'http://demo.mywebsite.com/purchase' }, + user: { + locale: 'en-US', + email: 'f0e388f53921a51f0bb0fc8a2944109ec188b59172935d8f23020b1614cc44bc', + phone: '2f9d2b4df907e5c9a7b3434351b55700167b998a83dc479b825096486ffcf4ea', + ip: '13.57.97.131', + user_agent: + 'Mozilla/5.0 (platform; rv:geckoversion) Gecko/geckotrail Firefox/firefoxversion', + external_id: 'id1', + }, + event: 'CompletePayment', + }, + { + event_id: '1616318632825_357', + event_time: 1600372167, + properties: { + contents: [ + { price: 8, quantity: 2, content_type: 'socks', content_id: '1077218' }, + { + price: 30, + quantity: 1, + content_type: 'dress', + content_id: '1197218', + }, + ], + content_type: 'product', + currency: 'USD', + value: 46, + }, + page: { url: 'http://demo.mywebsite.com/purchase' }, + user: { + locale: 'en-US', + email: 'f0e388f53921a51f0bb0fc8a2944109ec188b59172935d8f23020b1614cc44bc', + phone: '2f9d2b4df907e5c9a7b3434351b55700167b998a83dc479b825096486ffcf4ea', + ip: '13.57.97.131', + user_agent: + 'Mozilla/5.0 (platform; rv:geckoversion) Gecko/geckotrail Firefox/firefoxversion', + external_id: 'id2', + }, + event: 'CompletePayment', + }, + ], + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + }, + metadata: [ + { + jobId: 5, + }, + { + jobId: 1, + }, + { + jobId: 2, + }, + ], + batched: true, + statusCode: 200, + destination: { + Config: { + accessToken: 'dummyAccessToken', + pixelCode: 'dummyPixelCode', + hashUserProperties: false, + version: 'v2', + }, + }, + }, + { + batchedRequest: { + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://business-api.tiktok.com/open_api/v1.3/event/track/', + headers: { + 'Access-Token': 'dummyAccessToken', + 'Content-Type': 'application/json', + }, + params: {}, + body: { + JSON: { + event_source_id: 'dummyPixelCode', + event_source: 'web', + partner_name: 'RudderStack', + data: [ + { + event_id: '1616318632825_357', + event_time: 1600372167, + properties: { + contents: [ + { price: 8, quantity: 2, content_type: 'socks', content_id: '1077218' }, + { + price: 30, + quantity: 1, + content_type: 'dress', + content_id: '1197218', + }, + ], + content_type: 'product', + currency: 'USD', + value: 46, + }, + page: { url: 'http://demo.mywebsite.com/purchase' }, + user: { + locale: 'en-US', + email: 'f0e388f53921a51f0bb0fc8a2944109ec188b59172935d8f23020b1614cc44bc', + phone: '2f9d2b4df907e5c9a7b3434351b55700167b998a83dc479b825096486ffcf4ea', + ip: '13.57.97.131', + user_agent: + 'Mozilla/5.0 (platform; rv:geckoversion) Gecko/geckotrail Firefox/firefoxversion', + external_id: 'id4', + }, + event: 'CompletePayment', + }, + ], + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + }, + metadata: [ + { + jobId: 4, + }, + ], + batched: true, + statusCode: 200, + destination: { + Config: { + accessToken: 'dummyAccessToken', + pixelCode: 'dummyPixelCode', + hashUserProperties: false, + version: 'v2', + }, + }, + }, + ], + }, + }, + }, + mockFns: defaultMockFns, + }, + { + name: 'tiktok_ads', + description: 'Test 4 -> One input event is invalid with one event to multiple events', + feature: FEATURES.ROUTER, + module: 'destination', + version: 'v0', + input: { + request: { + body: { + input: [ + { + message: { + anonymousId: '21e13f4bc7ceddad', + channel: 'web', + context: { + page: { + url: 'http://demo.mywebsite.com/purchase', + referrer: 'http://demo.mywebsite.com', + }, + userAgent: + 'Mozilla/5.0 (platform; rv:geckoversion) Gecko/geckotrail Firefox/firefoxversion', + ip: '13.57.97.131', + locale: 'en-US', + externalId: [{ type: 'tiktokExternalId', id: 'id5' }], + }, + messageId: '84e26acc-56a5-4835-8233-591137fca468', + session_id: '3049dc4c-5a95-4ccd-a3e7-d74a7e411f22', + originalTimestamp: '2019-10-14T09:03:17.562Z', + timestamp: '2020-09-17T19:49:27Z', + type: 'track', + event: 'checkout step completed', + properties: { + eventId: '1616318632825_357', + clickId: 'dummyClickId', + contents: [ + { price: 8, quantity: 2, content_type: 'socks', content_id: '1077218' }, + { price: 30, quantity: 1, content_type: 'dress', content_id: '1197218' }, + ], + currency: 'USD', + value: 46, + phone: '2f9d2b4df907e5c9a7b3434351b55700167b998a83dc479b825096486ffcf4ea', + email: 'f0e388f53921a51f0bb0fc8a2944109ec188b59172935d8f23020b1614cc44bc', + }, + integrations: { + All: true, + }, + sentAt: '2019-10-14T09:03:22.563Z', + }, + metadata: { + jobId: 5, + }, + destination: { + Config: { + accessToken: 'dummyAccessToken', + pixelCode: 'dummyPixelCode', + hashUserProperties: false, + version: 'v2', + eventsToStandard: [ + { from: 'addToCart', to: 'CompletePayment' }, + { from: 'addToCart', to: 'download' }, + ], + }, + }, + }, + { + message: { + anonymousId: '21e13f4bc7ceddad', + channel: 'web', + context: { + userAgent: + 'Mozilla/5.0 (platform; rv:geckoversion) Gecko/geckotrail Firefox/firefoxversion', + ip: '13.57.97.131', + locale: 'en-US', + externalId: [{ type: 'tiktokExternalId', id: 'id1' }], + }, + messageId: '84e26acc-56a5-4835-8233-591137fca468', + session_id: '3049dc4c-5a95-4ccd-a3e7-d74a7e411f22', + originalTimestamp: '2019-10-14T09:03:17.562Z', + timestamp: '2020-09-17T19:49:27Z', + type: 'track', + event: 'abc', + properties: { + eventId: '1616318632825_357', + clickId: 'dummyClickId', + contents: [ + { price: 8, quantity: 2, content_type: 'socks', content_id: '1077218' }, + { price: 30, quantity: 1, content_type: 'dress', content_id: '1197218' }, + ], + currency: 'USD', + value: 46, + url: 'http://demo.mywebsite.com/purchase', + phone: '2f9d2b4df907e5c9a7b3434351b55700167b998a83dc479b825096486ffcf4ea', + email: 'f0e388f53921a51f0bb0fc8a2944109ec188b59172935d8f23020b1614cc44bc', + }, + integrations: { + All: true, + }, + sentAt: '2019-10-14T09:03:22.563Z', + }, + metadata: { + jobId: 1, + }, + destination: { + Config: { + accessToken: 'dummyAccessToken', + pixelCode: 'dummyPixelCode', + hashUserProperties: false, + version: 'v2', + eventsToStandard: [ + { from: 'addToCart', to: 'CompletePayment' }, + { from: 'addToCart', to: 'download' }, + ], + }, + }, + }, + { + message: { + anonymousId: '21e13f4bc7ceddad', + channel: 'web', + context: { + userAgent: + 'Mozilla/5.0 (platform; rv:geckoversion) Gecko/geckotrail Firefox/firefoxversion', + ip: '13.57.97.131', + locale: 'en-US', + externalId: [{ type: 'tiktokExternalId', id: 'id2' }], + }, + messageId: '84e26acc-56a5-4835-8233-591137fca468', + session_id: '3049dc4c-5a95-4ccd-a3e7-d74a7e411f22', + originalTimestamp: '2019-10-14T09:03:17.562Z', + timestamp: '2020-09-17T19:49:27Z', + type: 'track', + event: 'checkout step completed', + properties: { + eventId: '1616318632825_357', + clickId: 'dummyClickId', + contents: [ + { price: 8, quantity: 2, content_type: 'socks', content_id: '1077218' }, + { price: 30, quantity: 1, content_type: 'dress', content_id: '1197218' }, + ], + currency: 'USD', + value: 46, + url: 'http://demo.mywebsite.com/purchase', + phone: '2f9d2b4df907e5c9a7b3434351b55700167b998a83dc479b825096486ffcf4ea', + email: 'f0e388f53921a51f0bb0fc8a2944109ec188b59172935d8f23020b1614cc44bc', + }, + integrations: { + All: true, + }, + sentAt: '2019-10-14T09:03:22.563Z', + }, + metadata: { + jobId: 2, + }, + destination: { + Config: { + accessToken: 'dummyAccessToken', + pixelCode: 'dummyPixelCode', + hashUserProperties: false, + version: 'v2', + }, + }, + }, + { + message: { + anonymousId: '21e13f4bc7ceddad', + channel: 'web', + context: { + userAgent: + 'Mozilla/5.0 (platform; rv:geckoversion) Gecko/geckotrail Firefox/firefoxversion', + ip: '13.57.97.131', + locale: 'en-US', + externalId: [{ type: 'tiktokExternalId', id: 'id4' }], + }, + messageId: '84e26acc-56a5-4835-8233-591137fca468', + session_id: '3049dc4c-5a95-4ccd-a3e7-d74a7e411f22', + originalTimestamp: '2019-10-14T09:03:17.562Z', + timestamp: '2020-09-17T19:49:27Z', + type: 'track', + event: 'checkout step completed', + properties: { + eventId: '1616318632825_357', + clickId: 'dummyClickId', + contents: [ + { price: 8, quantity: 2, content_type: 'socks', content_id: '1077218' }, + { price: 30, quantity: 1, content_type: 'dress', content_id: '1197218' }, + ], + currency: 'USD', + value: 46, + url: 'http://demo.mywebsite.com/purchase', + phone: '2f9d2b4df907e5c9a7b3434351b55700167b998a83dc479b825096486ffcf4ea', + email: 'f0e388f53921a51f0bb0fc8a2944109ec188b59172935d8f23020b1614cc44bc', + }, + integrations: { + All: true, + }, + sentAt: '2019-10-14T09:03:22.563Z', + }, + metadata: { + jobId: 4, + }, + destination: { + Config: { + accessToken: 'dummyAccessToken', + pixelCode: 'dummyPixelCode', + hashUserProperties: false, + version: 'v2', + }, + }, + }, + ], + destType: 'tiktok_ads', + }, + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: [ + { + batchedRequest: { + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://business-api.tiktok.com/open_api/v1.3/event/track/', + headers: { + 'Access-Token': 'dummyAccessToken', + 'Content-Type': 'application/json', + }, + params: {}, + body: { + JSON: { + event_source_id: 'dummyPixelCode', + event_source: 'web', + partner_name: 'RudderStack', + data: [ + { + event_id: '1616318632825_357', + event_time: 1600372167, + properties: { + contents: [ + { price: 8, quantity: 2, content_type: 'socks', content_id: '1077218' }, + { + price: 30, + quantity: 1, + content_type: 'dress', + content_id: '1197218', + }, + ], + content_type: 'product', + currency: 'USD', + value: 46, + }, + page: { + url: 'http://demo.mywebsite.com/purchase', + referrer: 'http://demo.mywebsite.com', + }, + user: { + locale: 'en-US', + email: 'f0e388f53921a51f0bb0fc8a2944109ec188b59172935d8f23020b1614cc44bc', + phone: '2f9d2b4df907e5c9a7b3434351b55700167b998a83dc479b825096486ffcf4ea', + ip: '13.57.97.131', + user_agent: + 'Mozilla/5.0 (platform; rv:geckoversion) Gecko/geckotrail Firefox/firefoxversion', + external_id: 'id5', + }, + event: 'CompletePayment', + }, + { + event_id: '1616318632825_357', + event_time: 1600372167, + properties: { + contents: [ + { price: 8, quantity: 2, content_type: 'socks', content_id: '1077218' }, + { + price: 30, + quantity: 1, + content_type: 'dress', + content_id: '1197218', + }, + ], + content_type: 'product', + currency: 'USD', + value: 46, + }, + page: { url: 'http://demo.mywebsite.com/purchase' }, + user: { + locale: 'en-US', + email: 'f0e388f53921a51f0bb0fc8a2944109ec188b59172935d8f23020b1614cc44bc', + phone: '2f9d2b4df907e5c9a7b3434351b55700167b998a83dc479b825096486ffcf4ea', + ip: '13.57.97.131', + user_agent: + 'Mozilla/5.0 (platform; rv:geckoversion) Gecko/geckotrail Firefox/firefoxversion', + external_id: 'id2', + }, + event: 'CompletePayment', + }, + { + event_id: '1616318632825_357', + event_time: 1600372167, + properties: { + contents: [ + { price: 8, quantity: 2, content_type: 'socks', content_id: '1077218' }, + { + price: 30, + quantity: 1, + content_type: 'dress', + content_id: '1197218', + }, + ], + content_type: 'product', + currency: 'USD', + value: 46, + }, + page: { url: 'http://demo.mywebsite.com/purchase' }, + user: { + locale: 'en-US', + email: 'f0e388f53921a51f0bb0fc8a2944109ec188b59172935d8f23020b1614cc44bc', + phone: '2f9d2b4df907e5c9a7b3434351b55700167b998a83dc479b825096486ffcf4ea', + ip: '13.57.97.131', + user_agent: + 'Mozilla/5.0 (platform; rv:geckoversion) Gecko/geckotrail Firefox/firefoxversion', + external_id: 'id4', + }, + event: 'CompletePayment', + }, + ], + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + }, + metadata: [ + { + jobId: 5, + }, + { + jobId: 2, + }, + { + jobId: 4, + }, + ], + batched: true, + statusCode: 200, + destination: { + Config: { + accessToken: 'dummyAccessToken', + pixelCode: 'dummyPixelCode', + hashUserProperties: false, + version: 'v2', + eventsToStandard: [ + { from: 'addToCart', to: 'CompletePayment' }, + { from: 'addToCart', to: 'download' }, + ], + }, + }, + }, + { + batched: false, + destination: { + Config: { + accessToken: 'dummyAccessToken', + eventsToStandard: [ + { from: 'addToCart', to: 'CompletePayment' }, + { from: 'addToCart', to: 'download' }, + ], + hashUserProperties: false, + pixelCode: 'dummyPixelCode', + version: 'v2', + }, + }, + error: 'Event name (abc) is not valid, must be mapped to one of standard events', + metadata: [ + { + jobId: 1, + }, + ], + statTags: { + destType: 'TIKTOK_ADS', + feature: 'router', + implementation: 'native', + module: 'destination', + errorCategory: 'dataValidation', + errorType: 'instrumentation', + }, + statusCode: 400, + }, + ], + }, + }, + }, + mockFns: defaultMockFns, + }, + { + name: 'tiktok_ads', + description: 'Test 5 -> Some input events are test events with one event to multiple events', + feature: FEATURES.ROUTER, + module: 'destination', + version: 'v0', + input: { + request: { + body: { + input: [ + { + message: { + anonymousId: '21e13f4bc7ceddad', + channel: 'web', + context: { + page: { + url: 'http://demo.mywebsite.com/purchase', + referrer: 'http://demo.mywebsite.com', + }, + userAgent: + 'Mozilla/5.0 (platform; rv:geckoversion) Gecko/geckotrail Firefox/firefoxversion', + ip: '13.57.97.131', + locale: 'en-US', + externalId: [{ type: 'tiktokExternalId', id: 'id5' }], + }, + messageId: '84e26acc-56a5-4835-8233-591137fca468', + session_id: '3049dc4c-5a95-4ccd-a3e7-d74a7e411f22', + originalTimestamp: '2019-10-14T09:03:17.562Z', + timestamp: '2020-09-17T19:49:27Z', + type: 'track', + event: 'checkout step completed', + properties: { + eventId: '1616318632825_357', + clickId: 'dummyClickId', + contents: [ + { price: 8, quantity: 2, content_type: 'socks', content_id: '1077218' }, + { price: 30, quantity: 1, content_type: 'dress', content_id: '1197218' }, + ], + currency: 'USD', + value: 46, + phone: '2f9d2b4df907e5c9a7b3434351b55700167b998a83dc479b825096486ffcf4ea', + email: 'f0e388f53921a51f0bb0fc8a2944109ec188b59172935d8f23020b1614cc44bc', + }, + integrations: { + All: true, + }, + sentAt: '2019-10-14T09:03:22.563Z', + }, + metadata: { + jobId: 5, + }, + destination: { + Config: { + accessToken: 'dummyAccessToken', + pixelCode: 'dummyPixelCode', + hashUserProperties: false, + version: 'v2', + eventsToStandard: [ + { from: 'addToCart', to: 'CompletePayment' }, + { from: 'addToCart', to: 'download' }, + ], + }, + }, + }, + { + message: { + anonymousId: '21e13f4bc7ceddad', + channel: 'web', + context: { + userAgent: + 'Mozilla/5.0 (platform; rv:geckoversion) Gecko/geckotrail Firefox/firefoxversion', + ip: '13.57.97.131', + locale: 'en-US', + externalId: [{ type: 'tiktokExternalId', id: 'id1' }], + }, + messageId: '84e26acc-56a5-4835-8233-591137fca468', + session_id: '3049dc4c-5a95-4ccd-a3e7-d74a7e411f22', + originalTimestamp: '2019-10-14T09:03:17.562Z', + timestamp: '2020-09-17T19:49:27Z', + type: 'track', + event: 'addToCart', + properties: { + eventId: '1616318632825_357', + clickId: 'dummyClickId', + contents: [ + { price: 8, quantity: 2, content_type: 'socks', content_id: '1077218' }, + { price: 30, quantity: 1, content_type: 'dress', content_id: '1197218' }, + ], + currency: 'USD', + value: 46, + url: 'http://demo.mywebsite.com/purchase', + phone: '2f9d2b4df907e5c9a7b3434351b55700167b998a83dc479b825096486ffcf4ea', + email: 'f0e388f53921a51f0bb0fc8a2944109ec188b59172935d8f23020b1614cc44bc', + }, + integrations: { + All: true, + }, + sentAt: '2019-10-14T09:03:22.563Z', + }, + metadata: { + jobId: 1, + }, + destination: { + Config: { + accessToken: 'dummyAccessToken', + pixelCode: 'dummyPixelCode', + hashUserProperties: false, + version: 'v2', + eventsToStandard: [ + { from: 'addToCart', to: 'CompletePayment' }, + { from: 'addToCart', to: 'download' }, + ], + }, + }, + }, + { + message: { + anonymousId: '21e13f4bc7ceddad', + channel: 'web', + context: { + userAgent: + 'Mozilla/5.0 (platform; rv:geckoversion) Gecko/geckotrail Firefox/firefoxversion', + ip: '13.57.97.131', + locale: 'en-US', + externalId: [{ type: 'tiktokExternalId', id: 'id2' }], + }, + messageId: '84e26acc-56a5-4835-8233-591137fca468', + session_id: '3049dc4c-5a95-4ccd-a3e7-d74a7e411f22', + originalTimestamp: '2019-10-14T09:03:17.562Z', + timestamp: '2020-09-17T19:49:27Z', + type: 'track', + event: 'checkout step completed', + properties: { + testEventCode: 'Some test event code for testing setup', + eventId: '1616318632825_357', + clickId: 'dummyClickId', + contents: [ + { price: 8, quantity: 2, content_type: 'socks', content_id: '1077218' }, + { price: 30, quantity: 1, content_type: 'dress', content_id: '1197218' }, + ], + currency: 'USD', + value: 46, + url: 'http://demo.mywebsite.com/purchase', + phone: '2f9d2b4df907e5c9a7b3434351b55700167b998a83dc479b825096486ffcf4ea', + email: 'f0e388f53921a51f0bb0fc8a2944109ec188b59172935d8f23020b1614cc44bc', + }, + integrations: { + All: true, + }, + sentAt: '2019-10-14T09:03:22.563Z', + }, + metadata: { + jobId: 2, + }, + destination: { + Config: { + accessToken: 'dummyAccessToken', + pixelCode: 'dummyPixelCode', + hashUserProperties: false, + version: 'v2', + }, + }, + }, + { + message: { + anonymousId: '21e13f4bc7ceddad', + channel: 'web', + context: { + userAgent: + 'Mozilla/5.0 (platform; rv:geckoversion) Gecko/geckotrail Firefox/firefoxversion', + ip: '13.57.97.131', + locale: 'en-US', + externalId: [{ type: 'tiktokExternalId', id: 'id4' }], + }, + messageId: '84e26acc-56a5-4835-8233-591137fca468', + session_id: '3049dc4c-5a95-4ccd-a3e7-d74a7e411f22', + originalTimestamp: '2019-10-14T09:03:17.562Z', + timestamp: '2020-09-17T19:49:27Z', + type: 'track', + event: 'checkout step completed', + properties: { + eventId: '1616318632825_357', + clickId: 'dummyClickId', + contents: [ + { price: 8, quantity: 2, content_type: 'socks', content_id: '1077218' }, + { price: 30, quantity: 1, content_type: 'dress', content_id: '1197218' }, + ], + currency: 'USD', + value: 46, + url: 'http://demo.mywebsite.com/purchase', + phone: '2f9d2b4df907e5c9a7b3434351b55700167b998a83dc479b825096486ffcf4ea', + email: 'f0e388f53921a51f0bb0fc8a2944109ec188b59172935d8f23020b1614cc44bc', + }, + integrations: { + All: true, + }, + sentAt: '2019-10-14T09:03:22.563Z', + }, + metadata: { + jobId: 4, + }, + destination: { + Config: { + accessToken: 'dummyAccessToken', + pixelCode: 'dummyPixelCode', + hashUserProperties: false, + version: 'v2', + }, + }, + }, + ], + destType: 'tiktok_ads', + }, + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: [ + { + batchedRequest: { + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://business-api.tiktok.com/open_api/v1.3/event/track/', + headers: { + 'Access-Token': 'dummyAccessToken', + 'Content-Type': 'application/json', + }, + params: {}, + body: { + JSON: { + event_source_id: 'dummyPixelCode', + event_source: 'web', + partner_name: 'RudderStack', + data: [ + { + event_id: '1616318632825_357', + event_time: 1600372167, + properties: { + contents: [ + { price: 8, quantity: 2, content_type: 'socks', content_id: '1077218' }, + { + price: 30, + quantity: 1, + content_type: 'dress', + content_id: '1197218', + }, + ], + content_type: 'product', + currency: 'USD', + value: 46, + }, + page: { + url: 'http://demo.mywebsite.com/purchase', + referrer: 'http://demo.mywebsite.com', + }, + user: { + locale: 'en-US', + email: 'f0e388f53921a51f0bb0fc8a2944109ec188b59172935d8f23020b1614cc44bc', + phone: '2f9d2b4df907e5c9a7b3434351b55700167b998a83dc479b825096486ffcf4ea', + ip: '13.57.97.131', + user_agent: + 'Mozilla/5.0 (platform; rv:geckoversion) Gecko/geckotrail Firefox/firefoxversion', + external_id: 'id5', + }, + event: 'CompletePayment', + }, + { + event_id: '1616318632825_357', + event_time: 1600372167, + properties: { + contents: [ + { price: 8, quantity: 2, content_type: 'socks', content_id: '1077218' }, + { + price: 30, + quantity: 1, + content_type: 'dress', + content_id: '1197218', + }, + ], + content_type: 'product', + currency: 'USD', + value: 46, + }, + page: { url: 'http://demo.mywebsite.com/purchase' }, + user: { + locale: 'en-US', + email: 'f0e388f53921a51f0bb0fc8a2944109ec188b59172935d8f23020b1614cc44bc', + phone: '2f9d2b4df907e5c9a7b3434351b55700167b998a83dc479b825096486ffcf4ea', + ip: '13.57.97.131', + user_agent: + 'Mozilla/5.0 (platform; rv:geckoversion) Gecko/geckotrail Firefox/firefoxversion', + external_id: 'id1', + }, + event: 'CompletePayment', + }, + { + event_id: '1616318632825_357', + event_time: 1600372167, + properties: { + contents: [ + { price: 8, quantity: 2, content_type: 'socks', content_id: '1077218' }, + { + price: 30, + quantity: 1, + content_type: 'dress', + content_id: '1197218', + }, + ], + content_type: 'product', + currency: 'USD', + value: 46, + }, + page: { url: 'http://demo.mywebsite.com/purchase' }, + user: { + locale: 'en-US', + email: 'f0e388f53921a51f0bb0fc8a2944109ec188b59172935d8f23020b1614cc44bc', + phone: '2f9d2b4df907e5c9a7b3434351b55700167b998a83dc479b825096486ffcf4ea', + ip: '13.57.97.131', + user_agent: + 'Mozilla/5.0 (platform; rv:geckoversion) Gecko/geckotrail Firefox/firefoxversion', + external_id: 'id1', + }, + event: 'download', + }, + ], + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + }, + metadata: [ + { + jobId: 5, + }, + { + jobId: 1, + }, + ], + batched: true, + statusCode: 200, + destination: { + Config: { + accessToken: 'dummyAccessToken', + pixelCode: 'dummyPixelCode', + hashUserProperties: false, + version: 'v2', + eventsToStandard: [ + { from: 'addToCart', to: 'CompletePayment' }, + { from: 'addToCart', to: 'download' }, + ], + }, + }, + }, + { + batchedRequest: { + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://business-api.tiktok.com/open_api/v1.3/event/track/', + headers: { + 'Access-Token': 'dummyAccessToken', + 'Content-Type': 'application/json', + }, + params: {}, + body: { + JSON: { + event_source_id: 'dummyPixelCode', + event_source: 'web', + partner_name: 'RudderStack', + data: [ + { + event_id: '1616318632825_357', + event_time: 1600372167, + properties: { + contents: [ + { price: 8, quantity: 2, content_type: 'socks', content_id: '1077218' }, + { + price: 30, + quantity: 1, + content_type: 'dress', + content_id: '1197218', + }, + ], + content_type: 'product', + currency: 'USD', + value: 46, + }, + page: { url: 'http://demo.mywebsite.com/purchase' }, + user: { + locale: 'en-US', + email: 'f0e388f53921a51f0bb0fc8a2944109ec188b59172935d8f23020b1614cc44bc', + phone: '2f9d2b4df907e5c9a7b3434351b55700167b998a83dc479b825096486ffcf4ea', + ip: '13.57.97.131', + user_agent: + 'Mozilla/5.0 (platform; rv:geckoversion) Gecko/geckotrail Firefox/firefoxversion', + external_id: 'id4', + }, + event: 'CompletePayment', + }, + ], + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + }, + metadata: [ + { + jobId: 4, + }, + ], + batched: true, + statusCode: 200, + destination: { + Config: { + accessToken: 'dummyAccessToken', + pixelCode: 'dummyPixelCode', + hashUserProperties: false, + version: 'v2', + eventsToStandard: [ + { from: 'addToCart', to: 'CompletePayment' }, + { from: 'addToCart', to: 'download' }, + ], + }, + }, + }, + { + batchedRequest: [ + { + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://business-api.tiktok.com/open_api/v1.3/event/track/', + headers: { + 'Access-Token': 'dummyAccessToken', + 'Content-Type': 'application/json', + }, + params: {}, + body: { + JSON: { + event_source_id: 'dummyPixelCode', + event_source: 'web', + partner_name: 'RudderStack', + test_event_code: 'Some test event code for testing setup', + data: [ + { + event_id: '1616318632825_357', + event_time: 1600372167, + properties: { + contents: [ + { + price: 8, + quantity: 2, + content_type: 'socks', + content_id: '1077218', + }, + { + price: 30, + quantity: 1, + content_type: 'dress', + content_id: '1197218', + }, + ], + content_type: 'product', + currency: 'USD', + value: 46, + }, + page: { url: 'http://demo.mywebsite.com/purchase' }, + user: { + locale: 'en-US', + email: + 'f0e388f53921a51f0bb0fc8a2944109ec188b59172935d8f23020b1614cc44bc', + phone: + '2f9d2b4df907e5c9a7b3434351b55700167b998a83dc479b825096486ffcf4ea', + ip: '13.57.97.131', + user_agent: + 'Mozilla/5.0 (platform; rv:geckoversion) Gecko/geckotrail Firefox/firefoxversion', + external_id: 'id2', + }, + event: 'CompletePayment', + }, + ], + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + }, + ], + metadata: [ + { + jobId: 2, + }, + ], + batched: false, + statusCode: 200, + destination: { + Config: { + accessToken: 'dummyAccessToken', + pixelCode: 'dummyPixelCode', + hashUserProperties: false, + version: 'v2', + }, + }, + }, + ], + }, + }, + }, + mockFns: defaultMockFns, + }, + { + name: 'tiktok_ads', + description: 'Test 6 -> All input events are test events', + feature: FEATURES.ROUTER, + module: 'destination', + version: 'v0', + input: { + request: { + body: { + input: [ + { + message: { + anonymousId: '21e13f4bc7ceddad', + channel: 'web', + context: { + userAgent: + 'Mozilla/5.0 (platform; rv:geckoversion) Gecko/geckotrail Firefox/firefoxversion', + ip: '13.57.97.131', + locale: 'en-US', + externalId: [{ type: 'tiktokExternalId', id: 'id5' }], + }, + messageId: '84e26acc-56a5-4835-8233-591137fca468', + session_id: '3049dc4c-5a95-4ccd-a3e7-d74a7e411f22', + originalTimestamp: '2019-10-14T09:03:17.562Z', + timestamp: '2020-09-17T19:49:27Z', + type: 'track', + event: 'checkout step completed', + properties: { + testEventCode: 'TEST_EVENT_CODE', + eventId: '1616318632825_357', + clickId: 'dummyClickId', + contents: [ + { price: 8, quantity: 2, content_type: 'socks', content_id: '1077218' }, + { price: 30, quantity: 1, content_type: 'dress', content_id: '1197218' }, + ], + currency: 'USD', + value: 46, + url: 'http://demo.mywebsite.com/purchase', + phone: '2f9d2b4df907e5c9a7b3434351b55700167b998a83dc479b825096486ffcf4ea', + email: 'f0e388f53921a51f0bb0fc8a2944109ec188b59172935d8f23020b1614cc44bc', + }, + integrations: { + All: true, + }, + sentAt: '2019-10-14T09:03:22.563Z', + }, + metadata: { + jobId: 5, + }, + destination: { + Config: { + accessToken: 'dummyAccessToken', + pixelCode: 'dummyPixelCode', + hashUserProperties: false, + version: 'v2', + eventsToStandard: [ + { from: 'addToCart', to: 'CompletePayment' }, + { from: 'addToCart', to: 'download' }, + ], + }, + }, + }, + { + message: { + anonymousId: '21e13f4bc7ceddad', + channel: 'web', + context: { + userAgent: + 'Mozilla/5.0 (platform; rv:geckoversion) Gecko/geckotrail Firefox/firefoxversion', + ip: '13.57.97.131', + locale: 'en-US', + externalId: [{ type: 'tiktokExternalId', id: 'id2' }], + }, + messageId: '84e26acc-56a5-4835-8233-591137fca468', + session_id: '3049dc4c-5a95-4ccd-a3e7-d74a7e411f22', + originalTimestamp: '2019-10-14T09:03:17.562Z', + timestamp: '2020-09-17T19:49:27Z', + type: 'track', + event: 'checkout step completed', + properties: { + testEventCode: 'TEST_EVENT_CODE', + eventId: '1616318632825_357', + clickId: 'dummyClickId', + contents: [ + { price: 8, quantity: 2, content_type: 'socks', content_id: '1077218' }, + { price: 30, quantity: 1, content_type: 'dress', content_id: '1197218' }, + ], + currency: 'USD', + value: 46, + url: 'http://demo.mywebsite.com/purchase', + phone: '2f9d2b4df907e5c9a7b3434351b55700167b998a83dc479b825096486ffcf4ea', + email: 'f0e388f53921a51f0bb0fc8a2944109ec188b59172935d8f23020b1614cc44bc', + }, + integrations: { + All: true, + }, + sentAt: '2019-10-14T09:03:22.563Z', + }, + metadata: { + jobId: 2, + }, + destination: { + Config: { + accessToken: 'dummyAccessToken', + pixelCode: 'dummyPixelCode', + hashUserProperties: false, + version: 'v2', + }, + }, + }, + ], + destType: 'tiktok_ads', + }, + method: 'POST', + }, + }, + output: { + response: { + status: 200, + body: { + output: [ + { + batchedRequest: [ + { + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://business-api.tiktok.com/open_api/v1.3/event/track/', + headers: { + 'Access-Token': 'dummyAccessToken', + 'Content-Type': 'application/json', + }, + params: {}, + body: { + JSON: { + event_source_id: 'dummyPixelCode', + event_source: 'web', + partner_name: 'RudderStack', + test_event_code: 'TEST_EVENT_CODE', + data: [ + { + event_id: '1616318632825_357', + event_time: 1600372167, + properties: { + contents: [ + { + price: 8, + quantity: 2, + content_type: 'socks', + content_id: '1077218', + }, + { + price: 30, + quantity: 1, + content_type: 'dress', + content_id: '1197218', + }, + ], + content_type: 'product', + currency: 'USD', + value: 46, + }, + page: { url: 'http://demo.mywebsite.com/purchase' }, + user: { + locale: 'en-US', + email: + 'f0e388f53921a51f0bb0fc8a2944109ec188b59172935d8f23020b1614cc44bc', + phone: + '2f9d2b4df907e5c9a7b3434351b55700167b998a83dc479b825096486ffcf4ea', + ip: '13.57.97.131', + user_agent: + 'Mozilla/5.0 (platform; rv:geckoversion) Gecko/geckotrail Firefox/firefoxversion', + external_id: 'id5', + }, + event: 'CompletePayment', + }, + ], + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + }, + ], + metadata: [ + { + jobId: 5, + }, + ], + batched: false, + statusCode: 200, + destination: { + Config: { + accessToken: 'dummyAccessToken', + pixelCode: 'dummyPixelCode', + hashUserProperties: false, + version: 'v2', + eventsToStandard: [ + { from: 'addToCart', to: 'CompletePayment' }, + { from: 'addToCart', to: 'download' }, + ], + }, + }, + }, + { + batchedRequest: [ + { + version: '1', + type: 'REST', + method: 'POST', + endpoint: 'https://business-api.tiktok.com/open_api/v1.3/event/track/', + headers: { + 'Access-Token': 'dummyAccessToken', + 'Content-Type': 'application/json', + }, + params: {}, + body: { + JSON: { + event_source_id: 'dummyPixelCode', + event_source: 'web', + partner_name: 'RudderStack', + test_event_code: 'TEST_EVENT_CODE', + data: [ + { + event_id: '1616318632825_357', + event_time: 1600372167, + properties: { + contents: [ + { + price: 8, + quantity: 2, + content_type: 'socks', + content_id: '1077218', + }, + { + price: 30, + quantity: 1, + content_type: 'dress', + content_id: '1197218', + }, + ], + content_type: 'product', + currency: 'USD', + value: 46, + }, + page: { url: 'http://demo.mywebsite.com/purchase' }, + user: { + locale: 'en-US', + email: + 'f0e388f53921a51f0bb0fc8a2944109ec188b59172935d8f23020b1614cc44bc', + phone: + '2f9d2b4df907e5c9a7b3434351b55700167b998a83dc479b825096486ffcf4ea', + ip: '13.57.97.131', + user_agent: + 'Mozilla/5.0 (platform; rv:geckoversion) Gecko/geckotrail Firefox/firefoxversion', + external_id: 'id2', + }, + event: 'CompletePayment', + }, + ], + }, + JSON_ARRAY: {}, + XML: {}, + FORM: {}, + }, + files: {}, + }, + ], + metadata: [ + { + jobId: 2, + }, + ], + batched: false, + statusCode: 200, + destination: { + Config: { + accessToken: 'dummyAccessToken', + pixelCode: 'dummyPixelCode', + hashUserProperties: false, + version: 'v2', + }, + }, + }, + ], + }, + }, + }, + mockFns: defaultMockFns, + }, ]; diff --git a/test/integrations/testUtils.ts b/test/integrations/testUtils.ts index f5be8107ed4..09f3a82d40b 100644 --- a/test/integrations/testUtils.ts +++ b/test/integrations/testUtils.ts @@ -4,7 +4,10 @@ import { MockHttpCallsData, TestCaseData } from './testTypes'; import MockAdapter from 'axios-mock-adapter'; import isMatch from 'lodash/isMatch'; import { OptionValues } from 'commander'; +import { removeUndefinedAndNullValues } from '@rudderstack/integrations-lib'; +const generateAlphanumericId = (size = 36) => + [...Array(size)].map(() => ((Math.random() * size) | 0).toString(size)).join(''); export const getTestDataFilePaths = (dirPath: string, opts: OptionValues): string[] => { const globPattern = join(dirPath, '**', 'data.ts'); let testFilePaths = globSync(globPattern); @@ -74,3 +77,290 @@ export const overrideDestination = (destination, overrideConfigValues) => { Config: { ...destination.Config, ...overrideConfigValues }, }); }; + +export const generateIndentifyPayload = (parametersOverride: any) => { + const payload = { + type: 'identify', + sentAt: parametersOverride.sentAt || '2021-01-03T17:02:53.195Z', + userId: parametersOverride.userId || 'default-userId', + channel: 'web', + context: removeUndefinedAndNullValues({ + externalId: parametersOverride.externalId, + os: { name: '', version: '1.12.3' }, + app: { + name: 'RudderLabs JavaScript SDK', + build: '1.0.0', + version: '1.1.11', + namespace: 'com.rudderlabs.javascript', + }, + traits: parametersOverride.context.traits, + locale: 'en-US', + device: { token: 'token', id: 'id', type: 'ios' }, + screen: { density: 2 }, + library: { name: 'RudderLabs JavaScript SDK', version: '1.1.11' }, + campaign: {}, + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.16; rv:84.0) Gecko/20100101 Firefox/84.0', + }), + traits: parametersOverride.traits, + integrations: parametersOverride.integrations, + rudderId: parametersOverride.rudderId || generateAlphanumericId(36), + messageId: parametersOverride.messageId || generateAlphanumericId(36), + anonymousId: parametersOverride.anonymousId || 'default-anonymousId', + originalTimestamp: parametersOverride.originalTimestamp || '2021-01-03T17:02:53.193Z', + }; + + return removeUndefinedAndNullValues(payload); +}; + +export const generateSimplifiedIdentifyPayload = (parametersOverride: any) => { + return removeUndefinedAndNullValues({ + type: 'identify', + sentAt: parametersOverride.sentAt || '2021-01-03T17:02:53.195Z', + userId: parametersOverride.userId || 'default-userId', + traits: parametersOverride.traits, + integrations: parametersOverride.integrations, + rudderId: parametersOverride.rudderId || generateAlphanumericId(36), + messageId: parametersOverride.messageId || generateAlphanumericId(36), + context: { + externalId: parametersOverride.externalId, + traits: parametersOverride.context.traits, + }, + anonymousId: parametersOverride.anonymousId || 'default-anonymousId', + originalTimestamp: parametersOverride.originalTimestamp || '2021-01-03T17:02:53.193Z', + }); +}; + +export const generateTrackPayload = (parametersOverride: any) => { + const payload = { + type: 'track', + sentAt: parametersOverride.sentAt || '2021-01-03T17:02:53.195Z', + userId: parametersOverride.userId || 'default-user-id', + channel: 'web', + context: removeUndefinedAndNullValues({ + externalId: parametersOverride.externalId, + os: { name: '', version: '1.12.3' }, + app: { + name: 'RudderLabs JavaScript SDK', + build: '1.0.0', + version: '1.1.11', + namespace: 'com.rudderlabs.javascript', + }, + traits: parametersOverride.context.traits, + locale: 'en-US', + device: { token: 'token', id: 'id', type: 'ios' }, + screen: { density: 2 }, + library: { name: 'RudderLabs JavaScript SDK', version: '1.1.11' }, + campaign: {}, + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.16; rv:84.0) Gecko/20100101 Firefox/84.0', + }), + rudderId: parametersOverride.rudderId || generateAlphanumericId(36), + messageId: parametersOverride.messageId || generateAlphanumericId(36), + anonymousId: parametersOverride.anonymousId || 'default-anonymousId', + originalTimestamp: parametersOverride.originalTimestamp || '2021-01-03T17:02:53.193Z', + timestamp: parametersOverride.timestamp, + event: parametersOverride.event || 'test-event', + integrations: parametersOverride.integrations, + properties: parametersOverride.properties, + }; + return removeUndefinedAndNullValues(payload); +}; + +export const generateSimplifiedTrackPayload = (parametersOverride: any) => { + return removeUndefinedAndNullValues({ + type: 'track', + sentAt: parametersOverride.sentAt || '2021-01-03T17:02:53.195Z', + userId: parametersOverride.userId || 'default-user-id', + event: parametersOverride.event || 'test-event', + properties: parametersOverride.properties, + integrations: parametersOverride.integrations, + rudderId: parametersOverride.rudderId || generateAlphanumericId(36), + messageId: parametersOverride.messageId || generateAlphanumericId(36), + context: removeUndefinedAndNullValues({ + externalId: parametersOverride.externalId, + traits: parametersOverride.context.traits, + }), + anonymousId: parametersOverride.anonymousId || 'default-anonymousId', + originalTimestamp: parametersOverride.originalTimestamp || '2021-01-03T17:02:53.193Z', + }); +}; + +export const generatePageOrScreenPayload = (parametersOverride: any, eventType: string) => { + const payload = { + channel: 'web', + userId: parametersOverride.userId || 'default-userId', + rudderId: parametersOverride.rudderId || generateAlphanumericId(36), + originalTimestamp: parametersOverride.originalTimestamp || '2022-04-26T05:17:09Z', + timestamp: parametersOverride.timestamp, + context: removeUndefinedAndNullValues({ + app: { + build: '1.0.0', + name: 'RudderLabs JavaScript SDK', + namespace: 'com.rudderlabs.javascript', + version: '1.0.0', + }, + device: { + adTrackingEnabled: 'false', + advertisingId: 'T0T0T072-5e28-45a1-9eda-ce22a3e36d1a', + id: '3f034872-5e28-45a1-9eda-ce22a3e36d1a', + manufacturer: 'Google', + model: 'AOSP on IA Emulator', + name: 'generic_x86_arm', + type: 'ios', + attTrackingStatus: 3, + }, + ip: '0.0.0.0', + library: { + name: 'RudderLabs JavaScript SDK', + version: '1.0.0', + }, + locale: 'en-US', + os: { + name: 'iOS', + version: '14.4.1', + }, + screen: { + density: 2, + }, + traits: parametersOverride.context.traits, + externalId: parametersOverride.externalId, + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36', + }), + event: parametersOverride.event, + anonymousId: parametersOverride.anonymousId || 'default-anonymousId', + properties: parametersOverride.properties, + type: eventType || 'page', + integrations: parametersOverride.integrations, + sentAt: '2022-04-20T15:20:57Z', + }; + + return removeUndefinedAndNullValues(payload); +}; + +export const generateSimplifiedPageOrScreenPayload = ( + parametersOverride: any, + eventType: string, +) => { + return removeUndefinedAndNullValues({ + channel: 'web', + userId: parametersOverride.userId || 'default-userId', + type: eventType || 'page', + event: parametersOverride.event, + properties: parametersOverride.properties, + integrations: parametersOverride.integrations, + rudderId: parametersOverride.rudderId || generateAlphanumericId(36), + context: removeUndefinedAndNullValues({ + externalId: parametersOverride.externalId, + traits: parametersOverride.context.traits, + }), + timestamp: parametersOverride.timestamp, + anonymousId: parametersOverride.anonymousId || 'default-anonymousId', + originalTimestamp: parametersOverride.originalTimestamp || '2022-04-26T05:17:09Z', + }); +}; + +export const generateGroupPayload = (parametersOverride: any) => { + const payload = { + channel: 'web', + context: removeUndefinedAndNullValues({ + app: { + build: '1.0.0', + name: 'RudderLabs JavaScript SDK', + namespace: 'com.rudderlabs.javascript', + version: '1.0.0', + }, + library: { + name: 'RudderLabs JavaScript SDK', + version: '1.0.0', + }, + userAgent: + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', + locale: 'en-US', + ip: '0.0.0.0', + os: { + name: '', + version: '', + }, + screen: { + density: 2, + }, + traits: parametersOverride.context.traits, + externalId: parametersOverride.externalId, + }), + messageId: parametersOverride.messageId || generateAlphanumericId(36), + session_id: parametersOverride.session_id || generateAlphanumericId(36), + originalTimestamp: parametersOverride.originalTimestamp || '2019-10-14T09:03:17.562Z', + timestamp: parametersOverride.timestamp, + anonymousId: parametersOverride.anonymousId || generateAlphanumericId(36), + userId: parametersOverride.userId || 'default-user-id', + type: 'group', + groupId: parametersOverride.groupId, + traits: parametersOverride.traits, + integrations: parametersOverride.integrations, + sentAt: parametersOverride.sentAt || '2019-10-14T09:03:22.563Z', + }; + return removeUndefinedAndNullValues(payload); +}; + +export const generateSimplifiedGroupPayload = (parametersOverride: any) => { + return removeUndefinedAndNullValues({ + channel: 'web', + userId: parametersOverride.userId || 'default-userId', + type: 'group', + groupId: parametersOverride.groupId, + traits: parametersOverride.traits, + integrations: parametersOverride.integrations, + context: removeUndefinedAndNullValues({ + externalId: parametersOverride.externalId, + traits: parametersOverride.context.traits, + }), + timestamp: parametersOverride.timestamp, + anonymousId: parametersOverride.anonymousId || generateAlphanumericId(36), + originalTimestamp: parametersOverride.originalTimestamp || '2019-10-14T09:03:17.562Z', + }); +}; + +export const transformResultBuilder = (matchData) => { + return removeUndefinedAndNullValues({ + version: '1', + type: 'REST', + userId: matchData.userId, + method: matchData.method || 'POST', + endpoint: matchData.endpoint || '', + headers: matchData.headers || {}, + params: matchData.params || {}, + body: { + JSON: matchData.JSON || {}, + JSON_ARRAY: matchData.JSON_ARRAY || {}, + XML: matchData.XML || {}, + FORM: matchData.FORM || {}, + }, + files: matchData.files || {}, + }); +}; + +export const compareObjects = (obj1, obj2, logPrefix = '', differences: string[] = []) => { + for (const key in obj1) { + if (obj1.hasOwnProperty(key)) { + const fullKey = logPrefix ? `${logPrefix}.${key}` : key; + + if (typeof obj1[key] === 'object' && typeof obj2[key] === 'object') { + compareObjects(obj1[key], obj2[key], fullKey, differences); + } else if (obj1[key] !== obj2[key]) { + differences.push(fullKey); + } + } + } + + // Check for keys in obj2 that are not present in obj1 + for (const key in obj2) { + if (obj2.hasOwnProperty(key) && !obj1.hasOwnProperty(key)) { + const fullKey = logPrefix ? `${logPrefix}.${key}` : key; + differences.push(fullKey); + } + } + + return differences; +}; diff --git a/test/test_reporter/reporter.ts b/test/test_reporter/reporter.ts new file mode 100644 index 00000000000..135f9803984 --- /dev/null +++ b/test/test_reporter/reporter.ts @@ -0,0 +1,133 @@ +import fs from 'fs'; +import { compareObjects } from '../integrations/testUtils'; + +// Step 1: Generate the template HTML +const generateHTMLTemplate = () => ` + + + + + + Test Report + + + + + +

Test Report

+ + + + + + + + + + + + + + + + + +
Integration NameIdDescriptionSuccess CriteriaScenarioModuleFeatureAPI VersionTest InputTest OutputExpected OutputDiff KeysTest Status
+ + + +`; + +// Step 2: Iterate through each test data element and add it to the HTML template +const generateHTMLContent = (testData, expectedData, testStatus) => { + let htmlContent = ''; + let diffKeys: string[] = []; + diffKeys = compareObjects(testData.output.response.body, expectedData); + htmlContent += ` + + ${testData.name} + ${testData.id} + ${testData.description} + ${testData.successCriteria} + ${testData.scenario} + ${testData.module} + ${testData.feature} + ${testData.version} + ${JSON.stringify(testData.input.request.body)} + ${JSON.stringify(testData.output.response.body)} + ${JSON.stringify(expectedData)} + ${JSON.stringify(diffKeys)} + ${testStatus} + + + `; + + return htmlContent; +}; + +// Step 3: Write the HTML report to a file +export const generateTestReport = (testData, output, result) => { + fs.readFile('test_reports/test-report.html', 'utf8', (err, htmlTemplate) => { + if (err) { + console.error(err); + return; + } + + const htmlContent = generateHTMLContent(testData, output, result); + const finalHTML = htmlTemplate.replace('', htmlContent); + fs.writeFileSync('test_reports/test-report.html', finalHTML); + }); +}; + +export const initaliseReport = () => { + const htmlTemplate = generateHTMLTemplate(); + if (!fs.existsSync('test_reports')){ + fs.mkdirSync('test_reports'); +} + fs.writeFileSync('test_reports/test-report.html', htmlTemplate); + console.log('Report initialised'); +};