methodBody(ProjectService::class, 'bulkPauseResume'); $this->assertStringContainsString('paused_at', $body, 'bulkPauseResume должен явно обновлять paused_at вместе с is_active'); $this->assertStringContainsString('is_active', $body); } public function test_project_controller_toggle_active_writes_paused_at(): void { $body = $this->methodBody(ProjectController::class, 'toggleActive'); $this->assertStringContainsString('paused_at', $body, 'toggleActive должен явно обновлять paused_at вместе с is_active'); $this->assertStringContainsString('is_active', $body); } public function test_bulk_delete_distinguishes_supplier_snapshot_lock_from_has_deals(): void { $body = $this->methodBody(ProjectService::class, 'bulkDelete'); $this->assertStringContainsString('supplier_snapshot_locked', $body, 'bulkDelete должен помечать пропущенные проекты reason="supplier_snapshot_locked" при guard-блоке'); $this->assertStringContainsString('has_deals', $body); } private function methodBody(string $class, string $method): string { $rm = new ReflectionMethod($class, $method); $lines = file($rm->getFileName()); $body = array_slice($lines, $rm->getStartLine() - 1, $rm->getEndLine() - $rm->getStartLine() + 1); return implode('', $body); } }