call('TestDatabaseBootstrapSeeder'); } protected function setUp(): void { parent::setUp(); } protected function createItemQuantityRow(int $itemId, float $quantity): void { \Config\Database::connect()->table('item_quantities')->insert([ 'item_id' => $itemId, 'location_id' => self::LOCATION_ID, 'quantity' => $quantity, ]); } public function testDecrementQuantitySucceeds(): void { $itemId = $this->createTestItem(); $this->createItemQuantityRow($itemId, 10); $itemQuantityModel = model(Item_quantity::class); $result = $itemQuantityModel->changeQuantity($itemId, self::LOCATION_ID, -3); $this->assertTrue($result); $this->assertEqualsWithDelta(7.0, $this->getItemQuantity($itemId, self::LOCATION_ID), 0.001); } public function testDecrementQuantityAllowsGoingNegative(): void { $itemId = $this->createTestItem(); $this->createItemQuantityRow($itemId, 5); $itemQuantityModel = model(Item_quantity::class); $result = $itemQuantityModel->changeQuantity($itemId, self::LOCATION_ID, -8); $this->assertTrue($result); $this->assertEqualsWithDelta(-3.0, $this->getItemQuantity($itemId, self::LOCATION_ID), 0.001); } public function testDecrementQuantityConcurrentDecrementsBothApply(): void { $itemId = $this->createTestItem(); $this->createItemQuantityRow($itemId, 10); [$result1, $result2] = $this->raceTwoProcesses( 'itemQuantity', [$itemId, self::LOCATION_ID, -3.0], [$itemId, self::LOCATION_ID, -3.0] ); $this->assertTrue($result1); $this->assertTrue($result2); $this->assertEqualsWithDelta(4.0, $this->getItemQuantity($itemId, self::LOCATION_ID), 0.001); } }