<?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\library\UnitTest;
use seekquarry\yioop\library\wiki\WikiIssue;

/**
 * Tests for WikiIssue, which shapes the record of a single tracked issue.
 * Each test starts from a freshly opened issue and checks that opening,
 * assigning, closing, reopening, and re-prioritizing set the right fields
 * and add the right history entry, and that closing with an unknown reason
 * falls back to fixed.
 *
 * @author Chris Pollett
 */
class WikiIssueTest extends UnitTest
{
    /**
     * Nothing to set up; every test builds its own issue record.
     */
    public function setUp()
    {
    }
    /**
     * Nothing to tear down.
     */
    public function tearDown()
    {
    }
    /**
     * A freshly opened issue is open, unassigned, starts at middle urgency
     * because the reporter does not pick one, keeps its reporter, title,
     * branch, and version, and has a single opened history entry.
     */
    public function openTestCase()
    {
        $issue = WikiIssue::open(7, 1000, "Login bug", "main", "current");
        $this->assertEqual(WikiIssue::STATUS_OPEN, $issue["status"],
            "a new issue is open");
        $this->assertEqual(7, $issue["reporter"],
            "the reporter is remembered");
        $this->assertEqual(WikiIssue::PRIORITY_MEDIUM, $issue["priority"],
            "a new issue starts at middle urgency");
        $this->assertEqual("Login bug", $issue["title"],
            "the title is remembered");
        $this->assertEqual("main", $issue["branch"],
            "the branch is remembered");
        $this->assertEqual("current", $issue["version"],
            "the version is remembered");
        $this->assertEqual(0, $issue["assignee"],
            "a new issue is not assigned to anyone");
        $this->assertEqual(1, count($issue["history"]),
            "a new issue has one history entry");
        $this->assertEqual(WikiIssue::ACTION_OPENED,
            $issue["history"][0]["action"],
            "the one history entry records the opening");
    }
    /**
     * Assigning an issue records the assignee and adds an assigned history
     * entry naming who made the change and who received the issue.
     */
    public function assignTestCase()
    {
        $issue = WikiIssue::open(7, 1000, "Login bug", "main", "current");
        $issue = WikiIssue::assign($issue, 9, 3, 1100);
        $this->assertEqual(9, $issue["assignee"],
            "the issue is now assigned to person nine");
        $last = end($issue["history"]);
        $this->assertEqual(WikiIssue::ACTION_ASSIGNED, $last["action"],
            "the change is recorded as an assignment");
        $this->assertEqual(3, $last["by"],
            "the person who made the change is recorded");
        $this->assertEqual(9, $last["to"],
            "the person the issue went to is recorded");
    }
    /**
     * Closing an issue as fixed marks it closed with that reason and adds a
     * closed history entry.
     */
    public function closeFixedTestCase()
    {
        $issue = WikiIssue::open(7, 1000, "Login bug", "main", "current");
        $issue = WikiIssue::close($issue, WikiIssue::RESOLUTION_FIXED, 3,
            1200);
        $this->assertEqual(WikiIssue::STATUS_CLOSED, $issue["status"],
            "the issue is now closed");
        $this->assertEqual(WikiIssue::RESOLUTION_FIXED, $issue["resolution"],
            "the issue is recorded as fixed");
        $last = end($issue["history"]);
        $this->assertEqual(WikiIssue::ACTION_CLOSED, $last["action"],
            "the change is recorded as a closing");
    }
    /**
     * Closing an issue as won't-fix keeps that reason.
     */
    public function closeWontFixTestCase()
    {
        $issue = WikiIssue::open(7, 1000, "Login bug", "main", "current");
        $issue = WikiIssue::close($issue, WikiIssue::RESOLUTION_WONT_FIX, 3,
            1200);
        $this->assertEqual(WikiIssue::RESOLUTION_WONT_FIX,
            $issue["resolution"], "the won't-fix reason is kept");
    }
    /**
     * Closing an issue with an unrecognized reason falls back to fixed
     * rather than storing the bad reason.
     */
    public function closeUnknownReasonTestCase()
    {
        $issue = WikiIssue::open(7, 1000, "Login bug", "main", "current");
        $issue = WikiIssue::close($issue, "banana", 3, 1200);
        $this->assertEqual(WikiIssue::RESOLUTION_FIXED, $issue["resolution"],
            "an unknown closing reason becomes fixed");
    }
    /**
     * Reopening a closed issue makes it open again and clears the reason it
     * had been closed.
     */
    public function reopenTestCase()
    {
        $issue = WikiIssue::open(7, 1000, "Login bug", "main", "current");
        $issue = WikiIssue::close($issue, WikiIssue::RESOLUTION_FIXED, 3,
            1200);
        $issue = WikiIssue::reopen($issue, 3, 1300);
        $this->assertEqual(WikiIssue::STATUS_OPEN, $issue["status"],
            "the issue is open again");
        $this->assertEqual("", $issue["resolution"],
            "the old closing reason is cleared");
        $last = end($issue["history"]);
        $this->assertEqual(WikiIssue::ACTION_REOPENED, $last["action"],
            "the reopening is recorded");
    }
    /**
     * Changing an issue's priority updates it and records the change.
     */
    public function setPriorityTestCase()
    {
        $issue = WikiIssue::open(7, 1000, "Login bug", "main", "current");
        $issue = WikiIssue::setPriority($issue, 5, 3, 1400);
        $this->assertEqual(5, $issue["priority"],
            "the new priority is stored");
        $last = end($issue["history"]);
        $this->assertEqual(WikiIssue::ACTION_PRIORITY, $last["action"],
            "the priority change is recorded");
    }
    /**
     * Closing an issue as fixed with a commit keeps the commit on the record
     * and in the history entry.
     */
    public function closeWithCommitTestCase()
    {
        $issue = WikiIssue::open(7, 1000, "Login bug", "main", "current");
        $issue = WikiIssue::close($issue, WikiIssue::RESOLUTION_FIXED, 3,
            1200, "abc123");
        $this->assertEqual("abc123", $issue["fix_commit"],
            "the fixing commit is kept on the record");
        $last = end($issue["history"]);
        $this->assertEqual("abc123", $last["commit"],
            "the fixing commit is kept in the history entry");
    }
    /**
     * The display status reads reported, then assigned, then marked fixed,
     * and reported again once reopened.
     */
    public function displayStatusTestCase()
    {
        $issue = WikiIssue::open(7, 1000, "Login bug", "main", "current");
        $this->assertEqual(WikiIssue::DISPLAY_REPORTED,
            WikiIssue::displayStatus($issue),
            "a new issue reads as reported");
        $issue = WikiIssue::assign($issue, 9, 3, 1100);
        $this->assertEqual(WikiIssue::DISPLAY_ASSIGNED,
            WikiIssue::displayStatus($issue),
            "an assigned issue reads as assigned");
        $issue = WikiIssue::close($issue, WikiIssue::RESOLUTION_FIXED, 3,
            1200);
        $this->assertEqual(WikiIssue::DISPLAY_FIXED,
            WikiIssue::displayStatus($issue),
            "a fixed issue reads as marked fixed");
        $issue = WikiIssue::reopen($issue, 3, 1300);
        $this->assertEqual(WikiIssue::DISPLAY_ASSIGNED,
            WikiIssue::displayStatus($issue),
            "a reopened issue that still has an owner reads as assigned");
    }
    /**
     * Putting an issue back to reported opens it, clears the assignee, and
     * clears any closing reason.
     */
    public function reportTestCase()
    {
        $issue = WikiIssue::open(7, 1000, "Login bug", "main", "current");
        $issue = WikiIssue::assign($issue, 9, 3, 1100);
        $issue = WikiIssue::close($issue, WikiIssue::RESOLUTION_FIXED, 3,
            1200);
        $issue = WikiIssue::report($issue, 3, 1300);
        $this->assertEqual(WikiIssue::STATUS_OPEN, $issue["status"],
            "the issue is open again");
        $this->assertEqual(0, $issue["assignee"],
            "the issue is held by no one");
        $this->assertEqual(WikiIssue::DISPLAY_REPORTED,
            WikiIssue::displayStatus($issue),
            "the issue reads as reported");
    }
    /**
     * Checks that the time an issue was reported is read off the opening
     * line of its history, and that a record with no history reads as
     * zero rather than failing.
     */
    public function reportedTimeTestCase()
    {
        $record = WikiIssue::open(5, 1234, "a title", "main", "abc");
        $this->assertEqual(WikiIssue::reportedTime($record), 1234,
            "the reported time is the first history line's time");
        $this->assertEqual(WikiIssue::reportedTime([]), 0,
            "a record with no history reports zero");
    }
    /**
     * Checks that the reporter shown for an issue is the username when an
     * account reported it, and the handle typed in when nobody signed in
     * did. A handle is preferred even if a username is somehow also given,
     * since only an anonymous report carries one.
     */
    public function reporterNameTestCase()
    {
        $record = WikiIssue::open(5, 1000, "a title", "main", "abc");
        $this->assertEqual(WikiIssue::reporterName($record, "alice"),
            "alice", "an account's report shows that account's name");
        $this->assertEqual(WikiIssue::reporterName($record, ""), "",
            "no account and no handle shows nothing");
        $record["handle"] = "sam@example.com";
        $this->assertEqual(WikiIssue::reporterName($record, ""),
            "sam@example.com", "a report with no account shows its handle");
        $this->assertEqual(WikiIssue::reporterName($record, "alice"),
            "sam@example.com", "a handle is preferred to a username");
    }
    /**
     * The user answerable for an issue's status follows the status: the
     * reporter for one merely reported, the assignee for one assigned, and
     * whoever last closed it for one fixed or won't be fixed.
     */
    public function statusUserTestCase()
    {
        $issue = WikiIssue::open(7, 1000, "Login bug", "main", "current");
        $this->assertEqual(7, WikiIssue::statusUser($issue),
            "a reported issue points at its reporter");
        $assigned = WikiIssue::assign($issue, 9, 7, 1100);
        $this->assertEqual(9, WikiIssue::statusUser($assigned),
            "an assigned issue points at its assignee");
        $closed = WikiIssue::close($issue, WikiIssue::RESOLUTION_FIXED, 12,
            1200);
        $this->assertEqual(12, WikiIssue::statusUser($closed),
            "a closed issue points at whoever closed it");
    }
}
