Lege neue leere SQLite-Datenbank atomar via Use-Case und GUI an

Neuer Menüpunkt „Datenbank → Neue Datenbank anlegen…" mit FileChooser,
normalisierter Pfadprüfung gegen die aktive DB, gesammelter Überschreib-
Bestätigung, DB-Busy-Sperre auf Verlauf-Tab, Flyway-Migration auf den
neuesten Stand gegen eine Temp-Datei, Verbindungstest, atomarem Move
(ATOMIC_MOVE + REPLACE_EXISTING) und Umstellen der aktiven DB-Referenz
über einen neuen ActiveDatabaseContextPort. Konfig-Tab wechselt nach
Wechsel automatisch in den Dirty-State; Hinweismeldung mit Speichern-
Aufforderung wird im zentralen Meldungsbereich angezeigt.

Architektur entspricht Fall B aus der Spezifikation: Bootstrap hält den
Override prozessweit und verwendet ihn in resolveActiveJdbcUrl statt
des Werts aus der .properties-Datei. Bei Fehlern wird die Temp-Datei
zuverlässig entfernt; die aktive DB bleibt unverändert in Betrieb.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-05 16:52:54 +02:00
parent 90d95b9ff8
commit 3876e647b2
15 changed files with 1793 additions and 20 deletions
@@ -0,0 +1,65 @@
package de.gecheckt.pdf.umbenenner.bootstrap.adapter;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import java.nio.file.Path;
import java.util.Optional;
import org.junit.jupiter.api.Test;
/**
* Tests für {@link SqliteActiveDatabaseContextAdapter}.
* <p>
* Prüft das Setzen, Auslesen und Zurücksetzen des Override-Pfads sowie die Pflicht-Prüfung
* gegen {@code null}-Argumente.
*/
class SqliteActiveDatabaseContextAdapterTest {
@Test
void activeDatabaseOverride_returnsEmpty_initially() {
SqliteActiveDatabaseContextAdapter adapter = new SqliteActiveDatabaseContextAdapter();
assertThat(adapter.activeDatabaseOverride()).isEmpty();
}
@Test
void switchActiveDatabase_setsAbsoluteOverride() {
SqliteActiveDatabaseContextAdapter adapter = new SqliteActiveDatabaseContextAdapter();
Path target = Path.of("data/foo.sqlite");
adapter.switchActiveDatabase(target);
Optional<Path> override = adapter.activeDatabaseOverride();
assertThat(override).isPresent();
assertThat(override.get()).isEqualTo(target.toAbsolutePath().normalize());
}
@Test
void switchActiveDatabase_replacesPreviousOverride() {
SqliteActiveDatabaseContextAdapter adapter = new SqliteActiveDatabaseContextAdapter();
adapter.switchActiveDatabase(Path.of("first.sqlite"));
adapter.switchActiveDatabase(Path.of("second.sqlite"));
assertThat(adapter.activeDatabaseOverride())
.isPresent()
.get()
.isEqualTo(Path.of("second.sqlite").toAbsolutePath().normalize());
}
@Test
void clearOverride_removesPreviouslySetOverride() {
SqliteActiveDatabaseContextAdapter adapter = new SqliteActiveDatabaseContextAdapter();
adapter.switchActiveDatabase(Path.of("foo.sqlite"));
adapter.clearOverride();
assertThat(adapter.activeDatabaseOverride()).isEmpty();
}
@Test
void switchActiveDatabase_rejectsNull() {
SqliteActiveDatabaseContextAdapter adapter = new SqliteActiveDatabaseContextAdapter();
assertThatThrownBy(() -> adapter.switchActiveDatabase(null))
.isInstanceOf(NullPointerException.class);
}
}