repository = new AboRepository; }); afterEach(function () { Carbon::setTestNow(); }); /** * Szenario: Heute = 15. März, next_date = 5. April (März-Ausführung bereits erfolgt). * Änderung auf Tag 20 → neues Datum soll der 20. April sein, NICHT der 20. März. */ it('erlaubt Datumsänderung auf den 20. wenn next_date im April liegt und heute der 15. März ist', function () { Carbon::setTestNow('2026-03-15'); $abo = new UserAbo; $abo->next_date = '2026-04-05'; $this->repository->setModel($abo); $newNextDate = invadePrivateMethod($this->repository, 'calculateNewNextDate', [20]); expect($newNextDate->format('Y-m-d'))->toBe('2026-04-20'); Carbon::setTestNow(); }); /** * Szenario: Heute = 15. März, next_date = 25. März (im selben Monat, noch nicht ausgeführt). * Änderung auf Tag 20 → 20. März ist nur 5 Tage entfernt → Referenz bleibt now(). * setNextDate gibt 20. März zurück (5 Tage → zu nah). */ it('berechnet neues Datum im selben Monat wenn next_date noch im März liegt', function () { Carbon::setTestNow('2026-03-15'); $abo = new UserAbo; $abo->next_date = '2026-03-25'; $this->repository->setModel($abo); $newNextDate = invadePrivateMethod($this->repository, 'calculateNewNextDate', [20]); expect($newNextDate->format('Y-m-d'))->toBe('2026-03-20'); Carbon::setTestNow(); }); /** * Szenario: Heute = 15. März, next_date = 5. April. * Änderung auf Tag 5 → neues Datum soll 5. April sein (identisch mit aktuell). */ it('setzt Datum auf den 5. April wenn Tag 5 gewählt und next_date im April liegt', function () { Carbon::setTestNow('2026-03-15'); $abo = new UserAbo; $abo->next_date = '2026-04-05'; $this->repository->setModel($abo); $newNextDate = invadePrivateMethod($this->repository, 'calculateNewNextDate', [5]); expect($newNextDate->format('Y-m-d'))->toBe('2026-04-05'); Carbon::setTestNow(); }); /** * Szenario: Heute = 15. März, kein next_date gesetzt → Referenz ist now(). */ it('verwendet heute als Referenz wenn kein next_date gesetzt ist', function () { Carbon::setTestNow('2026-03-15'); $abo = new UserAbo; $abo->next_date = null; $this->repository->setModel($abo); $newNextDate = invadePrivateMethod($this->repository, 'calculateNewNextDate', [20]); expect($newNextDate->format('Y-m-d'))->toBe('2026-03-20'); Carbon::setTestNow(); }); it('erlaubt Admins den nächsten Ausführungstermin ohne User-Sperrfrist direkt zu setzen', function () { Carbon::setTestNow('2026-03-19 12:00:00'); $abo = createRepositoryTestAbo([ 'abo_interval' => 5, 'next_date' => '2026-03-20', ]); $this->repository->setModel($abo); $result = $this->repository->update([ 'action' => 'abo_update_settings', 'id' => $abo->id, 'view' => 'admin', 'abo_interval' => 20, 'abo_next_month' => '2026-03', 'abo_is_active' => 'true', ]); expect($result)->toBeTrue(); expect($abo->refresh()->abo_interval)->toBe(20); expect($abo->getRawOriginal('next_date'))->toBe('2026-03-20'); }); it('behält die User-Sperrfrist für normale Abo-Änderungen bei', function () { Carbon::setTestNow('2026-03-19 12:00:00'); $abo = createRepositoryTestAbo([ 'abo_interval' => 5, 'next_date' => '2026-03-20', ]); $this->actingAs($abo->user); $this->repository->setModel($abo); $result = $this->repository->update([ 'action' => 'abo_update_settings', 'id' => $abo->id, 'view' => 'me', 'abo_interval' => 20, 'abo_is_active' => 'true', ]); expect($result)->toBeFalse(); expect($abo->refresh()->abo_interval)->toBe(5); expect($abo->getRawOriginal('next_date'))->toBe('2026-03-20'); }); /** * Hilfsfunktion zum Aufruf privater Methoden. * * @param array $args */ function invadePrivateMethod(object $object, string $methodName, array $args = []): mixed { $reflection = new ReflectionMethod($object, $methodName); $reflection->setAccessible(true); return $reflection->invoke($object, ...$args); } /** * @param array $overrides */ function createRepositoryTestAbo(array $overrides = []): UserAbo { $country = Country::create([ 'code' => 'DE', 'phone' => '49', 'en' => 'Germany', 'de' => 'Deutschland', 'es' => 'Alemania', 'fr' => 'Allemagne', 'it' => 'Germania', 'ru' => 'Deutschland', ]); $user = User::forceCreate([ 'email' => 'abo-repository-'.uniqid('', true).'@example.com', 'password' => bcrypt('secret'), 'lang' => 'de', 'admin' => 0, ]); $shoppingUser = ShoppingUser::create([ 'auth_user_id' => $user->id, 'member_id' => $user->id, 'billing_country_id' => $country->id, 'shipping_country_id' => $country->id, 'billing_email' => $user->email, 'is_for' => 'me', 'is_from' => 'user_order', ]); return UserAbo::create(array_merge([ 'user_id' => $user->id, 'member_id' => $user->id, 'shopping_user_id' => $shoppingUser->id, 'is_for' => 'me', 'email' => $user->email, 'payone_userid' => random_int(100000, 999999), 'clearingtype' => 'cc', 'active' => true, 'status' => 2, 'abo_interval' => 5, 'start_date' => '2026-01-05', 'last_date' => '2026-02-05', 'next_date' => '2026-03-05', ], $overrides)); }