Some checks failed
Forgejo Android APK / Root app tests (push) Successful in 55s
Forgejo Docker Build / Root app tests (push) Successful in 48s
Forgejo Android APK / Build signed APK (push) Successful in 2m0s
Forgejo Docker Build / Build Docker image (push) Successful in 16s
Forgejo Docker Build / Deploy to the host (push) Failing after 0s
Adds the admin fixture the Search Sources screen needed, and repairs the reason no browser-driving e2e test could log in at all. The sign-in failure first. The suite drove the app over http on a container hostname, which is not a secure context, so the browser provides no crypto.randomUUID. AccountBoundary calls it to mint a session generation on every sign-in; the call threw, the boot handler's catch swallowed it, and every test landed on the login screen holding a perfectly valid session. Measured: isSecureContext false and randomUUID undefined on http://pediatric-ai-scribe-e2e:3000, both true on http://127.0.0.1:3553, where boundary.enter() returns true and the app enters. Chrome's --unsafely-treat-insecure-origin-as-secure was tried first and does not work: Playwright rejects the --user-data-dir it must be paired with, and the flag alone leaves isSecureContext false. Loopback needs no flags, so the runner now uses the host network and the published port. The seed is new. The e2e user was a registration someone did by hand once that the shared Postgres happened to keep — enough to log in and no more. There was no admin account, so nothing under /api/admin could be tested through a real request, which is how the Search Sources card came to be verified by reading its markup. e2e/seed.js creates both accounts and reconciles an existing one, so a leftover with the wrong role cannot fail the suite for a reason unrelated to the code. It resets passwords and grants admin, so it refuses any address outside @ped-ai.test. The runner seeds before it tests. The new spec covers what markup-reading could not: that an ordinary account is refused the settings and never offered the Admin menu item, that no API key comes back readable, that the Test button reports each source separately, and that every control the save handler reads exists in a real render. Each account gets its own browser context, because AccountBoundary allows one owner per document and freezing the page on a second is the behaviour, not a bug. 10/10 pass on both projects. Two unit tests pin the loopback requirement and the seed's domain guard so neither can be undone quietly. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Dv6sqaY6Vq3ChZHMem3cnU
82 lines
3.7 KiB
JavaScript
82 lines
3.7 KiB
JavaScript
// ============================================================
|
|
// 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');
|
|
var bcrypt = require('bcryptjs');
|
|
|
|
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 bcrypt.hash(PASSWORD, 12);
|
|
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);
|
|
}
|
|
})();
|