0996320bd1
Zero out-of-pocket; everything config-flip-ready but DISABLED/organic. - BOOK IT deep links: web/src/lib/bookLinks.js + affiliateConfig.js (all books enabled:false, Impact/Partnerize param shapes documented, empty params skipped). Wired into StatStrip BookItTeaser (real anchor now) + scan hand-off links. Every book anchor renders rel="sponsored noopener noreferrer" (BOOK_LINK_REL). - Best-price marker: slateAdapter.detectBestBook (only when >=2 books post the SAME line and prices differ — absent beats wrong) + subtle signal-green dot in StatStrip. Slate.groupByGame threads the grouped per-book rows (books[]) onto PropRowProp instead of discarding them. - Partner refs: ?ref=CODE -> vyndr_ref cookie (90d, first-touch, PartnerRefCapture in layout) -> signup metadata partner_ref -> internal GET /api/partners/report/:code (requireInternalAuth; honest zeros + note until the TODO migration in docs/PARTNERS.md adds user_profiles.partner_ref — NOT run). Stripe promo-code convention: partner code == promotion code, verbatim. - Tests: +41 (2398 -> 2439, 209 suites); bookItTeaser + vyndrCoreScreens invariants updated to the new (stronger) rel contract. Web build exit 0. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
100 lines
3.9 KiB
JavaScript
100 lines
3.9 KiB
JavaScript
// A1 Session 3 — internal partner attribution report.
|
|
// Pure report math + route behavior with an injected Supabase client.
|
|
|
|
const express = require('express');
|
|
const request = require('supertest');
|
|
|
|
const partnersRouter = require('../../src/routes/partners');
|
|
const { sanitizePartnerCode, buildPartnerReport, _setClientForTests } = partnersRouter;
|
|
|
|
const KEY = 'test-internal-key';
|
|
|
|
function makeApp() {
|
|
const app = express();
|
|
app.use('/api/partners', partnersRouter);
|
|
return app;
|
|
}
|
|
|
|
describe('sanitizePartnerCode', () => {
|
|
it('uppercases and accepts the cookie convention (A-Z 0-9 - _, ≤32)', () => {
|
|
expect(sanitizePartnerCode('hoopspod')).toBe('HOOPSPOD');
|
|
expect(sanitizePartnerCode('THE-WIRE_22')).toBe('THE-WIRE_22');
|
|
});
|
|
it('rejects unsafe input', () => {
|
|
expect(sanitizePartnerCode('a b')).toBeNull();
|
|
expect(sanitizePartnerCode('a'.repeat(33))).toBeNull();
|
|
expect(sanitizePartnerCode('')).toBeNull();
|
|
expect(sanitizePartnerCode("x';drop")).toBeNull();
|
|
});
|
|
});
|
|
|
|
describe('buildPartnerReport', () => {
|
|
it('counts signups, active paid conversions, and founder-aware MRR', () => {
|
|
const rows = [
|
|
{ tier: 'free', subscription_status: 'none', founder_pricing: false },
|
|
{ tier: 'analyst', subscription_status: 'active', founder_pricing: false }, // 19.99
|
|
{ tier: 'analyst', subscription_status: 'active', founder_pricing: true }, // 14.99
|
|
{ tier: 'desk', subscription_status: 'active', founder_pricing: false }, // 49.99
|
|
{ tier: 'desk', subscription_status: 'canceled', founder_pricing: false }, // not active
|
|
];
|
|
expect(buildPartnerReport(rows)).toEqual({ signups: 5, conversions: 3, mrr_attributed: 84.97 });
|
|
});
|
|
it('empty / bad input → zeros', () => {
|
|
expect(buildPartnerReport([])).toEqual({ signups: 0, conversions: 0, mrr_attributed: 0 });
|
|
expect(buildPartnerReport(null)).toEqual({ signups: 0, conversions: 0, mrr_attributed: 0 });
|
|
});
|
|
});
|
|
|
|
describe('GET /api/partners/report/:code', () => {
|
|
const OLD_KEY = process.env.VYNDR_INTERNAL_KEY;
|
|
beforeAll(() => { process.env.VYNDR_INTERNAL_KEY = KEY; });
|
|
afterAll(() => {
|
|
if (OLD_KEY === undefined) delete process.env.VYNDR_INTERNAL_KEY;
|
|
else process.env.VYNDR_INTERNAL_KEY = OLD_KEY;
|
|
_setClientForTests(null);
|
|
});
|
|
|
|
const fakeClient = (result) => () => ({
|
|
from: () => ({ select: () => ({ eq: () => Promise.resolve(result) }) }),
|
|
});
|
|
|
|
it('401s without the internal key', async () => {
|
|
const res = await request(makeApp()).get('/api/partners/report/HOOPSPOD');
|
|
expect(res.status).toBe(401);
|
|
});
|
|
|
|
it('400s an invalid code', async () => {
|
|
const res = await request(makeApp())
|
|
.get('/api/partners/report/bad%20code')
|
|
.set('x-internal-key', KEY);
|
|
expect(res.status).toBe(400);
|
|
});
|
|
|
|
it('returns real attribution when the partner_ref column exists', async () => {
|
|
_setClientForTests(fakeClient({
|
|
data: [
|
|
{ tier: 'analyst', subscription_status: 'active', founder_pricing: true },
|
|
{ tier: 'free', subscription_status: 'none', founder_pricing: false },
|
|
],
|
|
error: null,
|
|
}));
|
|
const res = await request(makeApp())
|
|
.get('/api/partners/report/hoopspod')
|
|
.set('x-internal-key', KEY);
|
|
expect(res.status).toBe(200);
|
|
expect(res.body).toEqual({ ok: true, code: 'HOOPSPOD', signups: 2, conversions: 1, mrr_attributed: 14.99 });
|
|
});
|
|
|
|
it('degrades to zeros + note when the column is not migrated yet', async () => {
|
|
_setClientForTests(fakeClient({ data: null, error: { message: 'column user_profiles.partner_ref does not exist' } }));
|
|
const res = await request(makeApp())
|
|
.get('/api/partners/report/HOOPSPOD')
|
|
.set('x-internal-key', KEY);
|
|
expect(res.status).toBe(200);
|
|
expect(res.body.signups).toBe(0);
|
|
expect(res.body.conversions).toBe(0);
|
|
expect(res.body.mrr_attributed).toBe(0);
|
|
expect(res.body.note).toContain('docs/PARTNERS.md');
|
|
});
|
|
});
|