// ============================================================ // E2E ACCOUNT SEED // ============================================================ // Run inside the app container, which is where the database credentials live: // // docker exec pediatric-ai-scribe-e2e node e2e/seed.js // // Before this existed the e2e user was a registration someone did by hand once // and the shared Postgres happened to keep. That was enough to log in and no // more: there was no admin account, so nothing under /api/admin could be tested // through a real request at all, and the Search Sources screen had to be // checked by reading its markup. // // Reconciles rather than only creating. An account left over from an earlier // run with the wrong role, an unverified address, a disabled flag or a // different password is repaired in place, so the suite cannot fail for a // reason that has nothing to do with the code under test. // // The domain guard is the important part. This script updates passwords and // grants the admin role, so it refuses to touch any address outside // @ped-ai.test — a mistyped environment variable can then do nothing worse // than create another test account. // ============================================================ // The entrypoint fetches secrets from OpenBao and exports them into the server // process, and nowhere else — not into the image config, not into an env file. // `docker exec` therefore starts with none of them and the database connection // refuses on localhost. Borrowing PID 1's environment is what makes this // runnable the documented way; without it the script only works on a stack // whose credentials happen to be in plain compose environment. require('fs').readFileSync('/proc/1/environ', 'utf8').split('\0').forEach(function (pair) { var i = pair.indexOf('='); if (i > 0 && !process.env[pair.slice(0, i)]) process.env[pair.slice(0, i)] = pair.slice(i + 1); }); var db = require('../src/db/database'); // The app's own hasher, not bcrypt directly: production writes argon2id, and a // seeded account hashed any other way exercises a path real users do not take. var passwords = require('../src/utils/passwords'); var TEST_DOMAIN = '@ped-ai.test'; var PASSWORD = process.env.E2E_TEST_PASSWORD || 'E2E-testPassword123!'; var ACCOUNTS = [ { email: process.env.E2E_TEST_EMAIL || 'e2e-user' + TEST_DOMAIN, name: 'E2E User', role: 'user' }, { email: process.env.E2E_ADMIN_EMAIL || 'e2e-admin' + TEST_DOMAIN, name: 'E2E Admin', role: 'admin' } ]; async function seed(account) { var email = String(account.email || '').toLowerCase().trim(); if (email.slice(-TEST_DOMAIN.length) !== TEST_DOMAIN) { throw new Error('refusing to seed ' + email + ': only ' + TEST_DOMAIN + ' addresses may be seeded'); } var hash = await passwords.hash(PASSWORD); var existing = await db.get('SELECT id, role, email_verified, disabled FROM users WHERE email = ?', [email]); if (!existing) { await db.run( 'INSERT INTO users (email, password, name, role, email_verified, disabled) VALUES (?, ?, ?, ?, true, false)', [email, hash, account.name, account.role] ); console.log('created ' + email + ' (' + account.role + ')'); return; } await db.run( 'UPDATE users SET password = ?, name = ?, role = ?, email_verified = true, disabled = false WHERE id = ?', [hash, account.name, account.role, existing.id] ); var drift = []; if (existing.role !== account.role) drift.push('role ' + existing.role + '→' + account.role); if (!existing.email_verified) drift.push('verified'); if (existing.disabled) drift.push('re-enabled'); console.log('repaired ' + email + ' (' + (drift.length ? drift.join(', ') : 'password reset') + ')'); } (async function () { try { for (var i = 0; i < ACCOUNTS.length; i++) await seed(ACCOUNTS[i]); console.log('e2e accounts ready'); process.exit(0); } catch (err) { console.error('e2e seed failed: ' + err.message); process.exit(1); } })();