import { Campaign, HeaderBiddingType, RequestInventorySource } from "../../../src/models"; import { StepBuilder } from "../../../src/steps/stepBuilder"; import { FakeRequestBuilder } from "../fakeRequestBuilder"; import BidfloorStep from "../../../src/steps/bidfloor/bidfloor.step"; import { FakeCampaign } from "../fake/Campaign"; import { CampaignModelFake } from "../fake/CampaignModelFake"; import FeatureConfigFake from "../fake/FeatureConfigFake"; import { FeatureConfigService } from "../../../src/services/FeatureConfig.service"; const featureConfigService = FeatureConfigService.getInstance(); const mockedFeatureConf: FeatureConfigFake = new FeatureConfigFake(); const chai = require("chai"); chai.should(); describe("Bidfloor", () => { let candidateCampaigns: Campaign[]; let campaignData: any[]; beforeEach(() => { // campaign 1000 and 3000 are prog // budget of each campaign are their id, so bid price will be: // 1000 => 10 // 2000 => 20 // 3000 => 30 campaignData = [ { id: "1000", budget: 1000, clicks_sold: 1000, margin: 0, ctr: 0.01, cpm: 10, prog_deal_id: "deal_1000", }, { id: "2000", budget: 2000, clicks_sold: 1000, margin: 0, ctr: 0.01, cpm: 20, prog_deal_id: undefined, }, { id: "3000", budget: 3000, clicks_sold: 1000, margin: 0, ctr: 0.01, cpm: 30, prog_deal_id: "deal_3000", }, { id: "4000", budget: 1000, clicks_sold: 1000, margin: 0, ctr: 0.01, cpm: 40, prog_deal_id: undefined, }, { id: "5000", budget: 1000, clicks_sold: 1000, margin: 0, ctr: 0.01, cpm: 50, prog_deal_id: undefined, }, ]; candidateCampaigns = [ new FakeCampaign("1000"), new FakeCampaign("2000"), new FakeCampaign("3000"), new FakeCampaign("4000", { context_score: 0 }), new FakeCampaign("5000", { context_score: 0.04 }), ]; }); describe("Bidfloor filter", () => { let bidfloorStep: any; before(() => { const innerStep = new BidfloorStep(new CampaignModelFake()); (innerStep as any).featureConfigService = featureConfigService; bidfloorStep = new StepBuilder(innerStep).build(); featureConfigService.setFeatureConfig(mockedFeatureConf); }); beforeEach(async () => { mockedFeatureConf.clearParams(); }); it("4 out of 5 campaigns should pass (low bidfloor)", async () => { const stepRes = await bidfloorStep.execute( new FakeRequestBuilder() .withCampaigns(...candidateCampaigns) .withCampaignData(campaignData) .withBidfloor(9) .build() ); stepRes.passedCampaigns.length.should.be.equals(4); stepRes.passedCampaigns.should.be.deep.equal( candidateCampaigns.filter((campaign) => campaign.id !== "4000") ); }); it("all campaigns should pass (low bidfloor): context scores null", async () => { candidateCampaigns = candidateCampaigns.map((c) => { c.metadata.context_score = null; return c; }); const stepRes = await bidfloorStep.execute( new FakeRequestBuilder() .withCampaigns(...candidateCampaigns) .withCampaignData(campaignData) .withBidfloor(1) .build() ); stepRes.passedCampaigns.length.should.be.equals(5); }); it("none campaigns should pass (high bidfloor)", async () => { const stepRes = await bidfloorStep.execute( new FakeRequestBuilder() .withCampaigns(...candidateCampaigns) .withCampaignData(campaignData) .withBidfloor(124) .build() ); stepRes.passedCampaigns.length.should.be.equals(0); }); it("none campaigns should pass (context score = 0)", async () => { candidateCampaigns = candidateCampaigns.map((c) => { c.metadata.context_score = 0; return c; }); const stepRes = await bidfloorStep.execute( new FakeRequestBuilder() .withCampaigns(...candidateCampaigns) .withCampaignData(campaignData) .withBidfloor(1) .build() ); stepRes.passedCampaigns.length.should.be.equals(0); }); it("only campaigns > 12 should pass", async () => { const stepRes = await bidfloorStep.execute( new FakeRequestBuilder() .withCampaigns(...candidateCampaigns) .withCampaignData(campaignData) .withBidfloor(12) .build() ); stepRes.passedCampaigns.length.should.be.equals(3); stepRes.passedCampaigns.should.be.deep.equal( candidateCampaigns.filter((c) => ["2000", "3000", "5000"].includes(c.id)) ); }); it("only campaigns > 22 should pass", async () => { const stepRes = await bidfloorStep.execute( new FakeRequestBuilder() .withCampaigns(...candidateCampaigns) .withCampaignData(campaignData) .withBidfloor(22) .build() ); stepRes.passedCampaigns.length.should.be.equals(2); stepRes.passedCampaigns.should.be.deep.equal( candidateCampaigns.filter((c) => ["3000", "5000"].includes(c.id)) ); }); it("only campaigns > 30 should pass with good third party fee", async () => { mockedFeatureConf.setParam( "ms-choices.header-bidding.third-party-extra-fee", `{"Amazon TAM":0.00001}` ); const stepRes = await bidfloorStep.execute( new FakeRequestBuilder() .withInventorySource(RequestInventorySource.HEADER_BIDDING) .withHeaderBiddingType(HeaderBiddingType.AmazonTAM) .withCampaigns(...candidateCampaigns) .withCampaignData(campaignData) .withBidfloor(30) .build() ); stepRes.passedCampaigns.length.should.be.equals(1); stepRes.passedCampaigns.should.be.deep.equal( candidateCampaigns.filter((c) => ["5000"].includes(c.id)) ); }); it("only campaigns > 30 should pass with default third party fee(0 fee)", async () => { mockedFeatureConf.setParam( "ms-choices.header-bidding.third-party-extra-fee", `{"Amazon TAM":0.00001}` ); const stepRes = await bidfloorStep.execute( new FakeRequestBuilder() .withInventorySource(RequestInventorySource.HEADER_BIDDING) .withHeaderBiddingType(HeaderBiddingType.MAX) .withCampaigns(...candidateCampaigns) .withCampaignData(campaignData) .withBidfloor(30) .build() ); stepRes.passedCampaigns.length.should.be.equals(2); stepRes.passedCampaigns.should.be.deep.equal( candidateCampaigns.filter((c) => ["3000", "5000"].includes(c.id)) ); }); }); });