import { assert, expect } from "chai"; import DVCategoryProvider from "../../../src/categories/DVCategoryProvider"; import nock from "nock"; import { BrandSafetySeverity, CategoryRating, CategoryProviderValue, CategoryProviderType, } from "../../../src/categories/categoryProvider"; import { config } from "../../../src/utils/config"; describe("DVCategoryProvider", function () { const defaultExpireTTL: number = 5 * 24 * 3600 * 1000; const defaultNoDataExpireTTL: number = 1 * 24 * 3600 * 1000; describe("authentication", function () { const testDataAuth = [ { input: "/dv-iqc?partnerid=1234&url=xyz.com", result: "/dv-iqc?partnerid=1234&url=xyz.com&hv=13c89020cecb125641950e76e0274a14dff53a929703df20d7230860ce89eb17", }, { input: "/dv-iqc?partnerid=1234&url=xyz.com/&useragent=chrome", result: "/dv-iqc?partnerid=1234&url=xyz.com/&useragent=chrome&hv=e08f011e7dc1d62a34280e8cda5ac9858e2bb0184e3c813eae88482ae37c8e91", }, { input: "/dv-iqc?partnerid=1234&url=xyz.com%2F&useragent=Mozilla%2F5.0%20(Linux%3B%20Android%205.0%3B%20SAMSUNG%20SM-N900V%204G%20Build%2FLRX21V)%20AppleWebKit%2F537.36%20(KHTML%2C%20like%20Gecko)%20SamsungBrowser%2F2.1%20Chrome%2F34.0.1847.76%20Mobile%20Safari%2F537.36&ip=24.92.248.86", result: "/dv-iqc?partnerid=1234&url=xyz.com%2F&useragent=Mozilla%2F5.0%20(Linux%3B%20Android%205.0%3B%20SAMSUNG%20SM-N900V%204G%20Build%2FLRX21V)%20AppleWebKit%2F537.36%20(KHTML%2C%20like%20Gecko)%20SamsungBrowser%2F2.1%20Chrome%2F34.0.1847.76%20Mobile%20Safari%2F537.36&ip=24.92.248.86&hv=49b29c578741d966e5c085e0a7f6d367c3df3c47a4efbb157357897e3129f447", }, ]; testDataAuth.forEach((data) => { it(`authentify ${data.input}`, function () { const provider: any = new DVCategoryProvider(); const auth: string = provider.authentify(data.input, "dvhashed"); expect(auth).to.be.equal(data.result); }); }); }); describe("generatePath", function () { const testDataPath = [ { url: "http://www.google.com", result: "/dv-iqc?partnerid=1234&url=google.com%2F&hv=55b4eaeac5bba0eb02e84715e64639085e9ebe17cf50157202d9b916acda0f06", }, { url: "https://www.xyz.com", result: "/dv-iqc?partnerid=1234&url=xyz.com%2F&hv=1e04f9a813e5ab1dd89c93a2b64035ff50dc0c68f7903aa242e3464429afe3c3", }, { url: "http://xyz.com/toto?sdfh=gfghh+2%2F23", result: "/dv-iqc?partnerid=1234&url=xyz.com%2Ftoto%3Fsdfh%3Dgfghh%2B2%252F23&hv=3c9524f29f9dbb6350c2b6c21b453fdf97d599354f0fc58c07abcc845d7e5c22", }, ]; beforeEach(() => { config["DV_CAT_BASEURL"] = "http://ogury.iq.doubleverify.com"; config["CAT_DV_PATH"] = "/dv-iqc"; config["DV_CAT_SALT"] = "dvhashed"; config["DV_CAT_PARTNERID"] = "1234"; }); testDataPath.forEach((data) => { it(`generatePath ${data.url}`, function () { const provider: any = new DVCategoryProvider(); const auth: string = provider.generateURL(data.url); expect(auth).to.be.equal(data.result); }); }); }); describe("sendRequest", function () { beforeEach(() => { config["DV_CAT_BASEURL"] = "http://ogury.iq.doubleverify.com"; config["CAT_DV_PATH"] = "/dv-iqc"; config["DV_CAT_SALT"] = "dvhashed"; config["DV_CAT_PARTNERID"] = "1234"; }); afterEach(() => { if (!nock.isDone()) { const missingMocks = nock.pendingMocks().join("\n"); nock.cleanAll(); nock.restore(); assert.fail("mocks not called \n" + missingMocks); } }); const testDataRequest = [ { name: "unsafe data", url: "http://www.google.com", request: "/dv-iqc?partnerid=1234&url=google.com%2F&hv=55b4eaeac5bba0eb02e84715e64639085e9ebe17cf50157202d9b916acda0f06", response: "80023001,80312001,80013001,80312001,80312001,80222001,80122001,80222013,81012999,82031130,82031140,82031150,82031155,82031160,82031165,82031170,82032130,82032140,82032150,82032155,82032160,82032165,82032170,82032175,82033130,82033140,82033150,82033155,82033160,82033165,82033170,82033175,82035140,82035150,82035155,82041110,82041115,82042105,82042110,82042115,82043105,82043110,82043115,82045110,82045115,83032002,83032003,83032004,83041130,83041140,83051110,99000001,99012001,99012012", segments: [ "80023001", "80312001", "80013001", "80312001", "80312001", "80222001", "80122001", "80222013", ], high: CategoryRating.Unsafe, medium: CategoryRating.Unsafe, low: CategoryRating.Unsafe, }, { name: "unknown data", url: "https://www.google.com", request: "/dv-iqc?partnerid=1234&url=google.com%2F&hv=55b4eaeac5bba0eb02e84715e64639085e9ebe17cf50157202d9b916acda0f06", response: "80310001,80120001,99000001,99012001,99012012", segments: ["80310001", "80120001"], high: CategoryRating.Unknown, medium: CategoryRating.Safe, low: CategoryRating.Unknown, }, { name: "unknown and unsafe data", url: "https://www.google.com", request: "/dv-iqc?partnerid=1234&url=google.com%2F&hv=55b4eaeac5bba0eb02e84715e64639085e9ebe17cf50157202d9b916acda0f06", response: "80220001,80312001,99000001,99012001,99012012", segments: ["80220001", "80312001"], high: CategoryRating.Unsafe, medium: CategoryRating.Unknown, low: CategoryRating.Safe, }, { name: "no data should be safe", url: "https://www.google.com", request: "/dv-iqc?partnerid=1234&url=google.com%2F&hv=55b4eaeac5bba0eb02e84715e64639085e9ebe17cf50157202d9b916acda0f06", response: "99000001,99012001,99012012", segments: [], high: CategoryRating.Safe, medium: CategoryRating.Safe, low: CategoryRating.Safe, }, { name: "positive category only should be safe with urlencoding", url: "https://www.xyz.com/toto?sdfh=gfghh+2%2F23", request: "/dv-iqc?partnerid=1234&url=xyz.com%2Ftoto%3Fsdfh%3Dgfghh%2B2%252F23&hv=3c9524f29f9dbb6350c2b6c21b453fdf97d599354f0fc58c07abcc845d7e5c22", response: "80042001,80142001,99000001,99012001,99012012", segments: ["80042001", "80142001"], high: CategoryRating.Safe, medium: CategoryRating.Safe, low: CategoryRating.Safe, }, { name: "low risk only should be safe with urlencoding", url: "https://xyz.com/toto?sdfh=gfghh+2%2F23", request: "/dv-iqc?partnerid=1234&url=xyz.com%2Ftoto%3Fsdfh%3Dgfghh%2B2%252F23&hv=3c9524f29f9dbb6350c2b6c21b453fdf97d599354f0fc58c07abcc845d7e5c22", response: "80122001,99000001,99012001,99012012", segments: ["80122001"], high: CategoryRating.Safe, medium: CategoryRating.Safe, low: CategoryRating.Unsafe, }, ]; testDataRequest.forEach((data) => { it(`simulate end to end request: ${data.name}`, async function () { const scope = nock("http://ogury.iq.doubleverify.com") .get(data.request) .reply(200, data.response, { "Cache-Control": "max-age=889", }); const provider: DVCategoryProvider = new DVCategoryProvider(); const values: CategoryProviderValue = await provider.getCategories({ urlOrBundle: data.url, device: false, provider: CategoryProviderType.DV, requestSource: { environmentType: "web", deviceOS: "android" }, }); expect(values.ttl).to.be.equal(defaultExpireTTL); const decoded = provider.decode(values.encodedValue); expect(decoded.getBrandSafety(BrandSafetySeverity.High)).to.equal(data.high); expect(decoded.getBrandSafety(BrandSafetySeverity.Medium)).to.equal(data.medium); expect(decoded.getBrandSafety(BrandSafetySeverity.Low)).to.equal(data.low); compareTTL(decoded.getRefreshTTL(), 889000); const segments = decoded.toSegmentList(); expect(segments).to.deep.equal(data.segments); for (const segment of data.segments) { expect(decoded.isBlockedBySegment(segment)).to.be.true; } expect(decoded.isBlockedBySegment("80122999")).to.be.false; }); }); it(`when dv respond with error, we should store unknown`, async function () { const scope = nock("http://ogury.iq.doubleverify.com") .get( "/dv-iqc?partnerid=1234&url=google.com%2F&hv=55b4eaeac5bba0eb02e84715e64639085e9ebe17cf50157202d9b916acda0f06" ) .replyWithError({ message: "testing request Failed", code: 400, }); const provider: DVCategoryProvider = new DVCategoryProvider(); const values: CategoryProviderValue = await provider.getCategories({ device: false, urlOrBundle: "http://www.google.com", provider: CategoryProviderType.DV, requestSource: { environmentType: "web", deviceOS: "android" }, }); expect(values.ttl).to.be.equal(defaultNoDataExpireTTL); const decoded = provider.decode(values.encodedValue); expect(decoded.getBrandSafety(BrandSafetySeverity.High)).to.equal(CategoryRating.Unknown); expect(decoded.getBrandSafety(BrandSafetySeverity.Medium)).to.equal(CategoryRating.Unknown); expect(decoded.getBrandSafety(BrandSafetySeverity.Low)).to.equal(CategoryRating.Unknown); expect(decoded.getRefreshTTL()).to.be.equal(15 * 60 * 1000); }); it(`test parallel requests`, async function () { const con = nock("http://ogury.iq.doubleverify.com") .get( "/dv-iqc?partnerid=1234&url=google.com%2F&hv=55b4eaeac5bba0eb02e84715e64639085e9ebe17cf50157202d9b916acda0f06" ) .delay(20) .reply(200, "80122001,99000001,99012001,99012012") .get( "/dv-iqc?partnerid=1234&url=xyz.com%2F&hv=1e04f9a813e5ab1dd89c93a2b64035ff50dc0c68f7903aa242e3464429afe3c3" ) .reply(200, "80122001,99000001,99012001,99012012"); const provider: DVCategoryProvider = new DVCategoryProvider(); const promise: Promise = provider.getCategories({ device: false, urlOrBundle: "http://google.com", provider: CategoryProviderType.DV, requestSource: { environmentType: "web", deviceOS: "android" }, }); let values: CategoryProviderValue = await provider.getCategories({ device: false, urlOrBundle: "https://www.xyz.com", provider: CategoryProviderType.DV, requestSource: { environmentType: "web", deviceOS: "android" }, }); expect(values.ttl).to.be.equal(defaultExpireTTL); let decoded = provider.decode(values.encodedValue); expect(decoded.getBrandSafety(BrandSafetySeverity.High)).to.equal(CategoryRating.Safe); expect(decoded.getBrandSafety(BrandSafetySeverity.Medium)).to.equal(CategoryRating.Safe); expect(decoded.getBrandSafety(BrandSafetySeverity.Low)).to.equal(CategoryRating.Unsafe); compareTTL(decoded.getRefreshTTL(), 86400 * 1000); values = await promise; expect(values.ttl).to.be.equal(defaultExpireTTL); decoded = provider.decode(values.encodedValue); expect(decoded.getBrandSafety(BrandSafetySeverity.High)).to.equal(CategoryRating.Safe); expect(decoded.getBrandSafety(BrandSafetySeverity.Medium)).to.equal(CategoryRating.Safe); expect(decoded.getBrandSafety(BrandSafetySeverity.Low)).to.equal(CategoryRating.Unsafe); compareTTL(decoded.getRefreshTTL(), 86400 * 1000); }); it(`test changing the config`, async function () { config["DV_CAT_BASEURL"] = "https://www.ogury.co"; config["CAT_DV_PATH"] = "/myPath"; config["DV_CAT_PARTNERID"] = "7894"; config["DV_CAT_SALT"] = "guerande"; const con = nock("https://www.ogury.co") .get( "/myPath?partnerid=7894&url=google.com%2F&hv=d661e0c01dacde80c053ca3514e866cac40e763ace5b0cc7445a477553b07462" ) .reply(200, "80122001,99000001,99012001,99012012"); const provider: DVCategoryProvider = new DVCategoryProvider(); const values: CategoryProviderValue = await provider.getCategories({ device: false, urlOrBundle: "http://google.com", provider: CategoryProviderType.DV, requestSource: { environmentType: "web", deviceOS: "android" }, }); expect(values.ttl).to.be.equal(defaultExpireTTL); const decoded = provider.decode(values.encodedValue); expect(decoded.getBrandSafety(BrandSafetySeverity.High)).to.equal(CategoryRating.Safe); expect(decoded.getBrandSafety(BrandSafetySeverity.Medium)).to.equal(CategoryRating.Safe); expect(decoded.getBrandSafety(BrandSafetySeverity.Low)).to.equal(CategoryRating.Unsafe); compareTTL(decoded.getRefreshTTL(), 86400 * 1000); }); }); function compareTTL(ttl: number, expected: number) { const delay = ttl - Date.now(); expect(delay).to.be.lessThanOrEqual(expected); expect(delay).to.be.greaterThan(expected - 5000); } });