// A1 Session 10 — public ledger profiles: /u/[handle] page + OG card + // settings claim/publish UI + proxies + migration. The .tsx surfaces are // asserted against source (plain-JS Jest, no TS transform) — same pattern as // vyndrAppShell/socialPreview. const fs = require('fs'); const path = require('path'); const ROOT = path.join(__dirname, '..', '..'); const WEB = path.join(ROOT, 'web', 'src'); const read = (rel) => fs.readFileSync(path.join(WEB, rel), 'utf8'); const exists = (rel) => fs.existsSync(path.join(WEB, rel)); const routes = require('../../web/src/lib/routes'); describe('/u routing — the share surface stays public', () => { it('never gates /u (a public record must load anonymous)', () => { expect(routes.isGatedRoute('/u')).toBe(false); expect(routes.isGatedRoute('/u/kev')).toBe(false); }); it('declares /u in OPEN_ROUTES', () => { expect(routes.OPEN_ROUTES).toContain('/u'); }); }); describe('/u/[handle] server shell', () => { const src = read('app/u/[handle]/page.tsx'); it('carries the CLV-verified metadata title', () => { expect(src).toContain('CLV-verified record — @'); expect(src).toContain('· VYNDR'); }); it('renders the client record component', () => { expect(src).toContain('PublicProfile'); expect(exists('app/u/[handle]/PublicProfile.tsx')).toBe(true); }); }); describe('/u/[handle] client record', () => { const src = read('app/u/[handle]/PublicProfile.tsx'); it('honors the n-gate: RECORD BUILDING under min_sample, never a small-sample %', () => { expect(src).toContain('RECORD BUILDING'); expect(src).toMatch(/settled >= minSample/); }); it('shows outcome + CLV chips on settled rows (ledger card patterns)', () => { expect(src).toContain('✓ HIT'); expect(src).toContain('✕ MISS'); expect(src).toContain('BEAT CLOSE'); expect(src).toContain('clv_result'); }); it('states the uncurated contract and one not-found state for unknown AND unpublished', () => { expect(src).toContain('Nothing curated'); expect(src).toContain('private by default'); expect(src).toMatch(/notfound/); }); it('fetches through the Next proxy (S25 rule)', () => { expect(src).toContain('/api/profiles/'); }); }); describe('/u/[handle] OG image', () => { const og = read('app/u/[handle]/opengraph-image.tsx'); it('is a 1200x630 PNG record card', () => { expect(og).toContain('width: 1200, height: 630'); expect(og).toContain("contentType = 'image/png'"); expect(og).toContain('PUBLIC LEDGER'); expect(og).toContain('misses included'); }); it('does NOT use the edge runtime (self-hosted standalone, S53 rule)', () => { expect(og).not.toContain("runtime = 'edge'"); }); }); describe('settings — claim handle + publish toggle', () => { const src = read('app/settings/page.tsx'); it('carries the explicit private-by-default publish copy verbatim', () => { expect(src).toContain('Publishing puts your ENTIRE settled record on a public page — wins and misses. Private by default.'); }); it('reads and writes /api/profiles/me with the bearer token', () => { expect(src).toContain("fetch('/api/profiles/me'"); expect(src).toMatch(/method: 'POST'[\s\S]*?\/api\/profiles\/me|\/api\/profiles\/me[\s\S]*?method: 'POST'/); }); it('sanitizes the handle input to the API shape (a-z 0-9 _ , max 20)', () => { expect(src).toContain("replace(/[^a-z0-9_]/g, '')"); expect(src).toContain('.slice(0, 20)'); }); }); describe('Next proxies (S25 rule — Express is not browser-reachable)', () => { it('me proxy forwards GET/POST with Authorization', () => { const src = read('app/api/profiles/me/route.ts'); expect(src).toContain('BACKEND_URL'); expect(src).toContain('/api/profiles/me'); expect(src).toContain('authorization'); expect(src).toContain('export async function POST'); }); it('handle proxy forwards the public read and passes the 404 through untouched', () => { const src = read('app/api/profiles/[handle]/route.ts'); expect(src).toContain('BACKEND_URL'); expect(src).toContain('/api/profiles/'); expect(src).toContain('status: upstream.status'); }); }); describe('migration 022 — public_profiles (committed, applied by the founder)', () => { const sql = fs.readFileSync(path.join(ROOT, 'supabase', 'migrations', '022_public_profiles.sql'), 'utf8'); it('creates the table with the handle CHECK + private-by-default published', () => { expect(sql).toContain('public.public_profiles'); expect(sql).toContain("handle ~ '^[a-z0-9_]{3,20}$'"); expect(sql).toMatch(/published\s+boolean NOT NULL DEFAULT false/); expect(sql).toContain('REFERENCES auth.users'); }); it('RLS: published rows readable by anyone, own row by owner, writes service-role only', () => { expect(sql).toContain('ENABLE ROW LEVEL SECURITY'); expect(sql).toContain('USING (published = true)'); expect(sql).toContain('USING (user_id = auth.uid())'); expect(sql).not.toMatch(/FOR (INSERT|UPDATE|DELETE)/); }); });