og-8.x-1.x-dev/tests/src/Unit/GroupTypeManagerTest.php

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

declare(strict_types=1);

namespace Drupal\Tests\og\Unit;

use Drupal\Core\Cache\CacheBackendInterface;
use Drupal\Core\Config\Config;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Entity\EntityStorageInterface;
use Drupal\Core\Entity\EntityTypeBundleInfoInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Routing\RouteBuilderInterface;
use Drupal\Tests\UnitTestCase;
use Drupal\og\Entity\OgRole;
use Drupal\og\Event\GroupCreationEvent;
use Drupal\og\Event\GroupCreationEventInterface;
use Drupal\og\Event\PermissionEventInterface;
use Drupal\og\GroupTypeManager;
use Drupal\og\GroupTypeManagerInterface;
use Drupal\og\OgGroupAudienceHelperInterface;
use Drupal\og\OgMembershipInterface;
use Drupal\og\OgMembershipTypeInterface;
use Drupal\og\OgRoleInterface;
use Drupal\og\OgRoleManagerInterface;
use Drupal\og\PermissionManagerInterface;
use Prophecy\Argument;
use Prophecy\PhpUnit\ProphecyTrait;
use Prophecy\Prophecy\ObjectProphecy;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;

/**
 * Tests the group manager.
 *
 * @group og
 * @coversDefaultClass \Drupal\og\GroupTypeManager
 */
class GroupTypeManagerTest extends UnitTestCase {

  use ProphecyTrait;

  /**
   * The config prophecy used in the test.
   */
  protected Config|ObjectProphecy $config;

  /**
   * The config factory prophecy used in the test.
   */
  protected ConfigFactoryInterface|ObjectProphecy $configFactory;

  /**
   * The entity type manager prophecy used in the test.
   */
  protected EntityTypeManagerInterface|ObjectProphecy $entityTypeManager;

  /**
   * The entity storage prophecy used in the test.
   */
  protected EntityStorageInterface|ObjectProphecy $entityStorage;

  /**
   * The OG role prophecy used in the test.
   */
  protected OgRoleInterface|ObjectProphecy $ogRole;

  /**
   * The entity type bundle info prophecy used in the test.
   */
  protected EntityTypeBundleInfoInterface|ObjectProphecy $entityTypeBundleInfo;

  /**
   * The event dispatcher prophecy used in the test.
   */
  protected EventDispatcherInterface|ObjectProphecy $eventDispatcher;

  /**
   * The permission event prophecy used in the test.
   */
  protected PermissionEventInterface|ObjectProphecy $permissionEvent;

  /**
   * The cache prophecy used in the test.
   */
  protected CacheBackendInterface|ObjectProphecy $cache;

  /**
   * The OG permission manager prophecy used in the test.
   */
  protected PermissionManagerInterface|ObjectProphecy $permissionManager;

  /**
   * The OG role manager prophecy used in the test.
   */
  protected OgRoleManagerInterface|ObjectProphecy $ogRoleManager;

  /**
   * The route builder service used in the test.
   */
  protected RouteBuilderInterface|ObjectProphecy $routeBuilder;

  /**
   * The OG group audience helper.
   */
  protected OgGroupAudienceHelperInterface|ObjectProphecy $groupAudienceHelper;

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

    $this->config = $this->prophesize(Config::class);
    $this->configFactory = $this->prophesize(ConfigFactoryInterface::class);
    $this->entityTypeBundleInfo = $this->prophesize(EntityTypeBundleInfoInterface::class);
    $this->entityTypeManager = $this->prophesize(EntityTypeManagerInterface::class);
    $this->entityStorage = $this->prophesize(EntityStorageInterface::class);
    $this->eventDispatcher = $this->prophesize(EventDispatcherInterface::class);
    $this->ogRole = $this->prophesize(OgRole::class);
    $this->ogRoleManager = $this->prophesize(OgRoleManagerInterface::class);
    $this->permissionEvent = $this->prophesize(PermissionEventInterface::class);
    $this->permissionManager = $this->prophesize(PermissionManagerInterface::class);
    $this->cache = $this->prophesize(CacheBackendInterface::class);
    $this->routeBuilder = $this->prophesize(RouteBuilderInterface::class);
    $this->groupAudienceHelper = $this->prophesize(OgGroupAudienceHelperInterface::class);
  }

  /**
   * Tests getting an instance of the group manager.
   *
   * @covers ::__construct
   */
  public function testInstance(): void {
    // Just creating an instance should be lightweight, no methods should be
    // called.
    $group_manager = $this->createGroupManager();
    $this->assertInstanceOf(GroupTypeManagerInterface::class, $group_manager);
  }

  /**
   * Tests checking if an entity is a group.
   *
   * @covers ::isGroup
   *
   * @dataProvider providerTestIsGroup
   */
  public function testIsGroup(string $entity_type_id, string $bundle_id, bool $expected_result): void {
    // It is expected that the group map will be retrieved from config.
    $groups = ['test_entity' => ['a', 'b']];
    $this->expectGroupMapRetrieval($groups);

    $manager = $this->createGroupManager();

    $this->assertSame($expected_result, $manager->isGroup($entity_type_id, $bundle_id));
  }

  /**
   * Data provider for testIsGroup.
   *
   * @return array
   *   array with the entity type ID, bundle ID and boolean indicating the
   *   expected result.
   */
  public static function providerTestIsGroup(): array {
    return [
      ['test_entity', 'a', TRUE],
      ['test_entity', 'b', TRUE],
      ['test_entity', 'c', FALSE],
      ['test_entity_non_existent', 'a', FALSE],
      ['test_entity_non_existent', 'c', FALSE],
    ];
  }

  /**
   * Tests getting all the groups IDs of an entity type.
   *
   * @covers ::getGroupBundleIdsByEntityType
   */
  public function testGetGroupBundleIdsByEntityType(): void {
    // It is expected that the group map will be retrieved from config.
    $groups = ['test_entity' => ['a', 'b']];
    $this->expectGroupMapRetrieval($groups);

    $manager = $this->createGroupManager();

    $this->assertSame($groups['test_entity'], $manager->getGroupBundleIdsByEntityType('test_entity'));
    $this->assertSame([], $manager->getGroupBundleIdsByEntityType('test_entity_non_existent'));
  }

  /**
   * Tests adding an existing group.
   *
   * @covers ::addGroup
   */
  public function testAddGroupExisting(): void {
    // It is expected that the group map will be retrieved from config.
    $groups_before = ['test_entity' => ['a', 'b']];
    $this->expectGroupMapRetrieval($groups_before);

    $groups_after = ['test_entity' => ['a', 'b', 'c']];

    $this->config->get('groups')
      ->willReturn($groups_after)
      ->shouldBeCalled();

    $manager = $this->createGroupManager();

    // Add to existing.
    $this->expectException(\InvalidArgumentException::class);
    $manager->addGroup('test_entity', 'c');

    $this->assertSame(
      ['a', 'b', 'c'],
      $manager->getGroupBundleIdsByEntityType('test_entity')
    );
    $this->assertTrue($manager->isGroup('test_entity', 'c'));
  }

  /**
   * Tests adding a new group.
   *
   * @covers ::addGroup
   */
  public function testAddGroupNew(): void {
    $this->configFactory->getEditable('og.settings')
      ->willReturn($this->config->reveal())
      ->shouldBeCalled();

    // It is expected that the group map will be retrieved from config.
    $groups_before = [];
    $this->expectGroupMapRetrieval($groups_before);

    $groups_after = ['test_entity_new' => ['a']];

    $config_prophecy = $this->config;
    $this->config->set('groups', $groups_after)
      ->will(function () use ($groups_after, $config_prophecy): void {
        $config_prophecy->get('groups')
          ->willReturn($groups_after)
          ->shouldBeCalled();
      })
      ->shouldBeCalled();

    $this->config->save()
      ->shouldBeCalled();

    $manager = $this->createGroupManager();

    $this->ogRoleManager->createPerBundleRoles('test_entity_new', 'a');

    $this->eventDispatcher->dispatch(Argument::type(GroupCreationEvent::class), GroupCreationEventInterface::EVENT_NAME)
      ->shouldBeCalled();

    // Add a new entity type.
    $manager->addGroup('test_entity_new', 'a');
    $this->assertSame(['a'], $manager->getGroupBundleIdsByEntityType('test_entity_new'));
    $this->assertTrue($manager->isGroup('test_entity_new', 'a'));
  }

  /**
   * Tests removing a group.
   *
   * @covers ::addGroup
   */
  public function testRemoveGroup(): void {
    $this->configFactory->getEditable('og.settings')
      ->willReturn($this->config->reveal())
      ->shouldBeCalled();

    // It is expected that the group map will be retrieved from config.
    $groups_before = ['test_entity' => ['a', 'b']];
    $this->expectGroupMapRetrieval($groups_before);

    $groups_after = ['test_entity' => ['a']];

    $membership_types_before = [
      'test_entity:a' => 'default',
      'test_entity:b' => 'default',
    ];
    $membership_types_after = [
      'test_entity:a' => 'default',
    ];

    $this->config->get('group_membership_types')
      ->willReturn($membership_types_before)
      ->shouldBeCalled();

    $this->config->set('groups', $groups_after)
      ->shouldBeCalled();

    $this->config->set('group_membership_types', $membership_types_after)
      ->shouldBeCalled();

    $this->config->save()
      ->shouldBeCalled();

    $this->config->get('groups')
      ->willReturn($groups_after)
      ->shouldBeCalled();

    $manager = $this->createGroupManager();

    // Add to existing.
    $manager->removeGroup('test_entity', 'b');
    $this->assertSame(['a'], $manager->getGroupBundleIdsByEntityType('test_entity'));
    $this->assertFalse($manager->isGroup('test_entity', 'b'));
    $this->assertTrue($manager->isGroup('test_entity', 'a'));
  }

  /**
   * Creates a group manager instance with a mock config factory.
   *
   * @return \Drupal\og\GroupTypeManagerInterface
   *   Returns the group manager.
   */
  protected function createGroupManager(): GroupTypeManagerInterface {
    return new GroupTypeManager(
      $this->configFactory->reveal(),
      $this->entityTypeManager->reveal(),
      $this->entityTypeBundleInfo->reveal(),
      $this->eventDispatcher->reveal(),
      $this->cache->reveal(),
      $this->permissionManager->reveal(),
      $this->ogRoleManager->reveal(),
      $this->routeBuilder->reveal(),
      $this->groupAudienceHelper->reveal()
    );
  }

  /**
   * Sets up an expectation that the group map will be retrieved from config.
   *
   * @param array $groups
   *   The expected group map that will be returned by the mocked config.
   */
  protected function expectGroupMapRetrieval(array $groups = []): void {
    $this->configFactory->get('og.settings')
      ->willReturn($this->config->reveal())
      ->shouldBeCalled();

    $this->config->get('groups')
      ->willReturn($groups)
      ->shouldBeCalled();
  }

  /**
   * Tests getting the membership type for a group bundle.
   *
   * @param array $membership_types
   *   The configured membership types.
   * @param string $entity_type_id
   *   The entity type ID to test.
   * @param string $bundle_id
   *   The bundle ID to test.
   * @param string $expected_result
   *   The expected membership type.
   *
   * @covers ::getGroupDefaultMembershipType
   * @dataProvider providerTestGetGroupDefaultMembershipType
   */
  public function testGetGroupDefaultMembershipType(array $membership_types, string $entity_type_id, string $bundle_id, string $expected_result): void {
    $this->configFactory->get('og.settings')
      ->willReturn($this->config->reveal())
      ->shouldBeCalled();

    $this->config->get('group_membership_types')
      ->willReturn($membership_types)
      ->shouldBeCalled();

    // Get the configured membership type for this bundle.
    $configured_type = $membership_types["$entity_type_id:$bundle_id"] ?? NULL;

    // If a custom type is configured, mock loading it.
    if ($configured_type && $configured_type !== OgMembershipInterface::TYPE_DEFAULT) {
      $storage = $this->prophesize(EntityStorageInterface::class);
      $mock_entity = $this->prophesize(OgMembershipTypeInterface::class);

      $storage->load($configured_type)
        ->willReturn($mock_entity->reveal())
        ->shouldBeCalled();

      $this->entityTypeManager->getStorage('og_membership_type')
        ->willReturn($storage->reveal())
        ->shouldBeCalled();
    }

    $manager = $this->createGroupManager();

    $this->assertSame($expected_result, $manager->getGroupDefaultMembershipType($entity_type_id, $bundle_id));
  }

  /**
   * Data provider for testGetGroupDefaultMembershipType.
   *
   * @return array
   *   Test cases with membership types config, entity type, bundle, and
   *   expected result.
   */
  public static function providerTestGetGroupDefaultMembershipType(): array {
    return [
      // No membership types configured - returns default.
      [[], 'node', 'group_type_a', OgMembershipInterface::TYPE_DEFAULT],
      // Custom membership type configured for this bundle.
      [
        ['node:group_type_a' => 'premium_membership'],
        'node',
        'group_type_a',
        'premium_membership',
      ],
      // Different bundle has different type.
      [
        [
          'node:group_type_a' => 'premium_membership',
          'node:group_type_b' => 'basic_membership',
        ],
        'node',
        'group_type_b',
        'basic_membership',
      ],
      // Non-configured bundle returns default.
      [
        ['node:group_type_a' => 'premium_membership'],
        'node',
        'group_type_b',
        OgMembershipInterface::TYPE_DEFAULT,
      ],
    ];
  }

  /**
   * Tests setting a custom membership type for a group bundle.
   *
   * @covers ::setGroupDefaultMembershipType
   * @covers ::getGroupDefaultMembershipType
   */
  public function testSetGroupDefaultMembershipType(): void {
    $this->configFactory->getEditable('og.settings')
      ->willReturn($this->config->reveal())
      ->shouldBeCalled();

    $this->configFactory->get('og.settings')
      ->willReturn($this->config->reveal())
      ->shouldBeCalled();

    // After setting a custom type.
    $membership_types_after = ['node:group_type_a' => 'premium_membership'];

    // Return empty array on first two calls, then the updated array.
    // Call 1: First getGroupDefaultMembershipType() - returns []
    // Call 2: setGroupDefaultMembershipType() reads current value - returns []
    // Call 3: Second getGroupDefaultMembershipType() - returns updated array.
    $this->config->get('group_membership_types')
      ->willReturn([], [], $membership_types_after);

    $this->config->set('group_membership_types', $membership_types_after)
      ->shouldBeCalled();

    $this->config->save()
      ->shouldBeCalled();

    // Mock entity storage for the second getGroupDefaultMembershipType call
    // which will validate the 'premium_membership' entity exists.
    $storage = $this->prophesize(EntityStorageInterface::class);
    $mock_entity = $this->prophesize(OgMembershipTypeInterface::class);

    $storage->load('premium_membership')
      ->willReturn($mock_entity->reveal())
      ->shouldBeCalled();

    $this->entityTypeManager->getStorage('og_membership_type')
      ->willReturn($storage->reveal())
      ->shouldBeCalled();

    $manager = $this->createGroupManager();

    // Verify default is returned initially.
    $this->assertSame(
      OgMembershipInterface::TYPE_DEFAULT,
      $manager->getGroupDefaultMembershipType('node', 'group_type_a')
    );

    // Set a custom membership type.
    $manager->setGroupDefaultMembershipType('node', 'group_type_a', 'premium_membership');

    // Verify the custom type is now returned.
    $this->assertSame(
      'premium_membership',
      $manager->getGroupDefaultMembershipType('node', 'group_type_a')
    );
  }

  /**
   * Tests that a deleted membership type falls back to default.
   *
   * This tests the scenario where a membership type is configured for a group
   * but the membership type entity has been deleted. In this case, the system
   * should gracefully fall back to the default membership type.
   *
   * @covers ::getGroupDefaultMembershipType
   */
  public function testGetGroupDefaultMembershipTypeDeletedType(): void {
    $this->configFactory->get('og.settings')
      ->willReturn($this->config->reveal())
      ->shouldBeCalled();

    // Configuration references a deleted membership type.
    $this->config->get('group_membership_types')
      ->willReturn(['node:group_type_a' => 'deleted_membership'])
      ->shouldBeCalled();

    // Mock the entity type manager and storage.
    $storage = $this->prophesize(EntityStorageInterface::class);

    // The membership type entity doesn't exist (returns NULL).
    $storage->load('deleted_membership')
      ->willReturn(NULL)
      ->shouldBeCalled();

    $this->entityTypeManager->getStorage('og_membership_type')
      ->willReturn($storage->reveal())
      ->shouldBeCalled();

    $manager = $this->createGroupManager();

    // Should fall back to default when the configured type doesn't exist.
    $this->assertSame(
      OgMembershipInterface::TYPE_DEFAULT,
      $manager->getGroupDefaultMembershipType('node', 'group_type_a')
    );
  }

}

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

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