import { expect } from "chai"; import HeaderBiddingFunnel from "../../../src/funnels/header-bidding.funnel"; import { MockedFeatureConfig } from "@ogury/feature-config-sdk"; import { FeatureConfigService } from "../../../src/services/FeatureConfig.service"; import MetricService, { BucketSet } from "../../../src/services/metric.service"; import { FakeRequestContractBuilder } from "../../integration/utils/helper"; import constants from "../../../src/utils/constants"; import { Response } from "express"; import { CampaignModelFake } from "../fake/CampaignModelFake"; import { DVFraudService } from "../../../src/services/dvFraud.service"; import { Campaign } from "../../../src/models"; import { ContextService } from "../../../src/services/targeting/ContextService"; import { FakeCampaign } from "../fake/Campaign"; import { MsAuctionService } from "../../../src/services/auction/MsAuction.service"; import SpeedService from "../../../src/services/speed.service"; import { StepFactory } from "../../../src/steps/stepFactory"; const res = { status: () => res, json: () => {}, end: () => {}, }; const ContextServiceFake = require("../fake/targeting/ContextServiceFake"); describe("HeaderBiddingFunnel", function () { let featureConfigService: FeatureConfigService; let mockedFeatureConf: MockedFeatureConfig; let campaignModel: CampaignModelFake; let candidateCampaigns: Campaign[]; let resultCampaigns: any[]; let metricService: MetricService; let headerBiddingService: HeaderBiddingFunnel; let msAuctionService: MsAuctionService; let dvFraudService: DVFraudService; let contextService: ContextService; before(() => { contextService = new ContextServiceFake({ "1000": { "1": { score: 0.01, bucketsMap: [{ inputUpper: 0.01, outputUpper: 0.01, outputLower: 0.01 }], }, }, "2000": { "2": { score: 0.02, bucketsMap: [{ inputUpper: 0.02, outputUpper: 0.02, outputLower: 0.02 }], }, }, "3000": { "3": { score: 0.03, bucketsMap: [{ inputUpper: 0.03, outputUpper: 0.03, outputLower: 0.03 }], }, }, }); featureConfigService = FeatureConfigService.getInstance(); mockedFeatureConf = new MockedFeatureConfig(); // mock campaignModel : // campaign 1000 and 3000 are prog // budget of each campaign are their id, so bid price will be: // 1000 => 10 // 2000 => 20 // 3000 => 30 campaignModel = new CampaignModelFake([ { id: "1000", budget: 1000, clicks_sold: 1000, margin: 0, ctr: 0.01, creatives: [{ id: 1, name: "cr1", format: { id: "format", ad_type: "display" } }], }, { id: "2000", budget: 1000, clicks_sold: 1000, margin: 0, ctr: 0.01, creatives: [{ id: 2, name: "cr2", format: { id: "format", ad_type: "display" } }], }, { id: "3000", budget: 1000, clicks_sold: 1000, margin: 0, ctr: 0.01, creatives: [{ id: 3, name: "cr3", format: { id: "format", ad_type: "display" } }], }, { id: "4000", budget: 1000, clicks_sold: 1000, margin: 0, ctr: 0.01, creatives: [{ id: 4, name: "cr4", format: { id: "format", ad_type: "display" } }], family: "marketplace", }, ]); candidateCampaigns = [ new FakeCampaign("1000", undefined, undefined, [1]), new FakeCampaign("2000", undefined, undefined, [2]), new FakeCampaign("3000", undefined, undefined, [3]), ]; resultCampaigns = [ { id: "3000", bidPrice: 30 * 1000 }, { id: "2000", bidPrice: 20 * 1000 }, { id: "1000", bidPrice: 10 * 1000 }, { id: "4000", bidPrice: 40 * 1000 }, ]; metricService = MetricService.getInstance(); metricService.addSection( constants.CHOICES_REQUEST_SECTION, constants.CHOICES_REQUEST_SECTION_LABELS, BucketSet.zeroTo600ms ); dvFraudService = new DVFraudService(); }); beforeEach(() => { featureConfigService.setFeatureConfig(mockedFeatureConf); msAuctionService = new MsAuctionService(new CampaignModelFake(), "https://nock", {} as any); const stepFactory = new StepFactory( campaignModel, contextService, dvFraudService, new SpeedService() ); stepFactory.initProgrammatic(msAuctionService); headerBiddingService = new HeaderBiddingFunnel(campaignModel, stepFactory, null); }); it("should respond 0 campaign because of bid floor step filter out every incoming request campaigns", async function () { const response = await headerBiddingService.choose( new FakeRequestContractBuilder() .withRandomId() .withCampaigns(...candidateCampaigns) .withBidfloor(124) .build(), res as unknown as Response ); expect(response, "validation error").to.not.be.null; expect(response?.campaigns).to.have.lengthOf(0); }); it("should respond 3 campaigns because of bid floor step filter out 0 incoming request campaigns", async function () { const response = await headerBiddingService.choose( new FakeRequestContractBuilder() .withRandomId() .withIP("1.2.3.4") .withCampaigns(...candidateCampaigns) .withBidfloor(9) .build(), res as unknown as Response ); expect(response, "validation error").to.not.be.null; expect(response?.campaigns).to.have.lengthOf(3); expect(response?.campaigns[0]).to.include(resultCampaigns[0]); expect(response?.campaigns[1]).to.include(resultCampaigns[1]); expect(response?.campaigns[2]).to.include(resultCampaigns[2]); }); it("should respond 2 campaigns because of bid floor step filter out 1 incoming request campaign", async function () { const response = await headerBiddingService.choose( new FakeRequestContractBuilder() .withRandomId() .withIP("1.2.3.4") .withCampaigns(...candidateCampaigns) .withBidfloor(15) .build(), res as unknown as Response ); expect(response, "validation error").to.not.be.null; expect(response?.campaigns).to.have.lengthOf(2); expect(response?.campaigns[0]).to.include(resultCampaigns[0]); expect(response?.campaigns[1]).to.include(resultCampaigns[1]); }); it("should respond 3 campaigns because of bid floor is 0 in incoming request campaign", async function () { const response = await headerBiddingService.choose( new FakeRequestContractBuilder() .withRandomId() .withIP("1.2.3.4") .withCampaigns(...candidateCampaigns) .build(), res as unknown as Response ); expect(response, "validation error").to.not.be.null; expect(response?.campaigns).to.have.lengthOf(3); expect(response?.campaigns[0]).to.include(resultCampaigns[0]); expect(response?.campaigns[1]).to.include(resultCampaigns[1]); expect(response?.campaigns[2]).to.include(resultCampaigns[2]); }); // it("should use the marketplace flow", async function () { // const campaigns = [...candidateCampaigns, { id: "4000", category_iab: [], creatives: [4] }]; // nock("https://nock") // .post("/mkp/bid") // .reply((_, requestBody: any) => { // return [ // 200, // { // advert_id: requestBody.advert_id, // campaign: requestBody.campaigns[0].id, // price: 0.1, // advertiser_domain: "advertiser_domain", // deal_id: requestBody.campaigns[0].deal_id, // seat: "wseat1", // }, // ]; // }); // const response = await headerBiddingService.choose( // new FakeRequestContractBuilder() // .withRandomId() // .withCampaigns(...campaigns) // .withBidfloor(124) // .build(), // res as unknown as Response // ); // expect(response.campaigns).to.have.lengthOf(1); // expect(response.campaigns[0]).to.include({ id: "4000" }); // }); });