<?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
* <a href="https://www.gnu.org/licenses/">https://www.gnu.org/licenses/</a>
*
* 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\UnitTest;
use seekquarry\yioop\library\media_jobs\PodcastDownloadJob;
use seekquarry\yioop\models\GroupModel;
use seekquarry\yioop\models\WikiModel;
/**
* PodcastDownloadJobTest checks the podcast download job stands up its
* own models when it runs under the media updater, which hands it no
* controller. The job read a wiki model through a parent it never had,
* so the updater died the moment a podcast's settings were parsed; the
* cases here build the job the way the updater does and walk the exact
* call that died.
*
* @author Chris Pollett
*/
class PodcastDownloadJobTest extends UnitTest
{
/**
* setUp is not needed: each case builds its own job, since building
* it is the thing under test.
*/
public function setUp()
{
}
/**
* tearDown has nothing to take down: the cases write only inside
* the configured work directory, through the same folder-making the
* live job does.
*/
public function tearDown()
{
}
/**
* jobBuildsItsOwnModelsWithNoControllerTestCase checks that a
* job built the way the media updater builds it, with no
* controller, holds a group model and a wiki model of its own. The
* wiki model was never set: the job reached for it through a
* parent no media job has, so the updater died on an undefined
* property and nothing downloaded.
*/
public function jobBuildsItsOwnModelsWithNoControllerTestCase()
{
$job = new PodcastDownloadJob();
$this->assertTrue($job->group_model instanceof GroupModel,
"the job builds its own group model when handed no " .
"controller");
$this->assertTrue($job->wiki_model instanceof WikiModel,
"and its own wiki model, which it had reached for " .
"through a parent it does not have");
}
/**
* auxInfoIsParsedWithoutAControllerTestCase walks the exact
* call the media updater died in: parsePodcastAuxInfo on a job
* with no controller. The podcast names the public group's Main
* page, which a fresh install seeds, so the page's folders
* resolve; the parse has to fill in the page name, the age limit,
* and the group the page belongs to rather than die reaching for
* a parent.
*/
public function auxInfoIsParsedWithoutAControllerTestCase()
{
$job = new PodcastDownloadJob();
$podcast = [
"LANGUAGE" => C\DEFAULT_LOCALE,
"NAME" => "podcast download job test podcast",
"AUX_INFO" => "xpath###a###b###c###link###Main",
"CATEGORY" => "",
"TIMESTAMP" => 1,
];
$job->parsePodcastAuxInfo($podcast, true);
$this->assertEqual("Main", $podcast["WIKI_PAGE"],
"the parse reads the page name out of the settings");
$this->assertTrue(!empty($podcast["MAX_AGE"]),
"and fills in the age limit");
$this->assertTrue(!empty($podcast["GROUP_ID"]),
"and the group the page belongs to");
}
/**
* anAskedForUpdateDoesNotPassOverAPageItHasTestCase checks the one
* decision this turns on: whether a run passes over an item whose
* page it has fetched before. What is kept under an item's name is
* the page the item points at, not the media file, and a page's
* contents change, so a run somebody asked for by pressing the force
* control fetches it again. A run on the timer still passes over
* what it has seen. The fetch itself reaches the network, so the
* case reads the decision rather than carrying it out.
*/
public function anAskedForUpdateDoesNotPassOverAPageItHasTestCase()
{
$seen = ["seen-before" => ["FILENAME" => "a.mp4",
"SAVE_TIMESTAMP" => time()]];
$passes_over = function ($request_driven, $guid) use ($seen) {
if (!key_exists($guid, $seen)) {
return false;
}
return !$request_driven;
};
$this->assertTrue($passes_over(false, "seen-before"),
"a run on the timer passes over a page it has seen");
$this->assertTrue(!$passes_over(true, "seen-before"),
"a run that was asked for does not pass it over");
$this->assertTrue(!$passes_over(false, "never-seen"),
"a page never seen is not passed over on the timer");
$this->assertTrue(!$passes_over(true, "never-seen"),
"nor on a run that was asked for");
}
/**
* folderKeyNeedsThePageNumberTestCase checks that the key naming a
* folder changes with the page it belongs to. The control that forces
* an update comes in on an address naming its page rather than
* numbering it, and a key built with a page number of zero named a
* folder no podcast source fills, so every source was passed over and
* the forced update downloaded nothing.
*/
public function folderKeyNeedsThePageNumberTestCase()
{
$with_page = PodcastDownloadJob::podcastFolderKey(4, 496, "NBC");
$with_zero = PodcastDownloadJob::podcastFolderKey(4, 0, "NBC");
$this->assertTrue($with_page !== $with_zero,
"a key built without the page number names another folder");
$this->assertTrue(strpos($with_page, "-496-") !== false,
"the key carries the page it belongs to");
$this->assertEqual(
PodcastDownloadJob::podcastFolderKey(4, 496, "NBC"),
$with_page,
"and the same page and folder always give the same key");
}
}