79 lines
3.3 KiB
PHP
79 lines
3.3 KiB
PHP
<?php
|
|
|
|
use App\Models\Customer;
|
|
use App\Models\Instance;
|
|
use App\Models\Seat;
|
|
use Illuminate\Support\Facades\DB;
|
|
use Illuminate\Support\Facades\Schema;
|
|
|
|
/**
|
|
* Laedt eine frische Instanz der Wanderung bei jedem Aufruf.
|
|
*
|
|
* `require`, nicht `require_once`: PHP wertet den `return new class ...`-
|
|
* Ausdruck bei jedem Aufruf neu aus, jeder Aufruf bekommt also seine eigene
|
|
* Instanz.
|
|
*/
|
|
function loadSeatNextcloudColumnsMigration(): object
|
|
{
|
|
return require database_path('migrations/2026_08_03_180000_add_nextcloud_columns_to_seats.php');
|
|
}
|
|
|
|
/**
|
|
* Stellt den Vor-Wanderungs-Zustand nach: RefreshDatabase hat up() schon
|
|
* einmal auf einer leeren seats-Tabelle laufen lassen, die vier Spalten
|
|
* stehen also schon. Ein zweiter Schema::table()-Aufruf auf bereits
|
|
* vorhandene Spalten wuerde scheitern — hier weg damit, Zeilen bleiben.
|
|
*/
|
|
function dropSeatNextcloudColumns(): void
|
|
{
|
|
Schema::table('seats', fn ($table) => $table->dropColumn(['nc_username', 'nc_state', 'nc_error', 'nc_synced_at']));
|
|
}
|
|
|
|
it('verknuepft einen bestehenden Inhaber-Sitz mit dem entschluesselten Anmeldenamen, nicht mit dem Chiffretext', function () {
|
|
// Genau die Zusicherung, die die Verschluesselungsfalle aus der
|
|
// Nachbesserung fuer die Zukunft absichert: nc_admin_ref ist am
|
|
// Instance-Modell 'encrypted' gecastet. Faellt Crypt::decryptString()
|
|
// in der Wanderung wieder heraus, landet hier der Chiffretext statt
|
|
// 'admin' — dieser Test bricht dann, nicht erst eine echte Instanz.
|
|
$customer = Customer::factory()->create();
|
|
Instance::factory()->for($customer)->create(['nc_admin_ref' => 'admin', 'status' => 'active']);
|
|
$sitz = Seat::factory()->for($customer)->create(['role' => 'owner']);
|
|
|
|
dropSeatNextcloudColumns();
|
|
loadSeatNextcloudColumnsMigration()->up();
|
|
|
|
$zeile = DB::table('seats')->where('id', $sitz->id)->first();
|
|
|
|
expect($zeile->nc_username)->toBe('admin')
|
|
->and($zeile->nc_state)->toBe('synced');
|
|
});
|
|
|
|
it('kommt bei derselben Ausgangslage zum selben Ergebnis wie Seat::linkToInstanceAdmin()', function () {
|
|
// Der Preis des Einfrierens: zwei Fassungen derselben Auswahl koennen
|
|
// auseinanderdriften, ohne dass es jemand merkt. Die Erwartung wird
|
|
// ueber die Modellmethode eingeholt, BEVOR die Wanderung dieselbe Zeile
|
|
// ueber ihren eigenen, eingefrorenen Weg anfasst — derselbe Kunde,
|
|
// dieselbe Instanz, derselbe Sitz, zwei Wege dorthin.
|
|
//
|
|
// Verglichen werden nur nc_username/nc_state, nicht der ganze
|
|
// Datensatz: das rohe update() der Wanderung setzt updated_at nicht,
|
|
// save() ueber das Modell schon — das ist gewollt (siehe andere
|
|
// Nachbefuellungen im Bestand) und keine Abweichung, die dieser Test
|
|
// aufdecken soll.
|
|
$customer = Customer::factory()->create();
|
|
Instance::factory()->for($customer)->create(['nc_admin_ref' => 'gleicher-name', 'status' => 'active']);
|
|
$sitz = Seat::factory()->for($customer)->create(['role' => 'owner']);
|
|
|
|
$sitz->linkToInstanceAdmin();
|
|
$erwarteterUsername = $sitz->nc_username;
|
|
$erwarteterState = $sitz->nc_state;
|
|
|
|
dropSeatNextcloudColumns();
|
|
loadSeatNextcloudColumnsMigration()->up();
|
|
|
|
$zeile = DB::table('seats')->where('id', $sitz->id)->first();
|
|
|
|
expect($zeile->nc_username)->toBe($erwarteterUsername)
|
|
->and($zeile->nc_state)->toBe($erwarteterState);
|
|
});
|