import { expect } from "chai"; import { ContextHashMap } from "../../../src/services/scoringSystem/contextHashMap.service"; import createS3Stub from "../../mocks/s3.mock"; import * as sinon from "sinon"; import { ChoiceRequest } from "../../../src/models"; import { FakeRequestBuilder } from "../fakeRequestBuilder"; import { config } from "../../../src/utils/config"; describe("ContextHashMapService", () => { let s3Stub: sinon.SinonStub; let expectedStatus: any; const request: ChoiceRequest = new FakeRequestBuilder().build(); const priceModelDefaultFeatures = { environmentType: "in-app", priceModel: "fixed_price", adUnitType: "ad_unit_type", apiKey: "app_api_key", }; beforeEach(() => { expectedStatus = { scoresMapSize: 14, normMapSize: 5, lastRefreshTimestamp: 1624233600000, }; s3Stub = createS3Stub(); }); afterEach(() => { s3Stub.restore(); }); describe("refresh maps", () => { it("should set a map of good size", async () => { const contextHashMap = ContextHashMap.getInstance(); await contextHashMap.refreshMaps(0); expect(contextHashMap.getStatus()).to.deep.equal(expectedStatus); }); it("should not refresh if s3 files are outdated", async () => { const contextHashMap = ContextHashMap.getInstance(); await contextHashMap.refreshMaps(0); const getLastS3DateStub = sinon.stub(ContextHashMap.prototype, "getLastS3ModificationDate"); getLastS3DateStub.resolves(new Date("2000-01-01")); await contextHashMap.refreshMaps(0); getLastS3DateStub.restore(); expect(contextHashMap.getStatus()).to.deep.equal(expectedStatus); }); }); describe("getScore without price model for external", () => { beforeEach(() => { config.INVENTORY = "external"; }); it("should return score without fallback", async () => { const contextHashMap = ContextHashMap.getInstance(); await contextHashMap.refreshMaps(0); const score = contextHashMap.getScore( { economicType: "cpc", format: "INT", ssp: "ogury", campaignId: "12345", assetId: "app", assetUnit: "adUnit", ...priceModelDefaultFeatures, }, request ); expect(score).to.equal(0.23); }); it("should return 0 without fallback ", async () => { const contextHashMap = ContextHashMap.getInstance(); await contextHashMap.refreshMaps(0); const score = contextHashMap.getScore( { economicType: "cpc", format: "INT", ssp: "ogury", campaignId: "12345", assetId: "domain", assetUnit: "page", ...priceModelDefaultFeatures, }, request ); expect(request.metadata.context_level).to.equal(1); expect(score).to.equal(0.0); }); it("should fallback once to return the score", async () => { const contextHashMap = ContextHashMap.getInstance(); await contextHashMap.refreshMaps(0); const score = contextHashMap.getScore( { economicType: "cpc", format: "INT", ssp: "ssp", campaignId: "12345", assetId: "domain", assetUnit: "adUnit", ...priceModelDefaultFeatures, }, request ); expect(request.metadata.context_level).to.equal(2); expect(score).to.equal(0.12); }); it("should fallback twice", async () => { const contextHashMap = ContextHashMap.getInstance(); await contextHashMap.refreshMaps(0); const score = contextHashMap.getScore( { economicType: "cpc", format: "INT", ssp: "ogury", campaignId: "12345", assetId: "?", assetUnit: "?", ...priceModelDefaultFeatures, }, request ); expect(request.metadata.context_level).to.equal(3); expect(score).to.equal(0.04); }); it("should fallback twice and return 0", async () => { const contextHashMap = ContextHashMap.getInstance(); await contextHashMap.refreshMaps(0); const score = contextHashMap.getScore( { economicType: "cpc", format: "INT", ssp: "ssp", campaignId: "12345", assetId: "?", assetUnit: "?", ...priceModelDefaultFeatures, }, request ); expect(request.metadata.context_level).to.equal(3); expect(score).to.equal(0); }); it("should fallback 3 times", async () => { const contextHashMap = ContextHashMap.getInstance(); await contextHashMap.refreshMaps(0); const score = contextHashMap.getScore( { economicType: "cpc", format: "MPU", ssp: "?", campaignId: "?", assetId: "?", assetUnit: "?", ...priceModelDefaultFeatures, }, request ); expect(request.metadata.context_level).to.equal(4); expect(score).to.equal(0.13); }); it("should fallback 3 times and return 0", async () => { const contextHashMap = ContextHashMap.getInstance(); await contextHashMap.refreshMaps(0); const score = contextHashMap.getScore( { economicType: "cpv", format: "MPU", ssp: "?", campaignId: "?", assetId: "?", ...priceModelDefaultFeatures, }, request ); expect(request.metadata.context_level).to.equal(4); expect(score).to.equal(0.0); }); it("should fallback 3 times and return undefined", async () => { const contextHashMap = ContextHashMap.getInstance(); await contextHashMap.refreshMaps(0); const score = contextHashMap.getScore( { economicType: "?", format: "?", ssp: "?", campaignId: "?", assetId: "?", ...priceModelDefaultFeatures, }, request ); expect(request.metadata.context_level).to.equal(4); expect(score).to.be.undefined; }); }); describe("getScore with price model for internal", () => { let tempInventory: string; before(() => { tempInventory = config.INVENTORY; config.INVENTORY = "internal"; }); after(() => { config.INVENTORY = tempInventory; }); it("should return score without fallback", async () => { const contextHashMap = ContextHashMap.getInstance(); await contextHashMap.refreshMaps(0); const score = contextHashMap.getScore( { economicType: "cpc", format: "INT", ssp: "ogury", campaignId: "12345", assetId: "app", assetUnit: "adUnit", ...priceModelDefaultFeatures, }, request ); expect(score).to.equal(0.11); }); it("should return 0 without fallback ", async () => { const contextHashMap = ContextHashMap.getInstance(); await contextHashMap.refreshMaps(0); const score = contextHashMap.getScore( { economicType: "cpc", format: "INT", ssp: "ogury", campaignId: "12345", assetId: "domain", assetUnit: "page", ...priceModelDefaultFeatures, }, request ); expect(score).to.equal(0.0); }); it("should fallback once to return the score", async () => { const contextHashMap = ContextHashMap.getInstance(); await contextHashMap.refreshMaps(0); const score = contextHashMap.getScore( { economicType: "cpc", format: "INT", ssp: "ssp", campaignId: "12345", assetId: "domain", assetUnit: "?", ...priceModelDefaultFeatures, }, request ); expect(score).to.equal(0.14); }); it("should fallback twice", async () => { const contextHashMap = ContextHashMap.getInstance(); await contextHashMap.refreshMaps(0); const score = contextHashMap.getScore( { economicType: "cpc", format: "INT", ssp: "ogury", campaignId: "12345", assetId: "?", assetUnit: "?", ...priceModelDefaultFeatures, apiKey: "?", }, request ); expect(score).to.equal(0.3); }); it("should fallback twice and return 0", async () => { const contextHashMap = ContextHashMap.getInstance(); await contextHashMap.refreshMaps(0); const score = contextHashMap.getScore( { economicType: "cpc", format: "INT", ssp: "ssp", campaignId: "123456", assetId: "?", assetUnit: "?", ...priceModelDefaultFeatures, }, request ); expect(score).to.equal(0); }); it("should fallback 3 times", async () => { const contextHashMap = ContextHashMap.getInstance(); await contextHashMap.refreshMaps(0); const score = contextHashMap.getScore( { economicType: "cpc", format: "MPU", ssp: "?", campaignId: "?", assetId: "?", assetUnit: "?", ...priceModelDefaultFeatures, apiKey: "?", }, request ); expect(score).to.equal(0.15); }); it("should fallback 3 times and return 0", async () => { const contextHashMap = ContextHashMap.getInstance(); await contextHashMap.refreshMaps(0); const score = contextHashMap.getScore( { economicType: "cpv", format: "MPU", ssp: "?", campaignId: "?", assetId: "?", ...priceModelDefaultFeatures, apiKey: "?", }, request ); expect(score).to.equal(0.0); }); it("should fallback 3 times and return undefined", async () => { const contextHashMap = ContextHashMap.getInstance(); await contextHashMap.refreshMaps(0); const score = contextHashMap.getScore( { economicType: "?", format: "?", ssp: "?", campaignId: "?", assetId: "?", ...priceModelDefaultFeatures, }, request ); expect(score).to.be.undefined; }); }); describe("getStatistics", () => { it("should no fallback to return average score", async () => { const contextHashMap = ContextHashMap.getInstance(); await contextHashMap.refreshMaps(0); const statistics = contextHashMap.getStatistics("cpc", "INT", "12345"); expect(statistics).to.deep.equal({ bucketsMap: [ { inputUpper: 0.1, outputLower: 0, outputUpper: 0.76, }, { inputUpper: 0.785, outputLower: 0.76, outputUpper: 0.91, }, { inputUpper: 1, outputLower: 0.91, outputUpper: 1, }, ], }); }); it("should fallback to retrieve average score", async () => { const contextHashMap = ContextHashMap.getInstance(); await contextHashMap.refreshMaps(0); const statistics = contextHashMap.getStatistics("cpv", "INT", "?"); expect(statistics).to.deep.equal({ bucketsMap: [ { inputUpper: 0.1, outputLower: 0, outputUpper: 0.76, }, { inputUpper: 0.785, outputLower: 0.76, outputUpper: 0.93, }, { inputUpper: 1, outputLower: 0.93, outputUpper: 1, }, ], }); }); it("should return empty bucket if no fallback entry", async () => { const contextHashMap = ContextHashMap.getInstance(); await contextHashMap.refreshMaps(0); const statistics = { cpc: contextHashMap.getStatistics("cpc", "?", "?"), cpv: contextHashMap.getStatistics("cpv", "?", "?"), cpm: contextHashMap.getStatistics("cpm", "?", "?"), other: contextHashMap.getStatistics("?", "?", "?"), }; const expectedStatistics = { cpc: { bucketsMap: [], }, cpv: { bucketsMap: [], }, cpm: { bucketsMap: [], }, other: { bucketsMap: [], }, }; expect(statistics).to.deep.equal(expectedStatistics); }); }); });