Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -1843,7 +1843,7 @@
"price": {
"label": "Item Price",
"description": "The price of the item, after discounts.",
"type": "integer",
"type": "number",
"required": false,
"multiple": false,
"allowNull": false,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,34 @@ describe('Microsoft Bing CAPI (Actions) - sendEvent (updated)', () => {
expect(scope.isDone()).toBe(true)
})

test('decimal item price is accepted and forwarded (not rejected as non-integer)', async () => {
const event = buildTrackEvent()
const scope = nock('https://capi.uet.microsoft.com')
.post(`/v1/${settings.UetTag}/events`, (body: any) => {
const items = body.data[0].customData.items
expect(items).toHaveLength(1)
expect(items[0].price).toBe(9.99)
expect(items[0].quantity).toBe(2)
expect(items[0].id).toBe('sku-1')
return true
})
.reply(200, {})
const responses: any = await testDestination.testAction('sendEvent', {
event,
settings,
mapping: {
data: { eventType: 'custom', eventTime: new Date('2024-01-01T00:00:00.000Z').toISOString() },
customData: { value: 9.99 },
items: [{ id: 'sku-1', name: 'Widget', price: 9.99, quantity: 2 }],
userData: { anonymousId: 'anon-1' },
timestamp: { '@path': '$.timestamp' }
}
})
// testAction resolves (no mapping-kit validation error thrown) for a decimal price
expect(responses[0].status).toBe(200)
expect(scope.isDone()).toBe(true)
})

test('pageLoad event requires page context mapping and is sent correctly', async () => {
const iso = '2024-06-01T12:00:00.000Z'
const event = buildTrackEvent({ type: 'page', event: undefined })
Expand Down Expand Up @@ -392,6 +420,108 @@ describe('Microsoft Bing CAPI (Actions) - sendEvent (updated)', () => {
expect(scope.isDone()).toBe(true)
})

test('batch: warning-only detail (isWarning:true) at an index is treated as SUCCESS, not failure', async () => {
const events = [buildTrackEvent({ messageId: 'm1' }), buildTrackEvent({ messageId: 'm2' })]
// Microsoft returns HTTP 200 and, because continueOnValidationError:true, reports
// non-fatal issues as warnings while still accepting the event.
const scope = nock('https://capi.uet.microsoft.com')
.post(`/v1/${settings.UetTag}/events`)
.reply(200, {
eventsReceived: 2,
error: {
details: [
{
errorCode: 'Empty',
errorMessage: "'price' must not be empty.",
index: 0,
isWarning: true,
propertyName: 'data[0].customData.items[0].price'
}
]
}
})
const responses: any = await testDestination.executeBatch('sendEvent', {
events,
settings,
mapping: {
enable_batching: true,
data: { eventType: 'custom' },
userData: { anonymousId: 'anon-1' },
timestamp: { '@path': '$.timestamp' }
}
})
expect(responses.length).toBe(2)
// Warning-only event was accepted by Microsoft -> must be a success, not a 400.
expect(responses[0].status).toBe(200)
expect(responses[1].status).toBe(200)
expect(scope.isDone()).toBe(true)
})

test('batch: real error (isWarning:false) is still marked 400 while warning at another index stays 200', async () => {
const events = [buildTrackEvent({ messageId: 'm1' }), buildTrackEvent({ messageId: 'm2' })]
const scope = nock('https://capi.uet.microsoft.com')
.post(`/v1/${settings.UetTag}/events`)
.reply(200, {
eventsReceived: 1,
error: {
details: [
{
errorCode: 'Empty',
errorMessage: "'price' must not be empty.",
index: 0,
isWarning: true,
propertyName: 'data[0].customData.items[0].price'
},
{
errorCode: 'Invalid',
errorMessage: 'Second failed',
index: 1,
isWarning: false,
propertyName: 'data[1].eventName'
}
]
}
})
const responses: any = await testDestination.executeBatch('sendEvent', {
events,
settings,
mapping: {
enable_batching: true,
data: { eventType: 'custom' },
userData: { anonymousId: 'anon-1' },
timestamp: { '@path': '$.timestamp' }
}
})
expect(responses.length).toBe(2)
// index 0 had only a warning -> success; index 1 had a real error -> failure.
expect(responses[0].status).toBe(200)
expect(responses[1].status).toBe(400)
expect(responses[1].errormessage).toContain('Second failed')
expect(scope.isDone()).toBe(true)
})

test('batch: error detail without isWarning field defaults to a real failure (400)', async () => {
const events = [buildTrackEvent({ messageId: 'm1' }), buildTrackEvent({ messageId: 'm2' })]
const scope = nock('https://capi.uet.microsoft.com')
.post(`/v1/${settings.UetTag}/events`)
.reply(200, { error: { details: [{ index: 1, errorMessage: 'Second failed' }] } })
const responses: any = await testDestination.executeBatch('sendEvent', {
events,
settings,
mapping: {
enable_batching: true,
data: { eventType: 'custom' },
userData: { anonymousId: 'anon-1' },
timestamp: { '@path': '$.timestamp' }
}
})
expect(responses.length).toBe(2)
expect(responses[0].status).toBe(200)
expect(responses[1].status).toBe(400)
expect(responses[1].errormessage).toContain('Second failed')
expect(scope.isDone()).toBe(true)
})

test('phone digits normalized before hashing', async () => {
const rawPhone = '+1 (555) 123-4567 ext.89'
const event = buildTrackEvent({ context: { traits: { phone: rawPhone, email: 'norm@example.com' } } })
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ export const items: InputField = {
price: {
label: 'Item Price',
description: 'The price of the item, after discounts.',
type: 'integer'
type: 'number'
},
Comment on lines 196 to 200
quantity: {
label: 'Item Quantity',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ async function send(request: RequestClient, payloads: Payload[], settings: Setti
const details = response.data?.error?.details ?? []

payloads.forEach((payload, index) => {
const error = details.find((detail) => detail.index === index)
const error = details.find((detail) => detail.index === index && !detail.isWarning)
if (error) {
multiStatusResponse.setErrorResponseAtIndex(index, {
status: 400,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ export interface MSMultiStatusResponse {
propertyName: string
attemptedValue: unknown
errorMessage: string
errorCode?: string
isWarning?: boolean
}>
}
traceId: string
Expand Down
Loading