create([ 'status' => 'cancellation_scheduled', 'service_ends_at' => now()->addDays(6), ]); $this->artisan('clupilot:remind-ending-services')->assertSuccessful(); Mail::assertQueued(ServiceEndingSoonMail::class); expect($instance->fresh()->export_reminded_at)->not->toBeNull(); }); it('erinnert auch, wenn das Ende genau in sieben Tagen liegt', function () { // Die obere Einschlussgrenze, gegenüber dem unteren Rand unten: die // Auswahl ist "höchstens sieben Tage", also gehört der siebte Tag noch // dazu, nicht erst der sechste. Mail::fake(); Instance::factory()->create([ 'status' => 'cancellation_scheduled', 'service_ends_at' => now()->addDays(7), ]); $this->artisan('clupilot:remind-ending-services')->assertSuccessful(); Mail::assertQueued(ServiceEndingSoonMail::class); }); it('erinnert nicht zweimal', function () { // Ein täglicher Zeitplan würde die Erinnerung sonst an jedem der // restlichen Tage erneut schicken. Eine Erinnerung im Sechserpack liest // sich als Mahnung. Mail::fake(); Instance::factory()->create([ 'status' => 'cancellation_scheduled', 'service_ends_at' => now()->addDays(6), 'export_reminded_at' => now()->subHour(), ]); $this->artisan('clupilot:remind-ending-services')->assertSuccessful(); Mail::assertNothingQueued(); }); it('erinnert nicht, wenn das Ende noch weit weg ist', function () { Mail::fake(); Instance::factory()->create([ 'status' => 'cancellation_scheduled', 'service_ends_at' => now()->addDays(30), ]); $this->artisan('clupilot:remind-ending-services')->assertSuccessful(); Mail::assertNothingQueued(); }); it('erinnert nicht mehr, wenn das Ende schon vorbei ist', function () { // Kein akademischer Rand: EndInstanceService lässt einen DNS-Fehler // bewusst durch, eine gekündigte Instanz mit abgelaufener Frist kann also // real tagelang stehenbleiben, bis der stündliche Lauf durchkommt. Genau // dann darf keine Erinnerung mehr ausgehen — die Tür ist zu, und eine // Aufforderung zum Selbst-Herunterladen wäre eine Aufforderung ins Leere. Mail::fake(); Instance::factory()->create([ 'status' => 'cancellation_scheduled', 'service_ends_at' => now()->subHour(), ]); $this->artisan('clupilot:remind-ending-services')->assertSuccessful(); Mail::assertNothingQueued(); }); it('erinnert eine Instanz, die gar nicht gekündigt wurde, nicht', function () { Mail::fake(); Instance::factory()->create(['status' => 'active', 'service_ends_at' => now()->addDays(6)]); $this->artisan('clupilot:remind-ending-services')->assertSuccessful(); Mail::assertNothingQueued(); }); it('erinnert auch dann, wenn das Ende schon in wenigen Stunden ist', function () { // Der wichtigste Fall und der, den ein "genau sieben Tage"-Vergleich // verfehlt: eine Instanz, die zwischen zwei Läufen durchgerutscht ist, // oder eine, die mit kurzer Frist gekündigt wurde. Lieber spät erinnern // als gar nicht — ab dem Laufzeitende kommt der Kunde nicht mehr hinein. Mail::fake(); Instance::factory()->create([ 'status' => 'cancellation_scheduled', 'service_ends_at' => now()->addHours(3), ]); $this->artisan('clupilot:remind-ending-services')->assertSuccessful(); Mail::assertQueued(ServiceEndingSoonMail::class); }); it('trägt in der Mail das Datum und den Weg zum Selbst-Herunterladen', function () { $instance = Instance::factory()->create([ 'status' => 'cancellation_scheduled', 'service_ends_at' => now()->addDays(6), 'export_wish' => false, ]); $text = (new ServiceEndingSoonMail($instance))->render(); // Kein roher Schlüssel, und die zwei Dinge, um die es geht: ab wann ist // zu, und wie komme ich vorher an meine Sachen — die Adresse selbst muss // also wirklich im Text stehen, nicht nur der Knopf, der theoretisch // hinführen könnte. expect($text)->not->toContain('service_ending.') ->and($text)->toContain($instance->service_ends_at->local()->isoFormat('LL')) ->and($text)->toContain('https://'.$instance->address(ProvisioningSettings::dnsZone())); }); it('behauptet keinen Vorgang, den niemand ausfuehrt', function () { // W1: der Satz stand im Präsens und sagte, wir bereiteten bereits einen // Export vor. Es gibt keinen — er hängt an einem Speicherserver, den es // noch nicht gibt. Was stimmt, ist: der Wunsch ist vermerkt, und es // meldet sich jemand. $instance = Instance::factory()->create([ 'status' => 'cancellation_scheduled', 'service_ends_at' => now()->addDays(6), 'export_wish' => true, ]); $text = (new ServiceEndingSoonMail($instance))->render(); expect($text)->toContain('vermerkt') ->and($text)->not->toContain('bereiten wir zusätzlich einen für Sie vor'); });