// apps/hq/test/module-access.test.ts — D28: module directory + portals quick-list. import { describe, it, expect } from 'vitest' import { openDb } from '../src/db' import { seedIfEmpty } from '../src/seed' import { createClient } from '../src/repos-clients' import { createModule, assignModule, setModuleFieldValues, setModulePassword, moduleDirectory, listPortals } from '../src/repos-modules' const KEY = '0'.repeat(64) // valid 32-byte hex key for the test's encryption describe('moduleDirectory (D28)', () => { it('returns the fieldSpec and every active client with its values', async () => { const db = openDb(':memory:'); await seedIfEmpty(db) const m = await createModule(db, 'u1', { code: 'RTGS', name: 'RTGS', sac: '998314', allowedKinds: ['yearly'], fieldSpec: [ { key: 'ip', label: 'IP', type: 'text' }, { key: 'portal_url', label: 'Portal URL', type: 'text' }, { key: 'admin_password', label: 'Admin password', type: 'secret' }, ], }) const a = await createClient(db, 'u1', { name: 'Alpha Bank', stateCode: '32' }) const b = await createClient(db, 'u1', { name: 'Beta Bank', stateCode: '32' }) const cmA = await assignModule(db, 'u1', { clientId: a.id, moduleId: m.id, kind: 'yearly' }) await assignModule(db, 'u1', { clientId: b.id, moduleId: m.id, kind: 'yearly' }) await setModuleFieldValues(db, 'u1', cmA.id, { ip: '10.0.0.1', portal_url: 'https://rtgs.example/login' }) const dir = await moduleDirectory(db, m.id) expect(dir).not.toBeNull() expect(dir!.code).toBe('RTGS') expect(dir!.fieldSpec.length).toBe(3) expect(dir!.total).toBe(2) const rowA = dir!.rows.find((r) => r.clientName === 'Alpha Bank')! expect(rowA.fieldValues.ip).toBe('10.0.0.1') expect(rowA.fieldValues.portal_url).toBe('https://rtgs.example/login') }) it('is null for an unknown module', async () => { const db = openDb(':memory:'); await seedIfEmpty(db) expect(await moduleDirectory(db, 'nope')).toBeNull() }) }) describe('listPortals (D28)', () => { it('lists only subscriptions with a login and surfaces URL fields (never the password)', async () => { const db = openDb(':memory:'); await seedIfEmpty(db) const m = await createModule(db, 'u1', { code: 'RECOVERX', name: 'RecoverX', sac: '998314', allowedKinds: ['yearly'], fieldSpec: [ { key: 'portal_url', label: 'Portal URL', type: 'text' }, { key: 'note', label: 'Note', type: 'text' }, ], }) const a = await createClient(db, 'u1', { name: 'Alpha', stateCode: '32' }) const b = await createClient(db, 'u1', { name: 'Beta', stateCode: '32' }) const cmA = await assignModule(db, 'u1', { clientId: a.id, moduleId: m.id, kind: 'yearly' }) const cmB = await assignModule(db, 'u1', { clientId: b.id, moduleId: m.id, kind: 'yearly' }) // Alpha has a URL + a stored password; Beta has only a note → no login → excluded. await setModuleFieldValues(db, 'u1', cmA.id, { portal_url: 'https://recoverx.example' }) await setModulePassword(db, 'u1', cmA.id, 'secret', KEY) await setModuleFieldValues(db, 'u1', cmB.id, { note: 'nothing here' }) const portals = await listPortals(db) expect(portals.length).toBe(1) const p = portals[0]! expect(p.clientName).toBe('Alpha') expect(p.hasPassword).toBe(true) expect(p.links.map((l) => l.url)).toContain('https://recoverx.example') // the password itself is never in the payload expect(JSON.stringify(p)).not.toContain('secret') }) })