feat: establish NekoNest Cloud control and relay
This commit is contained in:
@@ -0,0 +1,77 @@
|
||||
package tenantpurge
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/klarkxy/nekonest-cloud/relay/internal/tenantfs"
|
||||
)
|
||||
|
||||
const testTenantID = "tenant_0123456789abcdef0123456789abcdef"
|
||||
|
||||
func TestPurgeDeletesLiveDataAndEveryBackupIdempotently(t *testing.T) {
|
||||
dataRoot := t.TempDir()
|
||||
backupRoot := t.TempDir()
|
||||
paths, err := tenantfs.Resolve(dataRoot, testTenantID)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := os.WriteFile(paths.Database, []byte("sqlite"), 0o600); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
backupTenant := filepath.Join(backupRoot, filepath.Base(paths.Root))
|
||||
for _, name := range []string{"g1", "g2"} {
|
||||
path := filepath.Join(backupTenant, name)
|
||||
if err := os.MkdirAll(path, 0o700); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := os.WriteFile(filepath.Join(path, "manifest.json"), []byte("{}"), 0o600); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
first, err := Purge(context.Background(), dataRoot, backupRoot, testTenantID, 3)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !first.DataDeleted || !first.BackupsDeleted || len(first.EvidenceSHA256) != 64 {
|
||||
t.Fatalf("purge result = %#v", first)
|
||||
}
|
||||
if _, err := os.Lstat(paths.Root); !os.IsNotExist(err) {
|
||||
t.Fatalf("live tenant data survived: %v", err)
|
||||
}
|
||||
if _, err := os.Lstat(backupTenant); !os.IsNotExist(err) {
|
||||
t.Fatalf("tenant backups survived: %v", err)
|
||||
}
|
||||
second, err := Purge(context.Background(), dataRoot, backupRoot, testTenantID, 3)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if second.DataDeleted || second.BackupsDeleted || second.EvidenceSHA256 != first.EvidenceSHA256 {
|
||||
t.Fatalf("idempotent retry = %#v", second)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPurgeRefusesSymbolicLinksWithoutTouchingTarget(t *testing.T) {
|
||||
dataRoot := t.TempDir()
|
||||
backupRoot := t.TempDir()
|
||||
outside := t.TempDir()
|
||||
outsideFile := filepath.Join(outside, "keep.txt")
|
||||
if err := os.WriteFile(outsideFile, []byte("keep"), 0o600); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
paths, err := tenantfs.Resolve(dataRoot, testTenantID)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := os.Symlink(outsideFile, filepath.Join(paths.Root, "escape")); err != nil {
|
||||
t.Skipf("symbolic links unavailable: %v", err)
|
||||
}
|
||||
if _, err := Purge(context.Background(), dataRoot, backupRoot, testTenantID, 1); err == nil {
|
||||
t.Fatal("purge followed or accepted a symbolic link")
|
||||
}
|
||||
if data, err := os.ReadFile(outsideFile); err != nil || string(data) != "keep" {
|
||||
t.Fatalf("outside target changed: data=%q err=%v", data, err)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user