backup_test.go raw

   1  package blossom
   2  
   3  import (
   4  	"bytes"
   5  	"crypto/sha256"
   6  	"os"
   7  	"path/filepath"
   8  	"testing"
   9  
  10  	"git.smesh.lol/torly/pkg/nostr"
  11  )
  12  
  13  func putTestBlob(t *testing.T, s *Server, data []byte, pub, typ string, created int64) string {
  14  	t.Helper()
  15  	sum := sha256.Sum256(data)
  16  	hash := nostr.Hex(sum[:])
  17  	path := s.blobPath(hash)
  18  	if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil {
  19  		t.Fatal(err)
  20  	}
  21  	if err := os.WriteFile(path, data, 0o644); err != nil {
  22  		t.Fatal(err)
  23  	}
  24  	b := &Blob{Hash: hash, PubKey: pub, Size: int64(len(data)), Type: typ, Created: created}
  25  	s.mu.Lock()
  26  	s.putMem(b)
  27  	err := s.appendRecord('p', b)
  28  	s.mu.Unlock()
  29  	if err != nil {
  30  		t.Fatal(err)
  31  	}
  32  	return hash
  33  }
  34  
  35  func TestBlossomBackupRestore(t *testing.T) {
  36  	src, err := Open(filepath.Join(t.TempDir(), "blossom"), 1<<20)
  37  	if err != nil {
  38  		t.Fatal(err)
  39  	}
  40  	pub := "cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd"
  41  	h1 := putTestBlob(t, src, []byte("first blob"), pub, "text/plain", 1700000000)
  42  	h2 := putTestBlob(t, src, []byte("second blob"), pub, "application/octet-stream", 1700000001)
  43  
  44  	var buf bytes.Buffer
  45  	if err := src.Backup(&buf); err != nil {
  46  		t.Fatal(err)
  47  	}
  48  	if err := src.Close(); err != nil {
  49  		t.Fatal(err)
  50  	}
  51  
  52  	dst, err := Open(filepath.Join(t.TempDir(), "blossom"), 1<<20)
  53  	if err != nil {
  54  		t.Fatal(err)
  55  	}
  56  	defer dst.Close()
  57  	stats, err := dst.Restore(bytes.NewReader(buf.Bytes()))
  58  	if err != nil {
  59  		t.Fatal(err)
  60  	}
  61  	if stats.Restored != 2 {
  62  		t.Fatalf("restored = %d, want 2", stats.Restored)
  63  	}
  64  	blobs := dst.Snapshot()
  65  	if len(blobs) != 2 || blobs[0].Hash != h1 || blobs[1].Hash != h2 {
  66  		t.Fatalf("snapshot: %+v", blobs)
  67  	}
  68  	for _, b := range blobs {
  69  		if b.PubKey != pub || b.Size == 0 || b.Type == "" || b.Created == 0 {
  70  			t.Fatalf("metadata: %+v", b)
  71  		}
  72  		body, err := os.ReadFile(dst.blobPath(b.Hash))
  73  		if err != nil {
  74  			t.Fatal(err)
  75  		}
  76  		sum := sha256.Sum256(body)
  77  		if nostr.Hex(sum[:]) != b.Hash {
  78  			t.Fatalf("content hash mismatch for %s", b.Hash)
  79  		}
  80  	}
  81  
  82  	// A second restore must not duplicate blobs or fail.
  83  	stats, err = dst.Restore(bytes.NewReader(buf.Bytes()))
  84  	if err != nil {
  85  		t.Fatal(err)
  86  	}
  87  	if stats.Restored != 0 || stats.Skipped != 2 {
  88  		t.Fatalf("second restore: %+v", stats)
  89  	}
  90  	if len(dst.Snapshot()) != 2 {
  91  		t.Fatal("second restore duplicated blobs")
  92  	}
  93  }
  94