IT

jumphpsql

di davidnurdin · Demo ed esperimenti
non ancora votato
▶ Apri 💬 Opinioni Sorgenti 🕘 Versioni Modifica GitHub
apps/jumphpsql / tests / TestServer.php · commit 1d1d550
<?phpnamespace Tests;use App\Db\MysqlConnection;/** * The server the tests use: a throwaway MariaDB / MySQL, by default on port 3399, root / jumphp, of this computer * (127.0.0.1) or of its Docker gateway (172.17.0.1: what the test container sees): * *   docker run -d --name jumphpsql-mariadb -e MARIADB_ROOT_PASSWORD=jumphp -p 127.0.0.1:3399:3306 -p 172.17.0.1:3399:3306 mariadb:11 * * (JUMPHPSQL_TEST_PORT / JUMPHPSQL_TEST_PASSWORD change it.) Without it, the tests that need it are skipped. * seed() makes the database `jumphpsql_test` again: customers (primary key), notes (none), a view. */final class TestServer{    public const DATABASE = 'jumphpsql_test';    private static ?string $host = null;    /** @return array{host: string, port: int, user: string, password: string, name: string} */    public static function config(): array    {        return ['host' => self::$host ?? (getenv('JUMPHPSQL_TEST_HOST') ?: '127.0.0.1'), 'port' => (int)(getenv('JUMPHPSQL_TEST_PORT') ?: 3399),            'user' => getenv('JUMPHPSQL_TEST_USER') ?: 'root', 'password' => getenv('JUMPHPSQL_TEST_PASSWORD') ?: 'jumphp', 'name' => 'test server'];    }    /** A connection, connected, or null when no server answers. */    public static function connect(): ?MysqlConnection    {        // bin/jumphp test runs in a container of its own network: the server is on the host's Docker gateway there        $hosts = getenv('JUMPHPSQL_TEST_HOST') ? [getenv('JUMPHPSQL_TEST_HOST')] : ['127.0.0.1', '172.17.0.1', 'host.docker.internal'];        foreach ($hosts as $host) {            self::$host = $host;            $db = new MysqlConnection(self::config() + ['timeout' => 2]);            try {                $db->wait($db->connect(), 3);                return $db;            } catch (\Throwable) {            }        }        self::$host = null;        return null;    }    /**     * The test SSH server (a throwaway sshd: user tunnel / tunnelpw, keys from var/tmp/ssh/authorized_keys, forwarding     * allowed), on port 2299 of the same address as the database; the database as it sees it: the Docker gateway.     *     *   docker run -d --name jumphpsql-sshd -p 127.0.0.1:2299:22 -p 172.17.0.1:2299:22 -v $PWD/var/tmp/ssh:/keys:ro alpine sh -c \     *     'apk add --no-cache openssh && ssh-keygen -A && adduser -D tunnel && echo tunnel:tunnelpw | chpasswd \     *      && exec /usr/sbin/sshd -D -e -o AllowTcpForwarding=yes -o PasswordAuthentication=yes -o KbdInteractiveAuthentication=yes \     *         -o AuthorizedKeysFile=/keys/authorized_keys -o StrictModes=no -o PerSourcePenalties=no'     *     * @return array<string, mixed>|null null: no SSH server (or no ssh client) here     */    public static function sshConfig(): ?array    {        static $checked = null;          // probed once: sshd penalises connections that do not log in        if ($checked !== null) {            return $checked ?: null;        }        $checked = [];        $db = self::connect();        if ($db === null || trim((string)shell_exec('command -v ssh')) === '') {            return null;        }        $db->close();        $host = self::config()['host'];        $probe = @stream_socket_client("tcp://$host:" . (getenv('JUMPHPSQL_TEST_SSH_PORT') ?: 2299), $errno, $error, 2);        if ($probe === false) {            return null;        }        fclose($probe);        return $checked = ['ssh' => true, 'sshHost' => $host, 'sshPort' => (int)(getenv('JUMPHPSQL_TEST_SSH_PORT') ?: 2299), 'sshUser' => 'tunnel',            'sshAuth' => 'password', 'sshPassword' => 'tunnelpw',            'host' => getenv('JUMPHPSQL_TEST_DB_FROM_SSH') ?: '172.17.0.1', 'port' => self::config()['port']] + self::config();    }    /** var/tmp/ssh: a key without passphrase (key), one with 'secret phrase' (keyp), both in authorized_keys. */    public static function sshKeys(): string    {        $dir = dirname(__DIR__) . '/var/tmp/ssh';        if (!is_file("$dir/keyp")) {            @mkdir($dir, 0700, true);            @unlink("$dir/key");            $env = '';            foreach (\App\Db\SshTunnel::passwdWrapper() as $name => $value) {                $env .= $name . '=' . escapeshellarg($value) . ' ';      // ssh-keygen wants a passwd entry too            }            shell_exec($env . 'ssh-keygen -q -t ed25519 -N "" -C jumphpsql-test -f ' . escapeshellarg("$dir/key"));            shell_exec($env . 'ssh-keygen -q -t ed25519 -N "secret phrase" -C jumphpsql-test-p -f ' . escapeshellarg("$dir/keyp"));            file_put_contents("$dir/authorized_keys", file_get_contents("$dir/key.pub") . file_get_contents("$dir/keyp.pub"));        }        return $dir;    }    public static function seed(MysqlConnection $db): void    {        $d = self::DATABASE;        $db->wait($db->query(<<<SQL            DROP DATABASE IF EXISTS `$d`;            CREATE DATABASE `$d` CHARACTER SET utf8mb4;            CREATE TABLE `$d`.customers (id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(80) NOT NULL, city VARCHAR(80) NULL, photo BLOB NULL);            INSERT INTO `$d`.customers (name, city, photo) VALUES ('Ada', 'London', 0x89504E47), ('Grace', 'New York', NULL), ('Linus', NULL, NULL);            CREATE TABLE `$d`.notes (body TEXT, stars INT);            INSERT INTO `$d`.notes VALUES ('l''été', 3), ('same', 1), ('same', 1);            CREATE VIEW `$d`.londoners AS SELECT id, name FROM `$d`.customers WHERE city = 'London';            SQL));    }    /** @return list<list<?string>> */    public static function rows(MysqlConnection $db, string $sql): array    {        return $db->wait($db->first($sql))->rows;    }}