feat(core): add CLI management for tenant system stores
Signed-off-by: Sebastian Krupinski <krupinski01@gmail.com>
This commit is contained in:
@@ -0,0 +1,83 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace KTXT\Unit\Console\Tenant;
|
||||
|
||||
use KTXC\Console\Tenant\TenantStoreListCommand;
|
||||
use KTXC\Console\Tenant\TenantStoreRemoveCommand;
|
||||
use KTXC\Console\Tenant\TenantStoreSetCommand;
|
||||
use KTXC\SystemStore\SystemStoreConfigurationService;
|
||||
use KTXF\SystemStore\StoreReference;
|
||||
use PHPUnit\Framework\Attributes\Test;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Psr\Log\NullLogger;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Tester\CommandTester;
|
||||
|
||||
final class TenantStoreCommandsTest extends TestCase
|
||||
{
|
||||
#[Test]
|
||||
public function listsTenantStores(): void
|
||||
{
|
||||
$stores = $this->createStub(SystemStoreConfigurationService::class);
|
||||
$stores->method('list')->willReturn([
|
||||
'previews' => new StoreReference('local', 'default', 'previews'),
|
||||
]);
|
||||
$tester = new CommandTester(new TenantStoreListCommand($stores));
|
||||
|
||||
$status = $tester->execute(['tenant' => 'tenant-a']);
|
||||
|
||||
self::assertSame(Command::SUCCESS, $status);
|
||||
self::assertStringContainsString('previews', $tester->getDisplay());
|
||||
self::assertStringContainsString('local', $tester->getDisplay());
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function configuresATenantStore(): void
|
||||
{
|
||||
$stores = $this->createMock(SystemStoreConfigurationService::class);
|
||||
$stores->expects(self::once())
|
||||
->method('set')
|
||||
->with('tenant-a', 'previews', 'local', 'default', 'previews')
|
||||
->willReturn(new StoreReference('local', 'default', 'previews'));
|
||||
$tester = new CommandTester(new TenantStoreSetCommand($stores, new NullLogger()));
|
||||
|
||||
$status = $tester->execute([
|
||||
'tenant' => 'tenant-a',
|
||||
'name' => 'previews',
|
||||
'provider' => 'local',
|
||||
'service' => 'default',
|
||||
'namespace' => 'previews',
|
||||
]);
|
||||
|
||||
self::assertSame(Command::SUCCESS, $status);
|
||||
self::assertStringContainsString('configured', $tester->getDisplay());
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function removesATenantStore(): void
|
||||
{
|
||||
$stores = $this->createMock(SystemStoreConfigurationService::class);
|
||||
$stores->expects(self::once())->method('remove')->with('tenant-a', 'previews')->willReturn(true);
|
||||
$tester = new CommandTester(new TenantStoreRemoveCommand($stores, new NullLogger()));
|
||||
|
||||
$status = $tester->execute(['tenant' => 'tenant-a', 'name' => 'previews']);
|
||||
|
||||
self::assertSame(Command::SUCCESS, $status);
|
||||
self::assertStringContainsString('removed', $tester->getDisplay());
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function removingAnAbsentStoreIsIdempotent(): void
|
||||
{
|
||||
$stores = $this->createStub(SystemStoreConfigurationService::class);
|
||||
$stores->method('remove')->willReturn(false);
|
||||
$tester = new CommandTester(new TenantStoreRemoveCommand($stores, new NullLogger()));
|
||||
|
||||
$status = $tester->execute(['tenant' => 'tenant-a', 'name' => 'previews']);
|
||||
|
||||
self::assertSame(Command::SUCCESS, $status);
|
||||
self::assertStringContainsString('not configured', $tester->getDisplay());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace KTXT\Unit\SystemStore;
|
||||
|
||||
use KTXC\Models\Tenant\TenantConfiguration;
|
||||
use KTXC\Models\Tenant\TenantObject;
|
||||
use KTXC\Stores\TenantStore;
|
||||
use KTXC\SystemStore\SystemStoreConfigurationService;
|
||||
use KTXF\SystemStore\SystemStoreException;
|
||||
use PHPUnit\Framework\Attributes\Test;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
final class SystemStoreConfigurationServiceTest extends TestCase
|
||||
{
|
||||
private TenantStore&MockObject $tenants;
|
||||
private SystemStoreConfigurationService $configuration;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
$this->tenants = $this->createMock(TenantStore::class);
|
||||
$this->configuration = new SystemStoreConfigurationService($this->tenants);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function listsConfiguredLogicalStores(): void
|
||||
{
|
||||
$tenant = (new TenantObject())->setConfiguration(
|
||||
(new TenantConfiguration())->jsonDeserialize([
|
||||
'stores' => [
|
||||
'previews' => [
|
||||
'provider' => 'local',
|
||||
'service' => 'default',
|
||||
'namespace' => 'previews',
|
||||
],
|
||||
],
|
||||
]),
|
||||
);
|
||||
$this->tenants->expects(self::once())->method('fetch')->with('tenant-a')->willReturn($tenant);
|
||||
|
||||
$stores = $this->configuration->list('tenant-a');
|
||||
|
||||
self::assertSame('local', $stores['previews']->provider);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function atomicallyStoresOneLogicalStoreReference(): void
|
||||
{
|
||||
$this->tenants->expects(self::once())
|
||||
->method('storeConfigurationStore')
|
||||
->with('tenant-a', 'previews', [
|
||||
'provider' => 'local',
|
||||
'service' => 'default',
|
||||
'namespace' => 'previews',
|
||||
])
|
||||
->willReturn(true);
|
||||
|
||||
$reference = $this->configuration->set(
|
||||
'tenant-a', 'previews', 'local', 'default', 'previews',
|
||||
);
|
||||
|
||||
self::assertSame('local', $reference->provider);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function reportsAMissingTenantDuringConfiguration(): void
|
||||
{
|
||||
$this->tenants->expects(self::once())->method('storeConfigurationStore')->willReturn(false);
|
||||
$this->expectException(SystemStoreException::class);
|
||||
|
||||
$this->configuration->set('missing', 'previews', 'local', 'default', 'previews');
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function atomicallyRemovesOneLogicalStoreReference(): void
|
||||
{
|
||||
$this->tenants->expects(self::once())
|
||||
->method('removeConfigurationStore')
|
||||
->with('tenant-a', 'previews')
|
||||
->willReturn(true);
|
||||
|
||||
self::assertTrue($this->configuration->remove('tenant-a', 'previews'));
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function reportsAMissingTenantDuringRemoval(): void
|
||||
{
|
||||
$this->tenants->expects(self::once())->method('removeConfigurationStore')->willReturn(null);
|
||||
$this->expectException(SystemStoreException::class);
|
||||
|
||||
$this->configuration->remove('missing', 'previews');
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function rejectsUnsafeStoreNamesBeforeBuildingMongoPaths(): void
|
||||
{
|
||||
$this->tenants->expects(self::never())->method('storeConfigurationStore');
|
||||
$this->expectException(\InvalidArgumentException::class);
|
||||
|
||||
$this->configuration->set('tenant-a', 'previews.other', 'local', 'default', 'previews');
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user