Skip to content

Commit 8fcc713

Browse files
committed
fix: SDKE-317 Call deferred methods on Rokt Manager instead of kit
1 parent 97dd88a commit 8fcc713

2 files changed

Lines changed: 175 additions & 3 deletions

File tree

src/roktManager.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -319,22 +319,25 @@ export default class RoktManager {
319319
this.logger?.verbose(`RoktManager: Processing ${this.messageQueue.size} queued messages`);
320320

321321
this.messageQueue.forEach((message) => {
322-
if(!(message.methodName in this.kit) || !isFunction(this.kit[message.methodName])) {
323-
this.logger?.error(`RoktManager: Method ${message.methodName} not found in kit`);
322+
if(!(message.methodName in this) || !isFunction(this[message.methodName])) {
323+
this.logger?.error(`RoktManager: Method ${message.methodName} not found`);
324324
return;
325325
}
326326

327327
this.logger?.verbose(`RoktManager: Processing queued message: ${message.methodName} with payload: ${JSON.stringify(message.payload)}`);
328328

329329
try {
330-
const result = (this.kit[message.methodName] as Function)(message.payload);
330+
const result = (this[message.methodName] as Function)(message.payload);
331331
this.completePendingPromise(message.messageId, result);
332332
} catch (error) {
333333
const errorMessage = error instanceof Error ? error.message : String(error);
334334
this.logger?.error(`RoktManager: Error processing message '${message.methodName}': ${errorMessage}`);
335335
this.completePendingPromise(message.messageId, Promise.reject(error));
336336
}
337337
});
338+
339+
// Clear the queue after processing all messages
340+
this.messageQueue.clear();
338341
}
339342

340343
private queueMessage(message: IRoktMessage): void {

test/jest/roktManager.spec.ts

Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -420,6 +420,175 @@ describe('RoktManager', () => {
420420
expect(roktManager['messageQueue'].size).toBe(0);
421421
expect(kit.selectPlacements).toHaveBeenCalledTimes(3);
422422
});
423+
424+
it('should call RoktManager methods (not kit methods directly) when processing queue', () => {
425+
const kit: IRoktKit = {
426+
launcher: {
427+
selectPlacements: jest.fn(),
428+
hashAttributes: jest.fn(),
429+
use: jest.fn(),
430+
},
431+
filters: undefined,
432+
selectPlacements: jest.fn(),
433+
hashAttributes: jest.fn(),
434+
filteredUser: undefined,
435+
userAttributes: undefined,
436+
setExtensionData: jest.fn(),
437+
use: jest.fn(),
438+
};
439+
440+
// Queue some calls before kit is ready (these will be deferred)
441+
const selectOptions = { attributes: { test: 'value' } } as IRoktSelectPlacementsOptions;
442+
const hashAttrs = { email: 'test@example.com' };
443+
const extensionData = { 'test-ext': { config: true } };
444+
const useName = 'TestExtension';
445+
446+
roktManager.selectPlacements(selectOptions);
447+
roktManager.hashAttributes(hashAttrs);
448+
roktManager.setExtensionData(extensionData);
449+
roktManager.use(useName);
450+
451+
// Verify calls were queued
452+
expect(roktManager['messageQueue'].size).toBe(4);
453+
expect(kit.selectPlacements).not.toHaveBeenCalled(); // Kit methods not called yet
454+
expect(kit.hashAttributes).not.toHaveBeenCalled(); // Kit methods not called yet
455+
expect(kit.setExtensionData).not.toHaveBeenCalled(); // Kit methods not called yet
456+
expect(kit.use).not.toHaveBeenCalled(); // Kit methods not called yet
457+
458+
// Spy on RoktManager methods AFTER initial calls to track queue processing
459+
const selectPlacementsSpy = jest.spyOn(roktManager, 'selectPlacements');
460+
const hashAttributesSpy = jest.spyOn(roktManager, 'hashAttributes');
461+
const setExtensionDataSpy = jest.spyOn(roktManager, 'setExtensionData');
462+
const useSpy = jest.spyOn(roktManager, 'use');
463+
464+
// Attach kit (triggers processMessageQueue)
465+
roktManager.attachKit(kit);
466+
467+
// Verify RoktManager methods were called during queue processing
468+
expect(selectPlacementsSpy).toHaveBeenCalledTimes(1);
469+
expect(selectPlacementsSpy).toHaveBeenCalledWith(selectOptions);
470+
471+
expect(hashAttributesSpy).toHaveBeenCalledTimes(1);
472+
expect(hashAttributesSpy).toHaveBeenCalledWith(hashAttrs);
473+
474+
expect(setExtensionDataSpy).toHaveBeenCalledTimes(1);
475+
expect(setExtensionDataSpy).toHaveBeenCalledWith(extensionData);
476+
477+
expect(useSpy).toHaveBeenCalledTimes(1);
478+
expect(useSpy).toHaveBeenCalledWith(useName);
479+
480+
// Verify queue was cleared
481+
expect(roktManager['messageQueue'].size).toBe(0);
482+
483+
// Clean up spies
484+
selectPlacementsSpy.mockRestore();
485+
hashAttributesSpy.mockRestore();
486+
setExtensionDataSpy.mockRestore();
487+
useSpy.mockRestore();
488+
});
489+
490+
it('should preserve RoktManager preprocessing logic when processing deferred selectPlacements calls', () => {
491+
const kit: IRoktKit = {
492+
launcher: {
493+
selectPlacements: jest.fn(),
494+
hashAttributes: jest.fn(),
495+
use: jest.fn(),
496+
},
497+
filters: undefined,
498+
selectPlacements: jest.fn(),
499+
hashAttributes: jest.fn(),
500+
filteredUser: undefined,
501+
userAttributes: undefined,
502+
setExtensionData: jest.fn(),
503+
use: jest.fn(),
504+
};
505+
506+
// Set up placement attributes mapping to test preprocessing
507+
roktManager['placementAttributesMapping'] = [
508+
{
509+
jsmap: null,
510+
map: 'original_key',
511+
maptype: 'UserAttributeClass.Name',
512+
value: 'mapped_key'
513+
}
514+
];
515+
516+
// Set up current user mock
517+
roktManager['currentUser'] = {
518+
setUserAttributes: jest.fn(),
519+
getUserIdentities: jest.fn().mockReturnValue({
520+
userIdentities: {}
521+
})
522+
} as unknown as IMParticleUser;
523+
524+
// Queue a selectPlacements call with attributes that need mapping
525+
const originalOptions: IRoktSelectPlacementsOptions = {
526+
attributes: {
527+
original_key: 'test_value',
528+
other_attr: 'other_value'
529+
}
530+
};
531+
532+
roktManager.selectPlacements(originalOptions);
533+
expect(roktManager['messageQueue'].size).toBe(1);
534+
535+
// Attach kit (triggers processMessageQueue)
536+
roktManager.attachKit(kit);
537+
538+
// Verify the kit method was called with MAPPED attributes (proving preprocessing occurred)
539+
const expectedMappedOptions = {
540+
attributes: {
541+
mapped_key: 'test_value', // This key should be mapped
542+
other_attr: 'other_value' // This key should remain unchanged
543+
}
544+
};
545+
546+
expect(kit.selectPlacements).toHaveBeenCalledWith(expectedMappedOptions);
547+
expect(roktManager['currentUser'].setUserAttributes).toHaveBeenCalledWith({
548+
mapped_key: 'test_value',
549+
other_attr: 'other_value'
550+
});
551+
});
552+
553+
554+
it('should skip processing if method does not exist on RoktManager', () => {
555+
const kit: IRoktKit = {
556+
launcher: {
557+
selectPlacements: jest.fn(),
558+
hashAttributes: jest.fn(),
559+
use: jest.fn(),
560+
},
561+
filters: undefined,
562+
selectPlacements: jest.fn(),
563+
hashAttributes: jest.fn(),
564+
filteredUser: undefined,
565+
userAttributes: undefined,
566+
setExtensionData: jest.fn(),
567+
use: jest.fn(),
568+
};
569+
570+
// Manually add a message with a non-existent method name
571+
roktManager['queueMessage']({
572+
messageId: 'test_123',
573+
methodName: 'nonExistentMethod',
574+
payload: { test: 'data' },
575+
resolve: jest.fn(),
576+
reject: jest.fn()
577+
});
578+
579+
expect(roktManager['messageQueue'].size).toBe(1);
580+
581+
// Attach kit (triggers processMessageQueue)
582+
roktManager.attachKit(kit);
583+
584+
// Verify error was logged for non-existent method
585+
expect(mockMPInstance.Logger.error).toHaveBeenCalledWith(
586+
'RoktManager: Method nonExistentMethod not found'
587+
);
588+
589+
// Verify message was removed from queue even though method didn't exist
590+
expect(roktManager['messageQueue'].size).toBe(0);
591+
});
423592
});
424593

425594
describe('#selectPlacements', () => {

0 commit comments

Comments
 (0)