From 320e485215b1861e5e998f7161e44b24652f7cd2 Mon Sep 17 00:00:00 2001 From: nexxo Date: Mon, 3 Aug 2026 21:07:19 +0200 Subject: [PATCH] Dauerhafte Pruefung: Wanderung und linkToInstanceAdmin() bleiben gleich Die Gleichheit zwischen der eingefrorenen Wanderungsfassung und der Modellmethode war bisher nur mit einer geloeschten Testdatei belegt - gut fuer heute, nicht fuer morgen. Nach dem Muster von HostTokenKeyMigrationTest laedt der neue Test die echte Wanderungsdatei per require() und haelt sie gegen linkToInstanceAdmin(): einmal die entschluesselte Falle direkt geprueft (nc_username darf nie Chiffretext werden), einmal beide Wege bei derselben Ausgangslage gegeneinander. --- .../SeatNextcloudColumnsMigrationTest.php | 78 +++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 tests/Feature/Seats/SeatNextcloudColumnsMigrationTest.php diff --git a/tests/Feature/Seats/SeatNextcloudColumnsMigrationTest.php b/tests/Feature/Seats/SeatNextcloudColumnsMigrationTest.php new file mode 100644 index 0000000..983fae2 --- /dev/null +++ b/tests/Feature/Seats/SeatNextcloudColumnsMigrationTest.php @@ -0,0 +1,78 @@ + $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); +});