Ctrl K
ComunidadGuías…

tests/Repository/AlbumDiskRepositoryTest.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.
 *
 */

namespace Ampache\Repository;

use Ampache\Config\ConfigContainerInterface;
use Ampache\Config\ConfigurationKeyEnum;
use Ampache\Module\Database\DatabaseConnectionInterface;
use Ampache\Module\Database\Exception\QueryFailedException;
use Ampache\Repository\Model\Album;
use Ampache\Repository\Model\AlbumDisk;
use Ampache\Repository\Model\ModelFactoryInterface;
use PDOStatement;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;

class AlbumDiskRepositoryTest extends TestCase
{
    private CatalogMapRepositoryInterface&MockObject $catalogMapRepository;
    private ConfigContainerInterface&MockObject $configContainer;
    private DatabaseConnectionInterface&MockObject $connection;
    private ModelFactoryInterface&MockObject $modelFactory;
    private AlbumDiskRepository $subject;

    public function testCheckAdoptsTheCollidingRowWhenTheMoveHitTheUniqueKey(): void
    {
        $currentId  = 42;
        $collidedId = 99;

        $this->connection->expects(static::exactly(2))
            ->method('fetchOne')
            ->willReturnOnConsecutiveCalls(false, (string) $collidedId);

        $this->connection->expects(static::once())
            ->method('fetchRow')
            ->willReturn(['id' => $currentId, 'disk' => 2]);

        // the song renumbering is skipped because the disk number did not actually change
        $this->connection->expects(static::once())
            ->method('query')
            ->willThrowException(new QueryFailedException());

        self::assertSame($collidedId, $this->subject->check(21, 2, 7, null, $currentId));
    }

    public function testCheckCreatesTheDiskAndSeedsItsSongCount(): void
    {
        $albumDiskId = 666;

        // false for the findId() lookup, then the catalog read back after insert (an orphaned album can force it to 0)
        $this->connection->expects(static::exactly(2))
            ->method('fetchOne')
            ->willReturn(false, 7);

        $matcher = static::exactly(2);

        $this->connection->expects($matcher)
            ->method('query')
            ->willReturnCallback(function (string $sql) use ($matcher): PDOStatement {
                if ($matcher->numberOfInvocations() === 1) {
                    self::assertStringContainsString('REPLACE INTO `album_disk`', $sql);
                } else {
                    self::assertStringContainsString('`song_count` = `song_count` + 1', $sql);
                }

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

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

        $this->catalogMapRepository->expects(static::once())
            ->method('add')
            ->with(7, 'album_disk', $albumDiskId);

        self::assertSame($albumDiskId, $this->subject->check(21, 2, 7));
    }

    public function testCheckDoesNotMapAnOrphanedDiskToACatalog(): void
    {
        $this->connection->method('fetchOne')
            ->willReturn(false, 0);
        $this->connection->method('query')
            ->willReturn($this->createMock(PDOStatement::class));
        $this->connection->method('getLastInsertedId')
            ->willReturn(666);

        $this->catalogMapRepository->expects(static::never())
            ->method('add');

        self::assertSame(666, $this->subject->check(21, 2, 0));
    }

    public function testCheckMatchesOnTheSubtitleWhenOneIsGiven(): void
    {
        $this->connection->expects(static::once())
            ->method('fetchOne')
            ->with(
                self::stringContains('album_disk.`disksubtitle` = ?'),
                [21, 2, 7, 'some-subtitle']
            )
            ->willReturn('666');

        self::assertSame(666, $this->subject->check(21, 2, 7, 'some-subtitle'));
    }

    public function testCheckMovesTheCurrentDiskAndRenumbersItsSongs(): void
    {
        $currentId = 42;

        $this->connection->expects(static::once())
            ->method('fetchOne')
            ->willReturn(false);

        $this->connection->expects(static::once())
            ->method('fetchRow')
            ->with('SELECT * FROM `album_disk` WHERE `id` = ?;', [$currentId])
            ->willReturn(['id' => $currentId, 'disk' => 1]);

        $matcher = static::exactly(2);

        $this->connection->expects($matcher)
            ->method('query')
            ->willReturnCallback(function (string $sql, array $params) use ($matcher): PDOStatement {
                if ($matcher->numberOfInvocations() === 1) {
                    self::assertStringContainsString('UPDATE `album_disk` SET `album_id` = ?', $sql);
                } else {
                    // the songs follow the disk they were sitting on, keyed by the pre-move number
                    self::assertStringContainsString('UPDATE `song` SET `disk` = ?', $sql);
                    self::assertSame([2, 21, 1], $params);
                }

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

        self::assertSame($currentId, $this->subject->check(21, 2, 7, null, $currentId));
    }

    public function testCheckReturnsTheIdOfAnExistingDisk(): void
    {
        $this->connection->expects(static::once())
            ->method('fetchOne')
            ->with(
                self::stringContains('OR `album_disk`.`disksubtitle` IS NULL'),
                [21, 2, 7]
            )
            ->willReturn('666');

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

        self::assertSame(666, $this->subject->check(21, 2, 7));
    }

    public function testCheckReturnsZeroWhenTheInsertFailed(): void
    {
        $this->connection->expects(static::once())
            ->method('fetchOne')
            ->willReturn(false);

        $this->connection->expects(static::once())
            ->method('query')
            ->willThrowException(new QueryFailedException());

        self::assertSame(0, $this->subject->check(21, 2, 7));
    }

    public function testFindByIdReturnsNullWhenTheDiskDoesNotExist(): void
    {
        $albumDisk = $this->createMock(AlbumDisk::class);
        $albumDisk->method('isNew')->willReturn(true);

        $this->modelFactory->method('createAlbumDisk')->willReturn($albumDisk);

        self::assertNull($this->subject->findById(666));
    }

    public function testFindByIdReturnsTheLoadedDisk(): void
    {
        $albumDisk = $this->createMock(AlbumDisk::class);
        $albumDisk->method('isNew')->willReturn(false);

        $this->modelFactory->expects(static::once())
            ->method('createAlbumDisk')
            ->with(666)
            ->willReturn($albumDisk);

        self::assertSame($albumDisk, $this->subject->findById(666));
    }

    public function testGetArtistCountReturnsTheMappedArtistCount(): void
    {
        $albumDisk = $this->createMock(AlbumDisk::class);

        $albumDisk->method('getId')
            ->willReturn(666);

        $this->connection->expects(static::once())
            ->method('fetchOne')
            ->with(
                'SELECT COUNT(DISTINCT(`object_id`)) AS `artist_count` FROM `album_map` WHERE `album_id` = ?;',
                [666]
            )
            ->willReturn('3');

        self::assertSame(3, $this->subject->getArtistCount($albumDisk));
    }

    public function testGetByAlbumReturnsAlbumDisksForEachRow(): void
    {
        $album   = $this->createMock(Album::class);
        $albumId = 21;
        $result  = $this->createMock(PDOStatement::class);

        $album->method('getId')
            ->willReturn($albumId);

        $this->connection->expects(static::once())
            ->method('query')
            ->with(
                'SELECT DISTINCT `id`, `disk` FROM `album_disk` WHERE `album_id` = ? ORDER BY `disk`',
                [$albumId],
            )
            ->willReturn($result);

        $result->expects(static::exactly(3))
            ->method('fetchColumn')
            ->willReturnOnConsecutiveCalls(1, 2, false);

        $albumDisks = $this->subject->getByAlbum($album);

        self::assertCount(2, $albumDisks);
    }

    public function testGetByAlbumReturnsEmptyListWhenNoDisksExist(): void
    {
        $album   = $this->createMock(Album::class);
        $albumId = 21;
        $result  = $this->createMock(PDOStatement::class);

        $album->method('getId')
            ->willReturn($albumId);

        $this->connection->expects(static::once())
            ->method('query')
            ->with(
                'SELECT DISTINCT `id`, `disk` FROM `album_disk` WHERE `album_id` = ? ORDER BY `disk`',
                [$albumId],
            )
            ->willReturn($result);

        $result->expects(static::once())
            ->method('fetchColumn')
            ->willReturn(false);

        self::assertSame([], $this->subject->getByAlbum($album));
    }

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

        self::assertSame([], $this->subject->getRowsByAlbums([]));
    }

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

        $this->connection->expects(static::once())
            ->method('query')
            ->with(
                'SELECT * FROM `album_disk` WHERE `album_id` IN (?,?) ORDER BY `disk`',
                [21, 22]
            )
            ->willReturn($result);

        $result->method('fetch')->willReturnOnConsecutiveCalls(
            ['id' => '1', 'album_id' => '21', 'disk' => '1'],
            ['id' => '2', 'album_id' => '21', 'disk' => '2'],
            false
        );

        self::assertSame(
            [['id' => '1', 'album_id' => '21', 'disk' => '1'], ['id' => '2', 'album_id' => '21', 'disk' => '2']],
            $this->subject->getRowsByAlbums([21, 22])
        );
    }

    public function testGetSongsFiltersDisabledCatalogsWhenConfigured(): void
    {
        $albumDisk = new AlbumDisk();

        $albumDisk->album_id = 21;
        $albumDisk->disk     = 2;

        $result = $this->createMock(PDOStatement::class);

        $this->configContainer->expects(static::once())
            ->method('get')
            ->with(ConfigurationKeyEnum::CATALOG_DISABLE)
            ->willReturn(true);

        $this->connection->expects(static::once())
            ->method('query')
            ->with(self::stringContains('`catalog`.`enabled`'), [21, 2])
            ->willReturn($result);

        $result->expects(static::exactly(3))
            ->method('fetchColumn')
            ->willReturnOnConsecutiveCalls(1, 2, false);

        self::assertSame([1, 2], $this->subject->getSongs($albumDisk));
    }

    public function testGetSongsSkipsTheCatalogJoinWhenNotConfigured(): void
    {
        $albumDisk = new AlbumDisk();

        $albumDisk->album_id = 21;
        $albumDisk->disk     = 2;

        $result = $this->createMock(PDOStatement::class);

        $this->configContainer->expects(static::once())
            ->method('get')
            ->with(ConfigurationKeyEnum::CATALOG_DISABLE)
            ->willReturn(false);

        $this->connection->expects(static::once())
            ->method('query')
            ->with(
                'SELECT DISTINCT `song`.`id` FROM `song` WHERE `song`.`album` = ? AND `song`.`disk` = ?',
                [21, 2]
            )
            ->willReturn($result);

        $result->expects(static::once())
            ->method('fetchColumn')
            ->willReturn(false);

        self::assertSame([], $this->subject->getSongs($albumDisk));
    }

    protected function setUp(): void
    {
        $this->connection           = $this->createMock(DatabaseConnectionInterface::class);
        $this->configContainer      = $this->createMock(ConfigContainerInterface::class);
        $this->modelFactory         = $this->createMock(ModelFactoryInterface::class);
        $this->catalogMapRepository = $this->createMock(CatalogMapRepositoryInterface::class);

        $this->subject = new AlbumDiskRepository(
            $this->connection,
            $this->configContainer,
            $this->modelFactory,
            $this->catalogMapRepository
        );
    }
}