country = Country::create([ 'code' => 'DE', 'phone' => '49', 'en' => 'Germany', 'de' => 'Deutschland', 'es' => 'Alemania', 'fr' => 'Allemagne', 'it' => 'Germania', 'ru' => 'Германия', ]); $this->member = User::forceCreate([ 'email' => 'gcl-member-'.uniqid('', true).'@example.com', 'password' => Hash::make('secret'), 'lang' => 'de', ]); $this->userShop = UserShop::create([ 'user_id' => $this->member->id, 'name' => 'GCL'.substr(uniqid('', true), 0, 8), 'slug' => 'gcl-'.uniqid(), 'active' => true, ]); }); function gclCustomer(array $attributes = []): ShoppingUser { return ShoppingUser::create(array_merge([ 'auth_user_id' => 0, 'member_id' => test()->member->id, 'number' => 5100, 'billing_firstname' => 'Susann', 'billing_lastname' => 'Voigt', 'billing_address' => 'Hauptstraße 1', 'billing_zipcode' => '19243', 'billing_city' => 'Wittenburg', 'billing_phone' => '0123', 'billing_email' => 'susann@example.com', 'billing_country_id' => test()->country->id, 'shipping_country_id' => test()->country->id, ], $attributes)); } function gclOrderFor(ShoppingUser $customer): ShoppingOrder { return ShoppingOrder::create([ 'shopping_user_id' => $customer->id, 'auth_user_id' => 0, 'country_id' => test()->country->id, 'user_shop_id' => test()->userShop->id, 'payment_for' => 6, 'total' => 100, 'subtotal' => 90, ]); } it('markiert alle Einträge bei der DSGVO-Löschung und meldet den Status im Modal', function () { $first = gclCustomer(); $second = gclCustomer(); gclOrderFor($first); $service = app(GdprDeletionService::class); $before = $service->analyzeShoppingUser($first); expect($before['all_gdpr_deleted'])->toBeFalse() ->and($before['gdpr_deleted_rows'])->toBe(0); $service->deleteShoppingUser($first, null); expect($first->fresh()->isGdprDeleted())->toBeTrue() ->and($second->fresh()->isGdprDeleted())->toBeTrue() ->and($first->fresh()->billing_lastname)->toBe('Voigt'); $after = $service->analyzeShoppingUser($first->fresh()); expect($after['all_gdpr_deleted'])->toBeTrue() ->and($after['gdpr_deleted_rows'])->toBe(2) ->and($after['gdpr_deleted_at'])->not->toBeNull(); }); it('blendet DSGVO-gelöschte Einträge über den Scope aus', function () { $deleted = gclCustomer(); gclOrderFor($deleted); app(GdprDeletionService::class)->deleteShoppingUser($deleted, null); $other = gclCustomer(['number' => 5200, 'billing_email' => 'andere@example.com']); $visibleIds = ShoppingUser::query()->withoutGdprDeleted()->pluck('id')->all(); expect($visibleIds)->toContain($other->id) ->not->toContain($deleted->id); }); it('verhindert, dass Kontaktdaten in einen DSGVO-gelöschten Eintrag zurückgeschrieben werden', function (string $field, string $value) { $customer = gclCustomer(); gclOrderFor($customer); app(GdprDeletionService::class)->deleteShoppingUser($customer, null); $customer = $customer->fresh(); $customer->{$field} = $value; $customer->save(); })->with([ 'E-Mail' => ['billing_email', 'susann@example.com'], 'Telefon' => ['billing_phone', '0123'], 'Liefer-E-Mail' => ['shipping_email', 'susann@example.com'], 'Liefer-Telefon' => ['shipping_phone', '0456'], ])->throws(GdprDeletionException::class); it('erlaubt an DSGVO-gelöschten Einträgen weiterhin Änderungen ohne Kontaktdaten', function () { $customer = gclCustomer(); gclOrderFor($customer); app(GdprDeletionService::class)->deleteShoppingUser($customer, null); $newMember = User::forceCreate([ 'email' => 'gcl-new-member-'.uniqid('', true).'@example.com', 'password' => Hash::make('secret'), 'lang' => 'de', ]); $customer = $customer->fresh(); $customer->member_id = $newMember->id; $customer->save(); expect($customer->fresh()->member_id)->toBe($newMember->id); }); it('lässt eine erneute DSGVO-Löschung eines bereits gelöschten Kunden zu', function () { $customer = gclCustomer(); gclOrderFor($customer); $service = app(GdprDeletionService::class); $service->deleteShoppingUser($customer, null); $firstDeletedAt = $customer->fresh()->gdpr_deleted_at; $service->deleteShoppingUser($customer->fresh(), null); expect($customer->fresh()->gdpr_deleted_at->equalTo($firstDeletedAt))->toBeTrue(); }); it('markiert auch komplett gelöschte Kunden ohne Bestellung', function () { $customer = gclCustomer(); app(GdprDeletionService::class)->purgeShoppingUser($customer, null); $row = ShoppingUser::withTrashed()->find($customer->id); expect($row->trashed())->toBeTrue() ->and($row->isGdprDeleted())->toBeTrue(); }); it('setzt faker_mail bei DSGVO-gelöschten Einträgen nicht zurück', function () { $customer = gclCustomer(); gclOrderFor($customer); app(GdprDeletionService::class)->deleteShoppingUser($customer, null); ShoppingUserService::setFakerMail($this->member); expect($customer->fresh()->faker_mail)->toBeTrue(); }); it('zieht protokollierte Löschungen nach, deren Kontaktdaten wieder befüllt wurden', function () { $restored = gclCustomer(); $sibling = gclCustomer(); gclOrderFor($restored); gclOrderFor($sibling); $service = app(GdprDeletionService::class); $service->deleteShoppingUser($sibling, null); // Zustand wie bei Kunde 45013: Kontaktdaten vor Einführung der Sperre wieder befüllt ShoppingUser::whereKey($restored->id)->update([ 'billing_email' => 'susann@example.com', 'billing_phone' => '0123', 'faker_mail' => false, 'gdpr_deleted_at' => null, ]); $deletionLoggedAt = Logger::where('action', 'gdpr-delete-customer')->value('created_at'); $this->travelTo($deletionLoggedAt->copy()->addDay()); $lateCustomer = gclCustomer(['billing_email' => 'susann-neu@example.com']); gclOrderFor($lateCustomer); $preview = $service->repairLoggedShoppingUserDeletions(true); expect(array_column($preview, 'id'))->toBe([$restored->id]) ->and($restored->fresh()->billing_email)->toBe('susann@example.com'); $service->repairLoggedShoppingUserDeletions(false); $restored = $restored->fresh(); expect($restored->billing_email)->toEndWith('@dsgvo.invalid') ->and($restored->billing_phone)->toBeNull() ->and($restored->isGdprDeleted())->toBeTrue() ->and($lateCustomer->fresh()->billing_email)->toBe('susann-neu@example.com') ->and($lateCustomer->fresh()->isGdprDeleted())->toBeFalse() ->and($service->repairLoggedShoppingUserDeletions(true))->toBe([]); });