// The sorted-file paths the mixed-source tests do not reach: the .buf sidecar // branch of GetPrefix, Last across all three sources, Open's two file errors, // refreshSidecar on a handle without a sidecar, and Flush when there is nothing // to merge. package sorted import ( "os" "testing" ) func TestGetPrefixFromSidecar(t *testing.T) { dir, ok := srTmp(t) if !ok { return } defer os.RemoveAll(dir) f, ok2 := srOpen(t, dir|"/idx.dat") if !ok2 { return } defer f.Close() // Only the sidecar carries the record: QuickFlush moves the in-memory // buffer into .buf and leaves the main file empty. f.Put(srKey(1, 1, 0, 0, 0x0A)) if _, qerr := f.QuickFlush(1, false); qerr != nil { t.Fatal(qerr) return } rec, found := f.GetPrefix([]byte{1, 1}) if !found { t.Fatal("GetPrefix must find a record in the .buf sidecar") } if rec[0] != 1 || rec[1] != 1 { t.Fatal("GetPrefix returned a non-matching sidecar record") } if _, found2 := f.GetPrefix([]byte{2}); found2 { t.Fatal("GetPrefix of an absent prefix") } } func TestGetPrefixEmptyFile(t *testing.T) { dir, ok := srTmp(t) if !ok { return } defer os.RemoveAll(dir) f, ok2 := srOpen(t, dir|"/idx.dat") if !ok2 { return } defer f.Close() if rec, found := f.GetPrefix([]byte{1}); found || rec != nil { t.Fatal("GetPrefix on an empty file must report no match") } if rec, found := f.Last(); found || rec != nil { t.Fatal("Last on an empty file must report no record") } } func TestLastAcrossSources(t *testing.T) { dir, ok := srTmp(t) if !ok { return } defer os.RemoveAll(dir) f, ok2 := srOpen(t, dir|"/idx.dat") if !ok2 { return } defer f.Close() // Sidecar only. f.Put(srKey(5, 0, 0, 0, 0x05)) if _, qerr := f.QuickFlush(1, false); qerr != nil { t.Fatal(qerr) return } rec, found := f.Last() if !found || rec[0] != 5 { t.Fatalf("Last from the sidecar = %v", rec) } // Main file and sidecar: the file holds the larger key. f.Put(srKey(9, 0, 0, 0, 0x09)) if err := f.Flush(); err != nil { t.Fatal(err) return } f.Put(srKey(3, 0, 0, 0, 0x03)) if _, qerr := f.QuickFlush(2, false); qerr != nil { t.Fatal(qerr) return } rec2, found2 := f.Last() if !found2 || rec2[0] != 9 { t.Fatalf("Last with the largest key in the main file = %v", rec2) } // The memory buffer holds the largest key. f.Put(srKey(11, 0, 0, 0, 0x0B)) rec3, found3 := f.Last() if !found3 || rec3[0] != 11 { t.Fatalf("Last with the largest key in the memory buffer = %v", rec3) } } func TestOpenRejectsUnopenablePaths(t *testing.T) { dir, ok := srTmp(t) if !ok { return } defer os.RemoveAll(dir) // The index path is a directory. if err := os.MkdirAll(dir|"/idx.dat", 0755); err != nil { t.Fatal(err) } if f, oerr := Open(dir|"/idx.dat", 4, 4); oerr == nil { f.Close() t.Fatal("Open must fail when the index path is a directory") } // The sidecar path is a directory. if err := os.MkdirAll(dir|"/side.dat.buf", 0755); err != nil { t.Fatal(err) } if f, oerr := Open(dir|"/side.dat", 4, 4); oerr == nil { f.Close() t.Fatal("Open must fail when the sidecar path is a directory") } } func TestRefreshSidecarWithoutOne(t *testing.T) { // A File built without Open has no sidecar: refreshSidecar and readBufFile // must be no-ops rather than dereferencing nil. f := &File{recLen: 4, cmpLen: 4} f.refreshSidecar() if f.bufCount != 0 { t.Fatal("a handle without a sidecar must report no sidecar records") } if buf := f.readBufFile(); buf != nil { t.Fatal("readBufFile without a sidecar must return nil") } } func TestFlushWithNothingToMerge(t *testing.T) { dir, ok := srTmp(t) if !ok { return } defer os.RemoveAll(dir) f, ok2 := srOpen(t, dir|"/idx.dat") if !ok2 { return } defer f.Close() // An untouched file flushes cleanly. if err := f.Flush(); err != nil { t.Fatal(err) return } // Deleting every record and flushing must leave an empty file that still // answers Count and Get. f.Put(srRec(1, 0x01)) f.Put(srRec(2, 0x02)) if err := f.Flush(); err != nil { t.Fatal(err) return } f.Delete(srRec(1, 0)) f.Delete(srRec(2, 0)) if err := f.Flush(); err != nil { t.Fatal(err) return } if n := f.Count(); n != 0 { t.Fatalf("Count after deleting everything = %d", n) } if _, found := f.Get(srRec(1, 0)); found { t.Fatal("a deleted record must not be found") } }