// AdRevLinks popup syndication, driven against the REAL bridge on adrev.link. // // Creates a paused test campaign, reads its delivery back, pauses/resumes it, proves a // retry cannot double-book, then deletes it. Nothing is left behind and nothing is ever // activated, so no live traffic is spent. // // ADREVLNKS_BRIDGE_URL=... ADREVLNKS_BRIDGE_KEY=... node qa/adrevlnks-bridge.mjs import { createRequire } from 'node:module'; const require = createRequire(import.meta.url); const arl = require('../adrevlnks.js'); const ok = [], bad = []; const t = (n, c, extra) => { (c ? ok : bad).push(n + (c || !extra ? '' : ' -> ' + extra)); }; if (!arl.enabled()) { console.log('SKIP: ADREVLNKS_BRIDGE_URL / _KEY not set'); process.exit(0); } const ID = 999000 + Math.floor(Math.random() * 900); const camp = { id: ID, name: 'QA bridge campaign', targetUrl: 'https://instantadpay.com/', budget: 12000, spent: 0, accrued: 0, type: 'banner' }; // a campaign below the floor is skipped quietly rather than wasting a slot const small = await arl.push({ ...camp, id: ID + 1, budget: 200 }, { paused: true }); t('a campaign under the credit floor is not syndicated', small === null, JSON.stringify(small)); // the real push const r = await arl.push(camp, { paused: true }); t('campaign syndicated', !!(r && r.campaign_id), JSON.stringify(r)); t('it books the full credit value as views', r && r.views_booked === 12000, r && String(r.views_booked)); t('targeted Tier 1, not worldwide', r && Array.isArray(r.countries) && r.countries.includes('US') && !r.countries.includes('all'), r && JSON.stringify(r.countries)); t('created paused, so no live traffic is spent by the test', r && r.status === 2, r && String(r.status)); // idempotency: the guard that stops a retry double-booking someone's traffic const again = await arl.push(camp, { paused: true }); t('a retry returns the SAME campaign, never a second', again && again.campaign_id === r.campaign_id && again.duplicate === true, JSON.stringify(again)); // delivery is read from the rotator, never derived const st = await arl.readServed(ID); t('delivery reads back from the server', !!(st && st.ok), JSON.stringify(st).slice(0, 120)); t('ordered views match what was booked', st && st.ordered_views === 12000, st && String(st.ordered_views)); t('served starts at zero and is a real counter', st && st.served_views === 0, st && String(st.served_views)); t('per-country items came back', st && Array.isArray(st.items) && st.items.length >= 3, st && String((st.items || []).length)); // pause / resume mirror await arl.resume(ID); let s2 = await arl.readServed(ID); t('resume activates it', s2 && s2.status === 1, s2 && String(s2.status)); await arl.pause(ID); s2 = await arl.readServed(ID); t('pause deactivates it', s2 && s2.status === 2, s2 && String(s2.status)); // cleanup must actually work, or tests litter production const del = await arl.remove(ID); t('the test campaign is deleted', !!(del && del.ok), JSON.stringify(del)); const gone = await arl.readServed(ID); t('and it is really gone', gone === null, JSON.stringify(gone)); // an unknown campaign reads as null rather than throwing const none = await arl.readServed(ID + 12345); t('an unknown campaign reads as null', none === null, JSON.stringify(none)); console.log('PASS ' + ok.length); for (const b of bad) console.log('FAIL ' + b); process.exit(bad.length ? 1 : 0);