Skip to content

[STRATCONN-6836] - E2E proof of concept#3817

Draft
joe-ayoub-segment wants to merge 58 commits into
mainfrom
feat/e2e-test-types-and-fixtures
Draft

[STRATCONN-6836] - E2E proof of concept#3817
joe-ayoub-segment wants to merge 58 commits into
mainfrom
feat/e2e-test-types-and-fixtures

Conversation

@joe-ayoub-segment

Copy link
Copy Markdown
Contributor

Build a light weight end to end testing capability to exercise destination -> destination platform code.

Testing

Security Review

Please ensure sensitive data is properly protected in your integration.

  • Reviewed all field definitions for sensitive data (API keys, tokens, passwords, client secrets) and confirmed they use type: 'password'

New Destination Checklist

  • Extracted all action API versions to verioning-info.ts file. example

joe-ayoub-segment and others added 2 commits May 28, 2026 11:23
Introduce E2EFixture, E2EExpectation, and E2EDestinationConfig types in
actions-core for strongly typed end-to-end destination testing. Add initial
fixtures for Iterable trackEvent and updateUser actions as a proof of concept.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Rename HttpSuccessCode → E2EHttpSuccessCode and HttpFailureCode →
E2EHttpFailureCode to namespace them clearly as e2e testing types.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings May 28, 2026 10:30

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR is a spike that introduces a lightweight end-to-end testing capability for Action Destinations. It adds shared E2E type definitions to @segment/actions-core and demonstrates the pattern with a set of fixtures for the Iterable destination (track event success, track event missing-identifiers failure, and update user success).

Changes:

  • Adds e2e-types.ts to packages/core defining E2EFixture, E2EExpectation variants (success/failure/error), E2EDestinationConfig, and HTTP status code unions, and re-exports them from the package entry point.
  • Introduces an __e2e__/index.ts config file for the Iterable destination that wires per-action fixtures and references an E2E_ITERABLE_API_KEY env-backed setting.
  • Adds three Iterable fixtures (trackEvent/success, trackEvent/missing-identifiers, updateUser/success) illustrating both successful execution and a client-side PayloadValidationError case.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated no comments.

Show a summary per file
File Description
packages/core/src/e2e-types.ts New shared types for E2E fixtures, expectations, destination config, and HTTP status code unions.
packages/core/src/index.ts Re-exports the new E2E types from the core package entry point.
packages/destination-actions/src/destinations/iterable/e2e/index.ts Iterable E2E config: settings (with env-backed API key) and per-action fixture map.
packages/destination-actions/src/destinations/iterable/trackEvent/e2e/success.ts Fixture asserting a successful Iterable trackEvent for an Order Completed event.
packages/destination-actions/src/destinations/iterable/trackEvent/e2e/missing-identifiers.ts Fixture asserting a PayloadValidationError when both email and userId are missing.
packages/destination-actions/src/destinations/iterable/updateUser/e2e/success.ts Fixture asserting a successful Iterable updateUser identify call.

joe-ayoub-segment and others added 2 commits May 28, 2026 11:35
Replace individual fixture files with a single fixtures.ts per action
that exports an array of E2EFixture. Reduces boilerplate in the index.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Runner will discover fixtures via filesystem glob instead of explicit
imports. Index now only exports destination config.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings May 28, 2026 10:38

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 5 out of 5 changed files in this pull request and generated no new comments.

joe-ayoub-segment and others added 2 commits May 28, 2026 11:45
Introduce E2EDynamicValue type documenting runner-resolved markers.
Update Iterable fixtures to use $now for timestamps, $guid for messageIds,
and $guid:orderId for consistent order ID generation.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Derive mappings from action field definitions instead of hand-writing them.
Tests now stay in sync with field changes automatically.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings May 28, 2026 10:52

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 5 out of 5 changed files in this pull request and generated no new comments.

joe-ayoub-segment and others added 2 commits May 28, 2026 11:57
Auto-populates messageId ($guid) and timestamp ($now). Fixture authors
only provide type, event name, and test-specific overrides.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Move createE2EEvent to e2e-helpers.ts, keeping e2e-types.ts as pure
type definitions.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings May 28, 2026 11:01

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 6 out of 6 changed files in this pull request and generated no new comments.

joe-ayoub-segment and others added 2 commits May 28, 2026 15:49
JSONObject doesn't accept undefined values. Use destructuring to
exclude the keys entirely.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings May 28, 2026 15:07

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 9 out of 9 changed files in this pull request and generated no new comments.

joe-ayoub-segment and others added 2 commits May 29, 2026 09:47
Generates fully-formed Engage audience event payloads for e2e testing.
Handles context.personas structure, membership booleans, email placement,
audienceFields, and enrichedTraits for both track and identify types.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Return restricted E2EEngageAudienceEvent type instead of SegmentEvent
- Add optional eventName param (defaults to 'Test Engage Audience Membership Event')
- Remove email from track properties (stays in context.traits only)
- Rename traits param to enrichedTraits
- Flatten function to single const with spreading

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings May 29, 2026 09:05

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 9 out of 9 changed files in this pull request and generated no new comments.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 28 out of 29 changed files in this pull request and generated 5 comments.

Comment on lines +108 to +110
export async function fetchSeedUserId(request: RequestClient, endpoint: string): Promise<string> {
const url = getEndpointByRegion('usersearch', endpoint)
const batches = [['1', '2', '3'], ['4', '5', '6'], ['7', '8', '9']]
Comment on lines +110 to +130
const batches = [['1', '2', '3'], ['4', '5', '6'], ['7', '8', '9']]

for (const batch of batches) {
const results = await Promise.all(
batch.map(async (prefix) => {
const response = await request<UserSearchResponse>(`${url}?user=${prefix}`)
const matches = response?.data?.matches || []
for (const match of matches) {
if (match.user_id) {
return match.user_id
}
}
return undefined
})
)

const found = results.find((id) => id !== undefined)
if (found) {
return found
}
}
},
{
description: 'Engage Audience: Remove a user from the customer match list via track event',
subscribe: 'event = "Audience Entered"',
Comment thread packages/core/src/e2e-helpers.ts Outdated
* Only track events supported
*/
export function createE2EJourneysV1AudienceEvent<ComputationKey extends string>(options: E2EJourneysV1AudienceEventOptions<ComputationKey>): E2EJourneysV1AudienceTrackEvent<ComputationKey> {
const { computationKey, computationId, externalAudienceId, eventName, userId, anonymousId, email, audienceFields, enrichedTraits } = options
Comment on lines 153 to +155
actions: {
syncAudience
},
presets: [
{
name: 'Entities Audience Membership Changed',
partnerAction: 'syncAudience',
mapping: defaultValues(syncAudience.fields),
type: 'specificEvent',
eventSlug: 'warehouse_audience_membership_changed_identify'
},
{
name: 'Associated Entity Added',
partnerAction: 'syncAudience',
mapping: defaultValues(syncAudience.fields),
type: 'specificEvent',
eventSlug: 'warehouse_entity_added_track'
},
{
name: 'Associated Entity Removed',
partnerAction: 'syncAudience',
mapping: defaultValues(syncAudience.fields),
type: 'specificEvent',
eventSlug: 'warehouse_entity_removed_track'
},
{
name: 'Journeys Step Entered',
partnerAction: 'syncAudience',
mapping: defaultValues(syncAudience.fields),
type: 'specificEvent',
eventSlug: 'journeys_step_entered_track'
}
]
}
joe-ayoub-segment and others added 2 commits June 3, 2026 10:07
… field

- Extract buildAudienceEventBase() to eliminate duplicated context/personas
  construction across all three audience event helpers
- Remove unused `action` field from E2EJourneysV1AudienceEventOptions and
  all fixture call sites

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings June 3, 2026 09:33

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 28 out of 29 changed files in this pull request and generated 3 comments.

Comment on lines 153 to +155
actions: {
syncAudience
},
presets: [
{
name: 'Entities Audience Membership Changed',
partnerAction: 'syncAudience',
mapping: defaultValues(syncAudience.fields),
type: 'specificEvent',
eventSlug: 'warehouse_audience_membership_changed_identify'
},
{
name: 'Associated Entity Added',
partnerAction: 'syncAudience',
mapping: defaultValues(syncAudience.fields),
type: 'specificEvent',
eventSlug: 'warehouse_entity_added_track'
},
{
name: 'Associated Entity Removed',
partnerAction: 'syncAudience',
mapping: defaultValues(syncAudience.fields),
type: 'specificEvent',
eventSlug: 'warehouse_entity_removed_track'
},
{
name: 'Journeys Step Entered',
partnerAction: 'syncAudience',
mapping: defaultValues(syncAudience.fields),
type: 'specificEvent',
eventSlug: 'journeys_step_entered_track'
}
]
}
@@ -1,4 +1,4 @@
import { AudienceDestinationDefinition, defaultValues } from '@segment/actions-core'
import { AudienceDestinationDefinition } from '@segment/actions-core'
Comment on lines +82 to +87
const elapsed = Date.now() - startTime
if (elapsed < REMOVAL_AWAIT_THRESHOLD_MS) {
await removeSeedUser(request, id, endpoint, seedUserId, statsContext)
} else {
void removeSeedUser(request, id, endpoint, seedUserId, statsContext)
}
joe-ayoub-segment and others added 2 commits June 4, 2026 10:37
- Add e2e config and 9 fixtures for pinterest-conversions
- Fix createE2EEvent: use event for track, name for page/screen, throw
  for identify/group/alias when name is passed
- Fix createE2EEngageAudienceEvent: throw if eventName passed with
  type=identify
- Update iterable and amplitude identify fixtures accordingly
- Fix legacy custom_data quantity label and opt_out_type description
- Add presets for new event types, reference EVENT_NAME constants
- Add Pinterest API payload types and wire return types

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Use PAYLOAD_VALIDATION_FAILED error code for choices validation
- Pinterest returns 422 for stale timestamps, not 400

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings June 4, 2026 10:14

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 30 out of 31 changed files in this pull request and generated 2 comments.

Comment on lines +82 to +87
const elapsed = Date.now() - startTime
if (elapsed < REMOVAL_AWAIT_THRESHOLD_MS) {
await removeSeedUser(request, id, endpoint, seedUserId, statsContext)
} else {
void removeSeedUser(request, id, endpoint, seedUserId, statsContext)
}
Comment on lines 153 to +155
actions: {
syncAudience
},
presets: [
{
name: 'Entities Audience Membership Changed',
partnerAction: 'syncAudience',
mapping: defaultValues(syncAudience.fields),
type: 'specificEvent',
eventSlug: 'warehouse_audience_membership_changed_identify'
},
{
name: 'Associated Entity Added',
partnerAction: 'syncAudience',
mapping: defaultValues(syncAudience.fields),
type: 'specificEvent',
eventSlug: 'warehouse_entity_added_track'
},
{
name: 'Associated Entity Removed',
partnerAction: 'syncAudience',
mapping: defaultValues(syncAudience.fields),
type: 'specificEvent',
eventSlug: 'warehouse_entity_removed_track'
},
{
name: 'Journeys Step Entered',
partnerAction: 'syncAudience',
mapping: defaultValues(syncAudience.fields),
type: 'specificEvent',
eventSlug: 'journeys_step_entered_track'
}
]
}
Copilot AI review requested due to automatic review settings June 4, 2026 13:33

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 30 out of 31 changed files in this pull request and generated 6 comments.

Comment on lines 153 to +155
actions: {
syncAudience
},
presets: [
{
name: 'Entities Audience Membership Changed',
partnerAction: 'syncAudience',
mapping: defaultValues(syncAudience.fields),
type: 'specificEvent',
eventSlug: 'warehouse_audience_membership_changed_identify'
},
{
name: 'Associated Entity Added',
partnerAction: 'syncAudience',
mapping: defaultValues(syncAudience.fields),
type: 'specificEvent',
eventSlug: 'warehouse_entity_added_track'
},
{
name: 'Associated Entity Removed',
partnerAction: 'syncAudience',
mapping: defaultValues(syncAudience.fields),
type: 'specificEvent',
eventSlug: 'warehouse_entity_removed_track'
},
{
name: 'Journeys Step Entered',
partnerAction: 'syncAudience',
mapping: defaultValues(syncAudience.fields),
type: 'specificEvent',
eventSlug: 'journeys_step_entered_track'
}
]
}
const COMPUTATION_KEY = 'e2e_test_user_list'
const COMPUTATION_ID = 'aud_e2e_google_001'

const FAILURE_HINT = 'Ensure GOOGLE_ENHANCED_CONVERSIONS_CLIENT_ID, GOOGLE_ENHANCED_CONVERSIONS_CLIENT_SECRET, and ADWORDS_DEVELOPER_TOKEN env vars are set. The customerId must be a valid Google Ads account.'
Comment on lines +8 to +9
const FAILURE_HINT =
'Ensure GOOGLE_ENHANCED_CONVERSIONS_CLIENT_ID, GOOGLE_ENHANCED_CONVERSIONS_CLIENT_SECRET, and ADWORDS_DEVELOPER_TOKEN env vars are set. The customerId must be a valid Google Ads account.'
const COMPUTATION_KEY = 'e2e_test_user_list'
const COMPUTATION_ID = 'aud_e2e_google_retl_001'

const FAILURE_HINT = 'Ensure GOOGLE_ENHANCED_CONVERSIONS_CLIENT_ID, GOOGLE_ENHANCED_CONVERSIONS_CLIENT_SECRET, and ADWORDS_DEVELOPER_TOKEN env vars are set. The customerId must be a valid Google Ads account.'
Comment on lines +55 to +63
const response = await fetch(
`https://googleads.googleapis.com/v21/customers/${customerId}/userLists:mutate`,
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
'developer-token': process.env.ADWORDS_DEVELOPER_TOKEN ?? '',
authorization: `Bearer ${access_token}`
},
Comment on lines +16 to +52
export function createE2EEvent(
type: SegmentEvent['type'],
name?: string,
overrides?: Partial<Omit<SegmentEvent, 'type' | 'event' | 'name' | 'messageId' | 'timestamp'>>
): SegmentEvent {
if (type === 'track') {
return {
type,
event: name,
messageId: '$guid',
timestamp: '$now',
...overrides
}
}

if (type === 'page' || type === 'screen') {
return {
type,
name,
messageId: '$guid',
timestamp: '$now',
...overrides
}
}

if (name) {
throw new Error(
`createE2EEvent: "name" is not supported for "${type}" events. Only track, page, and screen accept a name.`
)
}

return {
type,
messageId: '$guid',
timestamp: '$now',
...overrides
}
Copilot AI review requested due to automatic review settings June 9, 2026 11:46

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 30 out of 31 changed files in this pull request and generated 2 comments.

Comment on lines 152 to +155
},
actions: {
syncAudience
},
presets: [
{
name: 'Entities Audience Membership Changed',
partnerAction: 'syncAudience',
mapping: defaultValues(syncAudience.fields),
type: 'specificEvent',
eventSlug: 'warehouse_audience_membership_changed_identify'
},
{
name: 'Associated Entity Added',
partnerAction: 'syncAudience',
mapping: defaultValues(syncAudience.fields),
type: 'specificEvent',
eventSlug: 'warehouse_entity_added_track'
},
{
name: 'Associated Entity Removed',
partnerAction: 'syncAudience',
mapping: defaultValues(syncAudience.fields),
type: 'specificEvent',
eventSlug: 'warehouse_entity_removed_track'
},
{
name: 'Journeys Step Entered',
partnerAction: 'syncAudience',
mapping: defaultValues(syncAudience.fields),
type: 'specificEvent',
eventSlug: 'journeys_step_entered_track'
}
]
}
Comment on lines +82 to +87
const elapsed = Date.now() - startTime
if (elapsed < REMOVAL_AWAIT_THRESHOLD_MS) {
await removeSeedUser(request, id, endpoint, seedUserId, statsContext)
} else {
void removeSeedUser(request, id, endpoint, seedUserId, statsContext)
}
Copilot AI review requested due to automatic review settings June 9, 2026 14:35

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 34 out of 35 changed files in this pull request and generated 7 comments.

Comment on lines 153 to +155
actions: {
syncAudience
},
presets: [
{
name: 'Entities Audience Membership Changed',
partnerAction: 'syncAudience',
mapping: defaultValues(syncAudience.fields),
type: 'specificEvent',
eventSlug: 'warehouse_audience_membership_changed_identify'
},
{
name: 'Associated Entity Added',
partnerAction: 'syncAudience',
mapping: defaultValues(syncAudience.fields),
type: 'specificEvent',
eventSlug: 'warehouse_entity_added_track'
},
{
name: 'Associated Entity Removed',
partnerAction: 'syncAudience',
mapping: defaultValues(syncAudience.fields),
type: 'specificEvent',
eventSlug: 'warehouse_entity_removed_track'
},
{
name: 'Journeys Step Entered',
partnerAction: 'syncAudience',
mapping: defaultValues(syncAudience.fields),
type: 'specificEvent',
eventSlug: 'journeys_step_entered_track'
}
]
}
Comment on lines +55 to +68
const response = await fetch(
`https://googleads.googleapis.com/v21/customers/${customerId}/userLists:mutate`,
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
'developer-token': process.env.ADWORDS_DEVELOPER_TOKEN ?? '',
authorization: `Bearer ${access_token}`
},
body: JSON.stringify({
operations: [{ remove: `customers/${customerId}/userLists/${externalAudienceId}` }]
})
}
)
const COMPUTATION_KEY = 'e2e_test_user_list'
const COMPUTATION_ID = 'aud_e2e_google_001'

const FAILURE_HINT = 'Ensure GOOGLE_ENHANCED_CONVERSIONS_CLIENT_ID, GOOGLE_ENHANCED_CONVERSIONS_CLIENT_SECRET, and ADWORDS_DEVELOPER_TOKEN env vars are set. The customerId must be a valid Google Ads account.'
Comment on lines +8 to +9
const FAILURE_HINT =
'Ensure GOOGLE_ENHANCED_CONVERSIONS_CLIENT_ID, GOOGLE_ENHANCED_CONVERSIONS_CLIENT_SECRET, and ADWORDS_DEVELOPER_TOKEN env vars are set. The customerId must be a valid Google Ads account.'
const COMPUTATION_KEY = 'e2e_test_user_list'
const COMPUTATION_ID = 'aud_e2e_google_retl_001'

const FAILURE_HINT = 'Ensure GOOGLE_ENHANCED_CONVERSIONS_CLIENT_ID, GOOGLE_ENHANCED_CONVERSIONS_CLIENT_SECRET, and ADWORDS_DEVELOPER_TOKEN env vars are set. The customerId must be a valid Google Ads account.'
Comment on lines +223 to +234
{
...createE2EEngageAudienceEvent({
type: 'identify',
action: 'remove',
computationKey: COMPUTATION_KEY,
computationId: COMPUTATION_ID,
externalAudienceId: '$externalAudienceId',
userId: 'e2e-fb-user-011',
email: 'e2e-fb-test-011@segment.com'
}),
userId: undefined as unknown as string
}
Comment on lines +18 to +21
properties: {
[COMPUTATION_KEY]: true,
email: options.email
},
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants