diff --git a/packages/contentstack-import/src/import/modules/entries.ts b/packages/contentstack-import/src/import/modules/entries.ts index 9e319be8a..5895cf196 100644 --- a/packages/contentstack-import/src/import/modules/entries.ts +++ b/packages/contentstack-import/src/import/modules/entries.ts @@ -282,21 +282,20 @@ export default class EntriesImport extends BaseClass { `No environments file found at ${this.envPath}. Entries will not be published.`, this.importConfig.context, ); - return; } else { log.debug(`Loaded ${Object.keys(this.envs).length} environments.`, this.importConfig.context); - } - for (let entryRequestOption of entryRequestOptions) { - await this.publishEntries(entryRequestOption).catch((error) => { - handleAndLogError( - error, - { ...this.importConfig.context, cTUid: entryRequestOption.cTUid, locale: entryRequestOption.locale }, - `Error in publishing entries of ${entryRequestOption.cTUid} in locale ${entryRequestOption.locale}`, - ); - }); + for (let entryRequestOption of entryRequestOptions) { + await this.publishEntries(entryRequestOption).catch((error) => { + handleAndLogError( + error, + { ...this.importConfig.context, cTUid: entryRequestOption.cTUid, locale: entryRequestOption.locale }, + `Error in publishing entries of ${entryRequestOption.cTUid} in locale ${entryRequestOption.locale}`, + ); + }); + } + log.success('All the entries have been published successfully', this.importConfig.context); } - log.success('All the entries have been published successfully', this.importConfig.context); } else { log.info('Skipping entry publishing as per configuration...', this.importConfig.context); } diff --git a/packages/contentstack-import/test/unit/import/modules/entries.test.ts b/packages/contentstack-import/test/unit/import/modules/entries.test.ts index 532089e1e..bf0ea2445 100644 --- a/packages/contentstack-import/test/unit/import/modules/entries.test.ts +++ b/packages/contentstack-import/test/unit/import/modules/entries.test.ts @@ -2886,8 +2886,10 @@ describe('EntriesImport', () => { await entriesImport.start(); - // Verify publishEntries was NOT called due to empty environments + // publish loop is skipped entirely when envs is empty — no pointless API work expect(publishEntriesStub.called).to.be.false; + // createEntryDataForVariantEntry must always run regardless of environments + expect(createEntryDataForVariantEntryStub.called).to.be.true; }); it('should handle errors in replaceEntries', async () => { diff --git a/packages/contentstack-variants/src/import/experiences.ts b/packages/contentstack-variants/src/import/experiences.ts index 1778bccd2..0e8b6f67d 100644 --- a/packages/contentstack-variants/src/import/experiences.ts +++ b/packages/contentstack-variants/src/import/experiences.ts @@ -1,7 +1,5 @@ import { join, resolve } from 'path'; import { existsSync } from 'fs'; -import values from 'lodash/values'; -import cloneDeep from 'lodash/cloneDeep'; import { sanitizePath, log, handleAndLogError } from '@contentstack/cli-utilities'; import { PersonalizationAdapter, fsUtil, lookUpAudiences, lookUpEvents } from '../utils'; import { @@ -119,10 +117,12 @@ export default class Experiences extends PersonalizationAdapter { const experiences = fsUtil.readFile(this.experiencesPath, true) as ExperienceStruct[]; log.info(`Found ${experiences.length} experiences to import`, this.config.context); + const experienceUidsWithVariants = new Set(); + for (const experience of experiences) { const { uid, ...restExperienceData } = experience; log.debug(`Processing experience: ${uid}`, this.config.context); - + //check whether reference audience exists or not that referenced in variations having __type equal to AudienceBasedVariation & targeting let experienceReqObj: CreateExperienceInput = lookUpAudiences(restExperienceData, this.audiencesUid); //check whether events exists or not that referenced in metrics @@ -135,7 +135,9 @@ export default class Experiences extends PersonalizationAdapter { try { // import versions of experience - await this.importExperienceVersions(expRes, uid); + if (await this.importExperienceVersions(expRes, uid)) { + experienceUidsWithVariants.add(expRes.uid); + } } catch (error) { handleAndLogError(error, this.config.context, `Failed to import experience versions for ${expRes.uid}`); } @@ -145,7 +147,7 @@ export default class Experiences extends PersonalizationAdapter { log.success('Experiences created successfully', this.config.context); log.info('Validating variant and variant group creation',this.config.context); - this.pendingVariantAndVariantGrpForExperience = values(cloneDeep(this.experiencesUidMapper)); + this.pendingVariantAndVariantGrpForExperience = Array.from(experienceUidsWithVariants); const jobRes = await this.validateVariantGroupAndVariantsCreated(); fsUtil.writeFile(this.cmsVariantPath, this.cmsVariants); fsUtil.writeFile(this.cmsVariantGroupPath, this.cmsVariantGroups); @@ -175,7 +177,7 @@ export default class Experiences extends PersonalizationAdapter { /** * function import experience versions from a JSON file and creates them in the project. */ - async importExperienceVersions(experience: ExperienceStruct, oldExperienceUid: string) { + async importExperienceVersions(experience: ExperienceStruct, oldExperienceUid: string): Promise { log.debug(`Importing versions for experience: ${oldExperienceUid}`, this.config.context); const versionsPath = resolve( @@ -186,33 +188,39 @@ export default class Experiences extends PersonalizationAdapter { if (!existsSync(versionsPath)) { log.debug(`No versions file found for experience: ${oldExperienceUid}`, this.config.context); - return; + return false; } const versions = fsUtil.readFile(versionsPath, true) as ExperienceStruct[]; log.debug(`Found ${versions.length} versions for experience: ${oldExperienceUid}`, this.config.context); - const versionMap: Record = { - ACTIVE: undefined, - DRAFT: undefined, - PAUSE: undefined, - }; + const HANDLED_STATUSES = new Set(['ACTIVE', 'DRAFT', 'PAUSE']); + const versionMap: { ACTIVE?: CreateExperienceVersionInput; DRAFT?: CreateExperienceVersionInput; PAUSE?: CreateExperienceVersionInput } = {}; // Process each version and map them by status versions.forEach((version) => { let versionReqObj = lookUpAudiences(version, this.audiencesUid) as CreateExperienceVersionInput; - versionReqObj = lookUpEvents(version, this.eventsUid) as CreateExperienceVersionInput; + versionReqObj = lookUpEvents(versionReqObj, this.eventsUid) as CreateExperienceVersionInput; if (versionReqObj && versionReqObj.status && (versionReqObj.variants?.length ?? 0) > 0) { - versionMap[versionReqObj.status] = versionReqObj; + if (!HANDLED_STATUSES.has(versionReqObj.status)) { + log.warn(`Skipping version with unrecognized status "${versionReqObj.status}" — expected one of ACTIVE, DRAFT, PAUSE`, this.config.context); + return; + } + versionMap[versionReqObj.status as 'ACTIVE' | 'DRAFT' | 'PAUSE'] = versionReqObj; log.debug(`Mapped version with status: ${versionReqObj.status}`, this.config.context); } else if (versionReqObj?.status && !(versionReqObj.variants?.length ?? 0)) { - log.warn(`Skipping version ${versionReqObj.status}: no valid variants (all had unmapped Lytics audiences)`, this.config.context); + log.warn(`Skipping version ${versionReqObj.status}: no valid variants after audience/event mapping`, this.config.context); } }); + if (!Object.values(versionMap).some((v) => v !== undefined)) { + return false; + } + // Prioritize updating or creating versions based on the order: ACTIVE -> DRAFT -> PAUSE - return await this.handleVersionUpdateOrCreate(experience, versionMap); + await this.handleVersionUpdateOrCreate(experience, versionMap); + return true; } // Helper method to handle version update or creation logic @@ -333,6 +341,10 @@ export default class Experiences extends PersonalizationAdapter { log.debug(`Attaching ${updatedContentTypes.length} content types to experience: ${newExpUid}`, this.config.context); const { variant_groups: [variantGroup] = [] } = (await this.getVariantGroup({ experienceUid: newExpUid })) || {}; + if (!variantGroup) { + log.warn(`No variant group found for experience: ${newExpUid} — skipping CT attachment`, this.config.context); + return; + } variantGroup.content_types = updatedContentTypes; // Update content types detail in the new experience asynchronously return await this.updateVariantGroup(variantGroup); diff --git a/packages/contentstack-variants/src/import/variant-entries.ts b/packages/contentstack-variants/src/import/variant-entries.ts index c4e474827..8b13c7aa8 100644 --- a/packages/contentstack-variants/src/import/variant-entries.ts +++ b/packages/contentstack-variants/src/import/variant-entries.ts @@ -85,7 +85,7 @@ export default class VariantEntries extends VariantAdapter = { + 'AB Test No Audiences': 'new-uid-empty', + 'Experience Lytics Only': 'new-uid-lytics', + 'Valid Experience': 'new-uid-valid', + 'Mixed Audiences Experience': 'new-uid-mixed', + 'No Versions File Experience': 'new-uid-no-versions', +}; + +function buildConfig(): ImportConfig { + const config = cloneDeep(importConf) as unknown as ImportConfig; + (config.modules as any).personalize = { + ...(config.modules as any).personalization, + dirName: 'personalize', + project_id: 'PROJ-TEST', + importData: true, + baseURL: { 'AWS-NA': 'https://personalization.na-api.contentstack.com' }, + }; + (config as any).region = { name: 'AWS-NA', cma: 'https://api.contentstack.io' }; + config.context = (config as any).context || {}; + return config; +} + +describe('Experiences Import', () => { + let sandbox: sinon.SinonSandbox; + + beforeEach(() => { + sandbox = sinon.createSandbox(); + sandbox.stub(Import.Experiences.prototype, 'init').resolves(); + sandbox.stub(FsUtility.prototype, 'writeFile').returns(undefined as any); + sandbox.stub(FsUtility.prototype, 'makeDirectory').resolves(undefined); + }); + + afterEach(() => { + sandbox.restore(); + }); + + // ────────────────────────────────────────────────────────────────────────── + // importExperienceVersions — unit tests (direct method call) + // ────────────────────────────────────────────────────────────────────────── + describe('importExperienceVersions', () => { + let updateVersionStub: sinon.SinonStub; + let createVersionStub: sinon.SinonStub; + + beforeEach(() => { + updateVersionStub = sandbox.stub(Import.Experiences.prototype, 'updateExperienceVersion').resolves(); + createVersionStub = sandbox.stub(Import.Experiences.prototype, 'createExperienceVersion').resolves(); + }); + + it('returns false when no versions file exists on disk', async () => { + const instance = new Import.Experiences(buildConfig()); + const result = await instance.importExperienceVersions( + { uid: 'new-uid-nofile', latestVersion: 'ver-nofile' } as any, + 'exp-no-versions-file', + ); + expect(result).to.equal(false); + expect(updateVersionStub.callCount).to.equal(0); + expect(createVersionStub.callCount).to.equal(0); + }); + + it('returns false when version has variants: [] (experience had no audiences)', async () => { + const instance = new Import.Experiences(buildConfig()); + const result = await instance.importExperienceVersions( + { uid: 'new-uid-empty', latestVersion: 'ver-empty-latest' } as any, + 'exp-empty-audiences', + ); + expect(result).to.equal(false); + expect(updateVersionStub.callCount).to.equal(0); + }); + + it('returns false when all Lytics audiences are stripped by lookUpAudiences', async () => { + const instance = new Import.Experiences(buildConfig()); + const result = await instance.importExperienceVersions( + { uid: 'new-uid-lytics', latestVersion: 'ver-lytics-latest' } as any, + 'exp-lytics-only', + ); + expect(result).to.equal(false); + expect(updateVersionStub.callCount).to.equal(0); + }); + + it('returns true when version has a valid mapped CS audience', async () => { + const instance = new Import.Experiences(buildConfig()); + const result = await instance.importExperienceVersions( + { uid: 'new-uid-valid', latestVersion: 'ver-valid-latest' } as any, + 'exp-valid', + ); + expect(result).to.equal(true); + }); + + it('calls updateExperienceVersion for ACTIVE status version', async () => { + const instance = new Import.Experiences(buildConfig()); + await instance.importExperienceVersions( + { uid: 'new-uid-valid', latestVersion: 'ver-valid-latest' } as any, + 'exp-valid', + ); + expect(updateVersionStub.callCount).to.equal(1); + expect(updateVersionStub.firstCall.args[0]).to.equal('new-uid-valid'); + expect(updateVersionStub.firstCall.args[2].status).to.equal('ACTIVE'); + expect(createVersionStub.callCount).to.equal(0); + }); + + it('returns true when mixed CS+Lytics variant — CS audience survives, Lytics stripped', async () => { + const instance = new Import.Experiences(buildConfig()); + const result = await instance.importExperienceVersions( + { uid: 'new-uid-mixed', latestVersion: 'ver-mixed-latest' } as any, + 'exp-mixed', + ); + expect(result).to.equal(true); + }); + + it('calls updateExperienceVersion for DRAFT when no ACTIVE version exists', async () => { + const instance = new Import.Experiences(buildConfig()); + const result = await instance.importExperienceVersions( + { uid: 'new-uid-draft-only', latestVersion: 'ver-draft-only-latest' } as any, + 'exp-draft-only', + ); + expect(result).to.equal(true); + expect(updateVersionStub.callCount).to.equal(1); + expect(updateVersionStub.firstCall.args[2].status).to.equal('DRAFT'); + expect(createVersionStub.callCount).to.equal(0); + }); + + it('calls updateExperienceVersion for ACTIVE then createExperienceVersion for DRAFT when both exist', async () => { + const instance = new Import.Experiences(buildConfig()); + const result = await instance.importExperienceVersions( + { uid: 'new-uid-active-and-draft', latestVersion: 'ver-ad-latest' } as any, + 'exp-active-and-draft', + ); + expect(result).to.equal(true); + expect(updateVersionStub.callCount).to.equal(1); + expect(updateVersionStub.firstCall.args[2].status).to.equal('ACTIVE'); + expect(createVersionStub.callCount).to.equal(1); + expect(createVersionStub.firstCall.args[1].status).to.equal('DRAFT'); + }); + + it('does not call any version API when all variants stripped after audience mapping', async () => { + const instance = new Import.Experiences(buildConfig()); + await instance.importExperienceVersions( + { uid: 'new-uid-lytics', latestVersion: 'ver-lytics-latest' } as any, + 'exp-lytics-only', + ); + expect(updateVersionStub.callCount).to.equal(0); + expect(createVersionStub.callCount).to.equal(0); + }); + }); + + // ────────────────────────────────────────────────────────────────────────── + // import() — integration tests across all 5 mock experiences + // ────────────────────────────────────────────────────────────────────────── + describe('import()', () => { + let capturedPendingList: string[]; + let attachCTsStub: sinon.SinonStub; + let createExperienceStub: sinon.SinonStub; + + beforeEach(() => { + capturedPendingList = []; + + createExperienceStub = sandbox.stub(Import.Experiences.prototype, 'createExperience') + .callsFake(async function (payload: any) { + const uid = NAME_TO_NEW_UID[payload.name] ?? `new-uid-${payload.name}`; + return { uid, latestVersion: `ver-${uid}-latest` }; + } as any); + + sandbox.stub(Import.Experiences.prototype, 'updateExperienceVersion').resolves(); + sandbox.stub(Import.Experiences.prototype, 'createExperienceVersion').resolves(); + + sandbox.stub(Import.Experiences.prototype, 'validateVariantGroupAndVariantsCreated') + .callsFake(async function (this: any) { + capturedPendingList = [...this.pendingVariantAndVariantGrpForExperience]; + return true; + }); + + attachCTsStub = sandbox.stub(Import.Experiences.prototype, 'attachCTsInExperience').resolves(); + sandbox.stub(Import.Experiences.prototype, 'createVariantIdMapper').resolves(); + }); + + it('pendingVariantAndVariantGrpForExperience contains only experiences with valid variants', async () => { + const instance = new Import.Experiences(buildConfig()); + await instance.import(); + + expect(capturedPendingList).to.include('new-uid-valid'); + expect(capturedPendingList).to.include('new-uid-mixed'); + }); + + it('pendingVariantAndVariantGrpForExperience excludes experiences with no valid variants', async () => { + const instance = new Import.Experiences(buildConfig()); + await instance.import(); + + expect(capturedPendingList).to.not.include('new-uid-empty'); + expect(capturedPendingList).to.not.include('new-uid-lytics'); + expect(capturedPendingList).to.not.include('new-uid-no-versions'); + }); + + it('pendingVariantAndVariantGrpForExperience has exactly 2 entries (valid + mixed)', async () => { + const instance = new Import.Experiences(buildConfig()); + await instance.import(); + + expect(capturedPendingList).to.have.length(2); + }); + + it('calls attachCTsInExperience when validateVariantGroupAndVariantsCreated returns true', async () => { + const instance = new Import.Experiences(buildConfig()); + await instance.import(); + + expect(attachCTsStub.callCount).to.equal(1); + }); + + it('does NOT call attachCTsInExperience when validateVariantGroupAndVariantsCreated returns false', async () => { + // Override validate stub to return false (simulates backend timeout) + (Import.Experiences.prototype.validateVariantGroupAndVariantsCreated as sinon.SinonStub) + .callsFake(async function (this: any) { + capturedPendingList = [...this.pendingVariantAndVariantGrpForExperience]; + return false; + }); + + const instance = new Import.Experiences(buildConfig()); + await instance.import(); + + expect(attachCTsStub.callCount).to.equal(0); + }); + + it('when all experiences produce no valid variants, pending list is empty and attachCTsInExperience is still called', async () => { + // Override importExperienceVersions to always return false for all experiences + sandbox.stub(Import.Experiences.prototype, 'importExperienceVersions').resolves(false); + + // Reset validate stub: empty pending list → real impl returns true immediately + // but validate is already stubbed to capture and return true, so it still works + const instance = new Import.Experiences(buildConfig()); + await instance.import(); + + expect(capturedPendingList).to.have.length(0); + expect(attachCTsStub.callCount).to.equal(1); + }); + + it('calls createExperience for every experience in experiences.json', async () => { + const instance = new Import.Experiences(buildConfig()); + await instance.import(); + + // 5 experiences in mock: empty, lytics, valid, mixed, no-versions-file + expect(createExperienceStub.callCount).to.equal(5); + }); + }); +}); diff --git a/packages/contentstack-variants/test/unit/mock/contents/personalize/experiences/experiences-content-types.json b/packages/contentstack-variants/test/unit/mock/contents/personalize/experiences/experiences-content-types.json new file mode 100644 index 000000000..91d501724 --- /dev/null +++ b/packages/contentstack-variants/test/unit/mock/contents/personalize/experiences/experiences-content-types.json @@ -0,0 +1,9 @@ +{ + "exp-valid": [ + { "uid": "ct1", "status": "linked" } + ], + "exp-mixed": [ + { "uid": "ct1", "status": "linked" }, + { "uid": "ct2", "status": "linked" } + ] +} diff --git a/packages/contentstack-variants/test/unit/mock/contents/personalize/experiences/experiences.json b/packages/contentstack-variants/test/unit/mock/contents/personalize/experiences/experiences.json new file mode 100644 index 000000000..76d090791 --- /dev/null +++ b/packages/contentstack-variants/test/unit/mock/contents/personalize/experiences/experiences.json @@ -0,0 +1,67 @@ +[ + { + "uid": "exp-empty-audiences", + "name": "AB Test No Audiences", + "__type": "AB_TEST", + "status": "DRAFT", + "description": "", + "referredAudiences": [], + "referredEvents": [], + "tags": [], + "variations": [], + "_cms": { "variantGroup": "vg-empty", "variants": {} }, + "latestVersion": "ver-empty-latest" + }, + { + "uid": "exp-lytics-only", + "name": "Experience Lytics Only", + "__type": "SEGMENTED", + "status": "DRAFT", + "description": "", + "referredAudiences": ["lytics-audience-001"], + "referredEvents": [], + "tags": [], + "variations": [], + "_cms": { "variantGroup": "vg-lytics", "variants": { "0": "old-variant-lytics" } }, + "latestVersion": "ver-lytics-latest" + }, + { + "uid": "exp-valid", + "name": "Valid Experience", + "__type": "SEGMENTED", + "status": "ACTIVE", + "description": "", + "referredAudiences": ["contentstack-audience-001"], + "referredEvents": [], + "tags": [], + "variations": [], + "_cms": { "variantGroup": "vg-valid", "variants": { "0": "old-variant-valid" } }, + "latestVersion": "ver-valid-latest" + }, + { + "uid": "exp-mixed", + "name": "Mixed Audiences Experience", + "__type": "SEGMENTED", + "status": "ACTIVE", + "description": "", + "referredAudiences": ["contentstack-audience-001", "lytics-audience-001"], + "referredEvents": [], + "tags": [], + "variations": [], + "_cms": { "variantGroup": "vg-mixed", "variants": { "0": "old-variant-mixed" } }, + "latestVersion": "ver-mixed-latest" + }, + { + "uid": "exp-no-versions-file", + "name": "No Versions File Experience", + "__type": "AB_TEST", + "status": "DRAFT", + "description": "", + "referredAudiences": [], + "referredEvents": [], + "tags": [], + "variations": [], + "_cms": { "variants": {} }, + "latestVersion": "ver-nofile-latest" + } +] diff --git a/packages/contentstack-variants/test/unit/mock/contents/personalize/experiences/versions/exp-active-and-draft.json b/packages/contentstack-variants/test/unit/mock/contents/personalize/experiences/versions/exp-active-and-draft.json new file mode 100644 index 000000000..29e25d062 --- /dev/null +++ b/packages/contentstack-variants/test/unit/mock/contents/personalize/experiences/versions/exp-active-and-draft.json @@ -0,0 +1,28 @@ +[ + { + "uid": "ver-active-draft-active", + "experienceUid": "exp-active-and-draft", + "status": "ACTIVE", + "variants": [ + { + "__type": "SegmentedVariant", + "audiences": ["contentstack-audience-001"], + "lyticsAudiences": [] + } + ], + "variantSplit": "EQUALLY_SPLIT" + }, + { + "uid": "ver-active-draft-draft", + "experienceUid": "exp-active-and-draft", + "status": "DRAFT", + "variants": [ + { + "__type": "SegmentedVariant", + "audiences": ["contentstack-audience-001"], + "lyticsAudiences": [] + } + ], + "variantSplit": "EQUALLY_SPLIT" + } +] diff --git a/packages/contentstack-variants/test/unit/mock/contents/personalize/experiences/versions/exp-draft-only.json b/packages/contentstack-variants/test/unit/mock/contents/personalize/experiences/versions/exp-draft-only.json new file mode 100644 index 000000000..62a46bcbd --- /dev/null +++ b/packages/contentstack-variants/test/unit/mock/contents/personalize/experiences/versions/exp-draft-only.json @@ -0,0 +1,15 @@ +[ + { + "uid": "ver-draft-only-001", + "experienceUid": "exp-draft-only", + "status": "DRAFT", + "variants": [ + { + "__type": "SegmentedVariant", + "audiences": ["contentstack-audience-001"], + "lyticsAudiences": [] + } + ], + "variantSplit": "EQUALLY_SPLIT" + } +] diff --git a/packages/contentstack-variants/test/unit/mock/contents/personalize/experiences/versions/exp-empty-audiences.json b/packages/contentstack-variants/test/unit/mock/contents/personalize/experiences/versions/exp-empty-audiences.json new file mode 100644 index 000000000..bfc14f64e --- /dev/null +++ b/packages/contentstack-variants/test/unit/mock/contents/personalize/experiences/versions/exp-empty-audiences.json @@ -0,0 +1,9 @@ +[ + { + "uid": "ver-empty-001", + "experienceUid": "exp-empty-audiences", + "status": "DRAFT", + "variants": [], + "variantSplit": "EQUALLY_SPLIT" + } +] diff --git a/packages/contentstack-variants/test/unit/mock/contents/personalize/experiences/versions/exp-lytics-only.json b/packages/contentstack-variants/test/unit/mock/contents/personalize/experiences/versions/exp-lytics-only.json new file mode 100644 index 000000000..5593b6ac9 --- /dev/null +++ b/packages/contentstack-variants/test/unit/mock/contents/personalize/experiences/versions/exp-lytics-only.json @@ -0,0 +1,15 @@ +[ + { + "uid": "ver-lytics-001", + "experienceUid": "exp-lytics-only", + "status": "ACTIVE", + "variants": [ + { + "__type": "SegmentedVariant", + "audiences": [], + "lyticsAudiences": ["lytics-audience-001"] + } + ], + "variantSplit": "EQUALLY_SPLIT" + } +] diff --git a/packages/contentstack-variants/test/unit/mock/contents/personalize/experiences/versions/exp-mixed.json b/packages/contentstack-variants/test/unit/mock/contents/personalize/experiences/versions/exp-mixed.json new file mode 100644 index 000000000..d799f0ab7 --- /dev/null +++ b/packages/contentstack-variants/test/unit/mock/contents/personalize/experiences/versions/exp-mixed.json @@ -0,0 +1,15 @@ +[ + { + "uid": "ver-mixed-001", + "experienceUid": "exp-mixed", + "status": "ACTIVE", + "variants": [ + { + "__type": "SegmentedVariant", + "audiences": ["contentstack-audience-001"], + "lyticsAudiences": ["lytics-audience-001"] + } + ], + "variantSplit": "EQUALLY_SPLIT" + } +] diff --git a/packages/contentstack-variants/test/unit/mock/contents/personalize/experiences/versions/exp-valid.json b/packages/contentstack-variants/test/unit/mock/contents/personalize/experiences/versions/exp-valid.json new file mode 100644 index 000000000..75702c781 --- /dev/null +++ b/packages/contentstack-variants/test/unit/mock/contents/personalize/experiences/versions/exp-valid.json @@ -0,0 +1,15 @@ +[ + { + "uid": "ver-valid-001", + "experienceUid": "exp-valid", + "status": "ACTIVE", + "variants": [ + { + "__type": "SegmentedVariant", + "audiences": ["contentstack-audience-001"], + "lyticsAudiences": [] + } + ], + "variantSplit": "EQUALLY_SPLIT" + } +]