import nock from "nock"; import CircuitBreaker from "opossum"; import MsAuctionClient, { AUCTION_ENDPOINT } from "../../../../src/services/auction/auction.client"; import * as auctionModel from "../../../../src/models/auction.model"; const chai = require("chai"); const expect = chai.expect; describe("MsAuctionClient Auction Endpoint", () => { let client: MsAuctionClient; let request: auctionModel.IAuctionRequest; let user: auctionModel.IUser; let app: auctionModel.IApp; let device: auctionModel.IDevice; let camp1: auctionModel.ICampaign; before(() => { client = new MsAuctionClient("https://nock"); }); beforeEach(() => { request = {} as auctionModel.IAuctionRequest; user = {} as auctionModel.IUser; app = {} as auctionModel.IApp; device = {} as auctionModel.IDevice; camp1 = {} as auctionModel.ICampaign; request.advert_id = "test"; request.user = user; request.user.aaid = "test"; request.user.odid = "test"; request.app = app; request.app.bundle = "test"; request.app.name = "test"; request.device = device; request.device.ip = "test"; request.device.language = "test"; request.device.os_type = "test"; request.device.user_agent = "test"; request.device.is_omid_compliant = true; request.device.screen_size = { width: 300, height: 600, density: 1, }; request.campaigns = [camp1]; request.campaigns[0].bid_floor = 0; request.campaigns[0].deal_id = "test"; request.campaigns[0].id = 1; request.campaigns[0].wseat = ["test", "test"]; request.campaigns[0].dsp_buyer_id = "667"; request.campaigns[0].ad_type = "video"; request.context = { country_code: "FR", inventory_source: "INTERNAL", environment_type: "app", auction_type: "fixed_price", }; }); describe("validateAuctionRequest", () => { it("should validate a proper request", async () => { expect(client.validateAuctionRequest(request)).equal(null); }); it("should verify either app or site in request for auction endpoint", async () => { delete (request as any).app; delete (request as any).site; expect(client.validateAuctionRequest(request)).instanceOf(Error); }); it("should verify app and site not in request for auction endpoint", async () => { request.site = { name: "Something Something", domain: "whatever.com", } as auctionModel.ISite; expect(client.validateAuctionRequest(request)).instanceOf(Error); }); it("should verify advertId in request for auction endpoint", async () => { delete (request as any).advert_id; expect(client.validateAuctionRequest(request)).instanceOf(Error); }); it("should verify user in request for auction endpoint", async () => { delete (request as any).user; expect(client.validateAuctionRequest(request)).instanceOf(Error); }); it("should verify user.odid in request for auction endpoint", async () => { delete (request as any).user.odid; expect(client.validateAuctionRequest(request)).instanceOf(Error); }); it("should verify app in request for auction endpoint", async () => { delete (request as any).app; expect(client.validateAuctionRequest(request)).instanceOf(Error); }); it("should verify app.bundle in request for auction endpoint", async () => { delete (request as any).app.bundle; expect(client.validateAuctionRequest(request)).instanceOf(Error); }); it("should verify app.name in request for auction endpoint", async () => { delete (request as any).app.name; expect(client.validateAuctionRequest(request)).instanceOf(Error); }); it("should validate a web request", async () => { delete (request as any).app; request.user.aaid = ""; request.site = { name: "Something Something", domain: "whatever.com", } as auctionModel.ISite; expect(client.validateAuctionRequest(request)).equal(null); }); it("should verify campaigns in request for auction endpoint", async () => { delete (request as any).campaigns; expect(client.validateAuctionRequest(request)).instanceOf(Error); }); it("should verify campaigns length in request for auction endpoint", async () => { request.campaigns = []; expect(client.validateAuctionRequest(request)).instanceOf(Error); }); it("should verify device length in request for auction endpoint", async () => { delete (request as any).device; expect(client.validateAuctionRequest(request)).instanceOf(Error); }); it("should verify device.ip length in request for auction endpoint", async () => { delete (request as any).device.ip; expect(client.validateAuctionRequest(request)).instanceOf(Error); }); it("should verify device.os_type length in request for auction endpoint", async () => { delete (request as any).device.os_type; expect(client.validateAuctionRequest(request)).instanceOf(Error); }); it("should verify device.user_agent length in request for auction endpoint", async () => { delete (request as any).device.user_agent; expect(client.validateAuctionRequest(request)).instanceOf(Error); }); it("should verify device.is_omid_compliant length in request for auction endpoint", async () => { delete (request as any).device.is_omid_compliant; expect(client.validateAuctionRequest(request)).instanceOf(Error); }); it("should verify context.country_code presence in request for auction endpoint", async () => { delete (request as any).context.country_code; expect(client.validateAuctionRequest(request)).instanceOf(Error); }); it("should verify context.auction_type presence in request for auction endpoint", async () => { delete (request as any).context.auction_type; expect(client.validateAuctionRequest(request)).instanceOf(Error); }); it("should verify context.environment_type presence in request for auction endpoint", async () => { delete (request as any).context.environment_type; expect(client.validateAuctionRequest(request)).instanceOf(Error); }); it("should verify device.screen_size.width is in good format", async () => { (request as any).device.screen_size.width = "NotValidNumber"; expect(client.validateAuctionRequest(request)).instanceOf(Error); }); it("should verify device.screen_size.height is in good format", async () => { (request as any).device.screen_size.height = "NotValidNumber"; expect(client.validateAuctionRequest(request)).instanceOf(Error); }); it("should verify device.screen_size.height should be present if width is present", async () => { delete (request as any).device.screen_size.height; expect(client.validateAuctionRequest(request)).instanceOf(Error); }); it("should verify device.screen_size.density is in good format", async () => { (request as any).device.screen_size.density = NaN; expect(client.validateAuctionRequest(request)).instanceOf(Error); }); it("should verify device.screen_size.density could be empty", async () => { delete (request as any).device.screen_size.density; expect(client.validateAuctionRequest(request)).equal(null); }); }); describe("callAuction", () => { const response: auctionModel.IAuctionResponse = {} as auctionModel.IAuctionResponse; beforeEach(() => { response.advert_id = "test"; response.advertiser_domain = "test"; response.campaign = 1; response.deal_id = "test"; response.price = 0; response.seat = "test"; }); afterEach(() => { nock.cleanAll(); }); it("should manage properly 404", async () => { nock("https://nock").post(AUCTION_ENDPOINT).reply(404); return client.callAuction(request).catch((e) => { expect(e.message).to.be.equal( 'ms-auction-client, error to call auction: [404] ms-auction return HTTP 404 error AND [Validation] Response validation error => "value" must be of type object' ); }); }); it("should manage properly all 4XX - 5XX(other than 404)", async () => { for (let i = 400; i <= 599; i++) { if (i === 404) { continue; } const errorBody = `error ${i}`; nock("https://nock").post(AUCTION_ENDPOINT).reply(i, errorBody); try { await client.callAuction(request); } catch (e: any) { expect(e.message).to.be.equal("error " + i); } nock.cleanAll(); } }); it("should manage properly 200 and body is not good", async () => { nock("https://nock").post(AUCTION_ENDPOINT).reply(200); return client.callAuction(request).catch((e) => { expect(e.message).to.be.equal( 'ms-auction-client, error to call auction: [Validation] Response validation error => "value" must be of type object' ); }); }); it("should manage properly 200 and body missing advert_id", async () => { delete (response as any).advert_id; nock("https://nock").post(AUCTION_ENDPOINT).reply(200, response); try { await client.callAuction(request); } catch (e: any) { expect(e.message).to.equal( 'ms-auction-client, error to call auction: [Validation] Response validation error => "advert_id" is required' ); } }); it("should manage properly 200 and body missing advertiser_domain", async () => { delete (response as any).advertiser_domain; nock("https://nock").post(AUCTION_ENDPOINT).reply(200, response); try { await client.callAuction(request); } catch (e: any) { expect(e.message).to.equal( 'ms-auction-client, error to call auction: [Validation] Response validation error => "advertiser_domain" is required' ); } }); it("should manage properly 200 and body missing price", async () => { delete (response as any).price; nock("https://nock").post(AUCTION_ENDPOINT).reply(200, response); try { await client.callAuction(request); } catch (e: any) { expect(e.message).to.equal( 'ms-auction-client, error to call auction: [Validation] Response validation error => "price" is required' ); } }); it("should manage properly 200 and body missing seat", async () => { delete (response as any).seat; nock("https://nock").post(AUCTION_ENDPOINT).reply(200, response); try { await client.callAuction(request); } catch (e: any) { expect(e.message).to.equal( 'ms-auction-client, error to call auction: [Validation] Response validation error => "seat" is required' ); } }); it("should manage properly 200 and status in body is good", async () => { nock("https://nock").post(AUCTION_ENDPOINT).reply(200, response); return await client.callAuction(request).then((resp) => { const expected = { advert_id: "test", advertiser_domain: "test", campaign: 1, deal_id: "test", price: 0, seat: "test", }; expect(resp).to.be.deep.equal(expected); }); }); }); });