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