mockConfig = new uConfig(false); } public static function setUpBeforeClass(): void { if (file_exists(__DIR__ . '/../.env')) { $dotenv = Dotenv\Dotenv::createUnsafeImmutable(__DIR__ . '/..'); $dotenv->load(); $dotenv->required(['DB_DSN', 'DB_USER', 'DB_PASS']); } $db_dsn = getenv('DB_DSN'); $db_user = getenv('DB_USER'); $db_pass = getenv('DB_PASS'); // pdo connection if (self::$pdo == null) { self::$pdo = new PDO($db_dsn, $db_user, $db_pass); self::$driver = self::$pdo->getAttribute(PDO::ATTR_DRIVER_NAME); } } public static function tearDownAfterClass(): void { self::$pdo = null; } /** * Set up database connection * This will also override uDb class connection * * @return Connection */ public function getConnection(): Connection { if ($this->conn === null) { $this->conn = $this->createDefaultDBConnection(self::$pdo, getenv('DB_NAME')); } return $this->conn; } /** * Create data set from xml fixture * * @return PHPUnit\DbUnit\DataSet\IDataSet */ protected function getDataSet(): IDataSet { $this->resetAutoincrement(); return $this->createFlatXMLDataSet(__DIR__ . '/../fixtures/fixture_empty.xml'); } protected function resetAutoincrement($users = 1, $tracks = 1, $positions = 1, $layers = 1): void { if (self::$driver === "pgsql") { self::$pdo->exec("ALTER SEQUENCE IF EXISTS users_id_seq RESTART WITH $users"); self::$pdo->exec("ALTER SEQUENCE IF EXISTS tracks_id_seq RESTART WITH $tracks"); self::$pdo->exec("ALTER SEQUENCE IF EXISTS positions_id_seq RESTART WITH $positions"); self::$pdo->exec("ALTER SEQUENCE IF EXISTS ol_layers_id_seq RESTART WITH $layers"); } else if (self::$driver === "sqlite") { $retry = 1; do { try { self::$pdo->exec("DELETE FROM sqlite_sequence WHERE NAME = 'users'"); self::$pdo->exec("DELETE FROM sqlite_sequence WHERE NAME = 'tracks'"); self::$pdo->exec("DELETE FROM sqlite_sequence WHERE NAME = 'positions'"); self::$pdo->exec("DELETE FROM sqlite_sequence WHERE NAME = 'ol_layers'"); $retry = 0; } catch (Exception $e) { // sqlite raises error when db schema changes in another connection. if (strpos($e->getMessage(), 'database schema has changed') !== false) { self::$pdo = null; self::setUpBeforeClass(); } } } while ($retry--); } } /** * Reset connection * Fixes sqlite error when db schema changes in another connection. */ protected function resetConnection(): void { $this->closeConnection($this->conn); $this->conn = null; self::tearDownAfterClass(); self::setUpBeforeClass(); } /** * Insert to database from array * * @param string $table Table name * @param array $rowsArr Array of rows * @return int|null Last insert id if available, null otherwise */ private function pdoInsert(string $table, array $rowsArr = []): ?int { $ret = null; if (!empty($rowsArr)) { $values = ':' . implode(', :', array_keys($rowsArr)); $columns = implode(', ', array_keys($rowsArr)); $query = "INSERT INTO $table ($columns) VALUES ($values)"; $stmt = self::$pdo->prepare($query); if ($stmt !== false) { $stmt->execute(array_combine(explode(', ', $values), array_values($rowsArr))); } $ret = self::$pdo->lastInsertId(); } return $ret; } /** * Execute raw insert query on database * * @param string $query Insert query * @return int|null Last insert id if available, null otherwise */ private function pdoInsertRaw(string $query): ?int { $ret = null; if (self::$pdo->exec($query) !== false) { $ret = self::$pdo->lastInsertId(); } return $ret; } /** * Get single column from first row of query result * * @param string $query SQL query * @param int $columnNumber Optional column number (default is first column) * @return string|bool Column or false if no data */ protected function pdoGetColumn(string $query, int $columnNumber = 0) { $column = false; $stmt = self::$pdo->query($query); if ($stmt !== false) { $column = $stmt->fetchColumn($columnNumber); $stmt->closeCursor(); } return $column; } /** * Insert user data to database * If parameters are omitted they default test values are used * * @param string|null $user User login * @param string|null $pass User password * @param bool $isAdmin User is admin * @return int|bool User id or false on error */ protected function addTestUser(?string $user = null, ?string $pass = null, bool $isAdmin = false) { if (is_null($user)) { $user = $this->testUser; } if (is_null($pass)) { $pass = $this->testPass; } $id = $this->pdoInsert('users', [ 'login' => $user, 'password' => $pass, 'admin' => (int) $isAdmin ]); if ($id !== false) { return (int) $id; } return false; } /** * Insert track data to database. * If parameters are omitted they default test values are used * * @param int|null $userId Optional track id * @param string|null $trackName Optional track name * @param string|null $comment Optional comment * @return int|bool Track id or false on error */ protected function addTestTrack(?int $userId = null, ?string $trackName = null, ?string $comment = null) { if (is_null($userId)) { $userId = $this->testUserId; } if (is_null($trackName)) { $trackName = $this->testTrackName; } if (is_null($comment)) { $comment = $this->testTrackComment; } $id = $this->pdoInsert('tracks', [ 'user_id' => $userId, 'name' => $trackName, 'comment' => $comment ]); if ($id !== false) { return (int) $id; } return false; } /** * Insert position data to database * If parameters are omitted they default test values are used * * @param int|null $userId * @param int|null $trackId * @param int|null $timeStamp * @param float|null $latitude * @param float|null $longitude * @return int|null Position id or false on error */ protected function addTestPosition(?int $userId = null, ?int $trackId = null, ?int $timeStamp = null, ?float $latitude = null, ?float $longitude = null): ?int { if (is_null($userId)) { $userId = $this->testUserId; } if (is_null($trackId)) { $trackId = $this->testTrackId; } if (is_null($timeStamp)) { $timeStamp = $this->testTimestamp; } if (is_null($latitude)) { $latitude = $this->testLat; } if (is_null($longitude)) { $longitude = $this->testLon; } $query = "INSERT INTO positions (user_id, track_id, time, latitude, longitude) VALUES ('$userId', '$trackId', " . $this->from_unixtime($timeStamp) . ", '$latitude', '$longitude')"; return $this->pdoInsertRaw($query); } public function unix_timestamp(string $column): string { switch (self::$driver) { default: case "mysql": return "UNIX_TIMESTAMP($column)"; case "pgsql": return "EXTRACT(EPOCH FROM $column)"; case "sqlite": return "STRFTIME('%s', $column)"; } } public function from_unixtime(string $column): string { switch (self::$driver) { default: case "mysql": return "FROM_UNIXTIME($column)"; case "pgsql": return "TO_TIMESTAMP($column)"; case "sqlite": return "DATETIME($column, 'unixepoch')"; } } } ?>