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.
- package/dist/33acrossAnalyticsAdapter.js +1 -1
- package/dist/33acrossBidAdapter.js +1 -1
- package/dist/33acrossIdSystem.js +1 -1
- package/dist/BTBidAdapter.js +1 -1
- package/dist/adagioAnalyticsAdapter.js +1 -1
- package/dist/adagioBidAdapter.js +1 -1
- package/dist/adagioRtdProvider.js +1 -1
- package/dist/adagioUtils.js +1 -1
- package/dist/addefendBidAdapter.js +1 -1
- package/dist/adgenerationBidAdapter.js +1 -1
- package/dist/adlooxRtdProvider.js +1 -1
- package/dist/adqueryBidAdapter.js +1 -1
- package/dist/adrelevantisBidAdapter.js +1 -1
- package/dist/adstirBidAdapter.js +1 -1
- package/dist/adtrgtmeBidAdapter.js +1 -1
- package/dist/adxcgAnalyticsAdapter.js +1 -1
- package/dist/adxcgBidAdapter.js +1 -1
- package/dist/adyoulikeBidAdapter.js +1 -1
- package/dist/agmaAnalyticsAdapter.js +1 -1
- package/dist/ajaBidAdapter.js +1 -1
- package/dist/amxBidAdapter.js +1 -1
- package/dist/amxIdSystem.js +1 -1
- package/dist/aniviewBidAdapter.js +1 -1
- package/dist/appierAnalyticsAdapter.js +1 -1
- package/dist/appnexusBidAdapter.js +1 -1
- package/dist/asoBidAdapter.js +1 -1
- package/dist/axonixBidAdapter.js +1 -1
- package/dist/beopBidAdapter.js +1 -1
- package/dist/bidderTimeoutUtils.js +1 -0
- package/dist/bidglassBidAdapter.js +1 -1
- package/dist/big-richmediaBidAdapter.js +1 -1
- package/dist/bitmediaBidAdapter.js +1 -1
- package/dist/bridBidAdapter.js +1 -1
- package/dist/bridgeuppBidAdapter.js +1 -1
- package/dist/bridgewellBidAdapter.js +1 -1
- package/dist/brightMountainMediaBidAdapter.js +1 -1
- package/dist/carodaBidAdapter.js +1 -1
- package/dist/chtnwBidAdapter.js +1 -1
- package/dist/chunk-core.js +1 -1
- package/dist/concertBidAdapter.js +1 -1
- package/dist/connectadBidAdapter.js +1 -1
- package/dist/consumableBidAdapter.js +1 -1
- package/dist/contxtfulBidAdapter.js +1 -1
- package/dist/conversantAnalyticsAdapter.js +1 -1
- package/dist/conversantBidAdapter.js +1 -1
- package/dist/craftBidAdapter.js +1 -1
- package/dist/criteoBidAdapter.js +1 -1
- package/dist/cwireBidAdapter.js +1 -1
- package/dist/dailymotionBidAdapter.js +1 -1
- package/dist/debugging-standalone.js +1 -1
- package/dist/dependencies.json +10 -1
- package/dist/dspxBidAdapter.js +1 -1
- package/dist/dxkultureBidAdapter.js +1 -1
- package/dist/eplanningBidAdapter.js +1 -1
- package/dist/equativBidAdapter.js +1 -1
- package/dist/eskimiBidAdapter.js +1 -1
- package/dist/euidIdSystem.js +1 -1
- package/dist/exadsBidAdapter.js +1 -1
- package/dist/excoBidAdapter.js +1 -1
- package/dist/feedadBidAdapter.js +1 -1
- package/dist/finativeBidAdapter.js +1 -1
- package/dist/freewheel-sspBidAdapter.js +1 -1
- package/dist/fwsspBidAdapter.js +1 -1
- package/dist/gmosspBidAdapter.js +1 -1
- package/dist/greenbidsAnalyticsAdapter.js +1 -1
- package/dist/greenbidsBidAdapter.js +1 -1
- package/dist/greenbidsRtdProvider.js +1 -1
- package/dist/gridBidAdapter.js +1 -1
- package/dist/gumgumBidAdapter.js +1 -1
- package/dist/h12mediaBidAdapter.js +1 -1
- package/dist/hypelabBidAdapter.js +1 -1
- package/dist/id5AnalyticsAdapter.js +1 -1
- package/dist/id5IdSystem.js +1 -1
- package/dist/imdsBidAdapter.js +1 -1
- package/dist/improvedigitalBidAdapter.js +1 -1
- package/dist/inmobiBidAdapter.js +1 -1
- package/dist/insticatorBidAdapter.js +1 -1
- package/dist/intentIqAnalyticsAdapter.js +1 -1
- package/dist/ixBidAdapter.js +1 -1
- package/dist/jixieBidAdapter.js +1 -1
- package/dist/justpremiumBidAdapter.js +1 -1
- package/dist/kargoBidAdapter.js +1 -1
- package/dist/kimberliteBidAdapter.js +1 -1
- package/dist/konduitAnalyticsAdapter.js +1 -1
- package/dist/kueezBidAdapter.js +1 -1
- package/dist/lassoBidAdapter.js +1 -1
- package/dist/lifestreetBidAdapter.js +1 -1
- package/dist/liveIntentId.js +1 -1
- package/dist/logicadBidAdapter.js +1 -1
- package/dist/loglyliftBidAdapter.js +1 -1
- package/dist/luceadBidAdapter.js +1 -1
- package/dist/mabidderBidAdapter.js +1 -1
- package/dist/madsenseBidAdapter.js +1 -1
- package/dist/magniteAnalyticsAdapter.js +1 -1
- package/dist/malltvAnalyticsAdapter.js +1 -1
- package/dist/marsmediaBidAdapter.js +1 -1
- package/dist/mediafuseBidAdapter.js +1 -1
- package/dist/medianetBidAdapter.js +1 -1
- package/dist/medianetUtils.js +1 -1
- package/dist/mediasquareBidAdapter.js +1 -1
- package/dist/mgidBidAdapter.js +1 -1
- package/dist/missenaBidAdapter.js +1 -1
- package/dist/mobilefuseBidAdapter.js +1 -1
- package/dist/nextMillenniumBidAdapter.js +1 -1
- package/dist/nexx360Utils.js +1 -1
- package/dist/nobidAnalyticsAdapter.js +1 -1
- package/dist/nobidBidAdapter.js +1 -1
- package/dist/nodalsAiRtdProvider.js +1 -1
- package/dist/not-for-prod/prebid.js +178 -175
- package/dist/objectGuard.js +1 -1
- package/dist/oguryBidAdapter.js +1 -1
- package/dist/onetagBidAdapter.js +1 -1
- package/dist/ooloAnalyticsAdapter.js +1 -1
- package/dist/openxBidAdapter.js +1 -1
- package/dist/optableRtdProvider.js +1 -1
- package/dist/optidigitalBidAdapter.js +1 -1
- package/dist/orbidderBidAdapter.js +1 -1
- package/dist/outbrainBidAdapter.js +1 -1
- package/dist/pixfutureBidAdapter.js +1 -1
- package/dist/publinkIdSystem.js +1 -1
- package/dist/pubmaticAnalyticsAdapter.js +1 -1
- package/dist/pubmaticBidAdapter.js +1 -1
- package/dist/pubmaticIdSystem.js +1 -1
- package/dist/pubmaticRtdProvider.js +1 -1
- package/dist/pubmaticUtils.js +1 -0
- package/dist/pubwiseAnalyticsAdapter.js +1 -1
- package/dist/pubxaiAnalyticsAdapter.js +1 -1
- package/dist/pxyzBidAdapter.js +1 -1
- package/dist/quantcastBidAdapter.js +1 -1
- package/dist/readpeakBidAdapter.js +1 -1
- package/dist/relaidoBidAdapter.js +1 -1
- package/dist/retailspotBidAdapter.js +1 -1
- package/dist/rhythmoneBidAdapter.js +1 -1
- package/dist/riseUtils.js +1 -1
- package/dist/rtdModule.js +1 -1
- package/dist/rubiconBidAdapter.js +1 -1
- package/dist/seedingAllianceBidAdapter.js +1 -1
- package/dist/seedtagBidAdapter.js +1 -1
- package/dist/sevioBidAdapter.js +1 -0
- package/dist/sharethroughAnalyticsAdapter.js +1 -1
- package/dist/sharethroughBidAdapter.js +1 -1
- package/dist/showheroes-bsBidAdapter.js +1 -1
- package/dist/smaatoBidAdapter.js +1 -1
- package/dist/smartadserverBidAdapter.js +1 -1
- package/dist/smartxBidAdapter.js +1 -1
- package/dist/smilewantedBidAdapter.js +1 -1
- package/dist/snigelBidAdapter.js +1 -1
- package/dist/sonobiBidAdapter.js +1 -1
- package/dist/sovrnBidAdapter.js +1 -1
- package/dist/sparteoBidAdapter.js +1 -1
- package/dist/sspBCBidAdapter.js +1 -1
- package/dist/stvBidAdapter.js +1 -1
- package/dist/sublimeBidAdapter.js +1 -1
- package/dist/taboolaBidAdapter.js +1 -1
- package/dist/tappxBidAdapter.js +1 -1
- package/dist/targetVideoBidAdapter.js +1 -1
- package/dist/teadsBidAdapter.js +1 -1
- package/dist/terceptAnalyticsAdapter.js +1 -1
- package/dist/themoneytizerBidAdapter.js +1 -1
- package/dist/timeoutRtdProvider.js +1 -1
- package/dist/trionBidAdapter.js +1 -1
- package/dist/tripleliftBidAdapter.js +1 -1
- package/dist/ttdBidAdapter.js +1 -1
- package/dist/ucfunnelAnalyticsAdapter.js +1 -1
- package/dist/uid2IdSystem.js +1 -1
- package/dist/underdogmediaBidAdapter.js +1 -1
- package/dist/undertoneBidAdapter.js +1 -1
- package/dist/unrulyBidAdapter.js +1 -1
- package/dist/userId.js +1 -1
- package/dist/vidazooUtils.js +1 -1
- package/dist/videobyteBidAdapter.js +1 -1
- package/dist/visxBidAdapter.js +1 -1
- package/dist/vuukleBidAdapter.js +1 -1
- package/dist/widespaceBidAdapter.js +1 -1
- package/dist/winrBidAdapter.js +1 -1
- package/dist/yahooAdsBidAdapter.js +1 -1
- package/dist/yandexBidAdapter.js +1 -1
- package/dist/yieldmoBidAdapter.js +1 -1
- package/dist/yieldoneAnalyticsAdapter.js +1 -1
- package/integrationExamples/gpt/pubmaticRtdProvider_Example.html +161 -0
- package/libraries/bidderTimeoutUtils/bidderTimeoutUtils.js +119 -0
- package/libraries/objectGuard/objectGuard.js +170 -48
- package/libraries/objectGuard/ortbGuard.js +33 -43
- package/libraries/pubmaticUtils/plugins/dynamicTimeout.js +209 -0
- package/libraries/pubmaticUtils/plugins/floorProvider.js +168 -0
- package/libraries/pubmaticUtils/plugins/pluginManager.js +106 -0
- package/libraries/pubmaticUtils/plugins/unifiedPricingRule.js +375 -0
- package/libraries/pubmaticUtils/pubmaticUtils.js +76 -0
- package/modules/adagioAnalyticsAdapter.js +6 -1
- package/modules/adagioBidAdapter.js +12 -5
- package/modules/adagioRtdProvider.js +41 -35
- package/modules/fwsspBidAdapter.js +134 -69
- package/modules/fwsspBidAdapter.md +121 -26
- package/modules/optableRtdProvider.js +33 -12
- package/modules/pubmaticAnalyticsAdapter.js +315 -587
- package/modules/pubmaticBidAdapter.js +71 -8
- package/modules/pubmaticIdSystem.js +4 -4
- package/modules/pubmaticRtdProvider.js +105 -613
- package/modules/rtdModule/index.js +23 -6
- package/modules/sevioBidAdapter.js +413 -0
- package/modules/sevioBidAdapter.md +29 -0
- package/modules/sparteoBidAdapter.js +122 -10
- package/modules/timeoutRtdProvider.js +2 -105
- package/modules/ttdBidAdapter.js +0 -5
- package/modules/userId/eids.js +1 -1
- package/modules/userId/index.js +32 -1
- package/package.json +1 -1
- package/src/auction.js +3 -0
- package/test/spec/activities/objectGuard_spec.js +189 -32
- package/test/spec/activities/ortbGuard_spec.js +10 -15
- package/test/spec/libraries/bidderTimeoutUtils/bidderTimeoutUtils_spec.js +213 -0
- package/test/spec/libraries/pubmaticUtils/plugins/dynamicTimeout_spec.js +746 -0
- package/test/spec/libraries/pubmaticUtils/plugins/floorProvider_spec.js +184 -0
- package/test/spec/libraries/pubmaticUtils/plugins/pluginManager_spec.js +489 -0
- package/test/spec/libraries/pubmaticUtils/plugins/unifiedPricingRule_spec.js +359 -0
- package/test/spec/libraries/pubmaticUtils/pubmaticUtils_spec.js +236 -0
- package/test/spec/modules/adagioAnalyticsAdapter_spec.js +94 -24
- package/test/spec/modules/adagioRtdProvider_spec.js +17 -17
- package/test/spec/modules/fwsspBidAdapter_spec.js +513 -78
- package/test/spec/modules/optableRtdProvider_spec.js +55 -5
- package/test/spec/modules/pubmaticAnalyticsAdapter_spec.js +634 -916
- package/test/spec/modules/pubmaticBidAdapter_spec.js +260 -1
- package/test/spec/modules/pubmaticRtdProvider_spec.js +252 -1505
- package/test/spec/modules/realTimeDataModule_spec.js +58 -8
- package/test/spec/modules/sevioBidAdapter_spec.js +513 -0
- package/test/spec/modules/sparteoBidAdapter_spec.js +528 -43
- package/test/spec/modules/timeoutRtdProvider_spec.js +1 -201
- package/test/spec/modules/ttdBidAdapter_spec.js +0 -33
- 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
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
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
|
-
|
|
37
|
-
|
|
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
|
-
|
|
50
|
-
|
|
51
|
-
|
|
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
|
-
|
|
58
|
-
|
|
59
|
-
|
|
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
|
-
|
|
92
|
-
|
|
93
|
-
|
|
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
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
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
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
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
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
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
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
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
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
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
|
-
|
|
433
|
-
|
|
434
|
-
|
|
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
|
-
|
|
445
|
-
|
|
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
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
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
|
-
|
|
491
|
-
|
|
153
|
+
it('should handle config fetch error gracefully', async () => {
|
|
154
|
+
configJsonManagerStub.fetchConfig.resolves(false);
|
|
492
155
|
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
});
|
|
156
|
+
const result = pubmaticRtdProvider.pubmaticSubmodule.init(validConfig);
|
|
157
|
+
expect(result).to.be.true;
|
|
496
158
|
|
|
497
|
-
|
|
498
|
-
|
|
159
|
+
try {
|
|
160
|
+
await pubmaticRtdProvider.getYmConfigPromise();
|
|
161
|
+
} catch (e) {
|
|
162
|
+
expect(e.message).to.equal('Failed to fetch configuration');
|
|
163
|
+
}
|
|
499
164
|
|
|
500
|
-
|
|
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
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
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
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
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
|
-
|
|
566
|
-
|
|
567
|
-
// });
|
|
192
|
+
it('should call pluginManager executeHook with correct parameters', (done) => {
|
|
193
|
+
pluginManagerStub.executeHook.resolves();
|
|
568
194
|
|
|
569
|
-
|
|
570
|
-
configMerged();
|
|
571
|
-
await pubmaticSubmodule.getBidRequestData(reqBidsConfigObj, callback);
|
|
195
|
+
pubmaticRtdProvider.pubmaticSubmodule.getBidRequestData(reqBidsConfigObj, callback);
|
|
572
196
|
|
|
573
|
-
|
|
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
|
-
|
|
578
|
-
|
|
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
|
-
|
|
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
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
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
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
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
|
-
|
|
623
|
-
|
|
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
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
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
|
-
|
|
1378
|
-
|
|
1379
|
-
});
|
|
1380
|
-
});
|
|
261
|
+
describe('ConfigJsonManager', () => {
|
|
262
|
+
let configManager;
|
|
1381
263
|
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
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
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
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
|
-
|
|
1404
|
-
const adUnitCodes = ['Div1'];
|
|
1405
|
-
const config = {};
|
|
1406
|
-
const userConsent = {};
|
|
277
|
+
fetchStub.resolves(mockResponse);
|
|
1407
278
|
|
|
1408
|
-
|
|
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
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1545
|
-
getHighestCpmBids: () => [highestWinningBidResponse]
|
|
1546
|
-
});
|
|
290
|
+
const result = await configManager.fetchConfig('pub-123', 'profile-456');
|
|
1547
291
|
|
|
1548
|
-
|
|
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
|
-
|
|
1551
|
-
|
|
297
|
+
it('should get config by name', () => {
|
|
298
|
+
const mockConfig = {
|
|
299
|
+
plugins: {
|
|
300
|
+
testPlugin: { enabled: true }
|
|
301
|
+
}
|
|
302
|
+
};
|
|
1552
303
|
|
|
1553
|
-
|
|
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
|
-
|
|
1558
|
-
|
|
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
|
});
|