441 lines
9.5 KiB
Go
441 lines
9.5 KiB
Go
package tim
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
|
|
"github.com/Snider/Borg/pkg/trix"
|
|
)
|
|
|
|
func TestToFromSigil(t *testing.T) {
|
|
// Create a TIM with some data
|
|
m, err := New()
|
|
if err != nil {
|
|
t.Fatalf("New() error = %v", err)
|
|
}
|
|
m.RootFS.AddData("hello.txt", []byte("Hello, World!"))
|
|
m.RootFS.AddData("subdir/nested.txt", []byte("Nested content"))
|
|
|
|
password := "testpassword123"
|
|
|
|
// Encrypt
|
|
stim, err := m.ToSigil(password)
|
|
if err != nil {
|
|
t.Fatalf("ToSigil() error = %v", err)
|
|
}
|
|
|
|
// Verify magic number
|
|
if len(stim) < 4 || string(stim[:4]) != "STIM" {
|
|
t.Errorf("Expected magic 'STIM', got '%s'", string(stim[:4]))
|
|
}
|
|
|
|
// Decrypt
|
|
restored, err := FromSigil(stim, password)
|
|
if err != nil {
|
|
t.Fatalf("FromSigil() error = %v", err)
|
|
}
|
|
|
|
// Verify config matches
|
|
if string(restored.Config) != string(m.Config) {
|
|
t.Error("Config mismatch after round-trip")
|
|
}
|
|
|
|
// Verify rootfs file exists
|
|
file, err := restored.RootFS.Open("hello.txt")
|
|
if err != nil {
|
|
t.Fatalf("Failed to open hello.txt: %v", err)
|
|
}
|
|
defer file.Close()
|
|
}
|
|
|
|
func TestFromSigilWrongPassword(t *testing.T) {
|
|
m, err := New()
|
|
if err != nil {
|
|
t.Fatalf("New() error = %v", err)
|
|
}
|
|
|
|
stim, err := m.ToSigil("correct")
|
|
if err != nil {
|
|
t.Fatalf("ToSigil() error = %v", err)
|
|
}
|
|
|
|
_, err = FromSigil(stim, "wrong")
|
|
if err == nil {
|
|
t.Error("Expected error with wrong password")
|
|
}
|
|
}
|
|
|
|
func TestToSigilEmptyPassword(t *testing.T) {
|
|
m, err := New()
|
|
if err != nil {
|
|
t.Fatalf("New() error = %v", err)
|
|
}
|
|
|
|
_, err = m.ToSigil("")
|
|
if err != ErrPasswordRequired {
|
|
t.Errorf("Expected ErrPasswordRequired, got %v", err)
|
|
}
|
|
}
|
|
|
|
func TestFromSigilEmptyPassword(t *testing.T) {
|
|
m, err := New()
|
|
if err != nil {
|
|
t.Fatalf("New() error = %v", err)
|
|
}
|
|
|
|
stim, err := m.ToSigil("password")
|
|
if err != nil {
|
|
t.Fatalf("ToSigil() error = %v", err)
|
|
}
|
|
|
|
_, err = FromSigil(stim, "")
|
|
if err != ErrPasswordRequired {
|
|
t.Errorf("Expected ErrPasswordRequired, got %v", err)
|
|
}
|
|
}
|
|
|
|
func TestCache(t *testing.T) {
|
|
// Create a temporary directory for the cache
|
|
tempDir, err := os.MkdirTemp("", "borg-cache-test-*")
|
|
if err != nil {
|
|
t.Fatalf("Failed to create temp dir: %v", err)
|
|
}
|
|
defer os.RemoveAll(tempDir)
|
|
|
|
password := "cachepassword"
|
|
cache, err := NewCache(tempDir, password)
|
|
if err != nil {
|
|
t.Fatalf("NewCache() error = %v", err)
|
|
}
|
|
|
|
// Create a TIM
|
|
m, err := New()
|
|
if err != nil {
|
|
t.Fatalf("New() error = %v", err)
|
|
}
|
|
m.RootFS.AddData("test.txt", []byte("Cache test content"))
|
|
|
|
// Store
|
|
if err := cache.Store("mytim", m); err != nil {
|
|
t.Fatalf("Store() error = %v", err)
|
|
}
|
|
|
|
// Verify file exists
|
|
if !cache.Exists("mytim") {
|
|
t.Error("Exists() returned false for stored TIM")
|
|
}
|
|
|
|
// List
|
|
names, err := cache.List()
|
|
if err != nil {
|
|
t.Fatalf("List() error = %v", err)
|
|
}
|
|
if len(names) != 1 || names[0] != "mytim" {
|
|
t.Errorf("List() = %v, want [mytim]", names)
|
|
}
|
|
|
|
// Load
|
|
loaded, err := cache.Load("mytim")
|
|
if err != nil {
|
|
t.Fatalf("Load() error = %v", err)
|
|
}
|
|
|
|
// Verify content
|
|
file, err := loaded.RootFS.Open("test.txt")
|
|
if err != nil {
|
|
t.Fatalf("Failed to open test.txt: %v", err)
|
|
}
|
|
defer file.Close()
|
|
|
|
// Delete
|
|
if err := cache.Delete("mytim"); err != nil {
|
|
t.Fatalf("Delete() error = %v", err)
|
|
}
|
|
|
|
if cache.Exists("mytim") {
|
|
t.Error("Exists() returned true after Delete()")
|
|
}
|
|
}
|
|
|
|
func TestCacheEmptyPassword(t *testing.T) {
|
|
tempDir, err := os.MkdirTemp("", "borg-cache-test-*")
|
|
if err != nil {
|
|
t.Fatalf("Failed to create temp dir: %v", err)
|
|
}
|
|
defer os.RemoveAll(tempDir)
|
|
|
|
_, err = NewCache(tempDir, "")
|
|
if err != ErrPasswordRequired {
|
|
t.Errorf("Expected ErrPasswordRequired, got %v", err)
|
|
}
|
|
}
|
|
|
|
func TestDeriveKey(t *testing.T) {
|
|
key := trix.DeriveKey("password")
|
|
if len(key) != 32 {
|
|
t.Errorf("DeriveKey() returned key of length %d, want 32", len(key))
|
|
}
|
|
|
|
// Same password should produce same key
|
|
key2 := trix.DeriveKey("password")
|
|
for i := range key {
|
|
if key[i] != key2[i] {
|
|
t.Error("DeriveKey() not deterministic")
|
|
break
|
|
}
|
|
}
|
|
|
|
// Different password should produce different key
|
|
key3 := trix.DeriveKey("different")
|
|
same := true
|
|
for i := range key {
|
|
if key[i] != key3[i] {
|
|
same = false
|
|
break
|
|
}
|
|
}
|
|
if same {
|
|
t.Error("DeriveKey() produced same key for different passwords")
|
|
}
|
|
}
|
|
|
|
func TestToSigilWithLargeData(t *testing.T) {
|
|
m, err := New()
|
|
if err != nil {
|
|
t.Fatalf("New() error = %v", err)
|
|
}
|
|
|
|
// Add large file
|
|
largeData := make([]byte, 1024*1024) // 1MB
|
|
for i := range largeData {
|
|
largeData[i] = byte(i % 256)
|
|
}
|
|
m.RootFS.AddData("large.bin", largeData)
|
|
|
|
password := "largetest"
|
|
|
|
// Encrypt
|
|
stim, err := m.ToSigil(password)
|
|
if err != nil {
|
|
t.Fatalf("ToSigil() error = %v", err)
|
|
}
|
|
|
|
// Decrypt
|
|
restored, err := FromSigil(stim, password)
|
|
if err != nil {
|
|
t.Fatalf("FromSigil() error = %v", err)
|
|
}
|
|
|
|
// Verify file exists
|
|
_, err = restored.RootFS.Open("large.bin")
|
|
if err != nil {
|
|
t.Fatalf("Failed to open large.bin: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestRunEncryptedFileNotFound(t *testing.T) {
|
|
err := RunEncrypted("/nonexistent/path.stim", "password")
|
|
if err == nil {
|
|
t.Error("Expected error for nonexistent file")
|
|
}
|
|
}
|
|
|
|
func TestRunEncryptedWithTempFile(t *testing.T) {
|
|
// Create a TIM
|
|
m, err := New()
|
|
if err != nil {
|
|
t.Fatalf("New() error = %v", err)
|
|
}
|
|
m.RootFS.AddData("test.txt", []byte("test"))
|
|
|
|
// Encrypt
|
|
password := "runtest"
|
|
stim, err := m.ToSigil(password)
|
|
if err != nil {
|
|
t.Fatalf("ToSigil() error = %v", err)
|
|
}
|
|
|
|
// Write to temp file
|
|
tempFile := filepath.Join(t.TempDir(), "test.stim")
|
|
if err := os.WriteFile(tempFile, stim, 0600); err != nil {
|
|
t.Fatalf("Failed to write temp file: %v", err)
|
|
}
|
|
|
|
// RunEncrypted will fail because runc is not available in test,
|
|
// but it should at least decrypt successfully
|
|
err = RunEncrypted(tempFile, password)
|
|
// We expect an error about runc not being found, not about decryption
|
|
if err != nil && err.Error() != "" {
|
|
// Check it's not a decryption error
|
|
if err.Error() == ErrDecryptionFailed.Error() {
|
|
t.Errorf("Unexpected decryption error: %v", err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestCacheSize(t *testing.T) {
|
|
tempDir, err := os.MkdirTemp("", "borg-cache-size-test-*")
|
|
if err != nil {
|
|
t.Fatalf("Failed to create temp dir: %v", err)
|
|
}
|
|
defer os.RemoveAll(tempDir)
|
|
|
|
password := "sizetest"
|
|
cache, err := NewCache(tempDir, password)
|
|
if err != nil {
|
|
t.Fatalf("NewCache() error = %v", err)
|
|
}
|
|
|
|
// Create and store a TIM
|
|
m, err := New()
|
|
if err != nil {
|
|
t.Fatalf("New() error = %v", err)
|
|
}
|
|
m.RootFS.AddData("test.txt", []byte("Size test content"))
|
|
|
|
if err := cache.Store("sizetest", m); err != nil {
|
|
t.Fatalf("Store() error = %v", err)
|
|
}
|
|
|
|
// Get size
|
|
size, err := cache.Size("sizetest")
|
|
if err != nil {
|
|
t.Fatalf("Size() error = %v", err)
|
|
}
|
|
|
|
if size <= 0 {
|
|
t.Errorf("Size() = %d, want > 0", size)
|
|
}
|
|
}
|
|
|
|
func TestCacheSizeNotFound(t *testing.T) {
|
|
tempDir, err := os.MkdirTemp("", "borg-cache-size-test-*")
|
|
if err != nil {
|
|
t.Fatalf("Failed to create temp dir: %v", err)
|
|
}
|
|
defer os.RemoveAll(tempDir)
|
|
|
|
cache, err := NewCache(tempDir, "password")
|
|
if err != nil {
|
|
t.Fatalf("NewCache() error = %v", err)
|
|
}
|
|
|
|
_, err = cache.Size("nonexistent")
|
|
if err == nil {
|
|
t.Error("Expected error for nonexistent TIM")
|
|
}
|
|
}
|
|
|
|
func TestFromTar(t *testing.T) {
|
|
// Create a TIM and convert to tar
|
|
m, err := New()
|
|
if err != nil {
|
|
t.Fatalf("New() error = %v", err)
|
|
}
|
|
m.RootFS.AddData("test.txt", []byte("Test content"))
|
|
m.RootFS.AddData("subdir/nested.txt", []byte("Nested content"))
|
|
|
|
tarData, err := m.ToTar()
|
|
if err != nil {
|
|
t.Fatalf("ToTar() error = %v", err)
|
|
}
|
|
|
|
// Parse the tar back to a TIM
|
|
restored, err := FromTar(tarData)
|
|
if err != nil {
|
|
t.Fatalf("FromTar() error = %v", err)
|
|
}
|
|
|
|
// Verify config
|
|
if string(restored.Config) != string(m.Config) {
|
|
t.Error("Config mismatch after FromTar")
|
|
}
|
|
|
|
// Verify files
|
|
file, err := restored.RootFS.Open("test.txt")
|
|
if err != nil {
|
|
t.Fatalf("Failed to open test.txt: %v", err)
|
|
}
|
|
file.Close()
|
|
|
|
file, err = restored.RootFS.Open("subdir/nested.txt")
|
|
if err != nil {
|
|
t.Fatalf("Failed to open subdir/nested.txt: %v", err)
|
|
}
|
|
file.Close()
|
|
}
|
|
|
|
func TestFromTarInvalidData(t *testing.T) {
|
|
_, err := FromTar([]byte("not a tar file"))
|
|
if err == nil {
|
|
t.Error("Expected error for invalid tar data")
|
|
}
|
|
}
|
|
|
|
func TestFromTarNoConfig(t *testing.T) {
|
|
// Create tar without config.json
|
|
_, err := FromTar([]byte{})
|
|
if err == nil {
|
|
t.Error("Expected error for tar without config.json")
|
|
}
|
|
}
|
|
|
|
func TestToTarNilConfig(t *testing.T) {
|
|
m := &TerminalIsolationMatrix{
|
|
Config: nil,
|
|
RootFS: nil,
|
|
}
|
|
|
|
_, err := m.ToTar()
|
|
if err != ErrConfigIsNil {
|
|
t.Errorf("Expected ErrConfigIsNil, got %v", err)
|
|
}
|
|
}
|
|
|
|
func TestToSigilNilConfig(t *testing.T) {
|
|
m := &TerminalIsolationMatrix{
|
|
Config: nil,
|
|
RootFS: nil,
|
|
}
|
|
|
|
_, err := m.ToSigil("password")
|
|
if err != ErrConfigIsNil {
|
|
t.Errorf("Expected ErrConfigIsNil, got %v", err)
|
|
}
|
|
}
|
|
|
|
func TestFromSigilInvalidData(t *testing.T) {
|
|
_, err := FromSigil([]byte("invalid stim data"), "password")
|
|
if err == nil {
|
|
t.Error("Expected error for invalid stim data")
|
|
}
|
|
}
|
|
|
|
func TestFromSigilTruncatedPayload(t *testing.T) {
|
|
// Create valid STIM but truncate the payload
|
|
m, err := New()
|
|
if err != nil {
|
|
t.Fatalf("New() error = %v", err)
|
|
}
|
|
|
|
stim, err := m.ToSigil("password")
|
|
if err != nil {
|
|
t.Fatalf("ToSigil() error = %v", err)
|
|
}
|
|
|
|
// Truncate to just magic + partial header
|
|
if len(stim) > 20 {
|
|
_, err = FromSigil(stim[:20], "password")
|
|
if err == nil {
|
|
t.Error("Expected error for truncated payload")
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestFromDataNodeNil(t *testing.T) {
|
|
_, err := FromDataNode(nil)
|
|
if err != ErrDataNodeRequired {
|
|
t.Errorf("Expected ErrDataNodeRequired, got %v", err)
|
|
}
|
|
}
|