mockRepository = Mockery::mock(BusinessUserRepository::class); $this->mockRenderer = Mockery::mock(TreeHtmlRenderer::class); $this->mockLogger = Mockery::mock(\Psr\Log\LoggerInterface::class); $this->treeCalcBot = new TreeCalcBotOptimized( 1, // month 2024, // year 'admin', $this->mockRepository, $this->mockRenderer, $this->mockLogger ); } protected function tearDown(): void { Mockery::close(); parent::tearDown(); } /** @test */ public function constructor_validates_month_boundaries() { $this->expectException(\InvalidArgumentException::class); $this->expectExceptionMessage('Invalid month: 13'); new TreeCalcBotOptimized(13, 2024); } /** @test */ public function constructor_validates_year_boundaries() { $this->expectException(\InvalidArgumentException::class); $this->expectExceptionMessage('Invalid year: 2019'); new TreeCalcBotOptimized(1, 2019); } /** @test */ public function constructor_initializes_date_object_correctly() { $treeCalcBot = new TreeCalcBotOptimized(3, 2024); $date = $treeCalcBot->__get('date'); $this->assertEquals(3, $date->month); $this->assertEquals(2024, $date->year); $this->assertStringStartsWith('2024-03-01', $date->start_date); $this->assertStringStartsWith('2024-03-31', $date->end_date); } /** @test */ public function isFromStored_returns_completed_structure() { // Create completed structure $structure = UserBusinessStructure::create([ 'month' => 1, 'year' => 2024, 'structure' => [], 'completed' => true ]); $result = TreeCalcBotOptimized::isFromStored(1, 2024); $this->assertNotNull($result); $this->assertEquals($structure->id, $result->id); } /** @test */ public function isFromStored_returns_null_for_incomplete_structure() { // Create incomplete structure UserBusinessStructure::create([ 'month' => 1, 'year' => 2024, 'structure' => [], 'completed' => false ]); $result = TreeCalcBotOptimized::isFromStored(1, 2024); $this->assertNull($result); } /** @test */ public function initStructureAdmin_uses_stored_structure_when_available() { $mockStructure = new UserBusinessStructure(); $mockStructure->structure = []; $mockStructure->parentless = []; $this->mockRepository ->shouldReceive('getStoredStructure') ->once() ->andReturn($mockStructure); $this->mockLogger ->shouldReceive('info') ->once() ->with('Loading stored business structure for 1/2024'); $this->treeCalcBot->initStructureAdmin(true); } /** @test */ public function initStructureAdmin_builds_fresh_when_no_stored_structure() { $this->mockRepository ->shouldReceive('getStoredStructure') ->once() ->andReturn(null); $this->mockRepository ->shouldReceive('getRootUsers') ->once() ->andReturn(collect([])); $this->mockLogger ->shouldReceive('info') ->once() ->with('Building fresh business structure for 1/2024'); $this->mockLogger ->shouldReceive('info') ->once() ->with('Loaded 0 root users with optimized relations. Memory used: 0 B'); $this->treeCalcBot->initStructureAdmin(true); } /** @test */ public function initStructureUser_handles_non_existent_user() { $this->mockRepository ->shouldReceive('getUserWithRelations') ->with(999) ->once() ->andReturn(null); $this->mockLogger ->shouldReceive('info') ->once() ->with('Initializing structure for user: 999'); $this->mockLogger ->shouldReceive('warning') ->once() ->with('User not found: 999'); $this->treeCalcBot->initStructureUser(999); } /** @test */ public function getGrowthBonus_returns_empty_array_when_no_business_user() { $result = $this->treeCalcBot->getGrowthBonus(); $this->assertEquals([], $result); } /** @test */ public function getKeybyLine_returns_zero_when_no_business_user() { $result = $this->treeCalcBot->getKeybyLine(1, 'points'); $this->assertEquals(0, $result); } /** @test */ public function makeHtmlTree_delegates_to_renderer() { $expectedHtml = '