71ab9555e2
A campaign's name is the member's own internal label, and some of them are
two characters ("MG"). DripOffers rejects anything under five, and more to
the point a click-earner scanning the offerwall learns nothing from "MG".
So the ad's own headline comes first, then the label, and a very short
label is qualified with the destination host — "MG (mailer.gold)" — rather
than dropped or dressed up in marketing copy we invented on the member's
behalf. Nothing usable at all returns null instead of a made-up title.
Found by the backfill: campaign 88 failed on it.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
100 lines
6.3 KiB
JavaScript
100 lines
6.3 KiB
JavaScript
// DripOffers paid-per-click syndication, driven against the REAL bridge on dripoffers.com.
|
|
//
|
|
// 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 publisher is ever paid for a test click.
|
|
//
|
|
// DRIPOFFERS_BRIDGE_URL=... DRIPOFFERS_BRIDGE_KEY=... node qa/dripoffers-bridge.mjs
|
|
import { createRequire } from 'node:module';
|
|
const require = createRequire(import.meta.url);
|
|
const drip = require('../dripoffers.js');
|
|
|
|
const ok = [], bad = [];
|
|
const t = (n, c, extra) => { (c ? ok : bad).push(n + (c || !extra ? '' : ' -> ' + extra)); };
|
|
|
|
// ---- pure logic first: these run with or without a configured bridge ------
|
|
const tiers = { t1: new Set(['US', 'CA', 'GB']), t2: new Set(['BR', 'PL']) };
|
|
t('no targeting means worldwide', drip.countriesFor('', tiers) === '*', drip.countriesFor('', tiers));
|
|
t('all three tiers collapse to worldwide', drip.countriesFor('1,2,3', tiers) === '*', drip.countriesFor('1,2,3', tiers));
|
|
t('tier 1 becomes a country list', drip.countriesFor('1', tiers) === 'US,CA,GB', drip.countriesFor('1', tiers));
|
|
t('tier 1+2 merges both lists', drip.countriesFor('1,2', tiers) === 'US,CA,GB,BR,PL', drip.countriesFor('1,2', tiers));
|
|
// tier 3 is "everywhere the others are not" — an inclusion list cannot say that, and sending it
|
|
// worldwide would deliver the Tier 1 traffic its owner deliberately excluded
|
|
t('tier 3 alone is skipped, not silently widened', drip.countriesFor('3', tiers) === null, String(drip.countriesFor('3', tiers)));
|
|
|
|
t('credits convert to clicks one for one', drip.clicksFor(4000) === 4000, String(drip.clicksFor(4000)));
|
|
t('one campaign cannot swallow the platform', drip.clicksFor(500000) === drip.MAX_CLICKS, String(drip.clicksFor(500000)));
|
|
// the offerwall title a stranger actually reads (the platform's floor is 5 characters)
|
|
const T = (c, u) => drip.titleFor(c, u || 'https://www.mailer.gold/x');
|
|
t('the ad headline wins over the internal label', T({ title: 'Get paid instantly', name: 'MG' }) === 'Get paid instantly', String(T({ title: 'Get paid instantly', name: 'MG' })));
|
|
t('a usable label is used as-is', T({ name: 'Branded Voice' }) === 'Branded Voice', String(T({ name: 'Branded Voice' })));
|
|
t('a two-letter label is qualified, not dropped', T({ name: 'MG' }) === 'MG (mailer.gold)', String(T({ name: 'MG' })));
|
|
t('no label at all falls back to the destination', T({}) === 'mailer.gold', String(T({})));
|
|
t('nothing usable returns null rather than a made-up title', T({}, 'not a url') === null, String(T({}, 'not a url')));
|
|
|
|
t('a click format is accepted', drip.kindOk('banner') && drip.kindOk('visits') && drip.kindOk('text'));
|
|
t('video and solo are not click formats', !drip.kindOk('video') && !drip.kindOk('solo') && !drip.kindOk('featured'));
|
|
|
|
if (!drip.enabled()) {
|
|
console.log('PASS ' + ok.length + ' (pure logic only)');
|
|
for (const b of bad) console.log('FAIL ' + b);
|
|
console.log('SKIP: DRIPOFFERS_BRIDGE_URL / _KEY not set — live bridge checks not run');
|
|
process.exit(bad.length ? 1 : 0);
|
|
}
|
|
|
|
// ---- live bridge ---------------------------------------------------------
|
|
const ID = 999000 + Math.floor(Math.random() * 900);
|
|
const camp = { id: ID, name: 'QA bridge campaign', title: 'A test offer that is never activated',
|
|
targetUrl: 'https://instantadpay.com/', budget: 12000, spent: 0, accrued: 0, type: 'banner' };
|
|
|
|
// a campaign below the credit floor is skipped quietly rather than taking a listing
|
|
const small = await drip.push({ ...camp, id: ID + 1, budget: 200 }, { paused: true, tiers });
|
|
t('a campaign under the credit floor is not syndicated', small === null, JSON.stringify(small));
|
|
|
|
// a format that is not a click offer never goes out
|
|
const wrongKind = await drip.push({ ...camp, id: ID + 2, type: 'video' }, { paused: true, tiers });
|
|
t('a video campaign is not syndicated as a click offer', wrongKind === null, JSON.stringify(wrongKind));
|
|
|
|
// the real push
|
|
const r = await drip.push(camp, { paused: true, tiers });
|
|
t('campaign syndicated', !!(r && r.campaign_id), JSON.stringify(r));
|
|
t('clicks are capped at the per-campaign ceiling', r && r.clicks_booked === drip.MAX_CLICKS, r && String(r.clicks_booked));
|
|
t('untargeted campaign goes worldwide', r && r.countries === '*', r && String(r.countries));
|
|
t('created paused, so no publisher is paid by the test', r && r.status === 0, r && String(r.status));
|
|
t('the funding account was actually debited', r && r.charged === true, r && String(r.charged));
|
|
|
|
// idempotency: the guard that stops a retry double-booking
|
|
const again = await drip.push(camp, { paused: true, tiers });
|
|
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 counted from the click ledger, never derived from remaining_visits
|
|
const st = await drip.readServed(ID);
|
|
t('delivery reads back from the platform', !!(st && st.ok), JSON.stringify(st).slice(0, 140));
|
|
t('ordered clicks match what was booked', st && st.ordered_clicks === drip.MAX_CLICKS, st && String(st.ordered_clicks));
|
|
t('delivered starts at zero and is a counted figure', st && st.delivered_clicks === 0, st && String(st.delivered_clicks));
|
|
|
|
// pause / resume mirror — and the reason this matters: a pause must NOT read back as delivered
|
|
await drip.resume(ID);
|
|
let s2 = await drip.readServed(ID);
|
|
t('resume activates it', s2 && s2.status === 1, s2 && String(s2.status));
|
|
await drip.pause(ID);
|
|
s2 = await drip.readServed(ID);
|
|
t('pause deactivates it', s2 && s2.status === 0, s2 && String(s2.status));
|
|
t('pausing does not fake delivery', s2 && s2.delivered_clicks === 0 && s2.remaining_visits === drip.MAX_CLICKS,
|
|
s2 && (s2.delivered_clicks + '/' + s2.remaining_visits));
|
|
|
|
// cleanup must actually work, or tests litter a production platform
|
|
const del = await drip.remove(ID);
|
|
t('the test campaign is deleted', !!(del && del.ok), JSON.stringify(del));
|
|
const gone = await drip.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 drip.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);
|