internal/tenantbackup: datenbank-scharfes pg_dump/pg_restore statt BAK-01s Cluster-weitem pg_basebackup - bei Modell C (TEN-01, physisch isolierte DB je Mandant) wuerde ein Cluster-Restore zwangslaeufig ALLE Mandanten ueberschreiben. Objekt-Seite nutzt BAK-02 direkt (Mandanten haben eigene Buckets/Pfad-Roots). Eigene Postgres-Rolle nexarch_tenantbackup (CREATEDB, kein Superuser, getrennt von nexarch_backup). Zwei-Tenant-Isolation real in beide Richtungen bewiesen (Markerwert-Nachweis), JSONL-Protokoll fuer Sicherung UND Restore. Realer End-zu-Ende-Lauf ueber tenantbackup-cli auf 131.
38 lines
1.1 KiB
Go
38 lines
1.1 KiB
Go
package tenantbackup
|
|
|
|
import (
|
|
"path/filepath"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
// TestLog_BackupAndRestoreFullyLogged ist Pruefung 3: Tenant-Sicherung
|
|
// UND -Restore vollstaendig protokolliert.
|
|
func TestLog_BackupAndRestoreFullyLogged(t *testing.T) {
|
|
logPath := filepath.Join(t.TempDir(), "tenantbackup.log")
|
|
|
|
entries := []LogEntry{
|
|
{Timestamp: time.Now().UTC(), Operation: OpBackupDB, TenantID: "a", Source: "/x/dump.pgcustom", Result: "ok"},
|
|
{Timestamp: time.Now().UTC(), Operation: OpRestoreDB, TenantID: "a", Source: "/x/dump.pgcustom", Target: "a_restored", Result: "ok"},
|
|
}
|
|
for _, e := range entries {
|
|
if err := AppendLog(logPath, e); err != nil {
|
|
t.Fatalf("appendlog: %v", err)
|
|
}
|
|
}
|
|
|
|
got, err := ReadLog(logPath)
|
|
if err != nil {
|
|
t.Fatalf("readlog: %v", err)
|
|
}
|
|
if len(got) != 2 {
|
|
t.Fatalf("erwartet 2 eintraege, habe %d", len(got))
|
|
}
|
|
if got[0].Operation != OpBackupDB || got[1].Operation != OpRestoreDB {
|
|
t.Fatalf("unerwartete reihenfolge/operationen: %+v", got)
|
|
}
|
|
if got[0].TenantID != "a" || got[1].Target != "a_restored" {
|
|
t.Fatalf("eintraege unvollstaendig: %+v", got)
|
|
}
|
|
}
|