<?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\yioop\configs as C;
use seekquarry\yioop\library\UnitTest;
use seekquarry\yioop\models\GroupModel;
use seekquarry\yioop\models\ProfileModel;
use seekquarry\yioop\models\datasources\Sqlite3Manager;

/**
 * Checks the counting methods GroupModel exposes for the role-group
 * limit enforcement: owned groups, group members, group wiki pages,
 * group threads, and thread posts.
 *
 * @author Chris Pollett
 */
class GroupModelCountsTest extends UnitTest
{
    /**
     * A throwaway Sqlite3 database built from ProfileModel definitions.
     * @var object
     */
    public $db;
    /**
     * File path of the throwaway database.
     * @var string
     */
    public $db_path;
    /**
     * GroupModel wired to the throwaway database.
     * @var object
     */
    public $group_model;
    /**
     * Builds a throwaway database and seeds groups, members, wiki pages,
     * threads, and posts to count against.
     */
    public function setUp()
    {
        $tag = uniqid();
        $this->db_path = C\WORK_DIRECTORY . "/temp/group_counts_$tag.db";
        $this->db = new Sqlite3Manager();
        $this->db->connect("", "", "", $this->db_path);
        $dbinfo = ["DBMS" => "Sqlite3", "DB_HOST" => ""];
        $profile = new ProfileModel(C\DB_NAME, false);
        $profile->initializeSql($this->db, $dbinfo);
        foreach (['SOCIAL_GROUPS', 'USER_GROUP', 'GROUP_PAGE',
            'GROUP_ITEM'] as $table) {
            $this->db->execute($profile->create_statements[$table]);
        }
        $this->group_model = new GroupModel(C\DB_NAME, false);
        $this->group_model->db = $this->db;
        /* user 5 owns groups 100 and 101, user 6 owns group 102 */
        foreach ([[100, 5], [101, 5], [102, 6]] as $group) {
            $this->db->execute("INSERT INTO SOCIAL_GROUPS (GROUP_ID, " .
                "GROUP_NAME, OWNER_ID) VALUES (?, ?, ?)",
                [$group[0], "Group " . $group[0], $group[1]]);
        }
        /* group 100 has three members */
        foreach ([5, 6, 7] as $member) {
            $this->group_model->addUserGroup($member, 100);
        }
        /* group 100 has two wiki pages */
        foreach ([1, 2] as $page_id) {
            $this->db->execute("INSERT INTO GROUP_PAGE (ID, GROUP_ID, " .
                "TITLE) VALUES (?, ?, ?)", [$page_id, 100, "Page $page_id"]);
        }
        /* group 100 has two threads (a top-level item has PARENT_ID = ID);
           the first thread has two replies, so it holds three posts */
        $rows = [[1, 1], [2, 2], [3, 1], [4, 1]];
        foreach ($rows as $row) {
            $this->db->execute("INSERT INTO GROUP_ITEM (ID, PARENT_ID, " .
                "GROUP_ID, USER_ID, TITLE, DESCRIPTION) " .
                "VALUES (?, ?, ?, ?, ?, ?)",
                [$row[0], $row[1], 100, 5, "Item " . $row[0], "body"]);
        }
    }
    /**
     * Disconnects and removes the throwaway database.
     */
    public function tearDown()
    {
        if ($this->db) {
            $this->db->disconnect();
        }
        if ($this->db_path && file_exists($this->db_path)) {
            unlink($this->db_path);
        }
    }
    /**
     * Owned-group counts reflect each owner's rows and no others.
     */
    public function countGroupsOwnedByUserTestCase()
    {
        $this->assertEqual(2,
            (int)$this->group_model->countGroupsOwnedByUser(5),
            "user 5 owns two groups");
        $this->assertEqual(1,
            (int)$this->group_model->countGroupsOwnedByUser(6),
            "user 6 owns one group");
        $this->assertEqual(0,
            (int)$this->group_model->countGroupsOwnedByUser(9),
            "a user owning nothing counts zero");
    }
    /**
     * Member count reflects the seeded memberships of a group.
     */
    public function countGroupUsersTestCase()
    {
        $this->assertEqual(3,
            (int)$this->group_model->countGroupUsers(100),
            "group 100 has three members");
    }
    /**
     * Wiki-page count reflects the pages belonging to a group.
     */
    public function countGroupWikiPagesTestCase()
    {
        $this->assertEqual(2,
            (int)$this->group_model->countGroupWikiPages(100),
            "group 100 has two wiki pages");
        $this->assertEqual(0,
            (int)$this->group_model->countGroupWikiPages(101),
            "group 101 has no wiki pages");
    }
    /**
     * Thread count reflects only the top-level items of a group.
     */
    public function countGroupThreadsTestCase()
    {
        $this->assertEqual(2,
            (int)$this->group_model->countGroupThreads(100),
            "group 100 has two threads, not counting replies");
    }
    /**
     * Post count reflects a thread's starting item and its replies.
     */
    public function countThreadPostsTestCase()
    {
        $this->assertEqual(3,
            (int)$this->group_model->countThreadPosts(1),
            "thread 1 holds its start item plus two replies");
        $this->assertEqual(1,
            (int)$this->group_model->countThreadPosts(2),
            "thread 2 holds only its start item");
    }
}
