invoicemgmt-1.0.0/tests/src/Unit/InvoiceNumberGeneratorTest.php

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

declare(strict_types=1);

namespace Drupal\Tests\invoicemgmt\Unit;

use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Config\ImmutableConfig;
use Drupal\Core\Database\Connection;
use Drupal\Core\Database\Query\Select;
use Drupal\Core\Database\StatementInterface;
use Drupal\invoicemgmt\InvoiceNumberGenerator;
use Drupal\Tests\UnitTestCase;
use PHPUnit\Framework\MockObject\MockObject;

/**
 * Unit tests for InvoiceNumberGenerator service.
 *
 * @coversDefaultClass \Drupal\invoicemgmt\InvoiceNumberGenerator
 * @group invoicemgmt
 */
class InvoiceNumberGeneratorTest extends UnitTestCase {

  /**
   * The config factory mock.
   */
  protected ConfigFactoryInterface|MockObject $configFactory;

  /**
   * The database connection mock.
   */
  protected Connection|MockObject $database;

  /**
   * The config mock.
   */
  protected ImmutableConfig|MockObject $config;

  /**
   * The invoice number generator service under test.
   */
  protected InvoiceNumberGenerator $invoiceNumberGenerator;

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

    $this->configFactory = $this->createMock(ConfigFactoryInterface::class);
    $this->database = $this->createMock(Connection::class);
    $this->config = $this->createMock(ImmutableConfig::class);

    $this->configFactory->method('get')
      ->with('invoicemgmt.settings')
      ->willReturn($this->config);

    $this->invoiceNumberGenerator = new InvoiceNumberGenerator(
      $this->configFactory,
      $this->database
    );
  }

  /**
   * Tests invoice number generation with default prefix.
   *
   * @covers ::generateInvoiceNumber
   * @covers ::getNextUniqueCode
   */
  public function testGenerateInvoiceNumberWithDefaultPrefix(): void {
    // Mock config to return null (default prefix).
    $this->config->method('get')
      ->with('invoice_number_prefix')
      ->willReturn(null);

    // Mock database query to return no existing invoices.
    $select = $this->createMock(Select::class);
    $statement = $this->createMock(StatementInterface::class);

    $this->database->method('select')
      ->with('node__field_invoice_number', 'fin')
      ->willReturn($select);

    $select->method('fields')
      ->with('fin', ['field_invoice_number_value'])
      ->willReturn($select);

    $select->method('condition')
      ->with('field_invoice_number_value', $this->stringContains('INV-'), 'LIKE')
      ->willReturn($select);

    $select->method('orderBy')
      ->with('field_invoice_number_value', 'DESC')
      ->willReturn($select);

    $select->method('range')
      ->with(0, 1)
      ->willReturn($select);

    $select->method('execute')
      ->willReturn($statement);

    $statement->method('fetchField')
      ->willReturn(FALSE);

    $result = $this->invoiceNumberGenerator->generateInvoiceNumber();

    // Expected format: INV-YYYYMM-001
    $expected_pattern = '/^INV-\d{6}-001$/';
    $this->assertMatchesRegularExpression($expected_pattern, $result);
  }

  /**
   * Tests invoice number generation with custom prefix.
   *
   * @covers ::generateInvoiceNumber
   * @covers ::getNextUniqueCode
   */
  public function testGenerateInvoiceNumberWithCustomPrefix(): void {
    // Mock config to return custom prefix.
    $this->config->method('get')
      ->with('invoice_number_prefix')
      ->willReturn('CUSTOM');

    // Mock database query to return no existing invoices.
    $select = $this->createMock(Select::class);
    $statement = $this->createMock(StatementInterface::class);

    $this->database->method('select')
      ->with('node__field_invoice_number', 'fin')
      ->willReturn($select);

    $select->method('fields')
      ->with('fin', ['field_invoice_number_value'])
      ->willReturn($select);

    $select->method('condition')
      ->with('field_invoice_number_value', $this->stringContains('CUSTOM-'), 'LIKE')
      ->willReturn($select);

    $select->method('orderBy')
      ->with('field_invoice_number_value', 'DESC')
      ->willReturn($select);

    $select->method('range')
      ->with(0, 1)
      ->willReturn($select);

    $select->method('execute')
      ->willReturn($statement);

    $statement->method('fetchField')
      ->willReturn(FALSE);

    $result = $this->invoiceNumberGenerator->generateInvoiceNumber();

    // Expected format: CUSTOM-YYYYMM-001
    $expected_pattern = '/^CUSTOM-\d{6}-001$/';
    $this->assertMatchesRegularExpression($expected_pattern, $result);
  }

  /**
   * Tests invoice number generation with existing invoices.
   *
   * @covers ::generateInvoiceNumber
   * @covers ::getNextUniqueCode
   */
  public function testGenerateInvoiceNumberWithExistingInvoices(): void {
    // Mock config to return default prefix.
    $this->config->method('get')
      ->with('invoice_number_prefix')
      ->willReturn('INV');

    // Mock database query to return existing invoice.
    $select = $this->createMock(Select::class);
    $statement = $this->createMock(StatementInterface::class);

    $this->database->method('select')
      ->with('node__field_invoice_number', 'fin')
      ->willReturn($select);

    $select->method('fields')
      ->with('fin', ['field_invoice_number_value'])
      ->willReturn($select);

    $select->method('condition')
      ->with('field_invoice_number_value', $this->stringContains('INV-'), 'LIKE')
      ->willReturn($select);

    $select->method('orderBy')
      ->with('field_invoice_number_value', 'DESC')
      ->willReturn($select);

    $select->method('range')
      ->with(0, 1)
      ->willReturn($select);

    $select->method('execute')
      ->willReturn($statement);

    $current_month = date('Ym');
    $existing_invoice = "INV-{$current_month}-005";
    $statement->method('fetchField')
      ->willReturn($existing_invoice);

    $result = $this->invoiceNumberGenerator->generateInvoiceNumber();

    // Expected format: INV-YYYYMM-006 (incremented from 005)
    $expected = "INV-{$current_month}-006";
    $this->assertEquals($expected, $result);
  }

  /**
   * Tests getNextUniqueCode with malformed existing invoice number.
   *
   * @covers ::getNextUniqueCode
   */
  public function testGetNextUniqueCodeWithMalformedInvoiceNumber(): void {
    // Mock config to return default prefix.
    $this->config->method('get')
      ->with('invoice_number_prefix')
      ->willReturn('INV');

    // Mock database query to return malformed invoice.
    $select = $this->createMock(Select::class);
    $statement = $this->createMock(StatementInterface::class);

    $this->database->method('select')
      ->with('node__field_invoice_number', 'fin')
      ->willReturn($select);

    $select->method('fields')
      ->with('fin', ['field_invoice_number_value'])
      ->willReturn($select);

    $select->method('condition')
      ->with('field_invoice_number_value', $this->stringContains('INV-'), 'LIKE')
      ->willReturn($select);

    $select->method('orderBy')
      ->with('field_invoice_number_value', 'DESC')
      ->willReturn($select);

    $select->method('range')
      ->with(0, 1)
      ->willReturn($select);

    $select->method('execute')
      ->willReturn($statement);

    // Return malformed invoice number (only 2 parts instead of 3).
    $statement->method('fetchField')
      ->willReturn('INV-202501');

    $result = $this->invoiceNumberGenerator->generateInvoiceNumber();

    // Should default to 001 when malformed number is found.
    $current_month = date('Ym');
    $expected = "INV-{$current_month}-001";
    $this->assertEquals($expected, $result);
  }

  /**
   * Tests getNextUniqueCode with non-numeric suffix.
   *
   * @covers ::getNextUniqueCode
   */
  public function testGetNextUniqueCodeWithNonNumericSuffix(): void {
    // Mock config to return default prefix.
    $this->config->method('get')
      ->with('invoice_number_prefix')
      ->willReturn('INV');

    // Mock database query to return invoice with non-numeric suffix.
    $select = $this->createMock(Select::class);
    $statement = $this->createMock(StatementInterface::class);

    $this->database->method('select')
      ->with('node__field_invoice_number', 'fin')
      ->willReturn($select);

    $select->method('fields')
      ->with('fin', ['field_invoice_number_value'])
      ->willReturn($select);

    $select->method('condition')
      ->with('field_invoice_number_value', $this->stringContains('INV-'), 'LIKE')
      ->willReturn($select);

    $select->method('orderBy')
      ->with('field_invoice_number_value', 'DESC')
      ->willReturn($select);

    $select->method('range')
      ->with(0, 1)
      ->willReturn($select);

    $select->method('execute')
      ->willReturn($statement);

    $current_month = date('Ym');
    // Return invoice with non-numeric suffix.
    $statement->method('fetchField')
      ->willReturn("INV-{$current_month}-ABC");

    $result = $this->invoiceNumberGenerator->generateInvoiceNumber();

    // Should default to 001 when non-numeric suffix is found.
    $expected = "INV-{$current_month}-001";
    $this->assertEquals($expected, $result);
  }

  /**
   * Tests constructor dependency injection.
   *
   * @covers ::__construct
   */
  public function testConstructor(): void {
    $generator = new InvoiceNumberGenerator($this->configFactory, $this->database);
    $this->assertInstanceOf(InvoiceNumberGenerator::class, $generator);
  }

  /**
   * Tests invoice number format consistency.
   *
   * @covers ::generateInvoiceNumber
   */
  public function testInvoiceNumberFormat(): void {
    // Mock config to return test prefix.
    $this->config->method('get')
      ->with('invoice_number_prefix')
      ->willReturn('TEST');

    // Mock database query to return no existing invoices.
    $select = $this->createMock(Select::class);
    $statement = $this->createMock(StatementInterface::class);

    $this->database->method('select')->willReturn($select);
    $select->method('fields')->willReturn($select);
    $select->method('condition')->willReturn($select);
    $select->method('orderBy')->willReturn($select);
    $select->method('range')->willReturn($select);
    $select->method('execute')->willReturn($statement);
    $statement->method('fetchField')->willReturn(FALSE);

    $result = $this->invoiceNumberGenerator->generateInvoiceNumber();

    // Verify format: PREFIX-YYYYMM-NNN
    $parts = explode('-', $result);
    $this->assertCount(3, $parts);
    $this->assertEquals('TEST', $parts[0]);
    $this->assertMatchesRegularExpression('/^\d{6}$/', $parts[1]);
    $this->assertEquals('001', $parts[2]);
  }

}

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

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