setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); $pdo->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC); $pdo->exec('PRAGMA foreign_keys = ON'); $schema = APP_ROOT . '/storage/schema.sql'; $pdo->exec(file_get_contents($schema)); $db = new Database($tmpDb); // real connection, isolated file AdminAuth::setDatabase($db); // ── Seed an existing admin password ───────────────────────────────────────── $db->setSetting('admin_password_hash', password_hash('initial-password-123', PASSWORD_BCRYPT)); check('admin password hash configured', AdminAuth::hasPassword()); // ── 1. Issue a reset token ─────────────────────────────────────────────────── $token = AdminAuth::issuePasswordResetToken(1800); check('issued 256-bit hex token', (bool) preg_match('/^[0-9a-f]{64}$/', $token)); $ot = new OneTimeToken($db->getPDO()); check('token is valid (shared model)', $ot->isValid('password_reset', $token)); // ── 2. Reject short password ───────────────────────────────────────────────── check('rejects <12 char password', AdminAuth::redeemPasswordResetToken($token, 'short') === false); // ── 3. Successful redeem installs a new hash ──────────────────────────────── $before = $db->getSetting('admin_password_hash'); check('redeems valid token + 12-char password', AdminAuth::redeemPasswordResetToken($token, 'new-secure-password-999')); $after = $db->getSetting('admin_password_hash'); check('hash changed', $after !== '' && $after !== $before); check('new password verifies', password_verify('new-secure-password-999', $after)); check('old password no longer verifies', !password_verify('initial-password-123', $after)); // ── 4. One-time use ───────────────────────────────────────────────────────── check('token now invalid (consumed)', $ot->isValid('password_reset', $token) === false); check('second redeem fails', AdminAuth::redeemPasswordResetToken($token, 'another-secure-password-000') === false); // ── 5. Unknown token fails ────────────────────────────────────────────────── check('unknown token fails', AdminAuth::redeemPasswordResetToken(str_repeat('ab', 32), 'another-secure-password-000') === false); // ── Cleanup ───────────────────────────────────────────────────────────────── $db->setSetting('admin_password_hash', ''); unlink($tmpDb); echo "\n"; if ($failures === 0) { echo "✅ Password-reset smoke test passed.\n"; exit(0); } echo "❌ {$failures} check(s) failed.\n"; exit(1);