SeekquarrySeekquarry - Yioop Repo - Seekquarry

/ tests / HotTargetsTest.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
 * @filesource
 */
namespace seekquarry\yioop\tests;

use seekquarry\yioop\configs as C;
use seekquarry\yioop\library\HotTargets;
use seekquarry\yioop\library\UnitTest;

/**
 * HotTargetsTest checks the count of refusals a wiki page has drawn and
 * the judgment that a page has drawn enough to be turned away early.
 *
 * @author Chris Pollett
 */
class HotTargetsTest extends UnitTest
{
    /**
     * setUp clears the counts file so each case starts with none.
     */
    public function setUp()
    {
        $path = C\TEMP_DIR . "/" . HotTargets::FILE_NAME;
        if (file_exists($path)) {
            unlink($path);
        }
    }
    /**
     * tearDown removes the counts file the cases wrote.
     */
    public function tearDown()
    {
        $this->setUp();
    }
    /**
     * pageBecomesHotAfterEnoughRefusalsTestCase checks that refusals of
     * one page's history from many addresses add up on the page, that it
     * is not hot before REFUSALS_TO_BE_HOT of them and is hot after, and
     * that an ask for a different page or for a plain read is never
     * counted.
     */
    public function pageBecomesHotAfterEnoughRefusalsTestCase()
    {
        $key = HotTargets::targetKey(40, "3971", "history");
        $this->assertEqual("40:3971:history", $key,
            "the key names the group, page and what was asked");
        $this->assertEqual("", HotTargets::targetKey(40, "3971", "read"),
            "a plain read is not something that is counted");
        $now = 1000000;
        for ($i = 1; $i < HotTargets::REFUSALS_TO_BE_HOT; $i++) {
            HotTargets::recordRefusal($key, $now + $i);
        }
        $this->assertFalse(HotTargets::isHot($key, $now + 100),
            "one short of the count, the page is not hot");
        HotTargets::recordRefusal($key, $now + 100);
        $this->assertTrue(HotTargets::isHot($key, $now + 101),
            "at the count, the page is hot");
        $this->assertFalse(HotTargets::isHot(
            HotTargets::targetKey(40, "1", "history"), $now + 101),
            "another page of the group is not hot");
    }
    /**
     * hotPageStaysHotWhileTheWebServerRefusesItTestCase checks that a
     * page that became hot stays hot well past WINDOW with no further
     * refusal recorded, since the web server answers those requests and
     * PHP never sees them, and cools only once HOT_FOR has passed; and
     * that a page that never became hot is forgotten after WINDOW so
     * old refusals do not count toward a new burst.
     */
    public function hotPageStaysHotWhileTheWebServerRefusesItTestCase()
    {
        $key = HotTargets::targetKey(40, "3971", "source");
        $now = 2000000;
        for ($i = 0; $i < HotTargets::REFUSALS_TO_BE_HOT; $i++) {
            HotTargets::recordRefusal($key, $now);
        }
        $this->assertTrue(HotTargets::isHot($key, $now + 1),
            "the page is hot right after its refusals");
        $this->assertTrue(HotTargets::isHot($key,
            $now + HotTargets::WINDOW + 1),
            "and is still hot past the window with nothing recorded since");
        $this->assertFalse(HotTargets::isHot($key,
            $now + HotTargets::HOT_FOR + 1),
            "and cools once the hot span has passed");
        $warm = HotTargets::targetKey(40, "1", "source");
        HotTargets::recordRefusal($warm, $now);
        $later = $now + HotTargets::WINDOW + 1;
        HotTargets::recordRefusal($warm, $later);
        $this->assertFalse(HotTargets::isHot($warm, $later + 1),
            "a page that never became hot starts its count over after the" .
            " window");
    }
    /**
     * keyAndMarkerAgreeAcrossRequestShapesTestCase checks that the three
     * shapes a scraper's request takes, a page by id in the query, a page
     * by name in the path, and a path name with an id in the query, each
     * key to what the web server's rewrite rule builds for it, so a
     * marker PHP writes is the one Apache tests for; and that markers are
     * written for hot targets only and taken away when a target cools.
     */
    public function keyAndMarkerAgreeAcrossRequestShapesTestCase()
    {
        $shapes = [
            ["/wiki/group/40?a=wiki&arg=history&page_id=3920&show=1",
                ["arg" => "history", "page_id" => "3920"], "40-3920-history"],
            ["/wiki/group/2/Main?arg=source&settings=true",
                ["arg" => "source"], "2-Main-source"],
            ["/group/40/faculty-office-hours?arg=source&page_id=3920",
                ["arg" => "source", "page_id" => "3920"], "40-3920-source"],
        ];
        foreach ($shapes as [$uri, $request, $marker]) {
            $this->assertEqual($marker, HotTargets::markerName(
                HotTargets::keyFromRequest($uri, $request)),
                "the marker for $uri is what the rewrite rule builds");
        }
        $folder = C\WORK_DIRECTORY . "/" . HotTargets::MARKER_FOLDER;
        $hot = ["40:3920:history" => ["count" =>
            HotTargets::REFUSALS_TO_BE_HOT, "last" => 5, "hot_since" => 5]];
        $cool = ["40:3920:history" => ["count" => 1, "last" => 5]];
        HotTargets::writeMarkers($hot);
        $this->assertTrue(file_exists($folder . "/40-3920-history"),
            "a hot target has its marker file");
        HotTargets::writeMarkers($cool);
        $this->assertFalse(file_exists($folder . "/40-3920-history"),
            "and loses it once it is no longer hot");
    }
}