Add PHPUnit test suite, PSR-4 refactor, lint+test CI jobs (v1.3.1)
Some checks failed
Create Release Package / PHP Lint (push) Successful in 47s
Create Release Package / test (push) Failing after 53s
Create Release Package / build-release (push) Has been skipped

- 57 unit tests covering ProductType, StockManager, CartHandler, Plugin,
  Admin/ProductData, Admin/Settings using Brain Monkey + Mockery
- WooCommerce class stubs for testing without WP installation
- PHP lint and test jobs in release workflow (test gate blocks release)
- PSR-4 namespace change: WC_Composable_Product -> Magdev\WcComposableProduct
- PascalCase filenames for all classes under includes/

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-01 13:08:22 +01:00
parent ea2261d8d7
commit a7d6a57f01
24 changed files with 3415 additions and 12 deletions

View File

@@ -0,0 +1,226 @@
<?php
/**
* StockManager Tests
*
* @package Magdev\WcComposableProduct\Tests
*/
namespace Magdev\WcComposableProduct\Tests\Unit;
use Magdev\WcComposableProduct\Tests\TestCase;
use Magdev\WcComposableProduct\StockManager;
use Brain\Monkey\Functions;
class StockManagerTest extends TestCase
{
private StockManager $manager;
protected function setUp(): void
{
parent::setUp();
$this->manager = new StockManager();
}
// --- validate_stock_availability ---
public function testValidateStock_ReturnsTrueWhenAllInStock(): void
{
$mock = $this->createProductMock([
'managing_stock' => false,
'is_in_stock' => true,
]);
Functions\expect('wc_get_product')->with(1)->andReturn($mock);
Functions\expect('wc_get_product')->with(2)->andReturn($mock);
$result = $this->manager->validate_stock_availability([1, 2]);
$this->assertTrue($result);
}
public function testValidateStock_ReturnsTrueWhenNotManagingStock(): void
{
$mock = $this->createProductMock(['managing_stock' => false]);
Functions\expect('wc_get_product')->with(1)->andReturn($mock);
$result = $this->manager->validate_stock_availability([1]);
$this->assertTrue($result);
}
public function testValidateStock_ReturnsErrorForOutOfStock(): void
{
$mock = $this->createProductMock([
'managing_stock' => true,
'is_in_stock' => false,
'get_name' => 'Widget',
]);
Functions\expect('wc_get_product')->with(1)->andReturn($mock);
$result = $this->manager->validate_stock_availability([1]);
$this->assertIsString($result);
$this->assertStringContainsString('Widget', $result);
}
public function testValidateStock_ReturnsErrorForInsufficientQuantity(): void
{
$mock = $this->createProductMock([
'managing_stock' => true,
'is_in_stock' => true,
'get_stock_quantity' => 1,
'get_name' => 'Widget',
'backorders_allowed' => false,
]);
Functions\expect('wc_get_product')->with(1)->andReturn($mock);
$result = $this->manager->validate_stock_availability([1], 5);
$this->assertIsString($result);
$this->assertStringContainsString('Widget', $result);
}
public function testValidateStock_PassesWhenBackordersAllowed(): void
{
// When stock_quantity is null the insufficient-stock check is skipped,
// and the backorders_allowed() branch is reached.
$mock = $this->createProductMock([
'managing_stock' => true,
'is_in_stock' => true,
'get_stock_quantity' => null,
'backorders_allowed' => true,
]);
Functions\expect('wc_get_product')->with(1)->andReturn($mock);
$result = $this->manager->validate_stock_availability([1], 5);
$this->assertTrue($result);
}
public function testValidateStock_SkipsNullProducts(): void
{
Functions\expect('wc_get_product')->with(999)->andReturn(false);
$result = $this->manager->validate_stock_availability([999]);
$this->assertTrue($result);
}
// --- get_product_stock_info ---
public function testGetProductStockInfo_ReturnsCorrectStructure(): void
{
$mock = $this->createProductMock([
'is_in_stock' => true,
'get_stock_quantity' => 10,
'backorders_allowed' => false,
'get_stock_status' => 'instock',
'managing_stock' => true,
]);
Functions\expect('wc_get_product')->with(1)->andReturn($mock);
$info = $this->manager->get_product_stock_info(1);
$this->assertTrue($info['in_stock']);
$this->assertSame(10, $info['stock_quantity']);
$this->assertFalse($info['backorders_allowed']);
$this->assertSame('instock', $info['stock_status']);
$this->assertTrue($info['managing_stock']);
$this->assertTrue($info['has_enough_stock']);
}
public function testGetProductStockInfo_ReturnsFallbackForInvalidProduct(): void
{
Functions\expect('wc_get_product')->with(999)->andReturn(false);
$info = $this->manager->get_product_stock_info(999);
$this->assertFalse($info['in_stock']);
$this->assertSame(0, $info['stock_quantity']);
$this->assertFalse($info['backorders_allowed']);
$this->assertSame('outofstock', $info['stock_status']);
}
public function testGetProductStockInfo_HasEnoughStockTrueWhenNotManaging(): void
{
$mock = $this->createProductMock([
'managing_stock' => false,
'get_stock_quantity' => null,
]);
Functions\expect('wc_get_product')->with(1)->andReturn($mock);
$info = $this->manager->get_product_stock_info(1, 100);
$this->assertTrue($info['has_enough_stock']);
}
public function testGetProductStockInfo_HasEnoughStockFalseWhenInsufficient(): void
{
$mock = $this->createProductMock([
'managing_stock' => true,
'get_stock_quantity' => 2,
]);
Functions\expect('wc_get_product')->with(1)->andReturn($mock);
$info = $this->manager->get_product_stock_info(1, 5);
$this->assertFalse($info['has_enough_stock']);
}
// --- prevent_composable_stock_reduction ---
public function testPreventStockReduction_ReturnsFalseForComposableItem(): void
{
$productMock = $this->createProductMock(['get_type' => 'composable']);
$itemMock = \Mockery::mock('WC_Order_Item_Product');
$itemMock->shouldReceive('get_product')->andReturn($productMock);
$orderMock = \Mockery::mock('WC_Order');
$orderMock->shouldReceive('get_items')->andReturn([$itemMock]);
$result = $this->manager->prevent_composable_stock_reduction(true, $orderMock);
$this->assertFalse($result);
}
public function testPreventStockReduction_PassesThroughForNonComposable(): void
{
$productMock = $this->createProductMock(['get_type' => 'simple']);
$itemMock = \Mockery::mock('WC_Order_Item_Product');
$itemMock->shouldReceive('get_product')->andReturn($productMock);
$orderMock = \Mockery::mock('WC_Order');
$orderMock->shouldReceive('get_items')->andReturn([$itemMock]);
$result = $this->manager->prevent_composable_stock_reduction(true, $orderMock);
$this->assertTrue($result);
}
// --- store_selected_products_in_order ---
public function testStoreSelectedProducts_AddsMetaWhenPresent(): void
{
$itemMock = \Mockery::mock('WC_Order_Item_Product');
$itemMock->shouldReceive('add_meta_data')
->once()
->with('_composable_products', [1, 2], true);
$this->manager->store_selected_products_in_order(
$itemMock,
'cart_key',
['composable_products' => [1, 2]]
);
}
public function testStoreSelectedProducts_DoesNothingWithoutData(): void
{
$itemMock = \Mockery::mock('WC_Order_Item_Product');
$itemMock->shouldNotReceive('add_meta_data');
$this->manager->store_selected_products_in_order(
$itemMock,
'cart_key',
[]
);
}
}