Die Instanz merkt sich, was der Kunde zu seinen Daten gesagt hat
tests / pest (push) Waiting to run Details
tests / assets (push) Waiting to run Details
tests / release (push) Blocked by required conditions Details

Drei Zustaende, nicht zwei: `null` heisst "noch nicht gefragt". Jede Instanz,
die vor diesem Bau gekuendigt wurde, hat die Frage nie gesehen — ihr ein "nein"
zu unterstellen hiesse, still fuer Menschen zu antworten, die niemand gefragt
hat.
claude/nice-moser-521659
nexxo 2026-08-04 08:07:56 +02:00
parent 31f96bd3c9
commit 5dd75dac86
3 changed files with 65 additions and 0 deletions

View File

@ -22,6 +22,7 @@ class Instance extends Model
'ram_mb', 'cores', 'restart_required_since',
'subdomain', 'custom_domain', 'nc_admin_ref', 'admin_password', 'credentials_acknowledged_at',
'route_written', 'routed_hostnames', 'routed_backend', 'cert_ok', 'status', 'suspended_at', 'cancel_requested_at', 'service_ends_at',
'export_wish', 'export_reminded_at',
'domain_token', 'domain_verified_at', 'domain_cert_ok', 'domain_checked_at', 'domain_error', 'domain_failures',
'security_log_offset',
];
@ -56,6 +57,11 @@ class Instance extends Model
'cores' => 'integer',
'cancel_requested_at' => 'datetime',
'service_ends_at' => 'datetime',
// export_wish fuehrt DREI Zustaende: null (nicht gefragt), true, false.
// Laravels boolean-Cast laesst null als null durch (kein (bool)null),
// deshalb unterstellt eine Bestandsinstanz ohne Antwort kein "nein".
'export_wish' => 'boolean',
'export_reminded_at' => 'datetime',
'restart_required_since' => 'datetime',
];
}

View File

@ -0,0 +1,37 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
/**
* Was der Kunde beim Kuendigen zu seinen Daten gesagt hat.
*
* `export_wish` fuehrt DREI Zustaende, nicht zwei: `null` heisst "noch nicht
* gefragt". Das ist kein Feinschliff — jede Instanz, die vor diesem Bau
* gekuendigt wurde, hat die Frage nie gesehen, und ihr ein "nein" zu
* unterstellen hiesse, einer Handvoll Menschen still zu antworten, die nie
* gefragt wurden.
*
* `export_reminded_at` haelt fest, dass die Erinnerung sieben Tage vor Ende
* hinaus ist. Ohne diese Spalte schickte ein stuendlich laufender Zeitplan sie
* jede Stunde erneut und eine Erinnerung, die im Sechserpack kommt, liest
* sich als Mahnung.
*/
return new class extends Migration
{
public function up(): void
{
Schema::table('instances', function (Blueprint $table) {
$table->boolean('export_wish')->nullable()->after('service_ends_at');
$table->timestamp('export_reminded_at')->nullable()->after('export_wish');
});
}
public function down(): void
{
Schema::table('instances', function (Blueprint $table) {
$table->dropColumn(['export_wish', 'export_reminded_at']);
});
}
};

View File

@ -0,0 +1,22 @@
<?php // tests/Feature/Cancellation/ExportWishTest.php
use App\Models\Instance;
it('weiss bei einer Instanz, die nie gekuendigt wurde, gar nichts', function () {
// Drei Zustaende, nicht zwei. `null` heisst "nicht gefragt" und ist etwas
// anderes als "nein" — eine Bestandsinstanz, die vor diesem Bau gekuendigt
// wurde, hat die Frage nie gesehen, und wir duerfen ihr keine Antwort
// unterstellen.
expect(Instance::factory()->create()->export_wish)->toBeNull();
});
it('haelt ein Ja und ein Nein auseinander', function () {
expect(Instance::factory()->create(['export_wish' => true])->export_wish)->toBeTrue()
->and(Instance::factory()->create(['export_wish' => false])->export_wish)->toBeFalse();
});
it('merkt sich, wann erinnert wurde, damit es nicht zweimal geschieht', function () {
$instance = Instance::factory()->create(['export_reminded_at' => now()]);
expect($instance->fresh()->export_reminded_at)->not->toBeNull();
});