package main import ( "errors" "io" "os" "strings" "testing" "time" ) func newStorage(t *testing.T) *Storage { t.Helper() dir := t.TempDir() s, err := NewStorage(dir) if err != nil { t.Fatalf("NewStorage: %v", err) } return s } func buildManifest(id, sha string) *Manifest { return &Manifest{ SnapshotID: id, CreatedAt: time.Date(2026, 6, 2, 12, 0, 0, 0, time.UTC), Files: map[string]FileEntry{ "options.txt": { SHA256: sha, Size: 5, Mtime: time.Date(2026, 6, 2, 11, 30, 0, 0, time.UTC), }, }, } } func TestStorage_RoundTripSnapshot(t *testing.T) { s := newStorage(t) sha, err := s.WriteBlob("123456789", []byte("hello")) if err != nil { t.Fatalf("WriteBlob: %v", err) } if sha != HashBytes([]byte("hello")) { t.Fatalf("sha mismatch: got %s", sha) } m := buildManifest("01J9XQK4Z3DEMO0001", sha) if err := s.StoreSnapshot("123456789", m, []byte("fake-tarball-bytes")); err != nil { t.Fatalf("StoreSnapshot: %v", err) } got, err := s.ReadManifest("123456789") if err != nil { t.Fatalf("ReadManifest: %v", err) } if got.SnapshotID != "01J9XQK4Z3DEMO0001" { t.Errorf("snapshot id mismatch: got %s", got.SnapshotID) } if _, ok := got.Files["options.txt"]; !ok { t.Errorf("files key missing") } rc, err := s.ReadBlob("123456789", sha) if err != nil { t.Fatalf("ReadBlob: %v", err) } defer rc.Close() data, _ := io.ReadAll(rc) if string(data) != "hello" { t.Errorf("blob content: got %q", string(data)) } } func TestStorage_BlobDedupe(t *testing.T) { s := newStorage(t) a, _ := s.WriteBlob("u1", []byte("xyz")) b, _ := s.WriteBlob("u1", []byte("xyz")) if a != b { t.Errorf("dedupe broken: got %s vs %s", a, b) } // Both calls should leave one blob on disk p := s.blobPath("u1", a) info, err := os.Stat(p) if err != nil { t.Fatalf("stat blob: %v", err) } if info.Size() != 3 { t.Errorf("blob size: got %d, want 3", info.Size()) } } func TestStorage_ReadManifest_MissingIsNotExist(t *testing.T) { s := newStorage(t) _, err := s.ReadManifest("never-stored") if !errors.Is(err, os.ErrNotExist) { t.Fatalf("expected os.ErrNotExist, got %v", err) } } func TestStorage_ListSnapshots_NewestFirst(t *testing.T) { s := newStorage(t) user := "u1" sha, _ := s.WriteBlob(user, []byte("x")) for _, id := range []string{"01ABC", "01BBB", "01ZZZ"} { m := buildManifest(id, sha) if err := s.StoreSnapshot(user, m, []byte("tar")); err != nil { t.Fatalf("StoreSnapshot %s: %v", id, err) } } list, err := s.ListSnapshots(user) if err != nil { t.Fatalf("ListSnapshots: %v", err) } if len(list) != 3 { t.Fatalf("expected 3 snapshots, got %d", len(list)) } if list[0].ID != "01ZZZ" || list[2].ID != "01ABC" { t.Errorf("ordering wrong: %v", []string{list[0].ID, list[1].ID, list[2].ID}) } } func TestStorage_DeleteSnapshot_RefusesLatest(t *testing.T) { s := newStorage(t) sha, _ := s.WriteBlob("u", []byte("y")) m := buildManifest("01OLD0", sha) if err := s.StoreSnapshot("u", m, []byte("t1")); err != nil { t.Fatal(err) } m2 := buildManifest("01NEW0", sha) if err := s.StoreSnapshot("u", m2, []byte("t2")); err != nil { t.Fatal(err) } // Latest = 01NEW0; deleting it should fail if err := s.DeleteSnapshot("u", "01NEW0"); err == nil { t.Error("expected error deleting latest, got nil") } // Old one deletes if err := s.DeleteSnapshot("u", "01OLD0"); err != nil { t.Errorf("delete old: %v", err) } } func TestStorage_StoreSnapshot_RejectsBadManifest(t *testing.T) { s := newStorage(t) bad := &Manifest{} if err := s.StoreSnapshot("u", bad, []byte("x")); err == nil { t.Error("expected validation error for empty manifest") } } func TestStorage_RejectsBadUserID(t *testing.T) { s := newStorage(t) cases := []string{"", "../etc", "u/path", "user\\back", "with.dot"} for _, u := range cases { t.Run(u, func(t *testing.T) { _, err := s.WriteBlob(u, []byte("x")) if err == nil { t.Errorf("user %q: expected error", u) } }) } } func TestStorage_RejectsBadSnapshotID(t *testing.T) { s := newStorage(t) cases := []string{"", "../escape", "with/slash", strings.Repeat("a", 100), "with space"} for _, id := range cases { t.Run(id, func(t *testing.T) { _, err := s.OpenSnapshot("u", id) if err == nil { t.Errorf("id %q: expected error", id) } }) } } func TestStorage_UsageBytes(t *testing.T) { s := newStorage(t) user := "u" _, _ = s.WriteBlob(user, []byte("0123456789")) // 10 bytes m := buildManifest("01X", HashBytes([]byte("0123456789"))) if err := s.StoreSnapshot(user, m, make([]byte, 50)); err != nil { t.Fatal(err) } got, err := s.UsageBytes(user) if err != nil { t.Fatalf("UsageBytes: %v", err) } // 10 (blob) + 50 (tar) + manifest JSON on disk (variable but small) if got < 60 { t.Errorf("expected at least 60 bytes, got %d", got) } }