← back to Rentv 2026
test(adslots): rate-card data contract + rate-card/slot endpoint coverage
adcaafca8ca2be6f4a71156f744e7272f822e792 · 2026-08-08 07:40:29 -0700 · Steve Abrams
Covers the half of src/ad-system.cjs cycle-1 left untested. Real-data contract on
data/ad-slots.json (unique ids, required id/name/type, numeric price, valid status
enum, created_at present) so a malformed hand-edit is caught in CI, not by an
advertiser. Plus fake-Express coverage of /api/ad/rate-card creative merge and
/api/ad/slot/:id known-id merge + unknown-id 404. No production code changed.
Suite 77->84 green.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Files touched
A test/adslots/ratecard.test.mjs
Diff
commit adcaafca8ca2be6f4a71156f744e7272f822e792
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Sat Aug 8 07:40:29 2026 -0700
test(adslots): rate-card data contract + rate-card/slot endpoint coverage
Covers the half of src/ad-system.cjs cycle-1 left untested. Real-data contract on
data/ad-slots.json (unique ids, required id/name/type, numeric price, valid status
enum, created_at present) so a malformed hand-edit is caught in CI, not by an
advertiser. Plus fake-Express coverage of /api/ad/rate-card creative merge and
/api/ad/slot/:id known-id merge + unknown-id 404. No production code changed.
Suite 77->84 green.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---
test/adslots/ratecard.test.mjs | 116 +++++++++++++++++++++++++++++++++++++++++
1 file changed, 116 insertions(+)
diff --git a/test/adslots/ratecard.test.mjs b/test/adslots/ratecard.test.mjs
new file mode 100644
index 00000000..ee09dc30
--- /dev/null
+++ b/test/adslots/ratecard.test.mjs
@@ -0,0 +1,116 @@
+// ─────────────────────────────────────────────────────────────────────────────
+// test/adslots/ratecard.test.mjs — the OTHER half of src/ad-system.cjs that
+// cycle-1 (inventory + reserve) didn't cover: the banner/newsletter RATE CARD.
+// yoloforever TK-10364 loop cycle 2.
+//
+// Two layers:
+// A. REAL-DATA CONTRACT — asserts the shipped data/ad-slots.json (served live by
+// /api/ad/rate-card + /api/ad/slot/:id) is well-formed, so a hand-edit that
+// drops an id / breaks a price silently breaking the advertiser rate card is
+// caught in CI, not by an advertiser.
+// B. ENDPOINT BEHAVIOR — via the same zero-dep fake-Express harness as cycle 1:
+// /api/ad/rate-card merges persisted creatives onto placements; /api/ad/slot/:id
+// returns a merged slot for a known id and 404 for an unknown one.
+// No production code changes — pure test coverage.
+// ─────────────────────────────────────────────────────────────────────────────
+import { test } from 'node:test';
+import assert from 'node:assert/strict';
+import { createRequire } from 'node:module';
+import fs from 'node:fs';
+import os from 'node:os';
+import path from 'node:path';
+
+const require = createRequire(import.meta.url);
+const mountAdSystem = require('../../src/ad-system.cjs');
+const realCard = require('../../data/ad-slots.json');
+
+// ── minimal fake Express app (captures last handler per "METHOD path") ────────
+function mount(DATA) {
+ const routes = {};
+ const reg = m => (p, ...h) => { routes[`${m} ${p}`] = h[h.length - 1]; };
+ const app = { get: reg('GET'), post: reg('POST'), put: reg('PUT'), use() {} };
+ mountAdSystem(app, { DATA, PUB: path.join(DATA, '__nopub__'), adminOnly: (_q, _r, n) => n && n() });
+ return routes;
+}
+function hit(routes, key, { query = {}, body = {}, params = {} } = {}) {
+ const req = { query, body, params, headers: {}, ip: '127.0.0.1' };
+ let out = { status: 200, body: null };
+ const res = { set() { return this; }, status(c) { out.status = c; return this; }, json(o) { out.body = o; return this; } };
+ routes[key](req, res);
+ return out;
+}
+function tmpData(files) {
+ const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'adcard-'));
+ for (const [f, o] of Object.entries(files)) fs.writeFileSync(path.join(dir, f), JSON.stringify(o));
+ return dir;
+}
+
+const VALID_STATUS = new Set(['available', 'reserved', 'sold']);
+
+// ── A. real-data contract on the SHIPPED rate card ───────────────────────────
+test('ad-slots.json: placements are a non-empty array', () => {
+ assert.ok(Array.isArray(realCard.placements) && realCard.placements.length > 0);
+});
+
+test('ad-slots.json: every placement has id/name/type + a numeric price + valid status', () => {
+ for (const p of realCard.placements) {
+ assert.equal(typeof p.id, 'string'); assert.ok(p.id.length, 'non-empty id');
+ assert.equal(typeof p.name, 'string'); assert.ok(p.name.length, 'non-empty name');
+ assert.equal(typeof p.type, 'string'); assert.ok(p.type.length, 'non-empty type');
+ assert.equal(typeof p.price_monthly, 'number');
+ assert.ok(p.price_monthly >= 0 && Number.isFinite(p.price_monthly), `${p.id} price sane`);
+ assert.ok(VALID_STATUS.has(p.status), `${p.id} status "${p.status}" is a known enum`);
+ }
+});
+
+test('ad-slots.json: placement ids are unique', () => {
+ const ids = realCard.placements.map(p => p.id);
+ assert.equal(new Set(ids).size, ids.length, 'no duplicate slot ids');
+});
+
+test('ad-slots.json: every placement carries a created_at (admin-card recency rule)', () => {
+ for (const p of realCard.placements) assert.equal(typeof p.created_at, 'string', `${p.id} has created_at`);
+});
+
+// ── B. /api/ad/rate-card merges persisted creatives onto placements ──────────
+test('rate-card: persisted creative/status merges onto the matching placement', () => {
+ const DATA = tmpData({
+ 'ad-slots.json': { placements: [
+ { id: 'home-leaderboard', name: 'HL', type: 'banner', price_monthly: 3500, status: 'available' },
+ { id: 'nl-top', name: 'NL', type: 'newsletter', price_monthly: 1200, status: 'available' },
+ ] },
+ 'ad-state.json': { videos: {}, creatives: {
+ 'home-leaderboard': { creative_url: '/x.png', click_url: 'https://acme.example', status: 'sold', advertiser: 'ACME' },
+ } },
+ });
+ const { status, body } = hit(mount(DATA), 'GET /api/ad/rate-card');
+ assert.equal(status, 200);
+ const hl = body.placements.find(p => p.id === 'home-leaderboard');
+ const nl = body.placements.find(p => p.id === 'nl-top');
+ assert.equal(hl.creative_url, '/x.png');
+ assert.equal(hl.click_url, 'https://acme.example');
+ assert.equal(hl.status, 'sold');
+ assert.equal(hl.advertiser, 'ACME');
+ assert.equal(nl.status, 'available', 'untouched placement stays as-is');
+});
+
+// ── B. /api/ad/slot/:id — known id merged, unknown id 404 ────────────────────
+test('slot/:id: returns the merged slot for a known id', () => {
+ const DATA = tmpData({
+ 'ad-slots.json': { placements: [{ id: 'home-leaderboard', name: 'HL', type: 'banner', price_monthly: 3500, status: 'available' }] },
+ 'ad-state.json': { videos: {}, creatives: { 'home-leaderboard': { creative_url: '/x.png', status: 'sold' } } },
+ });
+ const { status, body } = hit(mount(DATA), 'GET /api/ad/slot/:id', { params: { id: 'home-leaderboard' } });
+ assert.equal(status, 200);
+ assert.equal(body.ok, true);
+ assert.equal(body.slot.id, 'home-leaderboard');
+ assert.equal(body.slot.creative_url, '/x.png');
+ assert.equal(body.slot.status, 'sold');
+});
+
+test('slot/:id: unknown id returns 404', () => {
+ const DATA = tmpData({ 'ad-slots.json': { placements: [{ id: 'home-leaderboard', name: 'HL', type: 'banner', price_monthly: 3500, status: 'available' }] } });
+ const { status, body } = hit(mount(DATA), 'GET /api/ad/slot/:id', { params: { id: 'does-not-exist' } });
+ assert.equal(status, 404);
+ assert.equal(body.ok, false);
+});
← e15f5398 test(adslots): cover ad-system + fix filtered inventory summ
·
back to Rentv 2026
·
chore: sync package-lock version to 0.24.0 (trailing the v0. 9af4fe93 →