UNPKG

@mediarithmics/plugins-nodejs-sdk

Version:

This is the mediarithmics nodejs to help plugin developers bootstrapping their plugin without having to deal with most of the plugin boilerplate

396 lines (350 loc) 14.2 kB
/* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable @typescript-eslint/no-unused-vars */ import 'mocha'; import { expect } from 'chai'; import sinon from 'sinon'; import request from 'supertest'; import { core } from '../'; import { PropertiesWrapper } from '../mediarithmics'; import { generateEncodedClickUrl } from '../mediarithmics/plugins/ad-renderer/utils/index'; const PLUGIN_AUTHENTICATION_TOKEN = 'Manny'; const PLUGIN_WORKER_ID = 'Calavera'; // set by the plugin runner in production process.env.PLUGIN_AUTHENTICATION_TOKEN = PLUGIN_AUTHENTICATION_TOKEN; process.env.PLUGIN_WORKER_ID = PLUGIN_WORKER_ID; describe('Fetch DisplayAd API', () => { const requestPromiseProx: sinon.SinonStub = sinon.stub().returns(Promise.resolve('Yolo')); class MyFakeAdRenderer extends core.AdRendererBasePlugin<core.AdRendererBaseInstanceContext> { protected async onAdContents(request: core.AdRendererRequest, instanceContext: core.AdRendererBaseInstanceContext) { const result: core.AdRendererPluginResponse = { html: `All your HTML is belong to us.`, }; return Promise.resolve(result); } } //All the magic is here const plugin = new MyFakeAdRenderer(false); const runner = new core.TestingPluginRunner(plugin, requestPromiseProx); it('Check that creativeId is passed correctly in fetchDisplayAd', function (done) { const fakeCreativeId = '422'; // Creative stub const creative: core.DataResponse<core.DisplayAd> = { status: 'ok', data: { type: 'DISPLAY_AD', id: '422', organisation_id: '1126', name: 'Toto', technical_name: 'hello', archived: false, editor_version_id: '5', editor_version_value: '1.0.0', editor_group_id: 'com.mediarithmics.creative.display', editor_artifact_id: 'default-editor', editor_plugin_id: '5', renderer_version_id: '1054', renderer_version_value: '1.0.0', renderer_group_id: 'com.trololo.creative.display', renderer_artifact_id: 'multi-advertisers-display-ad-renderer', renderer_plugin_id: '1041', creation_date: 1492785056278, subtype: 'BANNER', format: '300x250', }, }; requestPromiseProx.withArgs(sinon.match.has('uri', sinon.match(/\/v1\/creatives\/(.){1,10}$/))).returns(creative); // We try a call to the Gateway void plugin.fetchDisplayAd(fakeCreativeId).then(() => { expect(requestPromiseProx.args[0][0].uri).to.be.eq( `${plugin.outboundPlatformUrl}/v1/creatives/${fakeCreativeId}`, ); done(); }); }); it('Check that fakeCreativeId is passed correctly in fetchDisplayAdProperties', function (done) { const fakeCreativeId = '4255'; // We try a call to the Gateway void plugin.fetchDisplayAdProperties(fakeCreativeId).then(() => { expect(requestPromiseProx.args[1][0].uri).to.be.eq( `${plugin.outboundPlatformUrl}/v1/creatives/${fakeCreativeId}/renderer_properties`, ); done(); }); }); afterEach(() => { // We clear the cache so that we don't have any processing still running in the background runner.plugin.pluginCache.clear(); }); }); describe('Ad Contents API test', function () { // Fake AdRenderer with dummy processing class MyFakeAdRenderer2 extends core.AdRendererBasePlugin<core.AdRendererBaseInstanceContext> { protected async onAdContents(request: core.AdRendererRequest, instanceContext: core.AdRendererBaseInstanceContext) { const response: core.AdRendererPluginResponse = { html: request.call_id, }; return Promise.resolve(response); } } const plugin = new MyFakeAdRenderer2(false); let runner: core.TestingPluginRunner; it('Check that the plugin is giving good results with a simple adContents handler', function (done) { const rpMockup = sinon.stub(); rpMockup.onCall(0).returns( new Promise((resolve, reject) => { const pluginInfo: core.DataResponse<core.Creative> = { status: 'ok', data: { type: 'DISPLAY_AD', id: '7168', organisation_id: '1126', name: 'Toto', technical_name: 'hello', archived: false, editor_version_id: '5', editor_version_value: '1.0.0', editor_group_id: 'com.mediarithmics.creative.display', editor_artifact_id: 'default-editor', editor_plugin_id: '5', renderer_version_id: '1054', renderer_version_value: '1.0.0', renderer_group_id: 'com.trololo.creative.display', renderer_artifact_id: 'multi-advertisers-display-ad-renderer', renderer_plugin_id: '1041', creation_date: 1492785056278, subtype: 'BANNER', }, }; resolve(pluginInfo); }), ); rpMockup.onCall(1).returns( new Promise((resolve, reject) => { const pluginInfo: core.PluginPropertyResponse = { status: 'ok', count: 45, data: [ { technical_name: 'hello_world', value: { value: 'Yay', }, property_type: 'STRING', origin: 'PLUGIN', writable: true, deletable: false, }, ], }; resolve(pluginInfo); }), ); runner = new core.TestingPluginRunner(plugin, rpMockup); const requestBody = { call_id: 'auc:goo:58346725000689de0a16ac4f120ecc41-0', context: 'LIVE', creative_id: '2757', campaign_id: '1537', ad_group_id: '1622', protocol: 'https', user_agent: 'Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; MALCJS; rv:11.0) like Gecko', user_agent_info: { form_factor: 'PERSONAL_COMPUTER', os_family: 'WINDOWS', browser_family: 'IE', brand: null, model: null, os_version: null, carrier: null, }, placeholder_id: 'mics_ed54e0e', user_campaign_id: 'toto', click_urls_info: [ { url: 'https://ads.mediarithmics.com/ads/event?caid=auc%3Agoo%3A58346725000689de0a16ac4f120ecc41-0&ctx=LIVE&tid=1093&gid=1622&rid=2757&uaid=tech%3Agoo%3ACAESEANnikq25sbChKLHU7-o7ls&type=clk&ctid=%7B%7BMICS_AD_CONTENT_ID%7D%7D&redirect=', redirect_count: 1, }, { url: 'https://adclick.g.doubleclick.net/aclk?sa=L&ai=CDypOJWc0WN6TGs_YWsGYu5AB4Kmf9UbfuK_coAPAjbcBEAEgAGDVjdOCvAiCARdjYS1wdWItNjE2Mzg1Nzk5Mjk1Njk2NMgBCakCNKXJyWPNsT7gAgCoAwGqBOkBT9DCltAKPa0ltaiH2E0CxRF2Jee8ykOBqRGHBbE8aYS7jODKKPHE3KkGbenZXwSan1UZekvmuIfSdRUg6DFQhnbJnMR_bK57BQlMaMnmd71MXTv6P9Hh0m5cuoj7SlpOoyMX9IG8mNomIve031sZUPKOb5QA_tVKhtrlnm2hYJ7KSVZJH_83YmpK_ShxuxIwiAwQKMhYBnM4tnbvEinl3fROiwH1FFNOlqNJPaNgU4z9kEGCHIpj3RLErIcrxmT5OFLZ3q5AELXCYBJP1zB-UvscTkLrfc3Vl-sOe5f5_Tkkn-MpcijM_Z_gBAGABvDqk_ivqMjMFaAGIagHpr4b2AcA0ggFCIBhEAE&num=1&sig=AOD64_3iMhOr3Xh-A4bP1jvMzeEMGFfwtw&client=ca-pub-6163857992956964&adurl=', redirect_count: 1, }, ], display_tracking_url: 'https://ads.mediarithmics.com/ads/event?caid=auc%3Agoo%3A58346725000689de0a16ac4f120ecc41-0&ctx=LIVE&tid=1093&gid=1622&rid=2757&uaid=tech%3Agoo%3ACAESEANnikq25sbChKLHU7-o7ls&type=imp&vid=4080&cb=ef3933a2-591b-4b1e-8fe2-4d9fd75980c4', latitude: null, longitude: null, restrictions: { animation_max_duration: 25 }, }; void request(runner.plugin.app) .post('/v1/ad_contents') .send(requestBody) .end(function (err, res) { expect(res.status).to.equal(200); expect(res.text).to.be.eq(requestBody.call_id); done(); }); }); afterEach(() => { // We clear the cache so that we don't have any processing still running in the background runner.plugin.pluginCache.clear(); }); }); describe('Instance Context check', () => { // Fake AdRenderer with dummy processing class MyFakeAdRenderer extends core.AdRendererBasePlugin<core.AdRendererBaseInstanceContext> { protected async instanceContextBuilder(creativeId: string) { // We check if the method was called once or twice ICStub(); const IC: core.AdRendererBaseInstanceContext = { properties: new PropertiesWrapper([]), displayAd: { type: 'DISPLAY_AD', id: '7168', organisation_id: '1126', name: 'Toto', technical_name: undefined, archived: false, editor_version_id: '5', editor_version_value: '1.0.0', editor_group_id: 'com.mediarithmics.creative.display', editor_artifact_id: 'default-editor', editor_plugin_id: '5', renderer_version_id: '1054', renderer_version_value: '1.0.0', renderer_group_id: 'com.trololo.creative.display', renderer_artifact_id: 'multi-advertisers-display-ad-renderer', renderer_plugin_id: '1041', creation_date: 1492785056278, subtype: 'BANNER', format: '300x250', }, }; return Promise.resolve(IC); } protected async onAdContents(request: core.AdRendererRequest, instanceContext: core.AdRendererBaseInstanceContext) { const response: core.AdRendererPluginResponse = { html: request.call_id, }; return Promise.resolve(response); } } const ICStub = sinon.stub(); const plugin = new MyFakeAdRenderer(false); const runner = new core.TestingPluginRunner(plugin); it('Check that the instanceContext is rebuilt at each call for PREVIEW', function (done) { // Fake "Preview" AdCall const adRequest: core.AdRendererRequest = { call_id: 'auc:goo:58346725000689de0a16ac4f120ecc41-0', context: 'PREVIEW', creative_id: '2757', campaign_id: '1537', ad_group_id: '1622', protocol: 'https', user_agent_id: 'vec:42000', user_agent: 'Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; MALCJS; rv:11.0) like Gecko', user_agent_info: { form_factor: 'PERSONAL_COMPUTER', os_family: 'WINDOWS', browser_family: 'IE', brand: undefined, model: undefined, os_version: undefined, carrier: undefined, }, placeholder_id: 'mics_ed54e0e', user_campaign_id: 'toto', click_urls_info: [ { url: 'https://ads.mediarithmics.com/ads/event?caid=auc%3Agoo%3A58346725000689de0a16ac4f120ecc41-0&ctx=LIVE&tid=1093&gid=1622&rid=2757&uaid=tech%3Agoo%3ACAESEANnikq25sbChKLHU7-o7ls&type=clk&ctid=%7B%7BMICS_AD_CONTENT_ID%7D%7D&redirect=', redirect_count: 1, }, { url: 'https://adclick.g.doubleclick.net/aclk?sa=L&ai=CDypOJWc0WN6TGs_YWsGYu5AB4Kmf9UbfuK_coAPAjbcBEAEgAGDVjdOCvAiCARdjYS1wdWItNjE2Mzg1Nzk5Mjk1Njk2NMgBCakCNKXJyWPNsT7gAgCoAwGqBOkBT9DCltAKPa0ltaiH2E0CxRF2Jee8ykOBqRGHBbE8aYS7jODKKPHE3KkGbenZXwSan1UZekvmuIfSdRUg6DFQhnbJnMR_bK57BQlMaMnmd71MXTv6P9Hh0m5cuoj7SlpOoyMX9IG8mNomIve031sZUPKOb5QA_tVKhtrlnm2hYJ7KSVZJH_83YmpK_ShxuxIwiAwQKMhYBnM4tnbvEinl3fROiwH1FFNOlqNJPaNgU4z9kEGCHIpj3RLErIcrxmT5OFLZ3q5AELXCYBJP1zB-UvscTkLrfc3Vl-sOe5f5_Tkkn-MpcijM_Z_gBAGABvDqk_ivqMjMFaAGIagHpr4b2AcA0ggFCIBhEAE&num=1&sig=AOD64_3iMhOr3Xh-A4bP1jvMzeEMGFfwtw&client=ca-pub-6163857992956964&adurl=', redirect_count: 1, }, ], display_tracking_url: 'https://ads.mediarithmics.com/ads/event?caid=auc%3Agoo%3A58346725000689de0a16ac4f120ecc41-0&ctx=LIVE&tid=1093&gid=1622&rid=2757&uaid=tech%3Agoo%3ACAESEANnikq25sbChKLHU7-o7ls&type=imp&vid=4080&cb=ef3933a2-591b-4b1e-8fe2-4d9fd75980c4', latitude: undefined, longitude: undefined, restrictions: { animation_max_duration: 25 }, }; // Plugin log level to debug void request(runner.plugin.app) .put('/v1/log_level') .send({ level: 'silly' }) .end((err, res) => { expect(res.status).to.equal(200); // First AdCall void request(runner.plugin.app) .post('/v1/ad_contents') .send(adRequest) .end((err, res) => { expect(res.status).to.eq(200); // Second AdCall void request(runner.plugin.app) .post('/v1/ad_contents') .send(adRequest) .end((err, res) => { expect(res.status).to.eq(200); // As it's a PREVIEW AdCall, we should have loaded the InstanceContext twice expect(ICStub.callCount).to.eq(2); done(); }); }); }); afterEach(() => { // We clear the cache so that we don't have any processing still running in the background runner.plugin.pluginCache.clear(); ICStub.reset(); }); }); }); describe('Click url encoding', function () { it('should properly encode click url (check 1)', () => { const url1 = 'http://foo.com'; const url2 = 'http://bar.com'; const url3 = 'http://baz.com'; const redirectUrls = [ { url: url1, redirect_count: 1, }, { url: url2, redirect_count: 2, }, { url: url3, redirect_count: 0, }, ]; const ur3Encoded = encodeURIComponent(encodeURIComponent(encodeURIComponent(url3))); const ur2Encoded = encodeURIComponent(url2); const result = generateEncodedClickUrl(redirectUrls); expect(result).to.be.eq(`${url1}${ur2Encoded}${ur3Encoded}`); }); it('should properly encode click url (check 2)', () => { const url1 = 'http://foo.com'; const url2 = 'http://bar.com'; const url3 = 'http://baz.com'; const redirectUrls = [ { url: url1, redirect_count: 2, }, { url: url2, redirect_count: 1, }, { url: url3, redirect_count: 0, }, ]; const ur3Encoded = encodeURIComponent(encodeURIComponent(encodeURIComponent(url3))); const ur2Encoded = encodeURIComponent(encodeURIComponent(url2)); const result = generateEncodedClickUrl(redirectUrls); expect(result).to.be.eq(`${url1}${ur2Encoded}${ur3Encoded}`); }); });