Incentive (Montenegro 2026): - Live-Tracker: whereDate() für Zeitraum-Vergleiche – Bestellungen am letzten Tag des Qualifikationszeitraums gingen verloren (DATE-Spalte vs. Datetime) - Repair/Rebuild: Neupartner-Definition auf Starterpaket-Bestelldatum im Zeitraum umgestellt (statt users.created_at) – Bestandskunden-Upgrades zählen jetzt wie im Live-Tracking (Fall Dino Portaro → Steffi Karrer) - Neu: removeStaleSponsorTrackings() – Partner-Trackings wandern nach Sponsorwechsel zum aktuellen Sponsor (Fall Anna Schall lag beim Firmenkonto) - incentive:calculate: Sponsorwechsel-Bereinigung eingebunden + Statistik Team-Struktur (/user/team/structure): - Downline sortiert nach active_date (Partner-Aktivierung) statt users.id: zentral in TreeHtmlRenderer::sortByActivation() (wirkt sofort, auch für gespeicherte Monats-Snapshots) + orderBy in beiden Downline-Queries Doku: dev/2026-08-12-intensiv-tickets/README.md (4 Kundentickets analysiert, Lücke durch verspätete Zeitraum-Verlängerung hatte sich per Nacht-Cron geheilt) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
221 lines
7.7 KiB
PHP
221 lines
7.7 KiB
PHP
<?php
|
|
|
|
namespace Tests\Feature\BusinessPlan;
|
|
|
|
use App\Models\UserAccount;
|
|
use App\Models\UserLevel;
|
|
use App\Services\BusinessPlan\TreeCalcBot;
|
|
use App\Services\BusinessPlan\TreeCalcBotOptimized;
|
|
use App\User;
|
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
use Tests\TestCase;
|
|
|
|
class TreeCalcBotIntegrationTest extends TestCase
|
|
{
|
|
use RefreshDatabase;
|
|
|
|
/** @test */
|
|
public function optimized_version_can_replace_original_version()
|
|
{
|
|
// Create test data
|
|
$user = User::factory()->create([
|
|
'm_sponsor' => null,
|
|
'm_level' => 1,
|
|
'payment_account' => '2024-01-15',
|
|
'active_date' => '2024-01-01',
|
|
]);
|
|
|
|
UserAccount::factory()->create([
|
|
'user_id' => $user->id,
|
|
'first_name' => 'Test',
|
|
'last_name' => 'User',
|
|
'm_account' => 'TEST123',
|
|
]);
|
|
|
|
UserLevel::factory()->create([
|
|
'id' => 1,
|
|
'name' => 'Bronze',
|
|
'pos' => 1,
|
|
]);
|
|
|
|
// Test original version
|
|
$originalBot = new TreeCalcBot(1, 2024, 'admin');
|
|
$originalBot->initStructureAdmin(false);
|
|
$originalItems = $originalBot->getItems();
|
|
|
|
// Test optimized version
|
|
$optimizedBot = new TreeCalcBotOptimized(1, 2024, 'admin');
|
|
$optimizedBot->initStructureAdmin(false);
|
|
$optimizedItems = $optimizedBot->getItems();
|
|
|
|
// Both should return same number of items
|
|
$this->assertEquals(count($originalItems), count($optimizedItems));
|
|
|
|
// Both should have same basic functionality
|
|
$this->assertEquals($originalBot->isParentless(), $optimizedBot->isParentless());
|
|
|
|
// HTML output should be generated (length > 0)
|
|
$this->assertGreaterThan(0, strlen($originalBot->makeHtmlTree()));
|
|
$this->assertGreaterThan(0, strlen($optimizedBot->makeHtmlTree()));
|
|
}
|
|
|
|
/** @test */
|
|
public function optimized_version_handles_memory_efficiently()
|
|
{
|
|
// Create multiple users to test memory efficiency
|
|
$users = [];
|
|
for ($i = 0; $i < 5; $i++) {
|
|
$user = User::factory()->create([
|
|
'm_sponsor' => null,
|
|
'm_level' => 1,
|
|
'payment_account' => '2024-01-15',
|
|
'active_date' => '2024-01-01',
|
|
]);
|
|
|
|
UserAccount::factory()->create([
|
|
'user_id' => $user->id,
|
|
'first_name' => 'User',
|
|
'last_name' => $i,
|
|
'm_account' => 'TEST'.$i,
|
|
]);
|
|
|
|
$users[] = $user;
|
|
}
|
|
|
|
$startMemory = memory_get_usage();
|
|
|
|
$optimizedBot = new TreeCalcBotOptimized(1, 2024, 'admin');
|
|
$optimizedBot->initStructureAdmin(false);
|
|
|
|
$endMemory = memory_get_usage();
|
|
$memoryUsed = $endMemory - $startMemory;
|
|
|
|
// Memory usage should be reasonable (less than 5MB for 5 users)
|
|
$this->assertLessThan(5 * 1024 * 1024, $memoryUsed);
|
|
|
|
// Should still function correctly
|
|
$this->assertEquals(5, count($optimizedBot->getItems()));
|
|
}
|
|
|
|
/** @test */
|
|
public function optimized_version_maintains_backward_compatibility()
|
|
{
|
|
$optimizedBot = new TreeCalcBotOptimized(1, 2024, 'admin');
|
|
|
|
// Test all public methods exist and are callable
|
|
$this->assertTrue(method_exists($optimizedBot, 'initStructureAdmin'));
|
|
$this->assertTrue(method_exists($optimizedBot, 'initStructureUser'));
|
|
$this->assertTrue(method_exists($optimizedBot, 'getItems'));
|
|
$this->assertTrue(method_exists($optimizedBot, 'makeHtmlTree'));
|
|
$this->assertTrue(method_exists($optimizedBot, 'makeParentlessHtml'));
|
|
$this->assertTrue(method_exists($optimizedBot, 'makeSponsorHtml'));
|
|
$this->assertTrue(method_exists($optimizedBot, 'isParentless'));
|
|
$this->assertTrue(method_exists($optimizedBot, 'getGrowthBonus'));
|
|
$this->assertTrue(method_exists($optimizedBot, 'getKeybyLine'));
|
|
|
|
// Test static methods
|
|
$this->assertTrue(method_exists($optimizedBot, 'isFromStored'));
|
|
$this->assertTrue(method_exists($optimizedBot, 'addUserID'));
|
|
|
|
// Test magic methods for property access
|
|
$date = $optimizedBot->__get('date');
|
|
$this->assertNotNull($date);
|
|
$this->assertEquals(1, $date->month);
|
|
$this->assertEquals(2024, $date->year);
|
|
}
|
|
|
|
/** @test */
|
|
public function optimized_version_produces_valid_html_output()
|
|
{
|
|
// Create test user
|
|
$user = User::factory()->create([
|
|
'm_sponsor' => null,
|
|
'm_level' => 1,
|
|
'payment_account' => '2024-01-15',
|
|
'active_date' => '2024-01-01',
|
|
]);
|
|
|
|
UserAccount::factory()->create([
|
|
'user_id' => $user->id,
|
|
'first_name' => 'Test',
|
|
'last_name' => 'User',
|
|
]);
|
|
|
|
$optimizedBot = new TreeCalcBotOptimized(1, 2024, 'admin');
|
|
$optimizedBot->initStructureAdmin(false);
|
|
|
|
// Test HTML outputs
|
|
$treeHtml = $optimizedBot->makeHtmlTree();
|
|
$parentlessHtml = $optimizedBot->makeParentlessHtml();
|
|
$sponsorHtml = $optimizedBot->makeSponsorHtml();
|
|
|
|
// All should produce valid HTML strings
|
|
$this->assertIsString($treeHtml);
|
|
$this->assertIsString($parentlessHtml);
|
|
$this->assertIsString($sponsorHtml);
|
|
|
|
// Tree HTML should contain user data when users exist
|
|
if (count($optimizedBot->getItems()) > 0) {
|
|
$this->assertStringContainsString('Test User', $treeHtml);
|
|
}
|
|
|
|
// HTML should be properly formed (basic check)
|
|
$this->assertStringNotContainsString('><', $treeHtml.$parentlessHtml.$sponsorHtml);
|
|
}
|
|
|
|
/** @test */
|
|
public function structure_children_are_ordered_by_active_date_not_by_user_id()
|
|
{
|
|
$makeUser = function (?int $sponsorId, string $activeDate): User {
|
|
return User::forceCreate([
|
|
'email' => 'tree-'.uniqid('', true).'@example.com',
|
|
'password' => bcrypt('secret'),
|
|
'lang' => 'de',
|
|
'm_sponsor' => $sponsorId,
|
|
'm_level' => 1,
|
|
'admin' => 0,
|
|
'payment_account' => $activeDate,
|
|
'active_date' => $activeDate,
|
|
]);
|
|
};
|
|
|
|
$sponsor = $makeUser(null, '2023-01-01');
|
|
|
|
// Kinder in ID-Reihenfolge angelegt, aber active_date bewusst durcheinander:
|
|
// Bestandskunde (frühe Registrierung/ID) wird erst spät Partner
|
|
$lateUpgrader = $makeUser($sponsor->id, '2024-01-10');
|
|
$earlyPartner = $makeUser($sponsor->id, '2023-05-01');
|
|
$midPartner = $makeUser($sponsor->id, '2024-01-05');
|
|
|
|
$bot = new TreeCalcBotOptimized(1, 2024, 'member');
|
|
$bot->initStructureUser($sponsor->id);
|
|
|
|
$items = $bot->getItems();
|
|
$this->assertCount(1, $items);
|
|
|
|
$childIds = array_map(fn ($item) => $item->user_id, $items[0]->businessUserItems);
|
|
|
|
$this->assertEquals(
|
|
[$earlyPartner->id, $midPartner->id, $lateUpgrader->id],
|
|
$childIds,
|
|
'Downline muss nach active_date sortiert sein, nicht nach users.id'
|
|
);
|
|
}
|
|
|
|
/** @test */
|
|
public function optimized_version_error_handling_works()
|
|
{
|
|
$optimizedBot = new TreeCalcBotOptimized(1, 2024, 'admin');
|
|
|
|
// Should handle non-existent user gracefully
|
|
$optimizedBot->initStructureUser(999999);
|
|
|
|
// Should return empty results but not crash
|
|
$this->assertEquals([], $optimizedBot->getItems());
|
|
$this->assertFalse($optimizedBot->isParentless());
|
|
|
|
// Should handle method calls on empty state
|
|
$this->assertEquals([], $optimizedBot->getGrowthBonus());
|
|
$this->assertEquals(0, $optimizedBot->getKeybyLine(1, 'points'));
|
|
}
|
|
}
|