@@ -2,7 +2,7 @@ import { IKitConfigs } from "../../src/configAPIClient";
22import { IMParticleUser } from "../../src/identity-user-interfaces" ;
33import { SDKIdentityApi } from "../../src/identity.interfaces" ;
44import { IMParticleWebSDKInstance } from "../../src/mp-instance" ;
5- import RoktManager , { IRoktKit , IRoktSelectPlacementsOptions } from "../../src/roktManager" ;
5+ import RoktManager , { IRoktKit , IRoktSelectPlacementsOptions , IRoktPartnerAttributes , IRoktLauncher , IRoktSelection } from "../../src/roktManager" ;
66import { testMPID } from '../src/config/constants' ;
77
88const resolvePromise = ( ) => new Promise ( resolve => setTimeout ( resolve , 0 ) ) ;
@@ -76,82 +76,68 @@ describe('RoktManager', () => {
7676 } ) ;
7777
7878 describe ( '#hashAttributes' , ( ) => {
79+ interface Hasher {
80+ sha256Hex ( input : string ) : Promise < string >
81+ }
82+
7983 beforeEach ( ( ) => {
8084 roktManager [ 'currentUser' ] = currentUser ;
8185 } ) ;
8286
83- it ( 'should call kit.hashAttributes with empty attributes' , ( ) => {
84- const kit : IRoktKit = {
85- launcher : {
86- selectPlacements : jest . fn ( ) ,
87- hashAttributes : jest . fn ( ) ,
88- use : jest . fn ( ) ,
89- } ,
90- filters : undefined ,
91- filteredUser : undefined ,
92- hashAttributes : jest . fn ( ) ,
93- selectPlacements : jest . fn ( ) ,
94- setExtensionData : jest . fn ( ) ,
95- use : jest . fn ( ) ,
96- userAttributes : undefined ,
97- } ;
98-
99- roktManager . attachKit ( kit ) ;
87+ it ( 'should not hash when calling roktManager.hashAttributes with empty attributes and no kit' , async ( ) => {
88+ const shaSpy = jest . spyOn ( roktManager as unknown as Hasher , 'sha256Hex' ) ;
89+ const attributes : IRoktPartnerAttributes = { } ;
90+ const messageQueueSizeBefore = roktManager [ 'messageQueue' ] . size ;
91+ await roktManager . hashAttributes ( attributes ) ;
92+ const messageQueueSizeAfter = roktManager [ 'messageQueue' ] . size ;
93+ expect ( messageQueueSizeAfter ) . toBe ( messageQueueSizeBefore ) ;
94+ expect ( shaSpy ) . not . toHaveBeenCalled ( ) ;
95+ shaSpy . mockRestore ( ) ;
96+ } ) ;
10097
101- const attributes = { } ;
98+ it ( 'should hash when calling roktManager.hashAttributes without kit' , async ( ) => {
99+ const nodeCrypto = require ( 'crypto' ) ;
100+ const shaSpy = jest . spyOn ( roktManager as unknown as Hasher , 'sha256Hex' ) ;
101+ shaSpy . mockImplementation ( ( s : any ) =>
102+ Promise . resolve ( nodeCrypto . createHash ( 'sha256' ) . update ( String ( s ) ) . digest ( 'hex' ) ) ,
103+ ) ;
102104
103- roktManager . hashAttributes ( attributes ) ;
104- expect ( kit . hashAttributes ) . toHaveBeenCalledWith ( attributes ) ;
105- } ) ;
105+ const attributes :
IRoktPartnerAttributes = { email :
'[email protected] ' , phone :
' 1234567890 ' } ; 106+ const messageQueueSizeBefore = roktManager [ 'messageQueue' ] . size ;
106107
107- it ( 'should call kit.hashAttributes with passed in attributes' , ( ) => {
108- const kit : IRoktKit = {
109- launcher : {
110- selectPlacements : jest . fn ( ) ,
111- hashAttributes : jest . fn ( ) ,
112- use : jest . fn ( ) ,
113- } ,
114- filters : undefined ,
115- filteredUser : undefined ,
116- hashAttributes : jest . fn ( ) ,
117- selectPlacements : jest . fn ( ) ,
118- setExtensionData : jest . fn ( ) ,
119- use : jest . fn ( ) ,
120- userAttributes : undefined ,
121- } ;
108+ await roktManager . hashAttributes ( attributes ) ;
122109
123- roktManager . attachKit ( kit ) ;
110+ const messageQueueSizeAfter = roktManager [ 'messageQueue' ] . size ;
111+ expect ( messageQueueSizeAfter ) . toBe ( messageQueueSizeBefore ) ;
124112
125- const attributes = {
126- 127- phone : '1234567890'
128- } ;
113+ expect ( shaSpy ) . toHaveBeenCalledWith ( '[email protected] ' ) ; 114+ expect ( shaSpy ) . toHaveBeenCalledWith ( '1234567890' ) ;
115+ expect ( shaSpy ) . toHaveBeenCalledTimes ( 2 ) ;
129116
130- roktManager . hashAttributes ( attributes ) ;
131- expect ( kit . hashAttributes ) . toHaveBeenCalledWith ( attributes ) ;
117+ shaSpy . mockRestore ( ) ;
132118 } ) ;
133119
134- it ( 'should queue the hashAttributes method if no launcher or kit is attached' , ( ) => {
135- const attributes = {
136- 137- } ;
120+ it ( 'should not queue when calling roktManager.hashAttributes with no kit attached' , async ( ) => {
121+ const shaSpy = jest . spyOn ( roktManager as unknown as Hasher , 'sha256Hex' ) . mockResolvedValue ( 'deadbeef' ) ;
138122
139- roktManager . hashAttributes ( attributes ) ;
123+ const attributes :
IRoktPartnerAttributes = { email :
'[email protected] ' } ; 124+ const messageQueueSizeBefore = roktManager [ 'messageQueue' ] . size ;
125+ await roktManager . hashAttributes ( attributes ) ;
126+ const messageQueueSizeAfter = roktManager [ 'messageQueue' ] . size ;
127+ expect ( messageQueueSizeAfter ) . toBe ( messageQueueSizeBefore ) ;
140128
141- expect ( roktManager [ 'kit' ] ) . toBeNull ( ) ;
142- expect ( roktManager [ 'messageQueue' ] . size ) . toBe ( 1 ) ;
143- const queuedMessage = Array . from ( roktManager [ 'messageQueue' ] . values ( ) ) [ 0 ] ;
144- expect ( queuedMessage . methodName ) . toBe ( 'hashAttributes' ) ;
145- expect ( queuedMessage . payload ) . toBe ( attributes ) ;
129+ shaSpy . mockRestore ( ) ;
146130 } ) ;
147131
148- it ( 'should process queued hashAttributes calls once the launcher and kit are attached' , ( ) => {
132+ it ( 'should remain non-deferred before and after attaching kit' , async ( ) => {
133+ const nodeCrypto = require ( 'crypto' ) ;
134+ const shaSpy = jest . spyOn ( roktManager as unknown as Hasher , 'sha256Hex' ) ;
135+ shaSpy . mockImplementation ( ( s : any ) =>
136+ Promise . resolve ( nodeCrypto . createHash ( 'sha256' ) . update ( String ( s ) ) . digest ( 'hex' ) ) ,
137+ ) ;
138+
149139 const kit : IRoktKit = {
150- launcher : {
151- selectPlacements : jest . fn ( ) ,
152- hashAttributes : jest . fn ( ) ,
153- use : jest . fn ( ) ,
154- } ,
140+ launcher : { selectPlacements : jest . fn ( ) , hashAttributes : jest . fn ( ) , use : jest . fn ( ) } ,
155141 filters : undefined ,
156142 filteredUser : undefined ,
157143 hashAttributes : jest . fn ( ) ,
@@ -161,52 +147,55 @@ describe('RoktManager', () => {
161147 userAttributes : undefined ,
162148 } ;
163149
164- const attributes = {
165- 166- } ;
150+ const attributes :
IRoktPartnerAttributes = { email :
'[email protected] ' } ; 167151
168- roktManager . hashAttributes ( attributes ) ;
169- expect ( roktManager [ 'kit' ] ) . toBeNull ( ) ;
170- expect ( roktManager [ 'messageQueue' ] . size ) . toBe ( 1 ) ;
171- const queuedMessage = Array . from ( roktManager [ 'messageQueue' ] . values ( ) ) [ 0 ] ;
172- expect ( queuedMessage . methodName ) . toBe ( 'hashAttributes' ) ;
173- expect ( queuedMessage . payload ) . toBe ( attributes ) ;
174- expect ( kit . hashAttributes ) . not . toHaveBeenCalled ( ) ;
152+ const sizeBefore = roktManager [ 'messageQueue' ] . size ;
153+ await roktManager . hashAttributes ( attributes ) ;
154+ expect ( roktManager [ 'messageQueue' ] . size ) . toBe ( sizeBefore ) ;
175155
176156 roktManager . attachKit ( kit ) ;
177- expect ( roktManager [ 'kit' ] ) . not . toBeNull ( ) ;
178- expect ( roktManager [ 'messageQueue' ] . size ) . toBe ( 0 ) ;
179- expect ( kit . hashAttributes ) . toHaveBeenCalledWith ( attributes ) ;
157+ const sizeAfterAttach = roktManager [ 'messageQueue' ] . size ;
158+ await roktManager . hashAttributes ( attributes ) ;
159+ expect ( roktManager [ 'messageQueue' ] . size ) . toBe ( sizeAfterAttach ) ;
160+
161+ shaSpy . mockRestore ( ) ;
180162 } ) ;
181163
182- it ( 'should pass through the correct attributes to kit.launcher.hashAttributes' , async ( ) => {
164+ it ( 'should allow calling launcher.hashAttributes directly when kit is attached' , async ( ) => {
165+ const launcher : IRoktLauncher = {
166+ selectPlacements : jest . fn ( ) . mockResolvedValue ( { close : jest . fn ( ) , getPlacements : jest . fn ( ) . mockResolvedValue ( [ ] ) } as unknown as IRoktSelection ) ,
167+ hashAttributes : jest . fn ( ) ,
168+ use : jest . fn ( ) . mockResolvedValue ( undefined as unknown as never ) ,
169+ } ;
183170 const kit : Partial < IRoktKit > = {
184- launcher : {
185- selectPlacements : jest . fn ( ) ,
186- hashAttributes : jest . fn ( ) ,
187- use : jest . fn ( ) ,
188- } ,
189-
190- // We are mocking the hashAttributes method to return the
191- // launcher's hashAttributes method and verify that
192- // both the kit's and the launcher's methods
193- // are called with the correct attributes.
194- // This will happen through the Web Kit's hashAttributes method
195- hashAttributes : jest . fn ( ) . mockImplementation ( ( attributes ) => {
196- return kit . launcher . hashAttributes ( attributes ) ;
197- } )
171+ launcher,
172+ hashAttributes : jest . fn ( ) ,
198173 } ;
199-
200174 roktManager . attachKit ( kit as IRoktKit ) ;
175+ const attributes :
IRoktPartnerAttributes = { email :
'[email protected] ' , phone :
'1234567890' } ; 176+ await launcher . hashAttributes ( attributes ) ;
177+ expect ( launcher . hashAttributes ) . toHaveBeenCalledWith ( attributes ) ;
178+ } ) ;
201179
202- const attributes = {
203- 204- phone : '1234567890'
205- } ;
206-
207- roktManager . hashAttributes ( attributes ) ;
208- expect ( kit . hashAttributes ) . toHaveBeenCalledWith ( attributes ) ;
209- expect ( kit . launcher . hashAttributes ) . toHaveBeenCalledWith ( attributes ) ;
180+ it ( 'should match node crypto output' , async ( ) => {
181+ const nodeCrypto = require ( 'crypto' ) ;
182+ const shaSpy = jest . spyOn ( roktManager as unknown as Hasher , 'sha256Hex' ) ;
183+ shaSpy . mockImplementation ( ( s : any ) =>
184+ Promise . resolve ( nodeCrypto . createHash ( 'sha256' ) . update ( String ( s ) ) . digest ( 'hex' ) ) ,
185+ ) ;
186+ const attributes : IRoktPartnerAttributes = {
187+ 188+ phone : ' 1234567890 ' ,
189+ blank : ' ' ,
190+ } ;
191+ const fromManager = await roktManager . hashAttributes ( attributes ) ;
192+ const expected = {
193+ emailsha256 :
nodeCrypto . createHash ( 'sha256' ) . update ( '[email protected] ' ) . digest ( 'hex' ) , 194+ phonesha256 : nodeCrypto . createHash ( 'sha256' ) . update ( '1234567890' ) . digest ( 'hex' ) ,
195+ blanksha256 : nodeCrypto . createHash ( 'sha256' ) . update ( '' ) . digest ( 'hex' ) ,
196+ } ;
197+ expect ( fromManager ) . toStrictEqual ( expected ) ;
198+ shaSpy . mockRestore ( ) ;
210199 } ) ;
211200 } ) ;
212201
@@ -543,22 +532,20 @@ describe('RoktManager', () => {
543532 expect ( kit . selectPlacements ) . toHaveBeenCalledTimes ( 3 ) ;
544533 } ) ;
545534
546- it ( 'should call RoktManager methods (not kit methods directly) when processing queue' , ( ) => {
535+ it ( 'should call RoktManager methods (not kit methods directly) when processing queue' , async ( ) => {
547536 // Queue some calls before kit is ready (these will be deferred)
548537 const selectOptions = { attributes : { test : 'value' } } as IRoktSelectPlacementsOptions ;
549538 const hashAttrs = { email :
'[email protected] ' } ; 550539 const extensionData = { 'test-ext' : { config : true } } ;
551540 const useName = 'TestExtension' ;
552541
553542 roktManager . selectPlacements ( selectOptions ) ;
554- roktManager . hashAttributes ( hashAttrs ) ;
555543 roktManager . setExtensionData ( extensionData ) ;
556544 roktManager . use ( useName ) ;
557545
558546 // Verify calls were queued
559- expect ( roktManager [ 'messageQueue' ] . size ) . toBe ( 4 ) ;
547+ expect ( roktManager [ 'messageQueue' ] . size ) . toBe ( 3 ) ;
560548 expect ( kit . selectPlacements ) . not . toHaveBeenCalled ( ) ; // Kit methods not called yet
561- expect ( kit . hashAttributes ) . not . toHaveBeenCalled ( ) ; // Kit methods not called yet
562549 expect ( kit . setExtensionData ) . not . toHaveBeenCalled ( ) ; // Kit methods not called yet
563550 expect ( kit . use ) . not . toHaveBeenCalled ( ) ; // Kit methods not called yet
564551
@@ -568,6 +555,8 @@ describe('RoktManager', () => {
568555 const setExtensionDataSpy = jest . spyOn ( roktManager , 'setExtensionData' ) ;
569556 const useSpy = jest . spyOn ( roktManager , 'use' ) ;
570557
558+ const shaSpy = jest . spyOn ( roktManager as any , 'sha256Hex' ) . mockResolvedValue ( 'deadbeef' ) ;
559+ await roktManager . hashAttributes ( hashAttrs ) ;
571560 // Attach kit (triggers processMessageQueue)
572561 roktManager . attachKit ( kit ) ;
573562
@@ -577,7 +566,7 @@ describe('RoktManager', () => {
577566
578567 expect ( hashAttributesSpy ) . toHaveBeenCalledTimes ( 1 ) ;
579568 expect ( hashAttributesSpy ) . toHaveBeenCalledWith ( hashAttrs ) ;
580-
569+
581570 expect ( setExtensionDataSpy ) . toHaveBeenCalledTimes ( 1 ) ;
582571 expect ( setExtensionDataSpy ) . toHaveBeenCalledWith ( extensionData ) ;
583572
@@ -592,6 +581,7 @@ describe('RoktManager', () => {
592581 hashAttributesSpy . mockRestore ( ) ;
593582 setExtensionDataSpy . mockRestore ( ) ;
594583 useSpy . mockRestore ( ) ;
584+ shaSpy . mockRestore ( ) ;
595585 } ) ;
596586
597587 it ( 'should preserve RoktManager preprocessing logic when processing deferred selectPlacements calls' , ( ) => {
@@ -747,6 +737,7 @@ describe('RoktManager', () => {
747737 } ) ;
748738
749739 it ( 'should process queued selectPlacements calls once the launcher and kit are attached' , async ( ) => {
740+ const shaSpy = jest . spyOn ( roktManager as any , 'sha256Hex' ) . mockResolvedValue ( 'deadbeef' ) ;
750741 const expectedResult = { placements : [ 'placement1' , 'placement2' ] } ;
751742 const kit : IRoktKit = {
752743 launcher : {
@@ -793,6 +784,7 @@ describe('RoktManager', () => {
793784 expect ( roktManager [ 'messageQueue' ] . size ) . toBe ( 0 ) ;
794785 expect ( kit . selectPlacements ) . toHaveBeenCalledWith ( options ) ;
795786 expect ( result ) . toEqual ( expectedResult ) ;
787+ shaSpy . mockRestore ( ) ;
796788 } ) ;
797789
798790 it ( 'should pass through the correct attributes to kit.selectPlacements' , ( ) => {
0 commit comments