utilikit-1.0.0/tests/src/Unit/UtilikitStateManagerTest.php

tests/src/Unit/UtilikitStateManagerTest.php
<?php

declare(strict_types=1);

namespace Drupal\Tests\utilikit\Unit;

use Drupal\Tests\UnitTestCase;
use Drupal\utilikit\Service\UtilikitStateManager;
use Drupal\utilikit\Service\UtilikitConstants;
use Drupal\Core\State\StateInterface;
use Drupal\Core\Lock\LockBackendInterface;
use PHPUnit\Framework\MockObject\MockObject;
use Psr\Log\LoggerInterface;

/**
 * Tests the UtilikitStateManager service.
 *
 * @group utilikit
 * @coversDefaultClass \Drupal\utilikit\Service\UtilikitStateManager
 */
class UtilikitStateManagerTest extends UnitTestCase {

  /**
   * The state manager service under test.
   *
   * @var \Drupal\utilikit\Service\UtilikitStateManager
   */
  protected UtilikitStateManager $stateManager;

  /**
   * Mock state service.
   *
   * @var \Drupal\Core\State\StateInterface&\PHPUnit\Framework\MockObject\MockObject
   */
  protected StateInterface&MockObject $state;

  /**
   * Mock lock backend.
   *
   * @var \Drupal\Core\Lock\LockBackendInterface&\PHPUnit\Framework\MockObject\MockObject
   */
  protected LockBackendInterface&MockObject $lock;

  /**
   * Mock logger.
   *
   * @var \Psr\Log\LoggerInterface&\PHPUnit\Framework\MockObject\MockObject
   */
  protected LoggerInterface&MockObject $logger;

  /**
   * {@inheritdoc}
   */
  protected function setUp(): void {
    parent::setUp();

    $this->state = $this->createMock(StateInterface::class);
    $this->lock = $this->createMock(LockBackendInterface::class);
    $this->logger = $this->createMock(LoggerInterface::class);

    $this->stateManager = new UtilikitStateManager(
      $this->state,
      $this->lock,
      $this->logger
    );
  }

  /**
   * Tests getStoredClasses method.
   *
   * @covers ::getStoredClasses
   */
  public function testGetStoredClasses(): void {
    $classes = [
      '.uk-pd--20' => 'padding: 20px;',
      '.uk-mg--10' => 'margin: 10px;',
    ];

    $this->state->expects($this->once())
      ->method('get')
      ->with(UtilikitConstants::STATE_CLASSES_KEY, [])
      ->willReturn($classes);

    $result = $this->stateManager->getStoredClasses();
    $this->assertEquals($classes, $result);
  }

  /**
   * Tests addClasses method.
   *
   * @covers ::addClasses
   */
  public function testAddClasses(): void {
    $existingClasses = [
      '.uk-pd--20' => 'padding: 20px;',
    ];

    $newClasses = [
      '.uk-mg--10' => 'margin: 10px;',
      '.uk-text--center' => 'text-align: center;',
    ];

    $this->state->expects($this->once())
      ->method('get')
      ->with(UtilikitConstants::STATE_CLASSES_KEY, [])
      ->willReturn($existingClasses);

    $expectedClasses = array_merge($existingClasses, $newClasses);

    $this->lock->expects($this->once())
      ->method('acquire')
      ->with(UtilikitConstants::LOCK_KEY)
      ->willReturn(TRUE);

    $this->state->expects($this->once())
      ->method('set')
      ->with(
        UtilikitConstants::STATE_CLASSES_KEY,
        $expectedClasses
      );

    $this->lock->expects($this->once())
      ->method('release')
      ->with(UtilikitConstants::LOCK_KEY);

    $this->stateManager->addClasses($newClasses);
  }

  /**
   * Tests addClasses with lock failure.
   *
   * @covers ::addClasses
   */
  public function testAddClassesLockFailure(): void {
    $newClasses = [
      '.uk-mg--10' => 'margin: 10px;',
    ];

    $this->lock->expects($this->once())
      ->method('acquire')
      ->with(UtilikitConstants::LOCK_KEY)
      ->willReturn(FALSE);

    $this->logger->expects($this->once())
      ->method('error')
      ->with($this->stringContains('Could not acquire lock'));

    $this->expectException(\RuntimeException::class);
    $this->expectExceptionMessage('Could not acquire lock');

    $this->stateManager->addClasses($newClasses);
  }

  /**
   * Tests removeClasses method.
   *
   * @covers ::removeClasses
   */
  public function testRemoveClasses(): void {
    $existingClasses = [
      '.uk-pd--20' => 'padding: 20px;',
      '.uk-mg--10' => 'margin: 10px;',
      '.uk-text--center' => 'text-align: center;',
    ];

    $classesToRemove = ['.uk-pd--20', '.uk-text--center'];

    $this->state->expects($this->once())
      ->method('get')
      ->with(UtilikitConstants::STATE_CLASSES_KEY, [])
      ->willReturn($existingClasses);

    $expectedClasses = [
      '.uk-mg--10' => 'margin: 10px;',
    ];

    $this->lock->expects($this->once())
      ->method('acquire')
      ->with(UtilikitConstants::LOCK_KEY)
      ->willReturn(TRUE);

    $this->state->expects($this->once())
      ->method('set')
      ->with(
        UtilikitConstants::STATE_CLASSES_KEY,
        $expectedClasses
      );

    $this->lock->expects($this->once())
      ->method('release')
      ->with(UtilikitConstants::LOCK_KEY);

    $this->stateManager->removeClasses($classesToRemove);
  }

  /**
   * Tests clearAllClasses method.
   *
   * @covers ::clearAllClasses
   */
  public function testClearAllClasses(): void {
    $this->lock->expects($this->once())
      ->method('acquire')
      ->with(UtilikitConstants::LOCK_KEY)
      ->willReturn(TRUE);

    $this->state->expects($this->once())
      ->method('set')
      ->with(UtilikitConstants::STATE_CLASSES_KEY, []);

    $this->lock->expects($this->once())
      ->method('release')
      ->with(UtilikitConstants::LOCK_KEY);

    $this->stateManager->clearAllClasses();
  }

  /**
   * Tests hasClass method.
   *
   * @covers ::hasClass
   */
  public function testHasClass(): void {
    $classes = [
      '.uk-pd--20' => 'padding: 20px;',
      '.uk-mg--10' => 'margin: 10px;',
    ];

    $this->state->expects($this->exactly(2))
      ->method('get')
      ->with(UtilikitConstants::STATE_CLASSES_KEY, [])
      ->willReturn($classes);

    $this->assertTrue($this->stateManager->hasClass('.uk-pd--20'));
    $this->assertFalse($this->stateManager->hasClass('.uk-text--center'));
  }

  /**
   * Tests getClassCount method.
   *
   * @covers ::getClassCount
   */
  public function testGetClassCount(): void {
    $classes = [
      '.uk-pd--20' => 'padding: 20px;',
      '.uk-mg--10' => 'margin: 10px;',
      '.uk-text--center' => 'text-align: center;',
    ];

    $this->state->expects($this->once())
      ->method('get')
      ->with(UtilikitConstants::STATE_CLASSES_KEY, [])
      ->willReturn($classes);

    $count = $this->stateManager->getClassCount();
    $this->assertEquals(3, $count);
  }

  /**
   * Tests getStatistics method.
   *
   * @covers ::getStatistics
   */
  public function testGetStatistics(): void {
    $classes = [
      '.uk-pd--20' => 'padding: 20px;',
      '.uk-mg--10' => 'margin: 10px;',
    ];

    $timestamp = time();

    $this->state->expects($this->exactly(3))
      ->method('get')
      ->willReturnMap([
        [UtilikitConstants::STATE_CLASSES_KEY, [], $classes],
        [UtilikitConstants::STATE_LAST_UPDATED_KEY, NULL, $timestamp],
        [UtilikitConstants::STATE_STATISTICS_KEY, [], ['cache_hits' => 100]],
      ]);

    $stats = $this->stateManager->getStatistics();

    $this->assertArrayHasKey('total_classes', $stats);
    $this->assertArrayHasKey('last_updated', $stats);
    $this->assertArrayHasKey('cache_hits', $stats);
    $this->assertEquals(2, $stats['total_classes']);
    $this->assertEquals($timestamp, $stats['last_updated']);
    $this->assertEquals(100, $stats['cache_hits']);
  }

  /**
   * Tests updateStatistics method.
   *
   * @covers ::updateStatistics
   */
  public function testUpdateStatistics(): void {
    $existingStats = [
      'cache_hits' => 100,
      'cache_misses' => 20,
    ];

    $updates = [
      'cache_hits' => 110,
      'new_classes_added' => 5,
    ];

    $this->state->expects($this->once())
      ->method('get')
      ->with(UtilikitConstants::STATE_STATISTICS_KEY, [])
      ->willReturn($existingStats);

    $expectedStats = [
      'cache_hits' => 110,
      'cache_misses' => 20,
      'new_classes_added' => 5,
    ];

    $this->lock->expects($this->once())
      ->method('acquire')
      ->with(UtilikitConstants::LOCK_KEY)
      ->willReturn(TRUE);

    $this->state->expects($this->once())
      ->method('set')
      ->with(
        UtilikitConstants::STATE_STATISTICS_KEY,
        $expectedStats
      );

    $this->lock->expects($this->once())
      ->method('release')
      ->with(UtilikitConstants::LOCK_KEY);

    $this->stateManager->updateStatistics($updates);
  }

  /**
   * Tests getLastUpdated method.
   *
   * @covers ::getLastUpdated
   */
  public function testGetLastUpdated(): void {
    $timestamp = time();

    $this->state->expects($this->once())
      ->method('get')
      ->with(UtilikitConstants::STATE_LAST_UPDATED_KEY)
      ->willReturn($timestamp);

    $result = $this->stateManager->getLastUpdated();
    $this->assertEquals($timestamp, $result);
  }

  /**
   * Tests setLastUpdated method.
   *
   * @covers ::setLastUpdated
   */
  public function testSetLastUpdated(): void {
    $timestamp = time();

    $this->state->expects($this->once())
      ->method('set')
      ->with(UtilikitConstants::STATE_LAST_UPDATED_KEY, $timestamp);

    $this->stateManager->setLastUpdated($timestamp);
  }

  /**
   * Tests batch operations.
   *
   * @covers ::batchAddClasses
   */
  public function testBatchAddClasses(): void {
    $existingClasses = [
      '.uk-pd--10' => 'padding: 10px;',
    ];

    $batch1 = [
      '.uk-pd--20' => 'padding: 20px;',
      '.uk-mg--10' => 'margin: 10px;',
    ];

    $batch2 = [
      '.uk-text--center' => 'text-align: center;',
      '.uk-bg--primary' => 'background-color: var(--color-primary);',
    ];

    $this->state->expects($this->once())
      ->method('get')
      ->with(UtilikitConstants::STATE_CLASSES_KEY, [])
      ->willReturn($existingClasses);

    $this->lock->expects($this->once())
      ->method('acquire')
      ->with(UtilikitConstants::LOCK_KEY)
      ->willReturn(TRUE);

    $expectedFinal = array_merge($existingClasses, $batch1, $batch2);

    $this->state->expects($this->once())
      ->method('set')
      ->with(
        UtilikitConstants::STATE_CLASSES_KEY,
        $expectedFinal
      );

    $this->lock->expects($this->once())
      ->method('release')
      ->with(UtilikitConstants::LOCK_KEY);

    $this->stateManager->batchAddClasses([$batch1, $batch2]);
  }

  /**
   * Tests getClassesByPrefix method.
   *
   * @covers ::getClassesByPrefix
   */
  public function testGetClassesByPrefix(): void {
    $classes = [
      '.uk-pd--20' => 'padding: 20px;',
      '.uk-pd--10' => 'padding: 10px;',
      '.uk-mg--20' => 'margin: 20px;',
      '.uk-text--center' => 'text-align: center;',
    ];

    $this->state->expects($this->once())
      ->method('get')
      ->with(UtilikitConstants::STATE_CLASSES_KEY, [])
      ->willReturn($classes);

    $pdClasses = $this->stateManager->getClassesByPrefix('.uk-pd');

    $this->assertCount(2, $pdClasses);
    $this->assertArrayHasKey('.uk-pd--20', $pdClasses);
    $this->assertArrayHasKey('.uk-pd--10', $pdClasses);
  }

  /**
   * Tests importClasses method.
   *
   * @covers ::importClasses
   */
  public function testImportClasses(): void {
    $importData = [
      '.uk-pd--30' => 'padding: 30px;',
      '.uk-mg--30' => 'margin: 30px;',
    ];

    $this->lock->expects($this->once())
      ->method('acquire')
      ->with(UtilikitConstants::LOCK_KEY)
      ->willReturn(TRUE);

    $this->state->expects($this->once())
      ->method('set')
      ->with(
        UtilikitConstants::STATE_CLASSES_KEY,
        $importData
      );

    $this->lock->expects($this->once())
      ->method('release')
      ->with(UtilikitConstants::LOCK_KEY);

    $this->stateManager->importClasses($importData);
  }

  /**
   * Tests exportClasses method.
   *
   * @covers ::exportClasses
   */
  public function testExportClasses(): void {
    $classes = [
      '.uk-pd--20' => 'padding: 20px;',
      '.uk-mg--10' => 'margin: 10px;',
    ];

    $this->state->expects($this->once())
      ->method('get')
      ->with(UtilikitConstants::STATE_CLASSES_KEY, [])
      ->willReturn($classes);

    $exported = $this->stateManager->exportClasses();

    $this->assertArrayHasKey('classes', $exported);
    $this->assertArrayHasKey('metadata', $exported);
    $this->assertEquals($classes, $exported['classes']);
    $this->assertArrayHasKey('exported_at', $exported['metadata']);
    $this->assertArrayHasKey('count', $exported['metadata']);
    $this->assertEquals(2, $exported['metadata']['count']);
  }

  /**
   * Tests error handling in state operations.
   *
   * @covers ::addClasses
   */
  public function testErrorHandling(): void {
    $this->state->expects($this->once())
      ->method('get')
      ->willThrowException(new \Exception('Database error'));

    $this->logger->expects($this->once())
      ->method('error')
      ->with($this->stringContains('Error reading state'));

    $classes = $this->stateManager->getStoredClasses();
    $this->assertEquals([], $classes);
  }

  /**
   * Tests concurrent access handling.
   *
   * @covers ::addClasses
   */
  public function testConcurrentAccess(): void {
    $attempts = 0;

    $this->lock->expects($this->exactly(3))
      ->method('acquire')
      ->willReturnCallback(function () use (&$attempts) {
        $attempts++;
        return $attempts === 3;
      });

    $this->lock->expects($this->exactly(2))
      ->method('wait')
      ->with(UtilikitConstants::LOCK_KEY);

    $this->state->expects($this->once())
      ->method('get')
      ->willReturn([]);

    $this->state->expects($this->once())
      ->method('set');

    $this->lock->expects($this->once())
      ->method('release');

    $this->stateManager->addClasses(['.uk-test--1' => 'test: 1;']);
  }

}

Главная | Обратная связь

drupal hosting | друпал хостинг | it patrol .inc