Ctrl K
ComunidadGuías…

tests/Repository/CollectionRepositoryTest.php

Rama develop · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
<?php

declare(strict_types=1);

/**
 * vim:set softtabstop=4 shiftwidth=4 expandtab:
 *
 * LICENSE: GNU Affero General Public License, version 3 (AGPL-3.0-or-later)
 * Copyright Ampache.org, 2001-2026
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero 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 Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 *
 */

namespace Ampache\Repository;

use Ampache\Module\Database\DatabaseConnectionInterface;
use Ampache\Module\Database\Exception\QueryFailedException;
use Ampache\Repository\Model\User;
use PDO;
use PDOStatement;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
use Psr\Log\LoggerInterface;
use SEEC\PhpUnit\Helper\ConsecutiveParams;

class CollectionRepositoryTest extends TestCase
{
    use ConsecutiveParams;

    private DatabaseConnectionInterface&MockObject $connection;
    private LoggerInterface&MockObject $logger;
    private CollectionRepository $subject;

    public function testAddItemAppendsAfterTheLastTrackNumber(): void
    {
        $collectionId = 666;

        $this->connection->expects(static::once())
            ->method('fetchOne')
            ->with(
                'SELECT MAX(`track`) FROM `collection_map` WHERE `collection` = ?;',
                [$collectionId]
            )
            ->willReturn('3');

        $this->connection->expects(static::exactly(2))
            ->method('query')
            ->with(
                ...self::withConsecutive(
                    [
                        'INSERT INTO `collection_map` (`collection`, `object_id`, `object_type`, `track`) VALUES (?, ?, ?, ?);',
                        [$collectionId, 42, 'song', 4],
                    ],
                    [
                        'UPDATE `collection` SET `last_update` = ?, `last_count` = (SELECT COUNT(*) FROM `collection_map` WHERE `collection` = ?) WHERE `id` = ?;',
                        self::isType('array'),
                    ]
                )
            );

        self::assertTrue($this->subject->addItem($collectionId, 42, 'song'));
    }

    public function testAddItemRefusesADuplicateWhenUniquenessIsAsked(): void
    {
        $this->connection->expects(static::once())
            ->method('fetchOne')
            ->with(
                'SELECT `id` FROM `collection_map` WHERE `collection` = ? AND `object_type` = ? AND `object_id` = ?;',
                [666, 'song', 42]
            )
            ->willReturn('7');

        $this->connection->expects(static::never())
            ->method('query');

        self::assertFalse($this->subject->addItem(666, 42, 'song', true));
    }

    public function testCollectGarbageRunsTheRestOfTheSweepAfterAFailedStatement(): void
    {
        $calls = 0;

        $this->connection->expects(static::atLeast(2))
            ->method('query')
            ->willReturnCallback(function () use (&$calls): PDOStatement {
                $calls++;

                if ($calls === 1) {
                    throw new QueryFailedException();
                }

                return $this->createMock(PDOStatement::class);
            });

        $this->logger->expects(static::once())
            ->method('debug');

        $this->subject->collectGarbage();

        self::assertGreaterThan(1, $calls);
    }

    public function testCreateReturnsNullWhenNothingWasInserted(): void
    {
        $user = $this->createMock(User::class);

        $user->method('getId')
            ->willReturn(42);

        $this->connection->expects(static::once())
            ->method('query');
        $this->connection->expects(static::once())
            ->method('getLastInsertedId')
            ->willReturn(0);

        self::assertNull($this->subject->create('some-collection', $user));
    }

    public function testDeleteDropsTheMembersBeforeTheCollection(): void
    {
        $collectionId = 666;

        $this->connection->expects(static::exactly(2))
            ->method('query')
            ->with(
                ...self::withConsecutive(
                    ['DELETE FROM `collection_map` WHERE `collection` = ?;', [$collectionId]],
                    ['DELETE FROM `collection` WHERE `id` = ?;', [$collectionId]]
                )
            );

        $this->subject->delete($collectionId);
    }

    public function testGetItemsReturnsMembersInStoredOrder(): void
    {
        $result = $this->createMock(PDOStatement::class);

        $this->connection->expects(static::once())
            ->method('query')
            ->with(
                'SELECT `id`, `object_id`, `object_type`, `track` FROM `collection_map` WHERE `collection` = ? ORDER BY `track`, `id`;',
                [666]
            )
            ->willReturn($result);

        $result->expects(static::exactly(2))
            ->method('fetch')
            ->with(PDO::FETCH_ASSOC)
            ->willReturn(
                ['id' => '7', 'object_id' => '42', 'object_type' => 'song', 'track' => '1'],
                false
            );

        self::assertSame(
            [
                [
                    'id' => 7,
                    'object_id' => 42,
                    'object_type' => 'song',
                    'track' => 1,
                ],
            ],
            $this->subject->getItems(666)
        );
    }

    public function testObjectExistsRefusesATypeACollectionCannotHold(): void
    {
        $this->connection->expects(static::never())
            ->method('fetchOne');

        self::assertFalse($this->subject->objectExists('some-type', 42));
    }

    public function testRegenerateTrackNumbersRenumbersFromOne(): void
    {
        $collectionId = 666;
        $result       = $this->createMock(PDOStatement::class);

        $this->connection->expects(static::exactly(4))
            ->method('query')
            ->with(
                ...self::withConsecutive(
                    [
                        'SELECT `id` FROM `collection_map` WHERE `collection` = ? ORDER BY `track`, `id`;',
                        [$collectionId],
                    ],
                    ['UPDATE `collection_map` SET `track` = ? WHERE `id` = ?;', [1, 9]],
                    ['UPDATE `collection_map` SET `track` = ? WHERE `id` = ?;', [2, 4]],
                    [
                        'UPDATE `collection` SET `last_update` = ?, `last_count` = (SELECT COUNT(*) FROM `collection_map` WHERE `collection` = ?) WHERE `id` = ?;',
                        self::isType('array'),
                    ]
                )
            )
            ->willReturn($result);

        $result->expects(static::exactly(3))
            ->method('fetchColumn')
            ->willReturn('9', '4', false);

        $this->subject->regenerateTrackNumbers($collectionId);
    }

    public function testSetTrackNumberIsScopedToTheOwnCollection(): void
    {
        // the map row id alone used to be enough to reorder another user's collection, so the collection id is checked
        $this->connection->expects(static::once())
            ->method('query')
            ->with(
                'UPDATE `collection_map` SET `track` = ? WHERE `id` = ? AND `collection` = ?;',
                [1, 555, 666]
            );

        $this->subject->setTrackNumber(555, 1, 666);
    }

    protected function setUp(): void
    {
        $this->connection = $this->createMock(DatabaseConnectionInterface::class);
        $this->logger     = $this->createMock(LoggerInterface::class);

        $this->subject = new CollectionRepository(
            $this->connection,
            $this->logger
        );
    }
}