Files
Snow LeeandClaude Fable 5 1ac128db2c rename: sfs -> BearDrive everywhere; CLI becomes bdrive
Product and project are BearDrive; the CLI binary is bdrive (bdrive mnt,
bdrive sync, ...), the web viewer is bdrive-web. All conventions follow
the full name: .beardrive settings file, .beardriveignore, ~/.beardrive
home, BEARDRIVE_HOME, .beardrive-conflict-* / .beardrive-tmp-* files.
Plugin/skill/marketplace renamed to beardrive (/beardrive:mount).

Module path is now github.com/runbear-io/beardrive — merge only after
renaming the GitHub repo, or go install breaks.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HHEUaYfFHhmDvqLYw74Ehz
2026-07-07 15:02:40 -07:00

117 lines
2.6 KiB
Go

package store
import (
"os"
"testing"
"github.com/runbear-io/beardrive/internal/journal"
)
func TestBlobRoundtrip(t *testing.T) {
s, err := Open(t.TempDir())
if err != nil {
t.Fatal(err)
}
sum, n, err := s.PutBlobBytes([]byte("hello beardrive"))
if err != nil {
t.Fatal(err)
}
if n != int64(len("hello beardrive")) {
t.Fatalf("size = %d, want %d", n, len("hello beardrive"))
}
if !s.HasBlob(sum) {
t.Fatal("blob not stored")
}
// dedupe: same content, same sum, no error
sum2, _, err := s.PutBlobBytes([]byte("hello beardrive"))
if err != nil || sum2 != sum {
t.Fatalf("dedupe failed: %v %v", sum2, err)
}
f, err := s.OpenBlob(sum)
if err != nil {
t.Fatal(err)
}
defer f.Close()
data := make([]byte, 16)
k, _ := f.Read(data)
if string(data[:k]) != "hello beardrive" {
t.Fatalf("content mismatch: %q", data[:k])
}
}
func TestJournalAndState(t *testing.T) {
s, err := Open(t.TempDir())
if err != nil {
t.Fatal(err)
}
ops := []journal.Op{{Seq: 1, Lamport: 1, Device: "devA", Kind: journal.KindPut, Path: "f", Blob: "b"}}
if err := s.AppendOps("devA", ops); err != nil {
t.Fatal(err)
}
if err := s.AppendOps("devB", []journal.Op{{Seq: 1, Lamport: 2, Device: "devB", Kind: journal.KindDelete, Path: "f"}}); err != nil {
t.Fatal(err)
}
all, err := s.AllOps()
if err != nil || len(all) != 2 {
t.Fatalf("AllOps = %v, %v", all, err)
}
devs, _ := s.Devices()
if len(devs) != 2 {
t.Fatalf("Devices = %v", devs)
}
cache := map[string]CachedFile{"f": {Blob: "b", Size: 1, MTimeNS: 42}}
if err := s.SaveCache("m1", cache); err != nil {
t.Fatal(err)
}
got, err := s.LoadCache("m1")
if err != nil || got["f"].MTimeNS != 42 {
t.Fatalf("cache roundtrip: %v %v", got, err)
}
// caches are isolated per mount
other, err := s.LoadCache("m2")
if err != nil || len(other) != 0 {
t.Fatalf("mount caches must be isolated: %v %v", other, err)
}
st := SyncState{Lamport: 7, PushedOps: 3}
if err := s.SaveSync(st); err != nil {
t.Fatal(err)
}
gotSt, err := s.LoadSync()
if err != nil || gotSt != st {
t.Fatalf("sync state roundtrip: %v %v", gotSt, err)
}
}
func TestLock(t *testing.T) {
s, err := Open(t.TempDir())
if err != nil {
t.Fatal(err)
}
unlock, err := s.Lock()
if err != nil {
t.Fatal(err)
}
if err := unlock(); err != nil {
t.Fatal(err)
}
// re-acquirable after unlock
unlock2, err := s.Lock()
if err != nil {
t.Fatal(err)
}
unlock2()
}
func TestWriteFileAtomic(t *testing.T) {
p := t.TempDir() + "/x.json"
if err := WriteFileAtomic(p, []byte("data"), 0o644); err != nil {
t.Fatal(err)
}
b, err := os.ReadFile(p)
if err != nil || string(b) != "data" {
t.Fatalf("got %q %v", b, err)
}
}