Commit 978aeeec authored by catch's avatar catch
Browse files

Issue #3468435 by mstrelan: Convert IpAddressBlockingTest to a Unit and Kernel test and improve

(cherry picked from commit 292aee30)
parent 0b288648
Loading
Loading
Loading
Loading
Loading
+0 −116
Original line number Diff line number Diff line
<?php

declare(strict_types=1);

namespace Drupal\Tests\ban\Functional;

use Drupal\Tests\BrowserTestBase;
use Drupal\Core\Database\Database;
use Drupal\ban\BanIpManager;

/**
 * Tests IP address banning.
 *
 * @group ban
 */
class IpAddressBlockingTest extends BrowserTestBase {

  /**
   * Modules to install.
   *
   * @var array
   */
  protected static $modules = ['ban'];

  /**
   * {@inheritdoc}
   */
  protected $defaultTheme = 'stark';

  /**
   * Tests various user input to confirm correct validation and saving of data.
   */
  public function testIPAddressValidation(): void {
    // Create user.
    $admin_user = $this->drupalCreateUser(['ban IP addresses']);
    $this->drupalLogin($admin_user);
    $this->drupalGet('admin/config/people/ban');
    $connection = Database::getConnection();

    // Ban a valid IP address.
    $edit = [];
    $edit['ip'] = '1.2.3.3';
    $this->drupalGet('admin/config/people/ban');
    $this->submitForm($edit, 'Add');
    $ip = $connection->select('ban_ip', 'bi')->fields('bi', ['iid'])->condition('ip', $edit['ip'])->execute()->fetchField();
    $this->assertNotEmpty($ip, 'IP address found in database.');
    $this->assertSession()->pageTextContains('The IP address 1.2.3.3 has been banned.');

    // Try to block an IP address that's already blocked.
    $edit = [];
    $edit['ip'] = '1.2.3.3';
    $this->drupalGet('admin/config/people/ban');
    $this->submitForm($edit, 'Add');
    $this->assertSession()->pageTextContains('This IP address is already banned.');

    // Try to block a reserved IP address.
    $edit = [];
    $edit['ip'] = '255.255.255.255';
    $this->drupalGet('admin/config/people/ban');
    $this->submitForm($edit, 'Add');
    $this->assertSession()->pageTextContains('Enter a valid IP address.');

    // Try to block a reserved IP address.
    $edit = [];
    $edit['ip'] = 'test.example.com';
    $this->drupalGet('admin/config/people/ban');
    $this->submitForm($edit, 'Add');
    $this->assertSession()->pageTextContains('Enter a valid IP address.');

    // Submit an empty form.
    $edit = [];
    $edit['ip'] = '';
    $this->drupalGet('admin/config/people/ban');
    $this->submitForm($edit, 'Add');
    $this->assertSession()->pageTextContains('Enter a valid IP address.');

    // Pass an IP address as a URL parameter and submit it.
    $submit_ip = '1.2.3.4';
    $this->drupalGet('admin/config/people/ban/' . $submit_ip);
    $this->submitForm([], 'Add');
    $ip = $connection->select('ban_ip', 'bi')->fields('bi', ['iid'])->condition('ip', $submit_ip)->execute()->fetchField();
    $this->assertNotEmpty($ip, 'IP address found in database');
    $this->assertSession()->pageTextContains("The IP address $submit_ip has been banned.");

    // Submit your own IP address. This fails, although it works when testing
    // manually.
    // @todo On some systems this test fails due to a bug/inconsistency in cURL.
    // $edit = array();
    // $edit['ip'] = \Drupal::request()->getClientIP();
    // $this->drupalGet('admin/config/people/ban');
    // $this->submitForm($edit, 'Save');
    // $this->assertSession()->pageTextContains('You may not ban your own IP address.');

    // Test duplicate ip address are not present in the 'blocked_ips' table.
    // when they are entered programmatically.
    $banIp = new BanIpManager($connection);
    $ip = '1.0.0.0';
    $banIp->banIp($ip);
    $banIp->banIp($ip);
    $banIp->banIp($ip);
    $query = $connection->select('ban_ip', 'bip');
    $query->fields('bip', ['iid']);
    $query->condition('bip.ip', $ip);
    $ip_count = $query->execute()->fetchAll();
    $this->assertCount(1, $ip_count);
    $ip = '';
    $banIp->banIp($ip);
    $banIp->banIp($ip);
    $query = $connection->select('ban_ip', 'bip');
    $query->fields('bip', ['iid']);
    $query->condition('bip.ip', $ip);
    $ip_count = $query->execute()->fetchAll();
    $this->assertCount(1, $ip_count);
  }

}
+66 −0
Original line number Diff line number Diff line
<?php

declare(strict_types=1);

namespace Drupal\Tests\ban\Kernel;

use Drupal\ban\BanIpManagerInterface;
use Drupal\Core\Database\Connection;
use Drupal\KernelTests\KernelTestBase;

/**
 * @group ban
 */
class BanIpTest extends KernelTestBase {

  /**
   * {@inheritdoc}
   */
  protected static $modules = ['ban'];

  /**
   * {@inheritdoc}
   */
  protected function setUp(): void {
    parent::setUp();
    $this->installSchema('ban', ['ban_ip']);
  }

  /**
   * Test banning IPs.
   */
  public function testBanIp(): void {
    $banIp = $this->container->get(BanIpManagerInterface::class);

    // Test valid IP addresses.
    $ip = '1.2.3.3';
    $this->assertCount(0, $this->getIpBans($ip));
    $banIp->banIp($ip);
    $this->assertCount(1, $this->getIpBans($ip));

    // Test duplicate ip address are not present in the 'blocked_ips' table
    // when they are entered programmatically.
    $ip = '1.0.0.0';
    $banIp->banIp($ip);
    $banIp->banIp($ip);
    $banIp->banIp($ip);
    $this->assertCount(1, $this->getIpBans($ip));

    $ip = '';
    $banIp->banIp($ip);
    $banIp->banIp($ip);
    $this->assertCount(1, $this->getIpBans($ip));
  }

  /**
   * Gets the IP bans.
   */
  protected function getIpBans(string $ip): array {
    $connection = $this->container->get(Connection::class);
    $query = $connection->select('ban_ip', 'bip');
    $query->fields('bip', ['iid']);
    $query->condition('bip.ip', $ip);
    return $query->execute()->fetchAll();
  }

}
+144 −0
Original line number Diff line number Diff line
<?php

declare(strict_types=1);

namespace Drupal\Tests\ban\Unit;

use Drupal\ban\BanIpManagerInterface;
use Drupal\ban\Form\BanAdmin;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Messenger\MessengerInterface;
use Drupal\Tests\UnitTestCase;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RequestStack;

/**
 * Tests the BanAdmin form.
 *
 * @coversDefaultClass \Drupal\ban\Form\BanAdmin
 * @group ban
 */
class BanAdminTest extends UnitTestCase {

  /**
   * Tests various user input to confirm correct validation.
   *
   * @covers ::validateForm
   * @dataProvider providerIpValidation
   */
  public function testIpValidation(string $ip, bool $isBanned, ?string $error): void {
    $manager = $this->getIpManagerMock();
    $manager->expects($this->once())
      ->method('isBanned')
      ->with($ip)
      ->willReturn($isBanned);

    $formObject = new BanAdmin($manager);
    $formObject->setStringTranslation($this->getStringTranslationStub());
    $formObject->setRequestStack($this->getRequestStackMock());

    $formState = $this->createMock(FormStateInterface::class);
    $formState->expects($this->any())
      ->method('getValue')
      ->with('ip')
      ->willReturn($ip);

    if ($error === NULL) {
      $formState->expects($this->never())
        ->method('setErrorByName');
    }
    else {
      $formState->expects($this->once())
        ->method('setErrorByName')
        ->with('ip', $error);
    }

    $form = [];
    $formObject->validateForm($form, $formState);
  }

  /**
   * Test form submission.
   */
  public function testSubmit(): void {
    $ip = '1.2.3.4';

    $manager = $this->getIpManagerMock();
    $manager->expects($this->once())
      ->method('banIp')
      ->with($ip);

    $messenger = $this->createMock(MessengerInterface::class);
    $messenger->expects($this->once())->method('addStatus');

    $formObject = new BanAdmin($manager);
    $formObject->setStringTranslation($this->getStringTranslationStub());
    $formObject->setMessenger($messenger);

    $formState = $this->createMock(FormStateInterface::class);
    $formState->expects($this->any())
      ->method('getValue')
      ->with('ip')
      ->willReturn($ip);

    $form = [];
    $formObject->submitForm($form, $formState);
  }

  /**
   * Test passing an IP address as a route parameter.
   *
   * @covers ::buildForm
   */
  public function testRouteParameter(): void {
    $ip = '1.2.3.4';
    $formObject = new BanAdmin($this->getIpManagerMock());
    $formObject->setStringTranslation($this->getStringTranslationStub());
    $formState = $this->createMock(FormStateInterface::class);
    $form = $formObject->buildForm([], $formState, $ip);
    $this->assertSame($ip, $form['ip']['#default_value']);
  }

  /**
   * Data provider for testIpValidation().
   */
  public static function providerIpValidation(): array {
    return [
      'valid ip' => ['1.2.3.3', FALSE, NULL],
      'already blocked' => ['1.2.3.3', TRUE, 'This IP address is already banned.'],
      'reserved ip' => ['255.255.255.255', FALSE, 'Enter a valid IP address.'],
      'fqdn' => ['test.example.com', FALSE, 'Enter a valid IP address.'],
      'empty' => ['', FALSE, 'Enter a valid IP address.'],
      'client ip' => ['127.0.0.1', FALSE, 'You may not ban your own IP address.'],
    ];
  }

  /**
   * Get a request stack with a dummy IP.
   */
  protected function getRequestStackMock(): RequestStack {
    $request = $this->createMock(Request::class);
    $request->expects($this->any())
      ->method('getClientIp')
      ->willReturn('127.0.0.1');

    $requestStack = $this->createMock(RequestStack::class);
    $requestStack->expects($this->any())
      ->method('getCurrentRequest')
      ->willReturn($request);

    return $requestStack;
  }

  /**
   * Get the mocked IP manager service.
   */
  protected function getIpManagerMock(): BanIpManagerInterface {
    $manager = $this->createMock(BanIpManagerInterface::class);
    $manager->expects($this->any())
      ->method('findAll')
      ->willReturn([]);
    return $manager;
  }

}