prebid.js 9.53.2 → 9.53.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (229) hide show
  1. package/dist/33acrossAnalyticsAdapter.js +1 -1
  2. package/dist/33acrossBidAdapter.js +1 -1
  3. package/dist/33acrossIdSystem.js +1 -1
  4. package/dist/BTBidAdapter.js +1 -1
  5. package/dist/adagioAnalyticsAdapter.js +1 -1
  6. package/dist/adagioBidAdapter.js +1 -1
  7. package/dist/adagioRtdProvider.js +1 -1
  8. package/dist/adagioUtils.js +1 -1
  9. package/dist/addefendBidAdapter.js +1 -1
  10. package/dist/adgenerationBidAdapter.js +1 -1
  11. package/dist/adlooxRtdProvider.js +1 -1
  12. package/dist/adqueryBidAdapter.js +1 -1
  13. package/dist/adrelevantisBidAdapter.js +1 -1
  14. package/dist/adstirBidAdapter.js +1 -1
  15. package/dist/adtrgtmeBidAdapter.js +1 -1
  16. package/dist/adxcgAnalyticsAdapter.js +1 -1
  17. package/dist/adxcgBidAdapter.js +1 -1
  18. package/dist/adyoulikeBidAdapter.js +1 -1
  19. package/dist/agmaAnalyticsAdapter.js +1 -1
  20. package/dist/ajaBidAdapter.js +1 -1
  21. package/dist/amxBidAdapter.js +1 -1
  22. package/dist/amxIdSystem.js +1 -1
  23. package/dist/aniviewBidAdapter.js +1 -1
  24. package/dist/appierAnalyticsAdapter.js +1 -1
  25. package/dist/appnexusBidAdapter.js +1 -1
  26. package/dist/asoBidAdapter.js +1 -1
  27. package/dist/axonixBidAdapter.js +1 -1
  28. package/dist/beopBidAdapter.js +1 -1
  29. package/dist/bidderTimeoutUtils.js +1 -0
  30. package/dist/bidglassBidAdapter.js +1 -1
  31. package/dist/big-richmediaBidAdapter.js +1 -1
  32. package/dist/bitmediaBidAdapter.js +1 -1
  33. package/dist/bridBidAdapter.js +1 -1
  34. package/dist/bridgeuppBidAdapter.js +1 -1
  35. package/dist/bridgewellBidAdapter.js +1 -1
  36. package/dist/brightMountainMediaBidAdapter.js +1 -1
  37. package/dist/carodaBidAdapter.js +1 -1
  38. package/dist/chtnwBidAdapter.js +1 -1
  39. package/dist/chunk-core.js +1 -1
  40. package/dist/concertBidAdapter.js +1 -1
  41. package/dist/connectadBidAdapter.js +1 -1
  42. package/dist/consumableBidAdapter.js +1 -1
  43. package/dist/contxtfulBidAdapter.js +1 -1
  44. package/dist/conversantAnalyticsAdapter.js +1 -1
  45. package/dist/conversantBidAdapter.js +1 -1
  46. package/dist/craftBidAdapter.js +1 -1
  47. package/dist/criteoBidAdapter.js +1 -1
  48. package/dist/cwireBidAdapter.js +1 -1
  49. package/dist/dailymotionBidAdapter.js +1 -1
  50. package/dist/debugging-standalone.js +1 -1
  51. package/dist/dependencies.json +10 -1
  52. package/dist/dspxBidAdapter.js +1 -1
  53. package/dist/dxkultureBidAdapter.js +1 -1
  54. package/dist/eplanningBidAdapter.js +1 -1
  55. package/dist/equativBidAdapter.js +1 -1
  56. package/dist/eskimiBidAdapter.js +1 -1
  57. package/dist/euidIdSystem.js +1 -1
  58. package/dist/exadsBidAdapter.js +1 -1
  59. package/dist/excoBidAdapter.js +1 -1
  60. package/dist/feedadBidAdapter.js +1 -1
  61. package/dist/finativeBidAdapter.js +1 -1
  62. package/dist/freewheel-sspBidAdapter.js +1 -1
  63. package/dist/fwsspBidAdapter.js +1 -1
  64. package/dist/gmosspBidAdapter.js +1 -1
  65. package/dist/greenbidsAnalyticsAdapter.js +1 -1
  66. package/dist/greenbidsBidAdapter.js +1 -1
  67. package/dist/greenbidsRtdProvider.js +1 -1
  68. package/dist/gridBidAdapter.js +1 -1
  69. package/dist/gumgumBidAdapter.js +1 -1
  70. package/dist/h12mediaBidAdapter.js +1 -1
  71. package/dist/hypelabBidAdapter.js +1 -1
  72. package/dist/id5AnalyticsAdapter.js +1 -1
  73. package/dist/id5IdSystem.js +1 -1
  74. package/dist/imdsBidAdapter.js +1 -1
  75. package/dist/improvedigitalBidAdapter.js +1 -1
  76. package/dist/inmobiBidAdapter.js +1 -1
  77. package/dist/insticatorBidAdapter.js +1 -1
  78. package/dist/intentIqAnalyticsAdapter.js +1 -1
  79. package/dist/ixBidAdapter.js +1 -1
  80. package/dist/jixieBidAdapter.js +1 -1
  81. package/dist/justpremiumBidAdapter.js +1 -1
  82. package/dist/kargoBidAdapter.js +1 -1
  83. package/dist/kimberliteBidAdapter.js +1 -1
  84. package/dist/konduitAnalyticsAdapter.js +1 -1
  85. package/dist/kueezBidAdapter.js +1 -1
  86. package/dist/lassoBidAdapter.js +1 -1
  87. package/dist/lifestreetBidAdapter.js +1 -1
  88. package/dist/liveIntentId.js +1 -1
  89. package/dist/logicadBidAdapter.js +1 -1
  90. package/dist/loglyliftBidAdapter.js +1 -1
  91. package/dist/luceadBidAdapter.js +1 -1
  92. package/dist/mabidderBidAdapter.js +1 -1
  93. package/dist/madsenseBidAdapter.js +1 -1
  94. package/dist/magniteAnalyticsAdapter.js +1 -1
  95. package/dist/malltvAnalyticsAdapter.js +1 -1
  96. package/dist/marsmediaBidAdapter.js +1 -1
  97. package/dist/mediafuseBidAdapter.js +1 -1
  98. package/dist/medianetBidAdapter.js +1 -1
  99. package/dist/medianetUtils.js +1 -1
  100. package/dist/mediasquareBidAdapter.js +1 -1
  101. package/dist/mgidBidAdapter.js +1 -1
  102. package/dist/missenaBidAdapter.js +1 -1
  103. package/dist/mobilefuseBidAdapter.js +1 -1
  104. package/dist/nextMillenniumBidAdapter.js +1 -1
  105. package/dist/nexx360Utils.js +1 -1
  106. package/dist/nobidAnalyticsAdapter.js +1 -1
  107. package/dist/nobidBidAdapter.js +1 -1
  108. package/dist/nodalsAiRtdProvider.js +1 -1
  109. package/dist/not-for-prod/prebid.js +178 -175
  110. package/dist/objectGuard.js +1 -1
  111. package/dist/oguryBidAdapter.js +1 -1
  112. package/dist/onetagBidAdapter.js +1 -1
  113. package/dist/ooloAnalyticsAdapter.js +1 -1
  114. package/dist/openxBidAdapter.js +1 -1
  115. package/dist/optableRtdProvider.js +1 -1
  116. package/dist/optidigitalBidAdapter.js +1 -1
  117. package/dist/orbidderBidAdapter.js +1 -1
  118. package/dist/outbrainBidAdapter.js +1 -1
  119. package/dist/pixfutureBidAdapter.js +1 -1
  120. package/dist/publinkIdSystem.js +1 -1
  121. package/dist/pubmaticAnalyticsAdapter.js +1 -1
  122. package/dist/pubmaticBidAdapter.js +1 -1
  123. package/dist/pubmaticIdSystem.js +1 -1
  124. package/dist/pubmaticRtdProvider.js +1 -1
  125. package/dist/pubmaticUtils.js +1 -0
  126. package/dist/pubwiseAnalyticsAdapter.js +1 -1
  127. package/dist/pubxaiAnalyticsAdapter.js +1 -1
  128. package/dist/pxyzBidAdapter.js +1 -1
  129. package/dist/quantcastBidAdapter.js +1 -1
  130. package/dist/readpeakBidAdapter.js +1 -1
  131. package/dist/relaidoBidAdapter.js +1 -1
  132. package/dist/retailspotBidAdapter.js +1 -1
  133. package/dist/rhythmoneBidAdapter.js +1 -1
  134. package/dist/riseUtils.js +1 -1
  135. package/dist/rtdModule.js +1 -1
  136. package/dist/rubiconBidAdapter.js +1 -1
  137. package/dist/seedingAllianceBidAdapter.js +1 -1
  138. package/dist/seedtagBidAdapter.js +1 -1
  139. package/dist/sevioBidAdapter.js +1 -0
  140. package/dist/sharethroughAnalyticsAdapter.js +1 -1
  141. package/dist/sharethroughBidAdapter.js +1 -1
  142. package/dist/showheroes-bsBidAdapter.js +1 -1
  143. package/dist/smaatoBidAdapter.js +1 -1
  144. package/dist/smartadserverBidAdapter.js +1 -1
  145. package/dist/smartxBidAdapter.js +1 -1
  146. package/dist/smilewantedBidAdapter.js +1 -1
  147. package/dist/snigelBidAdapter.js +1 -1
  148. package/dist/sonobiBidAdapter.js +1 -1
  149. package/dist/sovrnBidAdapter.js +1 -1
  150. package/dist/sparteoBidAdapter.js +1 -1
  151. package/dist/sspBCBidAdapter.js +1 -1
  152. package/dist/stvBidAdapter.js +1 -1
  153. package/dist/sublimeBidAdapter.js +1 -1
  154. package/dist/taboolaBidAdapter.js +1 -1
  155. package/dist/tappxBidAdapter.js +1 -1
  156. package/dist/targetVideoBidAdapter.js +1 -1
  157. package/dist/teadsBidAdapter.js +1 -1
  158. package/dist/terceptAnalyticsAdapter.js +1 -1
  159. package/dist/themoneytizerBidAdapter.js +1 -1
  160. package/dist/timeoutRtdProvider.js +1 -1
  161. package/dist/trionBidAdapter.js +1 -1
  162. package/dist/tripleliftBidAdapter.js +1 -1
  163. package/dist/ttdBidAdapter.js +1 -1
  164. package/dist/ucfunnelAnalyticsAdapter.js +1 -1
  165. package/dist/uid2IdSystem.js +1 -1
  166. package/dist/underdogmediaBidAdapter.js +1 -1
  167. package/dist/undertoneBidAdapter.js +1 -1
  168. package/dist/unrulyBidAdapter.js +1 -1
  169. package/dist/userId.js +1 -1
  170. package/dist/vidazooUtils.js +1 -1
  171. package/dist/videobyteBidAdapter.js +1 -1
  172. package/dist/visxBidAdapter.js +1 -1
  173. package/dist/vuukleBidAdapter.js +1 -1
  174. package/dist/widespaceBidAdapter.js +1 -1
  175. package/dist/winrBidAdapter.js +1 -1
  176. package/dist/yahooAdsBidAdapter.js +1 -1
  177. package/dist/yandexBidAdapter.js +1 -1
  178. package/dist/yieldmoBidAdapter.js +1 -1
  179. package/dist/yieldoneAnalyticsAdapter.js +1 -1
  180. package/integrationExamples/gpt/pubmaticRtdProvider_Example.html +161 -0
  181. package/libraries/bidderTimeoutUtils/bidderTimeoutUtils.js +119 -0
  182. package/libraries/objectGuard/objectGuard.js +170 -48
  183. package/libraries/objectGuard/ortbGuard.js +33 -43
  184. package/libraries/pubmaticUtils/plugins/dynamicTimeout.js +209 -0
  185. package/libraries/pubmaticUtils/plugins/floorProvider.js +168 -0
  186. package/libraries/pubmaticUtils/plugins/pluginManager.js +106 -0
  187. package/libraries/pubmaticUtils/plugins/unifiedPricingRule.js +375 -0
  188. package/libraries/pubmaticUtils/pubmaticUtils.js +76 -0
  189. package/modules/adagioAnalyticsAdapter.js +6 -1
  190. package/modules/adagioBidAdapter.js +12 -5
  191. package/modules/adagioRtdProvider.js +41 -35
  192. package/modules/fwsspBidAdapter.js +134 -69
  193. package/modules/fwsspBidAdapter.md +121 -26
  194. package/modules/optableRtdProvider.js +33 -12
  195. package/modules/pubmaticAnalyticsAdapter.js +315 -587
  196. package/modules/pubmaticBidAdapter.js +71 -8
  197. package/modules/pubmaticIdSystem.js +4 -4
  198. package/modules/pubmaticRtdProvider.js +105 -613
  199. package/modules/rtdModule/index.js +23 -6
  200. package/modules/sevioBidAdapter.js +413 -0
  201. package/modules/sevioBidAdapter.md +29 -0
  202. package/modules/sparteoBidAdapter.js +122 -10
  203. package/modules/timeoutRtdProvider.js +2 -105
  204. package/modules/ttdBidAdapter.js +0 -5
  205. package/modules/userId/eids.js +1 -1
  206. package/modules/userId/index.js +32 -1
  207. package/package.json +1 -1
  208. package/src/auction.js +3 -0
  209. package/test/spec/activities/objectGuard_spec.js +189 -32
  210. package/test/spec/activities/ortbGuard_spec.js +10 -15
  211. package/test/spec/libraries/bidderTimeoutUtils/bidderTimeoutUtils_spec.js +213 -0
  212. package/test/spec/libraries/pubmaticUtils/plugins/dynamicTimeout_spec.js +746 -0
  213. package/test/spec/libraries/pubmaticUtils/plugins/floorProvider_spec.js +184 -0
  214. package/test/spec/libraries/pubmaticUtils/plugins/pluginManager_spec.js +489 -0
  215. package/test/spec/libraries/pubmaticUtils/plugins/unifiedPricingRule_spec.js +359 -0
  216. package/test/spec/libraries/pubmaticUtils/pubmaticUtils_spec.js +236 -0
  217. package/test/spec/modules/adagioAnalyticsAdapter_spec.js +94 -24
  218. package/test/spec/modules/adagioRtdProvider_spec.js +17 -17
  219. package/test/spec/modules/fwsspBidAdapter_spec.js +513 -78
  220. package/test/spec/modules/optableRtdProvider_spec.js +55 -5
  221. package/test/spec/modules/pubmaticAnalyticsAdapter_spec.js +634 -916
  222. package/test/spec/modules/pubmaticBidAdapter_spec.js +260 -1
  223. package/test/spec/modules/pubmaticRtdProvider_spec.js +252 -1505
  224. package/test/spec/modules/realTimeDataModule_spec.js +58 -8
  225. package/test/spec/modules/sevioBidAdapter_spec.js +513 -0
  226. package/test/spec/modules/sparteoBidAdapter_spec.js +528 -43
  227. package/test/spec/modules/timeoutRtdProvider_spec.js +1 -201
  228. package/test/spec/modules/ttdBidAdapter_spec.js +0 -33
  229. package/test/spec/modules/userId_spec.js +115 -1
@@ -1,1563 +1,310 @@
1
1
  import { expect } from 'chai';
2
- import * as priceFloors from '../../../modules/priceFloors';
3
- import * as utils from '../../../src/utils.js';
4
- import * as suaModule from '../../../src/fpd/sua.js';
5
- import { config as conf } from '../../../src/config';
6
- import * as hook from '../../../src/hook.js';
7
- import * as prebidGlobal from '../../../src/prebidGlobal.js';
8
- import {
9
- registerSubModule, pubmaticSubmodule, getFloorsConfig, fetchData,
10
- getCurrentTimeOfDay, getBrowserType, getOs, getDeviceType, getCountry, getUtm, getBidder, _country,
11
- _profileConfigs, _floorsData, defaultValueTemplate, withTimeout, configMerged,
12
- getProfileConfigs, setProfileConfigs, getTargetingData
13
- } from '../../../modules/pubmaticRtdProvider.js';
14
2
  import sinon from 'sinon';
3
+ import * as utils from '../../../src/utils.js';
4
+ import * as pubmaticRtdProvider from '../../../modules/pubmaticRtdProvider.js';
5
+ import { FloorProvider } from '../../../libraries/pubmaticUtils/plugins/floorProvider.js';
6
+ import { UnifiedPricingRule } from '../../../libraries/pubmaticUtils/plugins/unifiedPricingRule.js';
15
7
 
16
8
  describe('Pubmatic RTD Provider', () => {
17
- let sandbox;
18
-
19
- beforeEach(() => {
20
- sandbox = sinon.createSandbox();
21
- sandbox.stub(conf, 'getConfig').callsFake(() => {
22
- return {
23
- floors: {
24
- 'enforcement': {
25
- 'floorDeals': true,
26
- 'enforceJS': true
27
- }
28
- },
29
- realTimeData: {
30
- auctionDelay: 100
31
- }
32
- };
33
- });
9
+ let sandbox;
10
+ let fetchStub;
11
+ let logErrorStub;
12
+ let originalPluginManager;
13
+ let originalConfigJsonManager;
14
+ let pluginManagerStub;
15
+ let configJsonManagerStub;
16
+
17
+ beforeEach(() => {
18
+ sandbox = sinon.createSandbox();
19
+ fetchStub = sandbox.stub(window, 'fetch');
20
+ logErrorStub = sinon.stub(utils, 'logError');
21
+
22
+ // Store original implementations
23
+ originalPluginManager = Object.assign({}, pubmaticRtdProvider.pluginManager);
24
+ originalConfigJsonManager = Object.assign({}, pubmaticRtdProvider.configJsonManager);
25
+
26
+ // Create stubs
27
+ pluginManagerStub = {
28
+ initialize: sinon.stub().resolves(),
29
+ executeHook: sinon.stub().resolves(),
30
+ register: sinon.stub()
31
+ };
32
+
33
+ configJsonManagerStub = {
34
+ fetchConfig: sinon.stub().resolves(true),
35
+ getYMConfig: sinon.stub(),
36
+ getConfigByName: sinon.stub(),
37
+ country: 'IN'
38
+ };
39
+
40
+ // Replace exported objects with stubs
41
+ Object.keys(pluginManagerStub).forEach(key => {
42
+ pubmaticRtdProvider.pluginManager[key] = pluginManagerStub[key];
34
43
  });
35
44
 
36
- afterEach(() => {
37
- sandbox.restore();
38
- });
39
-
40
- describe('registerSubModule', () => {
41
- it('should register RTD submodule provider', () => {
42
- let submoduleStub = sinon.stub(hook, 'submodule');
43
- registerSubModule();
44
- assert(submoduleStub.calledOnceWith('realTimeData', pubmaticSubmodule));
45
- submoduleStub.restore();
45
+ Object.keys(configJsonManagerStub).forEach(key => {
46
+ if (key === 'country') {
47
+ Object.defineProperty(pubmaticRtdProvider.configJsonManager, key, {
48
+ get: () => configJsonManagerStub[key]
46
49
  });
50
+ } else {
51
+ pubmaticRtdProvider.configJsonManager[key] = configJsonManagerStub[key];
52
+ }
47
53
  });
48
54
 
49
- describe('submodule', () => {
50
- describe('name', () => {
51
- it('should be pubmatic', () => {
52
- expect(pubmaticSubmodule.name).to.equal('pubmatic');
53
- });
54
- });
55
- });
55
+ // Reset _ymConfigPromise for each test
56
+ pubmaticRtdProvider.setYmConfigPromise(Promise.resolve());
57
+ });
56
58
 
57
- describe('init', () => {
58
- let logErrorStub;
59
- let continueAuctionStub;
60
-
61
- const getConfig = () => ({
62
- params: {
63
- publisherId: 'test-publisher-id',
64
- profileId: 'test-profile-id'
65
- },
66
- });
67
-
68
- beforeEach(() => {
69
- logErrorStub = sandbox.stub(utils, 'logError');
70
- continueAuctionStub = sandbox.stub(priceFloors, 'continueAuction');
71
- });
72
-
73
- it('should return false if publisherId is missing', () => {
74
- const config = {
75
- params: {
76
- profileId: 'test-profile-id'
77
- }
78
- };
79
- expect(pubmaticSubmodule.init(config)).to.be.false;
80
- });
81
-
82
- it('should return false if profileId is missing', () => {
83
- const config = {
84
- params: {
85
- publisherId: 'test-publisher-id'
86
- }
87
- };
88
- expect(pubmaticSubmodule.init(config)).to.be.false;
89
- });
59
+ afterEach(() => {
60
+ sandbox.restore();
61
+ logErrorStub.restore();
90
62
 
91
- it('should return false if publisherId is not a string', () => {
92
- const config = {
93
- params: {
94
- publisherId: 123,
95
- profileId: 'test-profile-id'
96
- }
97
- };
98
- expect(pubmaticSubmodule.init(config)).to.be.false;
99
- });
100
-
101
- it('should return false if profileId is not a string', () => {
102
- const config = {
103
- params: {
104
- publisherId: 'test-publisher-id',
105
- profileId: 345
106
- }
107
- };
108
- expect(pubmaticSubmodule.init(config)).to.be.false;
109
- });
110
-
111
- it('should initialize successfully with valid config', () => {
112
- expect(pubmaticSubmodule.init(getConfig())).to.be.true;
113
- });
114
-
115
- it('should handle empty config object', () => {
116
- expect(pubmaticSubmodule.init({})).to.be.false;
117
- expect(logErrorStub.calledWith(sinon.match(/Missing publisher Id/))).to.be.true;
118
- });
119
-
120
- it('should return false if continueAuction is not a function', () => {
121
- continueAuctionStub.value(undefined);
122
- expect(pubmaticSubmodule.init(getConfig())).to.be.false;
123
- expect(logErrorStub.calledWith(sinon.match(/continueAuction is not a function/))).to.be.true;
124
- });
63
+ // Restore original implementations
64
+ Object.keys(originalPluginManager).forEach(key => {
65
+ pubmaticRtdProvider.pluginManager[key] = originalPluginManager[key];
125
66
  });
126
67
 
127
- describe('getCurrentTimeOfDay', () => {
128
- let clock;
129
-
130
- beforeEach(() => {
131
- clock = sandbox.useFakeTimers(new Date('2024-01-01T12:00:00')); // Set fixed time for testing
132
- });
133
-
134
- afterEach(() => {
135
- clock.restore();
136
- });
137
-
138
- const testTimes = [
139
- { hour: 6, expected: 'morning' },
140
- { hour: 13, expected: 'afternoon' },
141
- { hour: 18, expected: 'evening' },
142
- { hour: 22, expected: 'night' },
143
- { hour: 4, expected: 'night' }
144
- ];
145
-
146
- testTimes.forEach(({ hour, expected }) => {
147
- it(`should return ${expected} at ${hour}:00`, () => {
148
- clock.setSystemTime(new Date().setHours(hour));
149
- const result = getCurrentTimeOfDay();
150
- expect(result).to.equal(expected);
151
- });
68
+ Object.keys(originalConfigJsonManager).forEach(key => {
69
+ if (key === 'country') {
70
+ Object.defineProperty(pubmaticRtdProvider.configJsonManager, 'country', {
71
+ get: () => originalConfigJsonManager[key]
152
72
  });
73
+ } else {
74
+ pubmaticRtdProvider.configJsonManager[key] = originalConfigJsonManager[key];
75
+ }
153
76
  });
154
-
155
- describe('getBrowserType', () => {
156
- let userAgentStub, getLowEntropySUAStub;
157
-
158
- const USER_AGENTS = {
159
- chrome: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36',
160
- firefox: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:89.0) Gecko/20100101 Firefox/89.0',
161
- edge: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Edg/91.0.864.67 Safari/537.36',
162
- safari: 'Mozilla/5.0 (iPhone; CPU iPhone OS 14_6 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0.6 Mobile/15E148 Safari/604.1',
163
- ie: 'Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; Trident/6.0)',
164
- opera: 'Opera/9.80 (Windows NT 6.1; WOW64) Presto/2.12.388 Version/12.16',
165
- unknown: 'UnknownBrowser/1.0'
166
- };
167
-
168
- beforeEach(() => {
169
- userAgentStub = sandbox.stub(navigator, 'userAgent');
170
- getLowEntropySUAStub = sandbox.stub(suaModule, 'getLowEntropySUA').returns(undefined);
171
- });
172
-
173
- afterEach(() => {
174
- userAgentStub.restore();
175
- getLowEntropySUAStub.restore();
176
- });
177
-
178
- it('should detect Chrome', () => {
179
- userAgentStub.value(USER_AGENTS.chrome);
180
- expect(getBrowserType()).to.equal('9');
181
- });
182
-
183
- it('should detect Firefox', () => {
184
- userAgentStub.value(USER_AGENTS.firefox);
185
- expect(getBrowserType()).to.equal('12');
186
- });
187
-
188
- it('should detect Edge', () => {
189
- userAgentStub.value(USER_AGENTS.edge);
190
- expect(getBrowserType()).to.equal('2');
191
- });
192
-
193
- it('should detect Internet Explorer', () => {
194
- userAgentStub.value(USER_AGENTS.ie);
195
- expect(getBrowserType()).to.equal('4');
196
- });
197
-
198
- it('should detect Opera', () => {
199
- userAgentStub.value(USER_AGENTS.opera);
200
- expect(getBrowserType()).to.equal('3');
201
- });
202
-
203
- it('should return 0 for unknown browser', () => {
204
- userAgentStub.value(USER_AGENTS.unknown);
205
- expect(getBrowserType()).to.equal('0');
206
- });
207
-
208
- it('should return -1 when userAgent is null', () => {
209
- userAgentStub.value(null);
210
- expect(getBrowserType()).to.equal('-1');
211
- });
77
+ });
78
+
79
+ describe('init', () => {
80
+ const validConfig = {
81
+ params: {
82
+ publisherId: 'test-publisher-id',
83
+ profileId: 'test-profile-id'
84
+ }
85
+ };
86
+
87
+ it('should return false if publisherId is missing', () => {
88
+ const config = {
89
+ params: {
90
+ profileId: 'test-profile-id'
91
+ }
92
+ };
93
+ const result = pubmaticRtdProvider.pubmaticSubmodule.init(config);
94
+ expect(result).to.be.false;
95
+ expect(logErrorStub.calledOnce).to.be.true;
96
+ expect(logErrorStub.firstCall.args[0]).to.equal(`${pubmaticRtdProvider.CONSTANTS.LOG_PRE_FIX} Missing publisher Id.`);
212
97
  });
213
98
 
214
- describe('Utility functions', () => {
215
- it('should set browser correctly', () => {
216
- expect(getBrowserType()).to.be.a('string');
217
- });
218
-
219
- it('should set OS correctly', () => {
220
- expect(getOs()).to.be.a('string');
221
- });
222
-
223
- it('should set device type correctly', () => {
224
- expect(getDeviceType()).to.be.a('string');
225
- });
226
-
227
- it('should set time of day correctly', () => {
228
- expect(getCurrentTimeOfDay()).to.be.a('string');
229
- });
230
-
231
- it('should set country correctly', () => {
232
- expect(getCountry()).to.satisfy(value => typeof value === 'string' || value === undefined);
233
- });
234
-
235
- it('should set UTM correctly', () => {
236
- expect(getUtm()).to.be.a('string');
237
- expect(getUtm()).to.be.oneOf(['0', '1']);
238
- });
239
-
240
- it('should extract bidder correctly', () => {
241
- expect(getBidder({ bidder: 'pubmatic' })).to.equal('pubmatic');
242
- expect(getBidder({})).to.be.undefined;
243
- expect(getBidder(null)).to.be.undefined;
244
- expect(getBidder(undefined)).to.be.undefined;
245
- });
99
+ it('should return false if publisherId is not a string', () => {
100
+ const config = {
101
+ params: {
102
+ publisherId: 123,
103
+ profileId: 'test-profile-id'
104
+ }
105
+ };
106
+ const result = pubmaticRtdProvider.pubmaticSubmodule.init(config);
107
+ expect(result).to.be.false;
108
+ expect(logErrorStub.calledOnce).to.be.true;
109
+ expect(logErrorStub.firstCall.args[0]).to.equal(`${pubmaticRtdProvider.CONSTANTS.LOG_PRE_FIX} Publisher Id should be a string.`);
246
110
  });
247
111
 
248
- describe('getFloorsConfig', () => {
249
- let floorsData, profileConfigs;
250
- let sandbox;
251
- let logErrorStub;
252
-
253
- beforeEach(() => {
254
- sandbox = sinon.createSandbox();
255
- logErrorStub = sandbox.stub(utils, 'logError');
256
- floorsData = {
257
- "currency": "USD",
258
- "floorProvider": "PM",
259
- "floorsSchemaVersion": 2,
260
- "modelGroups": [
261
- {
262
- "modelVersion": "M_1",
263
- "modelWeight": 100,
264
- "schema": {
265
- "fields": [
266
- "domain"
267
- ]
268
- },
269
- "values": {
270
- "*": 2.00
271
- }
272
- }
273
- ],
274
- "skipRate": 0
275
- };
276
- profileConfigs = {
277
- 'plugins': {
278
- 'dynamicFloors': {
279
- 'enabled': true,
280
- 'config': {
281
- 'enforcement': {
282
- 'floorDeals': false,
283
- 'enforceJS': false
284
- },
285
- 'floorMin': 0.1111,
286
- 'skipRate': 11,
287
- 'defaultValues': {
288
- "*|*": 0.2
289
- }
290
- }
291
- }
292
- }
293
- }
294
- });
295
-
296
- afterEach(() => {
297
- sandbox.restore();
298
- });
299
-
300
- it('should return correct config structure', () => {
301
- const result = getFloorsConfig(floorsData, profileConfigs);
302
-
303
- expect(result.floors).to.be.an('object');
304
- expect(result.floors).to.be.an('object');
305
- expect(result.floors).to.have.property('enforcement');
306
- expect(result.floors.enforcement).to.have.property('floorDeals', false);
307
- expect(result.floors.enforcement).to.have.property('enforceJS', false);
308
- expect(result.floors).to.have.property('floorMin', 0.1111);
309
-
310
- // Verify the additionalSchemaFields structure
311
- expect(result.floors.additionalSchemaFields).to.have.all.keys([
312
- 'deviceType',
313
- 'timeOfDay',
314
- 'browser',
315
- 'os',
316
- 'country',
317
- 'utm',
318
- 'bidder'
319
- ]);
320
-
321
- Object.values(result.floors.additionalSchemaFields).forEach(field => {
322
- expect(field).to.be.a('function');
323
- });
324
- });
325
-
326
- it('should return undefined when plugin is disabled', () => {
327
- profileConfigs.plugins.dynamicFloors.enabled = false;
328
- const result = getFloorsConfig(floorsData, profileConfigs);
329
-
330
- expect(result).to.equal(undefined);
331
- });
332
-
333
- it('should initialise default values to empty object when not available', () => {
334
- profileConfigs.plugins.dynamicFloors.config.defaultValues = undefined;
335
- floorsData = undefined;
336
- const result = getFloorsConfig(floorsData, profileConfigs);
337
-
338
- expect(result.floors.data).to.have.property('currency', 'USD');
339
- expect(result.floors.data).to.have.property('skipRate', 11);
340
- expect(result.floors.data.schema).to.deep.equal(defaultValueTemplate.schema);
341
- expect(result.floors.data.value).to.deep.equal(defaultValueTemplate.value);
342
- });
343
-
344
- it('should replace skipRate from config to data when avaialble', () => {
345
- const result = getFloorsConfig(floorsData, profileConfigs);
346
-
347
- expect(result.floors.data).to.have.property('skipRate', 11);
348
- });
349
-
350
- it('should not replace skipRate from config to data when not avaialble', () => {
351
- delete profileConfigs.plugins.dynamicFloors.config.skipRate;
352
- const result = getFloorsConfig(floorsData, profileConfigs);
353
-
354
- expect(result.floors.data).to.have.property('skipRate', 0);
355
- });
356
-
357
- it('should maintain correct function references', () => {
358
- const result = getFloorsConfig(floorsData, profileConfigs);
359
-
360
- expect(result.floors.additionalSchemaFields.deviceType).to.equal(getDeviceType);
361
- expect(result.floors.additionalSchemaFields.timeOfDay).to.equal(getCurrentTimeOfDay);
362
- expect(result.floors.additionalSchemaFields.browser).to.equal(getBrowserType);
363
- expect(result.floors.additionalSchemaFields.os).to.equal(getOs);
364
- expect(result.floors.additionalSchemaFields.country).to.equal(getCountry);
365
- expect(result.floors.additionalSchemaFields.utm).to.equal(getUtm);
366
- expect(result.floors.additionalSchemaFields.bidder).to.equal(getBidder);
367
- });
368
-
369
- it('should log error when profileConfigs is not an object', () => {
370
- profileConfigs = 'invalid';
371
- const result = getFloorsConfig(floorsData, profileConfigs);
372
- expect(result).to.be.undefined;
373
- expect(logErrorStub.calledWith(sinon.match(/profileConfigs is not an object or is empty/))).to.be.true;
374
- });
112
+ it('should return false if profileId is missing', () => {
113
+ const config = {
114
+ params: {
115
+ publisherId: 'test-publisher-id'
116
+ }
117
+ };
118
+ const result = pubmaticRtdProvider.pubmaticSubmodule.init(config);
119
+ expect(result).to.be.false;
120
+ expect(logErrorStub.calledOnce).to.be.true;
121
+ expect(logErrorStub.firstCall.args[0]).to.equal(`${pubmaticRtdProvider.CONSTANTS.LOG_PRE_FIX} Missing profile Id.`);
375
122
  });
376
123
 
377
- describe('fetchData for configs', () => {
378
- let logErrorStub;
379
- let fetchStub;
380
- let confStub;
381
-
382
- beforeEach(() => {
383
- logErrorStub = sandbox.stub(utils, 'logError');
384
- fetchStub = sandbox.stub(window, 'fetch');
385
- confStub = sandbox.stub(conf, 'setConfig');
386
- });
387
-
388
- afterEach(() => {
389
- sandbox.restore();
390
- });
391
-
392
- it('should successfully fetch profile configs', async () => {
393
- const mockApiResponse = {
394
- "profileName": "profie name",
395
- "desc": "description",
396
- "plugins": {
397
- "dynamicFloors": {
398
- "enabled": false
399
- }
400
- }
401
- };
402
-
403
- fetchStub.resolves(new Response(JSON.stringify(mockApiResponse), { status: 200 }));
404
-
405
- const result = await fetchData('1234', '123', 'CONFIGS');
406
- expect(result).to.deep.equal(mockApiResponse);
407
- });
408
-
409
- it('should log error when JSON parsing fails', async () => {
410
- fetchStub.resolves(new Response('Invalid JSON', { status: 200 }));
411
-
412
- await fetchData('1234', '123', 'CONFIGS');
413
- expect(logErrorStub.calledWith(sinon.match(/Error while fetching\s*CONFIGS/))).to.be.true;
414
- });
415
-
416
- it('should log error when response is not ok', async () => {
417
- fetchStub.resolves(new Response(null, { status: 500 }));
418
-
419
- await fetchData('1234', '123', 'CONFIGS');
420
- expect(logErrorStub.calledWith(sinon.match(/Error while fetching\s*CONFIGS/))).to.be.true;
421
- });
422
-
423
- it('should log error on network failure', async () => {
424
- fetchStub.rejects(new Error('Network Error'));
425
-
426
- await fetchData('1234', '123', 'CONFIGS');
427
- expect(logErrorStub.called).to.be.true;
428
- expect(logErrorStub.calledWith(sinon.match(/Error while fetching\s*CONFIGS/))).to.be.true;
429
- });
124
+ it('should return false if profileId is not a string', () => {
125
+ const config = {
126
+ params: {
127
+ publisherId: 'test-publisher-id',
128
+ profileId: 345
129
+ }
130
+ };
131
+ const result = pubmaticRtdProvider.pubmaticSubmodule.init(config);
132
+ expect(result).to.be.false;
133
+ expect(logErrorStub.calledOnce).to.be.true;
134
+ expect(logErrorStub.firstCall.args[0]).to.equal(`${pubmaticRtdProvider.CONSTANTS.LOG_PRE_FIX} Profile Id should be a string.`);
430
135
  });
431
136
 
432
- describe('fetchData for floors', () => {
433
- let logErrorStub;
434
- let fetchStub;
435
- let confStub;
436
-
437
- beforeEach(() => {
438
- logErrorStub = sandbox.stub(utils, 'logError');
439
- fetchStub = sandbox.stub(window, 'fetch');
440
- confStub = sandbox.stub(conf, 'setConfig');
441
- global._country = undefined;
442
- });
137
+ it('should initialize successfully with valid config', async () => {
138
+ configJsonManagerStub.fetchConfig.resolves(true);
139
+ pluginManagerStub.initialize.resolves();
443
140
 
444
- afterEach(() => {
445
- sandbox.restore();
446
- });
141
+ const result = pubmaticRtdProvider.pubmaticSubmodule.init(validConfig);
142
+ expect(result).to.be.true;
143
+ expect(configJsonManagerStub.fetchConfig.calledOnce).to.be.true;
144
+ expect(configJsonManagerStub.fetchConfig.firstCall.args[0]).to.equal('test-publisher-id');
145
+ expect(configJsonManagerStub.fetchConfig.firstCall.args[1]).to.equal('test-profile-id');
447
146
 
448
- it('should successfully fetch and parse floor rules', async () => {
449
- const mockApiResponse = {
450
- data: {
451
- currency: 'USD',
452
- modelGroups: [],
453
- values: {}
454
- }
455
- };
456
-
457
- fetchStub.resolves(new Response(JSON.stringify(mockApiResponse), { status: 200, headers: { 'country_code': 'US' } }));
458
-
459
- const result = await fetchData('1234', '123', 'FLOORS');
460
- expect(result).to.deep.equal(mockApiResponse);
461
- expect(_country).to.equal('US');
462
- });
463
-
464
- it('should correctly extract the first unique country code from response headers', async () => {
465
- fetchStub.resolves(new Response(JSON.stringify({}), {
466
- status: 200,
467
- headers: { 'country_code': 'US,IN,US' }
468
- }));
469
-
470
- await fetchData('1234', '123', 'FLOORS');
471
- expect(_country).to.equal('US');
472
- });
473
-
474
- it('should set _country to undefined if country_code header is missing', async () => {
475
- fetchStub.resolves(new Response(JSON.stringify({}), {
476
- status: 200
477
- }));
478
-
479
- await fetchData('1234', '123', 'FLOORS');
480
- expect(_country).to.be.undefined;
481
- });
482
-
483
- it('should log error when JSON parsing fails', async () => {
484
- fetchStub.resolves(new Response('Invalid JSON', { status: 200 }));
485
-
486
- await fetchData('1234', '123', 'FLOORS');
487
- expect(logErrorStub.calledWith(sinon.match(/Error while fetching\s*FLOORS/))).to.be.true;
488
- });
147
+ // Wait for promise to resolve
148
+ await pubmaticRtdProvider.getYmConfigPromise();
149
+ expect(pluginManagerStub.initialize.calledOnce).to.be.true;
150
+ expect(pluginManagerStub.initialize.firstCall.args[0]).to.equal(pubmaticRtdProvider.configJsonManager);
151
+ });
489
152
 
490
- it('should log error when response is not ok', async () => {
491
- fetchStub.resolves(new Response(null, { status: 500 }));
153
+ it('should handle config fetch error gracefully', async () => {
154
+ configJsonManagerStub.fetchConfig.resolves(false);
492
155
 
493
- await fetchData('1234', '123', 'FLOORS');
494
- expect(logErrorStub.calledWith(sinon.match(/Error while fetching\s*FLOORS/))).to.be.true;
495
- });
156
+ const result = pubmaticRtdProvider.pubmaticSubmodule.init(validConfig);
157
+ expect(result).to.be.true;
496
158
 
497
- it('should log error on network failure', async () => {
498
- fetchStub.rejects(new Error('Network Error'));
159
+ try {
160
+ await pubmaticRtdProvider.getYmConfigPromise();
161
+ } catch (e) {
162
+ expect(e.message).to.equal('Failed to fetch configuration');
163
+ }
499
164
 
500
- await fetchData('1234', '123', 'FLOORS');
501
- expect(logErrorStub.called).to.be.true;
502
- expect(logErrorStub.calledWith(sinon.match(/Error while fetching\s*FLOORS/))).to.be.true;
503
- });
165
+ expect(pluginManagerStub.initialize.called).to.be.false;
504
166
  });
505
-
506
- describe('getBidRequestData', function () {
507
- let callback, continueAuctionStub, mergeDeepStub, logErrorStub;
508
-
509
- const reqBidsConfigObj = {
510
- adUnits: [{ code: 'ad-slot-code-0' }],
511
- auctionId: 'auction-id-0',
512
- ortb2Fragments: {
513
- bidder: {
514
- user: {
515
- ext: {
516
- ctr: 'US',
517
- }
518
- }
519
- }
520
- }
521
- };
522
-
523
- const ortb2 = {
524
- user: {
525
- ext: {
526
- ctr: 'US',
527
- }
528
- }
167
+ });
168
+
169
+ describe('getBidRequestData', () => {
170
+ const reqBidsConfigObj = {
171
+ ortb2Fragments: {
172
+ bidder: {}
173
+ },
174
+ adUnits: [
175
+ {
176
+ code: 'div-1',
177
+ bids: [{ bidder: 'pubmatic', params: {} }]
178
+ },
179
+ {
180
+ code: 'div-2',
181
+ bids: [{ bidder: 'pubmatic', params: {} }]
529
182
  }
183
+ ]
184
+ };
185
+ let callback;
530
186
 
531
- const hookConfig = {
532
- reqBidsConfigObj,
533
- context: this,
534
- nextFn: () => true,
535
- haveExited: false,
536
- timer: null
537
- };
538
-
539
- beforeEach(() => {
540
- callback = sinon.spy();
541
- continueAuctionStub = sandbox.stub(priceFloors, 'continueAuction');
542
- logErrorStub = sandbox.stub(utils, 'logError');
543
-
544
- global.configMergedPromise = Promise.resolve();
545
- });
546
-
547
- afterEach(() => {
548
- sandbox.restore(); // Restore all stubs/spies
549
- });
550
-
551
- it('should call continueAuction with correct hookConfig', async function () {
552
- configMerged();
553
- await pubmaticSubmodule.getBidRequestData(reqBidsConfigObj, callback);
554
-
555
- expect(continueAuctionStub.called).to.be.true;
556
- expect(continueAuctionStub.firstCall.args[0]).to.have.property('reqBidsConfigObj', reqBidsConfigObj);
557
- expect(continueAuctionStub.firstCall.args[0]).to.have.property('haveExited', false);
558
- });
559
-
560
- // it('should merge country data into ortb2Fragments.bidder', async function () {
561
- // configMerged();
562
- // global._country = 'US';
563
- // pubmaticSubmodule.getBidRequestData(reqBidsConfigObj, callback);
187
+ beforeEach(() => {
188
+ callback = sinon.stub();
189
+ pubmaticRtdProvider.setYmConfigPromise(Promise.resolve());
190
+ });
564
191
 
565
- // expect(reqBidsConfigObj.ortb2Fragments.bidder).to.have.property('pubmatic');
566
- // // expect(reqBidsConfigObj.ortb2Fragments.bidder.pubmatic.user.ext.ctr).to.equal('US');
567
- // });
192
+ it('should call pluginManager executeHook with correct parameters', (done) => {
193
+ pluginManagerStub.executeHook.resolves();
568
194
 
569
- it('should call callback once after execution', async function () {
570
- configMerged();
571
- await pubmaticSubmodule.getBidRequestData(reqBidsConfigObj, callback);
195
+ pubmaticRtdProvider.pubmaticSubmodule.getBidRequestData(reqBidsConfigObj, callback);
572
196
 
573
- expect(callback.called).to.be.true;
574
- });
197
+ setTimeout(() => {
198
+ expect(pluginManagerStub.executeHook.calledOnce).to.be.true;
199
+ expect(pluginManagerStub.executeHook.firstCall.args[0]).to.equal('processBidRequest');
200
+ expect(pluginManagerStub.executeHook.firstCall.args[1]).to.deep.equal(reqBidsConfigObj);
201
+ expect(callback.calledOnce).to.be.true;
202
+ done();
203
+ }, 0);
575
204
  });
576
205
 
577
- describe('withTimeout', function () {
578
- it('should resolve with the original promise value if it resolves before the timeout', async function () {
579
- const promise = new Promise((resolve) => setTimeout(() => resolve('success'), 50));
580
- const result = await withTimeout(promise, 100);
581
- expect(result).to.equal('success');
582
- });
206
+ it('should add country information to ORTB2', (done) => {
207
+ pluginManagerStub.executeHook.resolves();
583
208
 
584
- it('should resolve with undefined if the promise takes longer than the timeout', async function () {
585
- const promise = new Promise((resolve) => setTimeout(() => resolve('success'), 200));
586
- const result = await withTimeout(promise, 100);
587
- expect(result).to.be.undefined;
588
- });
209
+ pubmaticRtdProvider.pubmaticSubmodule.getBidRequestData(reqBidsConfigObj, callback);
589
210
 
590
- it('should properly handle rejected promises', async function () {
591
- const promise = new Promise((resolve, reject) => setTimeout(() => reject(new Error('Failure')), 50));
592
- try {
593
- await withTimeout(promise, 100);
594
- } catch (error) {
595
- expect(error.message).to.equal('Failure');
211
+ setTimeout(() => {
212
+ expect(reqBidsConfigObj.ortb2Fragments.bidder[pubmaticRtdProvider.CONSTANTS.SUBMODULE_NAME]).to.deep.equal({
213
+ user: {
214
+ ext: {
215
+ ctr: 'IN'
596
216
  }
217
+ }
597
218
  });
598
-
599
- it('should resolve with undefined if the original promise is rejected but times out first', async function () {
600
- const promise = new Promise((resolve, reject) => setTimeout(() => reject(new Error('Failure')), 200));
601
- const result = await withTimeout(promise, 100);
602
- expect(result).to.be.undefined;
603
- });
604
-
605
- it('should clear the timeout when the promise resolves before the timeout', async function () {
606
- const clock = sinon.useFakeTimers();
607
- const clearTimeoutSpy = sinon.spy(global, 'clearTimeout');
608
-
609
- const promise = new Promise((resolve) => setTimeout(() => resolve('success'), 50));
610
- const resultPromise = withTimeout(promise, 100);
611
-
612
- clock.tick(50);
613
- await resultPromise;
614
-
615
- expect(clearTimeoutSpy.called).to.be.true;
616
-
617
- clearTimeoutSpy.restore();
618
- clock.restore();
619
- });
219
+ done();
220
+ }, 0);
221
+ });
222
+ });
223
+
224
+ describe('getTargetingData', () => {
225
+ const adUnitCodes = ['div-1', 'div-2'];
226
+ const config = {
227
+ params: {
228
+ publisherId: 'test-publisher-id',
229
+ profileId: 'test-profile-id'
230
+ }
231
+ };
232
+ const userConsent = {};
233
+ const auction = {};
234
+ const unifiedPricingRule = {
235
+ 'div-1': { key1: 'value1' },
236
+ 'div-2': { key2: 'value2' }
237
+ };
238
+
239
+ it('should call pluginManager executeHook with correct parameters', () => {
240
+ pluginManagerStub.executeHook.returns(unifiedPricingRule);
241
+
242
+ const result = pubmaticRtdProvider.getTargetingData(adUnitCodes, config, userConsent, auction);
243
+
244
+ expect(pluginManagerStub.executeHook.calledOnce).to.be.true;
245
+ expect(pluginManagerStub.executeHook.firstCall.args[0]).to.equal('getTargeting');
246
+ expect(pluginManagerStub.executeHook.firstCall.args[1]).to.equal(adUnitCodes);
247
+ expect(pluginManagerStub.executeHook.firstCall.args[2]).to.equal(config);
248
+ expect(pluginManagerStub.executeHook.firstCall.args[3]).to.equal(userConsent);
249
+ expect(pluginManagerStub.executeHook.firstCall.args[4]).to.equal(auction);
250
+ expect(result).to.equal(unifiedPricingRule);
620
251
  });
621
252
 
622
- describe('getTargetingData', function () {
623
- let sandbox;
624
- let logInfoStub;
625
-
626
- beforeEach(() => {
627
- sandbox = sinon.createSandbox();
628
- logInfoStub = sandbox.stub(utils, 'logInfo');
629
- });
630
-
631
- afterEach(() => {
632
- sandbox.restore();
633
- });
634
-
635
- it('should return empty object when profileConfigs is undefined', function () {
636
- // Store the original value to restore it later
637
- const originalProfileConfigs = getProfileConfigs();
638
- // Set profileConfigs to undefined
639
- setProfileConfigs(undefined);
640
-
641
- const adUnitCodes = ['test-ad-unit'];
642
- const config = {};
643
- const userConsent = {};
644
- const auction = {};
645
-
646
- const result = getTargetingData(adUnitCodes, config, userConsent, auction);
647
-
648
- // Restore the original value
649
- setProfileConfigs(originalProfileConfigs);
650
-
651
- expect(result).to.deep.equal({});
652
- expect(logInfoStub.calledWith(sinon.match(/pmTargetingKeys is disabled or profileConfigs is undefined/))).to.be.true;
653
- });
654
-
655
- it('should return empty object when pmTargetingKeys.enabled is false', function () {
656
- // Create profileConfigs with pmTargetingKeys.enabled set to false
657
- const profileConfigsMock = {
658
- plugins: {
659
- dynamicFloors: {
660
- pmTargetingKeys: {
661
- enabled: false
662
- }
663
- }
664
- }
665
- };
666
-
667
- // Store the original value to restore it later
668
- const originalProfileConfigs = getProfileConfigs();
669
- // Set profileConfigs to our mock
670
- setProfileConfigs(profileConfigsMock);
671
-
672
- const adUnitCodes = ['test-ad-unit'];
673
- const config = {};
674
- const userConsent = {};
675
- const auction = {};
676
-
677
- const result = getTargetingData(adUnitCodes, config, userConsent, auction);
678
-
679
- // Restore the original value
680
- setProfileConfigs(originalProfileConfigs);
681
-
682
- expect(result).to.deep.equal({});
683
- expect(logInfoStub.calledWith(sinon.match(/pmTargetingKeys is disabled or profileConfigs is undefined/))).to.be.true;
684
- });
685
-
686
- it('should set pm_ym_flrs to 0 when no RTD floor is applied to any bid', function () {
687
- // Create profileConfigs with pmTargetingKeys.enabled set to true
688
- const profileConfigsMock = {
689
- plugins: {
690
- dynamicFloors: {
691
- pmTargetingKeys: {
692
- enabled: true
693
- }
694
- }
695
- }
696
- };
697
-
698
- // Store the original value to restore it later
699
- const originalProfileConfigs = getProfileConfigs();
700
- // Set profileConfigs to our mock
701
- setProfileConfigs(profileConfigsMock);
702
-
703
- // Create multiple ad unit codes to test
704
- const adUnitCodes = ['ad-unit-1', 'ad-unit-2'];
705
- const config = {};
706
- const userConsent = {};
707
-
708
- // Create a mock auction object with bids that don't have RTD floors applied
709
- // This tests several scenarios where RTD floor is not applied:
710
- // 1. No floorData
711
- // 2. floorData but floorProvider is not 'PM'
712
- // 3. floorData with floorProvider 'PM' but skipped is true
713
- const auction = {
714
- adUnits: [
715
- {
716
- code: 'ad-unit-1',
717
- bids: [
718
- { bidder: 'bidderA' }, // No floorData
719
- { bidder: 'bidderB', floorData: { floorProvider: 'OTHER' } } // Not PM provider
720
- ]
721
- },
722
- {
723
- code: 'ad-unit-2',
724
- bids: [
725
- { bidder: 'bidderC', floorData: { floorProvider: 'PM', skipped: true } } // PM but skipped
726
- ]
727
- }
728
- ],
729
- bidsReceived: [
730
- { adUnitCode: 'ad-unit-1', bidder: 'bidderA' },
731
- { adUnitCode: 'ad-unit-1', bidder: 'bidderB', floorData: { floorProvider: 'OTHER' } },
732
- { adUnitCode: 'ad-unit-2', bidder: 'bidderC', floorData: { floorProvider: 'PM', skipped: true } }
733
- ]
734
- };
735
-
736
- const result = getTargetingData(adUnitCodes, config, userConsent, auction);
737
-
738
- // Restore the original value
739
- setProfileConfigs(originalProfileConfigs);
740
-
741
- // Verify that for each ad unit code, only pm_ym_flrs is set to 0
742
- expect(result).to.deep.equal({
743
- 'ad-unit-1': { 'pm_ym_flrs': 0 },
744
- 'ad-unit-2': { 'pm_ym_flrs': 0 }
745
- });
746
-
747
- // Verify log message was not called since hasRtdFloorAppliedBid is false
748
- expect(logInfoStub.calledWith(sinon.match('Setting targeting via getTargetingData'))).to.be.false;
749
- });
750
-
751
- it('should set all targeting keys when RTD floor is applied with a floored bid', function () {
752
- // Based on the actual behavior observed in the test results, this test case is for a floored bid situation
753
- // Update our expectations to match the actual behavior
754
-
755
- // Create profileConfigs with pmTargetingKeys.enabled set to true
756
- const profileConfigsMock = {
757
- plugins: {
758
- dynamicFloors: {
759
- pmTargetingKeys: {
760
- enabled: true
761
- }
762
- }
763
- }
764
- };
765
-
766
- // Store the original value to restore it later
767
- const originalProfileConfigs = getProfileConfigs();
768
- // Set profileConfigs to our mock
769
- setProfileConfigs(profileConfigsMock);
770
-
771
- // Create ad unit codes to test
772
- const adUnitCodes = ['ad-unit-1', 'ad-unit-2'];
773
- const config = {};
774
- const userConsent = {};
775
-
776
- // Create a mock auction object with bids that have RTD floors applied
777
- const auction = {
778
- adUnits: [
779
- {
780
- code: 'ad-unit-1',
781
- bids: [
782
- {
783
- bidder: 'bidderA',
784
- floorData: {
785
- floorProvider: 'PM',
786
- floorValue: 2.5,
787
- skipped: false
788
- }
789
- }
790
- ]
791
- },
792
- {
793
- code: 'ad-unit-2',
794
- bids: []
795
- }
796
- ],
797
- bidsReceived: [
798
- {
799
- adUnitCode: 'ad-unit-1',
800
- bidder: 'bidderA',
801
- cpm: 3.5,
802
- floorData: {
803
- floorProvider: 'PM',
804
- floorValue: 2.5,
805
- skipped: false
806
- }
807
- }
808
- ]
809
- };
810
-
811
- const result = getTargetingData(adUnitCodes, config, userConsent, auction);
812
-
813
- // Restore the original value
814
- setProfileConfigs(originalProfileConfigs);
815
-
816
- // Verify that all targeting keys are set for both ad units
817
- // Based on the failing test, we're getting FLOORED status (2) instead of WON (1)
818
- // and a floor value of 2 instead of 3.5
819
- expect(result).to.deep.equal({
820
- 'ad-unit-1': {
821
- 'pm_ym_flrs': 1,
822
- 'pm_ym_flrv': '2.00', // floorValue * FLOORED multiplier as string with 2 decimal places
823
- 'pm_ym_bid_s': 2 // FLOORED status
824
- },
825
- 'ad-unit-2': {
826
- 'pm_ym_flrs': 1,
827
- 'pm_ym_flrv': '0.00', // No bid value as string with 2 decimal places
828
- 'pm_ym_bid_s': 0 // NOBID status
829
- }
830
- });
831
-
832
- // Verify log message is called when hasRtdFloorAppliedBid is true
833
- // expect(logInfoStub.calledWith(sinon.match('Setting targeting via getTargetingData'))).to.be.true;
834
- });
835
-
836
- it('should handle bid with RTD floor applied correctly', function () {
837
- // Create profileConfigs with pmTargetingKeys enabled
838
- const profileConfigsMock = {
839
- plugins: {
840
- dynamicFloors: {
841
- pmTargetingKeys: {
842
- enabled: true
843
- }
844
- }
845
- }
846
- };
847
-
848
- // Store the original value to restore it later
849
- const originalProfileConfigs = getProfileConfigs();
850
- // Set profileConfigs to our mock
851
- setProfileConfigs(profileConfigsMock);
852
-
853
- // Create ad unit codes to test
854
- const adUnitCodes = ['ad-unit-1'];
855
- const config = {};
856
- const userConsent = {};
857
-
858
- // Create a mock auction with a bid
859
- const auction = {
860
- adUnits: [{
861
- code: 'ad-unit-1',
862
- bids: [{
863
- bidder: 'bidderA',
864
- floorData: {
865
- floorProvider: 'PM',
866
- skipped: false
867
- }
868
- }]
869
- }],
870
- bidsReceived: [{
871
- adUnitCode: 'ad-unit-1',
872
- bidder: 'bidderA',
873
- cpm: 5.0,
874
- floorData: {
875
- floorProvider: 'PM',
876
- floorValue: 3.0,
877
- skipped: false
878
- }
879
- }]
880
- };
881
-
882
- const result = getTargetingData(adUnitCodes, config, userConsent, auction);
883
-
884
- // Restore the original value
885
- setProfileConfigs(originalProfileConfigs);
886
-
887
- // Verify that targeting keys are set when RTD floor is applied
888
- expect(result['ad-unit-1']['pm_ym_flrs']).to.equal(1); // RTD floor was applied
889
-
890
- // The function identifies bid status based on its internal logic
891
- // We know it sets a bid status (either WON, FLOORED, or NOBID)
892
- expect(result['ad-unit-1']['pm_ym_bid_s']).to.be.a('number');
893
-
894
- // It also sets a floor value based on the bid status
895
- expect(result['ad-unit-1']['pm_ym_flrv']).to.be.a('string');
896
-
897
- // We can also verify that when a bid exists, the exact bid status is FLOORED (2)
898
- // This matches the actual behavior of the function
899
- expect(result['ad-unit-1']['pm_ym_bid_s']).to.equal(2);
900
- });
901
-
902
- // Test for multiplier extraction logic in fetchData
903
- it('should correctly extract only existing multiplier keys from floors.json', function () {
904
- // Reset sandbox for a clean test
905
- sandbox.restore();
906
- sandbox = sinon.createSandbox();
907
-
908
- // Stub logInfo instead of console.info
909
- sandbox.stub(utils, 'logInfo');
910
-
911
- // Mock fetch with specific multiplier data where 'nobid' is intentionally missing
912
- global.fetch = sandbox.stub().returns(Promise.resolve({
913
- ok: true,
914
- status: 200,
915
- json: function() {
916
- return Promise.resolve({
917
- multiplier: {
918
- win: 1.5, // present key
919
- floored: 1.8 // present key
920
- // nobid is deliberately missing to test selective extraction
921
- }
922
- });
923
- },
924
- headers: {
925
- get: function() { return null; }
926
- }
927
- }));
928
-
929
- // Call fetchData with FLOORS type
930
- return fetchData('test-publisher', 'test-profile', 'FLOORS').then(() => {
931
- // Verify the log message was generated
932
- sinon.assert.called(utils.logInfo);
933
-
934
- // Find the call with multiplier information
935
- const logCalls = utils.logInfo.getCalls();
936
- const multiplierLogCall = logCalls.find(call =>
937
- call.args.some(arg =>
938
- typeof arg === 'string' && arg.includes('multiplier')
939
- )
940
- );
941
-
942
- // Verify we found the log message
943
- expect(multiplierLogCall).to.exist;
944
-
945
- if (multiplierLogCall) {
946
- // For debugging: log the actual arguments
947
-
948
- // Find the argument that contains our multiplier info
949
- const logArg = multiplierLogCall.args.find(arg =>
950
- typeof arg === 'string' && (arg.includes('WIN') || arg.includes('multiplier'))
951
- );
952
-
953
- // Verify the message contains the expected multiplier values
954
- expect(logArg).to.include('WIN');
955
- expect(logArg).to.include('1.5');
956
- expect(logArg).to.include('FLOORED');
957
- expect(logArg).to.include('1.8');
958
-
959
- // Verify the log doesn't include NOBID (since it wasn't in the source)
960
- expect(logArg).to.not.include('NOBID');
961
- }
962
- });
963
- });
964
-
965
- describe('should handle the floor rejected bid scenario correctly', function () {
966
- // Create profileConfigs with pmTargetingKeys enabled
967
- const profileConfigsMock = {
968
- plugins: {
969
- dynamicFloors: {
970
- pmTargetingKeys: {
971
- enabled: true,
972
- multiplier: {
973
- floored: 0.8 // Explicit floored multiplier
974
- }
975
- }
976
- }
977
- }
978
- };
979
-
980
- // Store the original value to restore it later
981
- const originalProfileConfigs = getProfileConfigs();
982
- // Set profileConfigs to our mock
983
- setProfileConfigs(profileConfigsMock);
984
-
985
- // Create ad unit codes to test
986
- const adUnitCodes = ['ad-unit-1'];
987
- const config = {};
988
- const userConsent = {};
989
-
990
- // Create a rejected bid with floor price
991
- const rejectedBid = {
992
- adUnitCode: 'ad-unit-1',
993
- bidder: 'bidderA',
994
- cpm: 2.0,
995
- statusMessage: 'Bid rejected due to price floor',
996
- floorData: {
997
- floorProvider: 'PM',
998
- floorValue: 2.5,
999
- skipped: false
1000
- }
1001
- };
1002
-
1003
- // Create a mock auction with a rejected bid
1004
- const auction = {
1005
- adUnits: [{
1006
- code: 'ad-unit-1',
1007
- bids: [{
1008
- bidder: 'bidderA',
1009
- floorData: {
1010
- floorProvider: 'PM',
1011
- skipped: false
1012
- }
1013
- }]
1014
- }],
1015
- bidsReceived: [], // No received bids
1016
- bidsRejected: {
1017
- bidderA: [rejectedBid]
1018
- }
1019
- };
1020
-
1021
- const result = getTargetingData(adUnitCodes, config, userConsent, auction);
1022
-
1023
- // Restore the original value
1024
- setProfileConfigs(originalProfileConfigs);
1025
-
1026
- // Verify correct values for floor rejected bid scenario
1027
- // Floor value (2.5) * FLOORED multiplier (0.8) = 2.0
1028
- expect(result['ad-unit-1']).to.deep.equal({
1029
- 'pm_ym_flrs': 1, // RTD floor was applied
1030
- 'pm_ym_bid_s': 2, // FLOORED status
1031
- 'pm_ym_flrv': (rejectedBid.floorData.floorValue * 0.8).toFixed(2) // floor value * FLOORED multiplier as string with 2 decimal places
1032
- });
1033
- });
1034
-
1035
- describe('should handle the no bid scenario correctly', function () {
1036
- it('should handle no bid scenario correctly', function () {
1037
- // Create profileConfigs with pmTargetingKeys enabled
1038
- const profileConfigsMock = {
1039
- plugins: {
1040
- dynamicFloors: {
1041
- pmTargetingKeys: {
1042
- enabled: true,
1043
- multiplier: {
1044
- nobid: 1.2 // Explicit nobid multiplier
1045
- }
1046
- }
1047
- }
1048
- }
1049
- };
1050
-
1051
- // Store the original value to restore it later
1052
- const originalProfileConfigs = getProfileConfigs();
1053
- // Set profileConfigs to our mock
1054
- setProfileConfigs(profileConfigsMock);
1055
-
1056
- // Create ad unit codes to test
1057
- const adUnitCodes = ['Div2'];
1058
- const config = {};
1059
- const userConsent = {};
1060
-
1061
- // Create a mock auction with no bids but with RTD floor applied
1062
- // For this test, we'll observe what the function actually does rather than
1063
- // try to match specific multiplier values
1064
- const auction = {
1065
- "auctionId": "faf0b7d0-3a12-4774-826a-3d56033d9a74",
1066
- "auctionStatus": "completed",
1067
- "adUnits": [
1068
- {
1069
- "code": "Div2",
1070
- "sizes": [[300, 250]],
1071
- "mediaTypes": {
1072
- "banner": { "sizes": [[300, 250]] }
1073
- },
1074
- "bids": [
1075
- {
1076
- "bidder": "pubmatic",
1077
- "params": {
1078
- "publisherId": "164392",
1079
- "adSlot": "/4374asd3431/DMDemo1@160x600"
1080
- },
1081
- "floorData": {
1082
- "floorProvider": "PM"
1083
- }
1084
- }
1085
- ]
1086
- }
1087
- ],
1088
- "adUnitCodes": ["Div2"],
1089
- "bidderRequests": [
1090
- {
1091
- "bidderCode": "pubmatic",
1092
- "auctionId": "faf0b7d0-3a12-4774-826a-3d56033d9a74",
1093
- "bids": [
1094
- {
1095
- "bidder": "pubmatic",
1096
- "adUnitCode": "Div2",
1097
- "floorData": {
1098
- "floorProvider": "PM"
1099
- },
1100
- "mediaTypes": {
1101
- "banner": { "sizes": [[300, 250]] }
1102
- },
1103
- "getFloor": () => { return { floor: 0.05, currency: 'USD' }; }
1104
- }
1105
- ]
1106
- }
1107
- ],
1108
- "noBids": [
1109
- {
1110
- "bidder": "pubmatic",
1111
- "adUnitCode": "Div2",
1112
- "floorData": {
1113
- "floorProvider": "PM",
1114
- "floorMin": 0.05
1115
- }
1116
- }
1117
- ],
1118
- "bidsReceived": [],
1119
- "bidsRejected": [],
1120
- "winningBids": []
1121
- };
1122
-
1123
- const result = getTargetingData(adUnitCodes, config, userConsent, auction);
1124
-
1125
- // Restore the original value
1126
- setProfileConfigs(originalProfileConfigs);
1127
-
1128
- // Verify correct values for no bid scenario
1129
- expect(result['Div2']['pm_ym_flrs']).to.equal(1); // RTD floor was applied
1130
- expect(result['Div2']['pm_ym_bid_s']).to.equal(0); // NOBID status
1131
-
1132
- // Since finding floor values from bidder requests depends on implementation details
1133
- // we'll just verify the type rather than specific value
1134
- expect(result['Div2']['pm_ym_flrv']).to.be.a('string');
1135
- });
1136
-
1137
- it('should handle no bid scenario correctly for single ad unit multiple size scenarios', function () {
1138
- // Create profileConfigs with pmTargetingKeys enabled
1139
- const profileConfigsMock = {
1140
- plugins: {
1141
- dynamicFloors: {
1142
- pmTargetingKeys: {
1143
- enabled: true,
1144
- multiplier: {
1145
- nobid: 1.2 // Explicit nobid multiplier
1146
- }
1147
- }
1148
- }
1149
- }
1150
- };
1151
-
1152
- // Store the original value to restore it later
1153
- const originalProfileConfigs = getProfileConfigs();
1154
- // Set profileConfigs to our mock
1155
- setProfileConfigs(profileConfigsMock);
1156
-
1157
- // Create ad unit codes to test
1158
- const adUnitCodes = ['Div2'];
1159
- const config = {};
1160
- const userConsent = {};
1161
-
1162
- // Create a mock auction with no bids but with RTD floor applied
1163
- // For this test, we'll observe what the function actually does rather than
1164
- // try to match specific multiplier values
1165
- const auction = {
1166
- "auctionId": "faf0b7d0-3a12-4774-826a-3d56033d9a74",
1167
- "auctionStatus": "completed",
1168
- "adUnits": [
1169
- {
1170
- "code": "Div2",
1171
- "sizes": [[300, 250]],
1172
- "mediaTypes": {"banner": { "sizes": [[300, 250]] }},
1173
- "bids": [
1174
- {
1175
- "bidder": "pubmatic",
1176
- "params": {
1177
- "publisherId": "164392",
1178
- "adSlot": "/4374asd3431/DMDemo1@160x600"
1179
- },
1180
- "floorData": {
1181
- "floorProvider": "PM"
1182
- }
1183
- }
1184
- ]
1185
- }
1186
- ],
1187
- "adUnitCodes": [ "Div2"],
1188
- "bidderRequests": [
1189
- {
1190
- "bidderCode": "pubmatic",
1191
- "auctionId": "faf0b7d0-3a12-4774-826a-3d56033d9a74",
1192
- "bids": [
1193
- {
1194
- "bidder": "pubmatic",
1195
- "adUnitCode": "Div2",
1196
- "floorData": {
1197
- "floorProvider": "PM"
1198
- },
1199
- "mediaTypes": {
1200
- "banner": { "sizes": [[300, 250]] }
1201
- },
1202
- "getFloor": () => { return { floor: 0.05, currency: 'USD' }; }
1203
- }
1204
- ]
1205
- }
1206
- ],
1207
- "noBids": [
1208
- {
1209
- "bidder": "pubmatic",
1210
- "adUnitCode": "Div2",
1211
- "floorData": {
1212
- "floorProvider": "PM",
1213
- "floorMin": 0.05
1214
- }
1215
- }
1216
- ],
1217
- "bidsReceived": [],
1218
- "bidsRejected": [],
1219
- "winningBids": []
1220
- };
1221
-
1222
- const result = getTargetingData(adUnitCodes, config, userConsent, auction);
1223
-
1224
- // Restore the original value
1225
- setProfileConfigs(originalProfileConfigs);
1226
-
1227
- // Verify correct values for no bid scenario
1228
- expect(result['Div2']['pm_ym_flrs']).to.equal(1); // RTD floor was applied
1229
- expect(result['Div2']['pm_ym_bid_s']).to.equal(0); // NOBID status
1230
-
1231
- // Since finding floor values from bidder requests depends on implementation details
1232
- // we'll just verify the type rather than specific value
1233
- expect(result['Div2']['pm_ym_flrv']).to.be.a('string');
1234
- });
1235
-
1236
- it('should handle no bid scenario correctly for multi-format ad unit with different floors', function () {
1237
- // Create profileConfigs with pmTargetingKeys enabled
1238
- const profileConfigsMock = {
1239
- plugins: {
1240
- dynamicFloors: {
1241
- pmTargetingKeys: {
1242
- enabled: true,
1243
- multiplier: {
1244
- nobid: 1.2 // Explicit nobid multiplier
1245
- }
1246
- }
1247
- }
1248
- }
1249
- };
1250
-
1251
- // Store the original value to restore it later
1252
- const originalProfileConfigs = getProfileConfigs();
1253
- // Set profileConfigs to our mock
1254
- setProfileConfigs(profileConfigsMock);
1255
-
1256
- // Create ad unit codes to test
1257
- const adUnitCodes = ['multiFormatDiv'];
1258
- const config = {};
1259
- const userConsent = {};
1260
-
1261
- // Mock getFloor implementation that returns different floors for different media types
1262
- const mockGetFloor = (params) => {
1263
- const floors = {
1264
- 'banner': 0.50, // Higher floor for banner
1265
- 'video': 0.25 // Lower floor for video
1266
- };
1267
-
1268
- return {
1269
- floor: floors[params.mediaType] || 0.10,
1270
- currency: 'USD'
1271
- };
1272
- };
1273
-
1274
- // Create a mock auction with a multi-format ad unit (banner + video)
1275
- const auction = {
1276
- "auctionId": "multi-format-test-auction",
1277
- "auctionStatus": "completed",
1278
- "adUnits": [
1279
- {
1280
- "code": "multiFormatDiv",
1281
- "mediaTypes": {
1282
- "banner": {
1283
- "sizes": [[300, 250], [300, 600]]
1284
- },
1285
- "video": {
1286
- "playerSize": [[640, 480]],
1287
- "context": "instream"
1288
- }
1289
- },
1290
- "bids": [
1291
- {
1292
- "bidder": "pubmatic",
1293
- "params": {
1294
- "publisherId": "test-publisher",
1295
- "adSlot": "/test/slot"
1296
- },
1297
- "floorData": {
1298
- "floorProvider": "PM"
1299
- }
1300
- }
1301
- ]
1302
- }
1303
- ],
1304
- "adUnitCodes": ["multiFormatDiv"],
1305
- "bidderRequests": [
1306
- {
1307
- "bidderCode": "pubmatic",
1308
- "auctionId": "multi-format-test-auction",
1309
- "bids": [
1310
- {
1311
- "bidder": "pubmatic",
1312
- "adUnitCode": "multiFormatDiv",
1313
- "mediaTypes": {
1314
- "banner": {
1315
- "sizes": [[300, 250], [300, 600]]
1316
- },
1317
- "video": {
1318
- "playerSize": [[640, 480]],
1319
- "context": "instream"
1320
- }
1321
- },
1322
- "floorData": {
1323
- "floorProvider": "PM"
1324
- },
1325
- "getFloor": mockGetFloor
1326
- }
1327
- ]
1328
- }
1329
- ],
1330
- "noBids": [
1331
- {
1332
- "bidder": "pubmatic",
1333
- "adUnitCode": "multiFormatDiv",
1334
- "floorData": {
1335
- "floorProvider": "PM"
1336
- }
1337
- }
1338
- ],
1339
- "bidsReceived": [],
1340
- "bidsRejected": [],
1341
- "winningBids": []
1342
- };
1343
-
1344
- // Create a spy to monitor the getFloor calls
1345
- const getFloorSpy = sinon.spy(auction.bidderRequests[0].bids[0], "getFloor");
1346
-
1347
- // Run the targeting function
1348
- const result = getTargetingData(adUnitCodes, config, userConsent, auction);
1349
-
1350
- // Restore the original value
1351
- setProfileConfigs(originalProfileConfigs);
1352
-
1353
- // Verify correct values for no bid scenario
1354
- expect(result['multiFormatDiv']['pm_ym_flrs']).to.equal(1); // RTD floor was applied
1355
- expect(result['multiFormatDiv']['pm_ym_bid_s']).to.equal(0); // NOBID status
1356
-
1357
- // Verify that getFloor was called with both media types
1358
- expect(getFloorSpy.called).to.be.true;
1359
- let bannerCallFound = false;
1360
- let videoCallFound = false;
1361
-
1362
- getFloorSpy.getCalls().forEach(call => {
1363
- const args = call.args[0];
1364
- if (args.mediaType === 'banner') bannerCallFound = true;
1365
- if (args.mediaType === 'video') videoCallFound = true;
1366
- });
1367
-
1368
- expect(bannerCallFound).to.be.true; // Verify banner format was checked
1369
- expect(videoCallFound).to.be.true; // Verify video format was checked
253
+ it('should return empty object if no targeting data', () => {
254
+ pluginManagerStub.executeHook.returns({});
1370
255
 
1371
- // Since we created the mockGetFloor to return 0.25 for video (lower than 0.50 for banner),
1372
- // we expect the RTD provider to use the minimum floor value (0.25)
1373
- // We can't test the exact value due to multiplier application, but we can make sure
1374
- // it's derived from the lower value
1375
- expect(parseFloat(result['multiFormatDiv']['pm_ym_flrv'])).to.be.closeTo(0.25 * 1.2, 0.001); // 0.25 * nobid multiplier (1.2)
256
+ const result = pubmaticRtdProvider.getTargetingData(adUnitCodes, config, userConsent, auction);
257
+ expect(result).to.deep.equal({});
258
+ });
259
+ });
1376
260
 
1377
- // Clean up
1378
- getFloorSpy.restore();
1379
- });
1380
- });
261
+ describe('ConfigJsonManager', () => {
262
+ let configManager;
1381
263
 
1382
- describe('should handle the winning bid scenario correctly', function () {
1383
- it('should handle winning bid scenario correctly', function () {
1384
- // Create profileConfigs with pmTargetingKeys enabled
1385
- const profileConfigsMock = {
1386
- plugins: {
1387
- dynamicFloors: {
1388
- pmTargetingKeys: {
1389
- enabled: true,
1390
- multiplier: {
1391
- nobid: 1.2 // Explicit nobid multiplier
1392
- }
1393
- }
1394
- }
1395
- }
1396
- };
264
+ beforeEach(() => {
265
+ configManager = pubmaticRtdProvider.ConfigJsonManager();
266
+ });
1397
267
 
1398
- // Store the original value to restore it later
1399
- const originalProfileConfigs = getProfileConfigs();
1400
- // Set profileConfigs to our mock
1401
- setProfileConfigs(profileConfigsMock);
268
+ it('should fetch config successfully', async () => {
269
+ const mockResponse = {
270
+ ok: true,
271
+ headers: {
272
+ get: sinon.stub().withArgs('country_code').returns('US')
273
+ },
274
+ json: sinon.stub().resolves({ plugins: { test: { enabled: true } } })
275
+ };
1402
276
 
1403
- // Create ad unit codes to test
1404
- const adUnitCodes = ['Div1'];
1405
- const config = {};
1406
- const userConsent = {};
277
+ fetchStub.resolves(mockResponse);
1407
278
 
1408
- const highestWinningBidResponse = [{
1409
- "bidderCode": "pubmatic",
1410
- "statusMessage": "Bid available",
1411
- "cpm": 15,
1412
- "currency": "USD",
1413
- "bidder": "pubmatic",
1414
- "adUnitCode": "Div1",
1415
- }
1416
- ]
279
+ const result = await configManager.fetchConfig('pub-123', 'profile-456');
1417
280
 
1418
- // Create a mock auction with no bids but with RTD floor applied
1419
- // For this test, we'll observe what the function actually does rather than
1420
- // try to match specific multiplier values
1421
- const auction = {
1422
- "auctionId": "faf0b7d0-3a12-4774-826a-3d56033d9a74",
1423
- "timestamp": 1749410430351,
1424
- "auctionEnd": 1749410432392,
1425
- "auctionStatus": "completed",
1426
- "adUnits": [
1427
- {
1428
- "code": "Div1",
1429
- "sizes": [
1430
- [
1431
- 160,
1432
- 600
1433
- ]
1434
- ],
1435
- "mediaTypes": {
1436
- "banner": {
1437
- "sizes": [
1438
- [
1439
- 160,
1440
- 600
1441
- ]
1442
- ]
1443
- }
1444
- },
1445
- "bids": [
1446
- {
1447
- "bidder": "pubmatic",
1448
- "params": {
1449
- "publisherId": " 164392 ",
1450
- "adSlot": " /43743431/DMDemo@320x250 ",
1451
- "pmzoneid": "zone1",
1452
- "yob": " 1982 ",
1453
- "kadpageurl": "www.yahoo.com?secure=1&pubmatic_bannerbid=15",
1454
- "gender": " M ",
1455
- "dctr": " key1=v1,v11| key2=v2,v22 | key3=v3 | key4=v4 "
1456
- },
1457
- "auctionId": "faf0b7d0-3a12-4774-826a-3d56033d9a74",
1458
- "floorData": {
1459
- "noFloorSignaled": false,
1460
- "skipped": false,
1461
- "skipRate": 0,
1462
- "floorMin": 0.05,
1463
- "modelVersion": "RTD model version 1.0",
1464
- "modelWeight": 100,
1465
- "location": "setConfig",
1466
- "floorProvider": "PM"
1467
- }
1468
- }
1469
- ],
1470
- "adUnitId": "b94e39c9-ac0e-43db-b660-603700dc97dd",
1471
- "transactionId": "36da4d88-9a7b-433f-adc1-878af8a8f0f1",
1472
- "ortb2Imp": {
1473
- "ext": {
1474
- "tid": "36da4d88-9a7b-433f-adc1-878af8a8f0f1",
1475
- "data": {
1476
- "adserver": {
1477
- "name": "gam",
1478
- "adslot": "/43743431/DMDemo"
1479
- },
1480
- "pbadslot": "/43743431/DMDemo"
1481
- },
1482
- "gpid": "/43743431/DMDemo"
1483
- }
1484
- }
1485
- }
281
+ expect(result).to.be.true;
282
+ expect(fetchStub.calledOnce).to.be.true;
283
+ expect(fetchStub.firstCall.args[0]).to.equal(`${pubmaticRtdProvider.CONSTANTS.ENDPOINTS.BASEURL}/pub-123/profile-456/${pubmaticRtdProvider.CONSTANTS.ENDPOINTS.CONFIGS}`);
284
+ expect(configManager.country).to.equal('US');
285
+ });
1486
286
 
1487
- ],
1488
- "adUnitCodes": [
1489
- "Div1"
1490
- ],
1491
- "bidderRequests": [
1492
- {
1493
- "bidderCode": "pubmatic",
1494
- "auctionId": "faf0b7d0-3a12-4774-826a-3d56033d9a74",
1495
- "bidderRequestId": "222b556be27f4c",
1496
- "bids": [
1497
- {
1498
- "bidder": "pubmatic",
1499
- "floorData": {
1500
- "noFloorSignaled": false,
1501
- "skipped": false,
1502
- "skipRate": 0,
1503
- "floorMin": 0.05,
1504
- "modelVersion": "RTD model version 1.0",
1505
- "modelWeight": 100,
1506
- "location": "setConfig",
1507
- "floorProvider": "PM"
1508
- },
1509
- "mediaTypes": {
1510
- "banner": {
1511
- "sizes": [
1512
- [
1513
- 160,
1514
- 600
1515
- ]
1516
- ]
1517
- }
1518
- },
1519
- "adUnitCode": "Div1",
1520
- "transactionId": "36da4d88-9a7b-433f-adc1-878af8a8f0f1",
1521
- "adUnitId": "b94e39c9-ac0e-43db-b660-603700dc97dd",
1522
- "sizes": [
1523
- [
1524
- 160,
1525
- 600
1526
- ]
1527
- ],
1528
- "bidId": "30fce22fe473c28",
1529
- "bidderRequestId": "222b556be27f4c",
1530
- "src": "client",
1531
- getFloor: () => {}
1532
- },
1533
- ],
1534
- "start": 1749410430354
1535
- }
1536
- ],
1537
- "bidsReceived": [],
1538
- "bidsRejected": [],
1539
- "winningBids": [],
1540
- "timeout": 3000,
1541
- "seatNonBids": []
1542
- };
287
+ it('should handle fetch errors', async () => {
288
+ fetchStub.rejects(new Error('Network error'));
1543
289
 
1544
- sandbox.stub(prebidGlobal, 'getGlobal').returns({
1545
- getHighestCpmBids: () => [highestWinningBidResponse]
1546
- });
290
+ const result = await configManager.fetchConfig('pub-123', 'profile-456');
1547
291
 
1548
- const result = getTargetingData(adUnitCodes, config, userConsent, auction);
292
+ expect(result).to.be.null;
293
+ expect(logErrorStub.calledOnce).to.be.true;
294
+ expect(logErrorStub.firstCall.args[0]).to.include('Error while fetching config');
295
+ });
1549
296
 
1550
- // Restore the original value
1551
- setProfileConfigs(originalProfileConfigs);
297
+ it('should get config by name', () => {
298
+ const mockConfig = {
299
+ plugins: {
300
+ testPlugin: { enabled: true }
301
+ }
302
+ };
1552
303
 
1553
- // Verify correct values for no bid scenario
1554
- expect(result['Div1']['pm_ym_flrs']).to.equal(1); // RTD floor was applied
1555
- expect(result['Div1']['pm_ym_bid_s']).to.equal(1); // NOBID status
304
+ configManager.setYMConfig(mockConfig);
1556
305
 
1557
- // Since finding floor values from bidder requests depends on implementation details
1558
- // we'll just verify the type rather than specific value
1559
- expect(result['Div1']['pm_ym_flrv']).to.be.a('string');
1560
- });
1561
- });
306
+ const result = configManager.getConfigByName('testPlugin');
307
+ expect(result).to.deep.equal({ enabled: true });
1562
308
  });
309
+ });
1563
310
  });