Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,29 @@ public function testBackupHonorsCustomPath(): void
$this->assertEmpty($default, 'Backup was written to the default dir instead of --path');
}

/**
* getPath() only asks interactively when neither the env var nor --path supplied a
* value. CommandTester runs with interactive=false (see executeCommandTest()), and
* Symfony's QuestionHelper answers a non-interactive ask() with the question's default
* without touching any input stream -- so this exercises the prompt branch without
* risking the "blocks forever" trap the other CLI tests warn about, and still ends up
* backing up to the same default path as testBackupIsSuccessful().
*
* @throws DependencyException
* @throws NotFoundException
*/
public function testBackupAsksForPathWhenNeitherEnvVarNorOptionIsSet(): void
{
$this->setupDatabase();

$commandTester = $this->executeCommandTest(BackupCommand::class, ['--path' => '']);

$output = $commandTester->getDisplay();
$this->assertStringContainsString('Application and database backup completed successfully', $output);

$this->checkBackupFilesAreCreated();
}

/**
* Without a reachable database the dump must fail
*
Expand Down
68 changes: 68 additions & 0 deletions tests/Unit/Infrastructure/Adapter/In/Cli/CliCommandHelperTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
<?php

declare(strict_types=1);
/*
* sysPass
*
* @author nuxsmin
* @link https://syspass.org
* @copyright 2012-2024, Rubén Domínguez nuxsmin@$syspass.org
*
* This file is part of sysPass.
*
* sysPass 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.
*
* sysPass 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 sysPass. If not, see <http://www.gnu.org/licenses/>.
*/

namespace SP\Tests\Unit\Infrastructure\Adapter\In\Cli;

use PHPUnit\Framework\Attributes\Group;
use PHPUnit\Framework\Attributes\Test;
use PHPUnit\Framework\TestCase;
use ReflectionClass;
use SP\Infrastructure\Adapter\In\Cli\Commands\BackupCommand;
use SP\Infrastructure\Adapter\In\Cli\Commands\Crypt\UpdateMasterPasswordCommand;
use SP\Infrastructure\Adapter\In\Cli\Commands\InstallCommand;
use SP\Infrastructure\Adapter\In\Cli\CliCommandHelper;

/**
* This is what bin/cli.php registers on the Symfony Application: whatever it hands back is the
* full set of `sp:*` commands an operator can run. A command dropped here silently disappears
* from the CLI without any error -- there is nowhere else that would notice.
*
* InstallCommand, BackupCommand and UpdateMasterPasswordCommand are all `final`, so they cannot
* be doubled with createMock(); their constructors also pull in the real DI graph (installer,
* master password, account services...) that has nothing to do with what this class does.
* newInstanceWithoutConstructor() gives real, distinguishable instances of the exact types
* CliCommandHelper is wired against without any of that, which is all identity-based assertions
* below need.
*/
#[Group('unitary')]
class CliCommandHelperTest extends TestCase
{
#[Test]
public function everyRegisteredCommandIsReturnedInTheOrderItWasWired(): void
{
$installCommand = (new ReflectionClass(InstallCommand::class))->newInstanceWithoutConstructor();
$backupCommand = (new ReflectionClass(BackupCommand::class))->newInstanceWithoutConstructor();
$updateMasterPasswordCommand = (new ReflectionClass(UpdateMasterPasswordCommand::class))
->newInstanceWithoutConstructor();

$helper = new CliCommandHelper($installCommand, $backupCommand, $updateMasterPasswordCommand);

self::assertSame(
[$installCommand, $backupCommand, $updateMasterPasswordCommand],
$helper->getCommands()
);
}
}
52 changes: 52 additions & 0 deletions tests/Unit/Infrastructure/Adapter/In/Web/DataGrid/DataGridTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,28 @@ public function updatingThePagerTakesTheRowCountFromTheData()
self::assertSame(2, $grid->getPager()->getTotalRows());
}

/**
* A grid built without a pager (e.g. a listing too short to page) must not blow up when
* asked to update one -- updatePager() is called unconditionally by the controllers that
* build a grid, whether or not that grid ever got a pager attached.
*
* @throws Exception
*/
#[Test]
public function updatingThePagerIsANoOpWhenNoPagerWasSet()
{
$data = new DataGridData();
$data->addDataRowSource('name');
$data->setData(QueryResult::withTotalNumRows([], 0));

$grid = $this->buildGrid();
$grid->setData($data);

self::assertNull($grid->getPager());
self::assertSame($grid, $grid->updatePager());
self::assertNull($grid->getPager());
}

/**
* Every action counts towards the listing's own total, which the template uses to size the
* actions column, and the menu keeps its own count.
Expand Down Expand Up @@ -191,6 +213,36 @@ public function aMissingTemplateIsNotSet()
self::assertNull($grid->getDataActionsTemplate());
}

/**
* A template that does exist is resolved to its full path and kept, so the screen renders
* that section instead of silently skipping it. Covers both branches of the template path
* (with and without a base subdirectory) that a missing-template test cannot reach, since
* that one never gets past the is_readable() check.
*
* @throws Exception
*/
#[Test]
public function aReadableTemplateIsResolvedToItsFullPathAndKept()
{
// Keyed on something unique to this test: TMP_PATH's vfs filesystem is shared by
// every test in the process.
$viewsPath = TMP_PATH . '/datagrid_template_test_' . uniqid();
mkdir($viewsPath . '/rows', 0755, true);
file_put_contents($viewsPath . '/header.inc', 'header content');
file_put_contents($viewsPath . '/rows/row.inc', 'row content');

$theme = $this->createStub(ThemeInterface::class);
$theme->method('getViewsPath')->willReturn($viewsPath);

$grid = new DataGrid($theme);

$grid->setDataHeaderTemplate('header');
$grid->setDataRowTemplate('row', 'rows');

self::assertSame($viewsPath . '/header.inc', $grid->getDataHeaderTemplate());
self::assertSame($viewsPath . '/rows/row.inc', $grid->getDataRowTemplate());
}

/**
* What the listing does when it is closed — the id of the action to return to — is carried on
* the grid.
Expand Down