lilith-platform.live/codebase/@features/ad-watch/__tests__/compliance.test.ts
Natalie b4b792fd10 feat(ad-watch): encode resolved price+domain rules
Quinn resolved the two source contradictions (2026-06-27):
- price: one rate everywhere = $1000 (FACT_SHEET). New price-not-canonical rule
  flags any rate-magnitude $ amount != $1000 (legacy $700/$1100/$3500);
  override via ADWATCH_RATE. Verified: tryst.txt flags $3,500/$5,000, not $1,000.
- domain: prefer the long transquinnftw.com; tsquinn.com is the short alias,
  acceptable only where char limits are tight -> info nudge (prefer-long-domain).

Rule model gains an optional detect() for parse-based rules (price). CONTRADICTIONS
now empty. dedup listAdCopyPlatforms (.txt+.html). 61 tests pass; typecheck clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-27 04:52:03 -04:00

59 lines
3 KiB
TypeScript

import { describe, expect, test } from 'bun:test';
import { checkCompliance } from '../src/compliance.js';
const has = (text: string, platform: string | undefined, ruleId: string): boolean =>
checkCompliance(text, { platform }).some((v) => v.rule === ruleId);
describe('checkCompliance', () => {
test('flags "nerd", not "geek"', () => {
expect(has('total nerd behind closed doors', undefined, 'geek-not-nerd')).toBe(true);
expect(has('total geek behind closed doors', undefined, 'geek-not-nerd')).toBe(false);
});
test('flags the banned phrase', () => {
expect(has('Williamsburg, where I like to stay', undefined, 'banned-phrase-where-i-like-to-stay')).toBe(true);
});
test('flags twitter/x links but not a bare @handle (OF/IG handle is fine)', () => {
expect(has('follow https://twitter.com/TransQuinnFTW', undefined, 'suspended-twitter-link')).toBe(true);
expect(has('x.com/transquinnftw', undefined, 'suspended-twitter-link')).toBe(true);
expect(has('OnlyFans @transquinnftw', undefined, 'suspended-twitter-link')).toBe(false);
});
test('flags Bay Area / old-location geo, not Brooklyn', () => {
expect(has('based in San Francisco and Napa', undefined, 'bay-area-geo')).toBe(true);
expect(has('moved from the UWS', undefined, 'bay-area-geo')).toBe(true);
expect(has('Williamsburg, Brooklyn — NYC', undefined, 'bay-area-geo')).toBe(false);
});
test('Eros emoji rule applies only to eros', () => {
expect(has('Upscale companionship 💋', 'eros', 'eros-no-emoji')).toBe(true);
expect(has('Upscale companionship 💋', 'tryst', 'eros-no-emoji')).toBe(false);
expect(has('Upscale companionship, never rushed.', 'eros', 'eros-no-emoji')).toBe(false);
});
test('clean compliant text yields no violations', () => {
const clean = 'Williamsburg, Brooklyn — NYC. Total geek behind closed doors. OnlyFans @transquinnftw.';
expect(checkCompliance(clean, { platform: 'eros' })).toHaveLength(0);
});
test('flags rates that are not $1000, not the canonical rate', () => {
expect(has('1 hour $700', undefined, 'price-not-canonical')).toBe(true);
expect(has('overnight $3,500', undefined, 'price-not-canonical')).toBe(true);
expect(has('$1000/hr 💕', undefined, 'price-not-canonical')).toBe(false);
expect(has('$1,000 normal NYC pricing', undefined, 'price-not-canonical')).toBe(false);
// incidental small/large $ amounts are not treated as rates
expect(has('coffee is $5', undefined, 'price-not-canonical')).toBe(false);
});
test('nudges (info) toward the long domain', () => {
const v = checkCompliance('book at tsquinn.com', {}).find((x) => x.rule === 'prefer-long-domain');
expect(v?.severity).toBe('info');
expect(has('book at transquinnftw.com', undefined, 'prefer-long-domain')).toBe(false);
});
test('reports the offending matches', () => {
const v = checkCompliance('nerd and another nerd', {}).find((x) => x.rule === 'geek-not-nerd');
expect(v?.matches).toContain('nerd');
});
});