Files
XC_VM/tests/e2e/tools/create-admin.php
T
rootandClaude Opus 5 d0458a8eeb test(e2e): drive the admin panel the way an administrator does
The Playwright suite only checked that pages render. It now performs the
administrator's work against a live test panel and asserts the panel's own
data after each step:

- catalogue: a stream category, a bouquet and a reseller package — created,
  renamed / edited, reopened, deleted;
- subscribers: a line with a bouquet (search, edit in the modal, disable /
  enable, ban / unban, delete), a MAG and an Enigma2 device;
- bulk: two lines selected with the header checkbox, disabled and deleted;
- resellers: created with credits, topped up, edited, disabled, deleted;
- block lists: an IP (RFC 5737 address — blocking adds an iptables rule), a
  user agent and an ISP;
- streams: a live stream added with a source and a server, started, running
  with codecs and the Resources column filled in, stopped, renamed, deleted;
- sign-in: a second administrator refused with a wrong password, signing in
  and out — a separate account, because every admin login re-hashes the
  password and ends that account's other sessions.

Records are named `e2e-<run>-…`; a teardown project sweeps whatever a run
leaves behind and nothing else. tools/create-admin.php provisions the
dedicated test administrator on the panel host.

The first runs found three save paths that answered an empty page (fixed in
f7bba5cb, fd13c940, ee2f586a). Against the test panel: 82 passed, 1 skipped
(no series to select).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016WDhDajBPziJwjWZcXnh6R
2026-09-16 14:39:39 +00:00

66 lines
2.5 KiB
PHP

<?php
/**
* Provision the administrator the E2E suite logs in as — or re-key it.
*
* Runs ON the panel host, with the panel's own PHP, and creates the account the
* way the first-run setup page does (Views/admin/setup.php): the `users` row
* defaults from the schema, the panel's password hash, the Administrators group.
* The password is read from stdin so it never appears in a process list or a
* shell history:
*
* /home/xc_vm/bin/php/bin/php create-admin.php e2e_admin < password.txt
*
* Running it again for an existing username resets that account's password and
* puts it back in the Administrators group, enabled. XC_VM_HOME overrides the
* install path (default /home/xc_vm).
*/
use XcVm\Core\Auth\Authenticator;
use XcVm\Core\Database\QueryHelper;
if (PHP_SAPI !== 'cli') {
exit(1);
}
$rUsername = (string) ($argv[1] ?? '');
$rPassword = rtrim((string) stream_get_contents(STDIN), "\r\n");
// The setup page's own floor for the first administrator.
if (strlen($rUsername) < 8 || strlen($rPassword) < 8) {
fwrite(STDERR, "usage: php create-admin.php <username, 8+ chars> (password, 8+ chars, on stdin)\n");
exit(2);
}
$rHome = rtrim(getenv('XC_VM_HOME') ?: '/home/xc_vm', '/');
require_once $rHome . '/bootstrap.php';
XC_Bootstrap::boot(XC_Bootstrap::CONTEXT_CLI, ['process' => 'XC_VM[E2E admin]']);
global $db;
// group_id 1 is the seeded Administrators group (bin/install/database.sql).
$rAdminGroup = 1;
$rHash = Authenticator::hashPassword($rPassword);
// fetchOne() answers an empty array, not false, when nothing matches.
$rExisting = $db->fetchOne('SELECT `id` FROM `users` WHERE `username` = ?', $rUsername);
if (!empty($rExisting['id'])) {
$db->query('UPDATE `users` SET `password` = ?, `member_group_id` = ?, `status` = 1 WHERE `id` = ?', $rHash, $rAdminGroup, $rExisting['id']);
echo 'Updated administrator ' . $rUsername . ' (id ' . $rExisting['id'] . ")\n";
exit(0);
}
$rArray = QueryHelper::verifyPostTable('users');
$rArray['username'] = $rUsername;
$rArray['password'] = $rHash;
$rArray['email'] = '';
$rArray['date_registered'] = time();
$rArray['last_login'] = null;
$rArray['member_group_id'] = $rAdminGroup;
$rArray['ip'] = '';
$rArray['notes'] = 'E2E test administrator (tests/e2e/tools/create-admin.php)';
$rPrepare = QueryHelper::prepareArray($rArray);
if (!$db->query('INSERT INTO `users`(' . $rPrepare['columns'] . ') VALUES(' . $rPrepare['placeholder'] . ');', ...$rPrepare['data'])) {
fwrite(STDERR, "Insert failed.\n");
exit(1);
}
echo 'Created administrator ' . $rUsername . ' (id ' . $db->last_insert_id() . ")\n";