SeekquarrySeekquarry - Yioop Repo - Seekquarry

/ tests / FetchControllerTest.php⤓
<?php
/**
 * SeekQuarry/Yioop --
 * Open Source Pure PHP Search Engine, Crawler, and Indexer
 *
 * Copyright (C) 2009 - 2026  Chris Pollett chris@pollett.org
 *
 * LICENSE:
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see
 * <https://www.gnu.org/licenses/>.
 *
 * END LICENSE
 *
 * @author Chris Pollett chris@pollett.org
 * @license https://www.gnu.org/licenses/ GPL3
 * @link https://www.seekquarry.com/
 * @copyright 2009 - 2026
 * @package seek_quarry\tests
 */
namespace seekquarry\yioop\tests;

use seekquarry\atto as A;
use seekquarry\yioop\configs as C;
use seekquarry\yioop\controllers\FetchController;
use seekquarry\yioop\library as L;
use seekquarry\yioop\library\UnitTest;
use seekquarry\yioop\models\Model;

/**
 * FetchControllerTest checks how FetchController keeps the robot table,
 * the list of the fetchers that spoke with a machine lately, which the
 * queue server reads for the most recent fetcher and the fetchers'
 * download rates.
 *
 * @author Chris Pollett
 */
class FetchControllerTest extends UnitTest
{
    /**
     * FOLDER is the folder, under tests/test_files, that the cases keep
     * their robot table in.
     */
    const FOLDER = C\TEST_DIR . "/test_files/fetch_controller_test";
    /**
     * setUp makes FOLDER.
     */
    public function setUp()
    {
        L\makePath(self::FOLDER);
    }
    /**
     * tearDown deletes FOLDER with the robot table in it.
     */
    public function tearDown()
    {
        (new Model())->db->unlinkRecursive(self::FOLDER);
    }
    /**
     * emptyRobotTableIsStartedAgainTestCase checks that recordRobot puts a
     * fetcher's entry in a robot table that cannot be read back. On the
     * crawl of 15 October a write of the table failed with no space left
     * on the device and left it empty; FetchController then never wrote
     * the table again, and the queue server found no fetcher from then on.
     */
    public function emptyRobotTableIsStartedAgainTestCase()
    {
        $table = self::FOLDER . "/0-RobotTable.txt";
        file_put_contents($table, "");
        $this->assertTrue(FetchController::recordRobot($table, "0-0-Yioop",
            ["1.2.3.4", "/", 1000, 17000]), "the table is opened");
        $this->assertEqual(unserialize(file_get_contents($table)),
            ["0-0-Yioop" => ["1.2.3.4", "/", 1000, 17000]],
            "the empty table now holds the fetcher's entry");
        file_put_contents($table, "a:2:{s:9:\"0-0-Yioop\";a:4:{i:0;s:7:");
        FetchController::recordRobot($table, "1-0-Yioop",
            ["1.2.3.4", "/", 1001, 9000]);
        $this->assertEqual(unserialize(file_get_contents($table)),
            ["1-0-Yioop" => ["1.2.3.4", "/", 1001, 9000]],
            "a table cut off part way is started again");
        $this->assertEqual(fileperms($table) & 0004, 0004,
            "the table can be read by the crawl processes");
    }
    /**
     * robotEntryKeepsItsRateTestCase checks that recordRobot keeps the
     * other fetchers' entries, and that a request with no rate, given as
     * -1, keeps the rate the fetcher sent before.
     */
    public function robotEntryKeepsItsRateTestCase()
    {
        $table = self::FOLDER . "/0-RobotTable.txt";
        FetchController::recordRobot($table, "0-0-Yioop",
            ["1.2.3.4", "/", 1000, 17000]);
        FetchController::recordRobot($table, "1-0-Yioop",
            ["1.2.3.5", "/", 1005, 9000]);
        FetchController::recordRobot($table, "0-0-Yioop",
            ["1.2.3.4", "/", 1010, -1]);
        $this->assertEqual(unserialize(file_get_contents($table)),
            ["0-0-Yioop" => ["1.2.3.4", "/", 1010, 17000],
            "1-0-Yioop" => ["1.2.3.5", "/", 1005, 9000]],
            "each fetcher has its entry, and the rate is kept");
    }
    /**
     * uploadPartsAreSavedInFolderTestCase checks savePart. The first part
     * of an upload starts its file again, so a fetcher that restarts an
     * upload with a new part size does not leave the old parts in front of
     * the new ones; before, every part was added to the end of the file.
     * A hash with a path in it cannot name a file outside the folder.
     */
    public function uploadPartsAreSavedInFolderTestCase()
    {
        $file = FetchController::savePart(self::FOLDER, "abc123", "old1", 1);
        FetchController::savePart(self::FOLDER, "abc123", "old2", 2);
        FetchController::savePart(self::FOLDER, "abc123", "new1", 1);
        FetchController::savePart(self::FOLDER, "abc123", "new2", 2);
        $this->assertEqual(file_get_contents($file), "new1new2",
            "a restarted upload holds only its new parts");
        $file = FetchController::savePart(self::FOLDER, "../../x/y", "z", 1);
        $this->assertEqual($file, self::FOLDER . "/xy",
            "the file stays in the folder");
    }
    /**
     * postMaxSizeUnderOwnWebServerTestCase checks the largest post
     * FetchController tells fetchers. Under Yioop's own web server, index.php
     * used to raise MAX_REQUEST_LEN to the Git push size limit, which by
     * default has none, so MAX_REQUEST_LEN could be PHP_INT_MAX. The
     * controller then told fetchers they could post that much, and a
     * fetcher sent each upload, tens of megabytes, as one post. The size
     * now follows post_max_size, raised to the web server's default, less
     * POST_HEADER_ROOM, whatever MAX_REQUEST_LEN is.
     */
    public function postMaxSizeUnderOwnWebServerTestCase()
    {
        $controller = new FetchController();
        $php_post_max = L\metricToInt(ini_get("post_max_size"));
        $this->assertEqual($controller->postMaxSize(), $php_post_max,
            "under another web server it is PHP's post_max_size");
        $default_max = A\WebSite::defaultServerGlobals()['MAX_REQUEST_LEN'];
        $controller->web_site = (object)["default_server_globals" =>
            ["MAX_REQUEST_LEN" => PHP_INT_MAX]];
        $this->assertEqual($controller->postMaxSize(),
            max($php_post_max, $default_max) -
            FetchController::POST_HEADER_ROOM,
            "a request limit raised for Git pushes is not given to fetchers");
        $controller->web_site->default_server_globals['MAX_REQUEST_LEN'] =
            $default_max - 1000000;
        $this->assertEqual($controller->postMaxSize(),
            $default_max - 1000000 - FetchController::POST_HEADER_ROOM,
            "the size stays under the web server's request limit");
    }
}