Files
nekonest-cloud/relay/internal/tenantbackup/backup_test.go
T

100 lines
3.4 KiB
Go

package tenantbackup
import (
"context"
"os"
"path/filepath"
"testing"
"time"
"github.com/klarkxy/nekonest-cloud/relay/internal/tenantfs"
"github.com/klarkxy/nekonest-cloud/relay/internal/tenantstore"
"github.com/klarkxy/nekonest/relaycore/protocol"
)
func TestCreateVerifyAndRestoreTenantBackup(t *testing.T) {
const tenantID = "tenant_0123456789abcdef0123456789abcdef"
sourceRoot := t.TempDir()
paths, err := tenantfs.Resolve(sourceRoot, tenantID)
if err != nil {
t.Fatal(err)
}
store, err := tenantstore.NewWithTransportMode(paths.Database, string(protocol.TransportSealed))
if err != nil {
t.Fatal(err)
}
token, err := store.RegisterDevice("host_0123456789abcdef0123456789abcdef", "Host", "linux")
if err != nil {
t.Fatal(err)
}
if err := store.Close(); err != nil {
t.Fatal(err)
}
attachmentID := "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
if err := os.WriteFile(filepath.Join(paths.Attachments, attachmentID+".bin"), []byte("sealed payload"), 0o600); err != nil {
t.Fatal(err)
}
if err := os.WriteFile(filepath.Join(paths.Attachments, attachmentID+".json"), []byte(`{"id":"`+attachmentID+`"}`), 0o600); err != nil {
t.Fatal(err)
}
result, err := Create(context.Background(), sourceRoot, t.TempDir(), tenantID, 7, time.Date(2026, 8, 12, 12, 0, 0, 0, time.UTC))
if err != nil {
t.Fatal(err)
}
if _, err := Verify(context.Background(), result.Path, tenantID, 7, result.ManifestSHA256); err != nil {
t.Fatal(err)
}
if resolved, err := ResolveReference(filepath.Dir(filepath.Dir(result.Path)), result.BackupRef); err != nil || resolved != result.Path {
t.Fatalf("backup reference did not resolve: %q err=%v", resolved, err)
}
targetRoot := t.TempDir()
restored, err := Restore(context.Background(), result.Path, targetRoot, tenantID, 7, result.ManifestSHA256)
if err != nil {
t.Fatal(err)
}
if retried, err := Restore(context.Background(), result.Path, targetRoot, tenantID, 7, result.ManifestSHA256); err != nil || retried.Root != restored.Root {
t.Fatalf("idempotent restore failed: %#v err=%v", retried, err)
}
restoredStore, err := tenantstore.NewWithTransportMode(restored.Database, string(protocol.TransportSealed))
if err != nil {
t.Fatal(err)
}
defer restoredStore.Close()
if !restoredStore.ValidateDeviceToken("host_0123456789abcdef0123456789abcdef", token) {
t.Fatal("restored database lost the device credential")
}
payload, err := os.ReadFile(filepath.Join(restored.Attachments, attachmentID+".bin"))
if err != nil || string(payload) != "sealed payload" {
t.Fatalf("restored attachment = %q err=%v", payload, err)
}
}
func TestVerifyRejectsTamperedBackup(t *testing.T) {
const tenantID = "tenant_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
root := t.TempDir()
paths, err := tenantfs.Resolve(root, tenantID)
if err != nil {
t.Fatal(err)
}
store, err := tenantstore.NewWithTransportMode(paths.Database, string(protocol.TransportSealed))
if err != nil {
t.Fatal(err)
}
if err := store.Close(); err != nil {
t.Fatal(err)
}
result, err := Create(context.Background(), root, t.TempDir(), tenantID, 1, time.Now().UTC())
if err != nil {
t.Fatal(err)
}
file, err := os.OpenFile(filepath.Join(result.Path, "relay.db"), os.O_WRONLY|os.O_APPEND, 0)
if err != nil {
t.Fatal(err)
}
_, _ = file.Write([]byte("tamper"))
_ = file.Close()
if _, err := Verify(context.Background(), result.Path, tenantID, 1, result.ManifestSHA256); err == nil {
t.Fatal("tampered backup was accepted")
}
}