diff --git a/apps/hq/test/module-access.test.ts b/apps/hq/test/module-access.test.ts new file mode 100644 index 0000000..ef9eebd --- /dev/null +++ b/apps/hq/test/module-access.test.ts @@ -0,0 +1,71 @@ +// 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') + }) +})