package log import ( "os" "testing" "github.com/hashicorp/raft" raftboltdb "github.com/rqlite/raft-boltdb/v2" ) func Test_LogNewEmpty(t *testing.T) { path := mustTempFile() defer os.Remove(path) l, err := New(path, false) if err != nil { t.Fatalf("failed to create log: %s", err) } fi, err := l.FirstIndex() if err != nil { t.Fatalf("failed to get first index: %s", err) } if fi != 0 { t.Fatalf("got non-zero value for first index of empty log: %d", fi) } li, err := l.LastIndex() if err != nil { t.Fatalf("failed to get last index: %s", err) } if li != 0 { t.Fatalf("got non-zero value for last index of empty log: %d", li) } lci, err := l.LastCommandIndex(fi, li) if err != nil { t.Fatalf("failed to get last command index: %s", err) } if lci != 0 { t.Fatalf("got wrong value for last command index of not empty log: %d", lci) } f, err := l.HasCommand() if err != nil { t.Fatalf("failed to get has command: %s", err) } if f { t.Fatalf("got wrong value for has command of empty log: %v", f) } } func Test_LogNewExistNotEmpty(t *testing.T) { path := mustTempFile() defer os.Remove(path) // Write some entries directory to the BoltDB Raft store. bs, err := raftboltdb.NewBoltStore(path) if err != nil { t.Fatalf("failed to create bolt store: %s", err) } for i := 4; i > 0; i-- { if err := bs.StoreLog(&raft.Log{ Index: uint64(i), }); err != nil { t.Fatalf("failed to write entry to raft log: %s", err) } } if err := bs.Close(); err != nil { t.Fatalf("failed to close bolt db: %s", err) } l, err := New(path, false) if err != nil { t.Fatalf("failed to create new log: %s", err) } fi, err := l.FirstIndex() if err != nil { t.Fatalf("failed to get first index: %s", err) } if fi != 1 { t.Fatalf("got wrong value for first index of empty log: %d", fi) } li, err := l.LastIndex() if err != nil { t.Fatalf("failed to get last index: %s", err) } if li != 4 { t.Fatalf("got wrong value for last index of not empty log: %d", li) } lci, err := l.LastCommandIndex(fi, li) if err != nil { t.Fatalf("failed to get last command index: %s", err) } if lci != 4 { t.Fatalf("got wrong value for last command index of not empty log: %d", lci) } f, err := l.HasCommand() if err != nil { t.Fatalf("failed to get has command: %s", err) } if !f { t.Fatalf("got wrong value for has command of non-empty log: %v", f) } if err := l.Close(); err != nil { t.Fatalf("failed to close log: %s", err) } // Delete an entry, recheck index functionality. bs, err = raftboltdb.NewBoltStore(path) if err != nil { t.Fatalf("failed to re-open bolt store: %s", err) } if err := bs.DeleteRange(1, 1); err != nil { t.Fatalf("failed to delete range: %s", err) } if err := bs.Close(); err != nil { t.Fatalf("failed to close bolt db: %s", err) } l, err = New(path, false) if err != nil { t.Fatalf("failed to create new log: %s", err) } fi, err = l.FirstIndex() if err != nil { t.Fatalf("failed to get first index: %s", err) } if fi != 2 { t.Fatalf("got wrong value for first index of empty log: %d", fi) } li, err = l.LastIndex() if err != nil { t.Fatalf("failed to get last index: %s", err) } if li != 4 { t.Fatalf("got wrong value for last index of empty log: %d", li) } fi, li, err = l.Indexes() if err != nil { t.Fatalf("failed to get indexes: %s", err) } if fi != 2 { t.Fatalf("got wrong value for first index of empty log: %d", fi) } if li != 4 { t.Fatalf("got wrong value for last index of empty log: %d", li) } if err := l.Close(); err != nil { t.Fatalf("failed to close log: %s", err) } } func Test_LogNewExistNotEmptyNoFreelistSync(t *testing.T) { path := mustTempFile() defer os.Remove(path) // Write some entries directory to the BoltDB Raft store. bs, err := raftboltdb.NewBoltStore(path) if err != nil { t.Fatalf("failed to create bolt store: %s", err) } for i := 4; i > 0; i-- { if err := bs.StoreLog(&raft.Log{ Index: uint64(i), }); err != nil { t.Fatalf("failed to write entry to raft log: %s", err) } } if err := bs.Close(); err != nil { t.Fatalf("failed to close bolt db: %s", err) } l, err := New(path, true) if err != nil { t.Fatalf("failed to create new log: %s", err) } fi, err := l.FirstIndex() if err != nil { t.Fatalf("failed to get first index: %s", err) } if fi != 1 { t.Fatalf("got wrong value for first index of empty log: %d", fi) } li, err := l.LastIndex() if err != nil { t.Fatalf("failed to get last index: %s", err) } if li != 4 { t.Fatalf("got wrong value for last index of not empty log: %d", li) } lci, err := l.LastCommandIndex(fi, li) if err != nil { t.Fatalf("failed to get last command index: %s", err) } if lci != 4 { t.Fatalf("got wrong value for last command index of not empty log: %d", lci) } if err := l.Close(); err != nil { t.Fatalf("failed to close log: %s", err) } // Delete an entry, recheck index functionality. bs, err = raftboltdb.NewBoltStore(path) if err != nil { t.Fatalf("failed to re-open bolt store: %s", err) } if err := bs.DeleteRange(1, 1); err != nil { t.Fatalf("failed to delete range: %s", err) } if err := bs.Close(); err != nil { t.Fatalf("failed to close bolt db: %s", err) } l, err = New(path, true) if err != nil { t.Fatalf("failed to create new log: %s", err) } fi, err = l.FirstIndex() if err != nil { t.Fatalf("failed to get first index: %s", err) } if fi != 2 { t.Fatalf("got wrong value for first index of empty log: %d", fi) } li, err = l.LastIndex() if err != nil { t.Fatalf("failed to get last index: %s", err) } if li != 4 { t.Fatalf("got wrong value for last index of empty log: %d", li) } fi, li, err = l.Indexes() if err != nil { t.Fatalf("failed to get indexes: %s", err) } if fi != 2 { t.Fatalf("got wrong value for first index of empty log: %d", fi) } if li != 4 { t.Fatalf("got wrong value for last index of empty log: %d", li) } if err := l.Close(); err != nil { t.Fatalf("failed to close log: %s", err) } } func Test_LogDeleteAll(t *testing.T) { path := mustTempFile() defer os.Remove(path) // Write some entries directory to the BoltDB Raft store. bs, err := raftboltdb.NewBoltStore(path) if err != nil { t.Fatalf("failed to create bolt store: %s", err) } for i := 1; i < 5; i++ { if err := bs.StoreLog(&raft.Log{ Index: uint64(i), }); err != nil { t.Fatalf("failed to write entry to raft log: %s", err) } } if err := bs.Close(); err != nil { t.Fatalf("failed to close bolt db: %s", err) } l, err := New(path, true) if err != nil { t.Fatalf("failed to create new log: %s", err) } // Check indices. fi, err := l.FirstIndex() if err != nil { t.Fatalf("failed to get first index: %s", err) } if fi != 1 { t.Fatalf("got wrong value for first index of log: %d", fi) } li, err := l.LastIndex() if err != nil { t.Fatalf("failed to get last index: %s", err) } if li != 4 { t.Fatalf("got wrong value for last index of log: %d", li) } lci, err := l.LastCommandIndex(fi, li) if err != nil { t.Fatalf("failed to get last command index: %s", err) } if lci != 4 { t.Fatalf("got wrong value for last command index of log: %d", lci) } if err := l.DeleteRange(1, 4); err != nil { t.Fatalf("failed to delete range: %s", err) } fi, err = l.FirstIndex() if err != nil { t.Fatalf("failed to get first index: %s", err) } if fi != 0 { t.Fatalf("got wrong value for first index of empty log: %d", fi) } li, err = l.LastIndex() if err != nil { t.Fatalf("failed to get last index: %s", err) } if li != 0 { t.Fatalf("got wrong value for last index of empty log: %d", li) } f, err := l.HasCommand() if err != nil { t.Fatalf("failed to get has command: %s", err) } if f { t.Fatalf("got wrong value for has command of empty log: %v", f) } } func Test_LogLastCommandIndexNotExist(t *testing.T) { path := mustTempFile() defer os.Remove(path) // Write some entries directory to the BoltDB Raft store. bs, err := raftboltdb.NewBoltStore(path) if err != nil { t.Fatalf("failed to create bolt store: %s", err) } for i := 4; i > 0; i-- { if err := bs.StoreLog(&raft.Log{ Index: uint64(i), Type: raft.LogNoop, }); err != nil { t.Fatalf("failed to write entry to raft log: %s", err) } } if err := bs.Close(); err != nil { t.Fatalf("failed to close bolt db: %s", err) } l, err := New(path, false) if err != nil { t.Fatalf("failed to create new log: %s", err) } fi, err := l.FirstIndex() if err != nil { t.Fatalf("failed to get first index: %s", err) } if fi != 1 { t.Fatalf("got wrong value for first index of empty log: %d", fi) } li, err := l.LastIndex() if err != nil { t.Fatalf("failed to get last index: %s", err) } if li != 4 { t.Fatalf("got wrong for last index of not empty log: %d", li) } lci, err := l.LastCommandIndex(fi, li) if err != nil { t.Fatalf("failed to get last command index: %s", err) } if lci != 0 { t.Fatalf("got wrong value for last command index of not empty log: %d", lci) } if err := l.Close(); err != nil { t.Fatalf("failed to close log: %s", err) } // Delete first log. bs, err = raftboltdb.NewBoltStore(path) if err != nil { t.Fatalf("failed to re-open bolt store: %s", err) } if err := bs.DeleteRange(1, 1); err != nil { t.Fatalf("failed to delete range: %s", err) } if err := bs.Close(); err != nil { t.Fatalf("failed to close bolt db: %s", err) } l, err = New(path, false) if err != nil { t.Fatalf("failed to create new log: %s", err) } fi, li, err = l.Indexes() if err != nil { t.Fatalf("failed to get indexes: %s", err) } lci, err = l.LastCommandIndex(fi, li) if err != nil { t.Fatalf("failed to get last command index: %s", err) } if lci != 0 { t.Fatalf("got wrong value for last command index of not empty log: %d", lci) } } func Test_LogAppliedIndex(t *testing.T) { path := mustTempFile() defer os.Remove(path) l, err := New(path, false) if err != nil { t.Fatalf("failed to create new log: %s", err) } ai, err := l.GetAppliedIndex() if err != nil { t.Fatalf("failed to get applied index: %s", err) } if ai != 0 { t.Fatalf("got wrong applied index for non-existent key: %d", ai) } if l.SetAppliedIndex(1234); err != nil { t.Fatalf("failed to set applied index: %s", err) } ai, err = l.GetAppliedIndex() if err != nil { t.Fatalf("failed to get applied index: %s", err) } if ai != 1234 { t.Fatalf("got wrong applied index: %d", ai) } } func Test_LogStats(t *testing.T) { path := mustTempFile() defer os.Remove(path) // Write some entries directory to the BoltDB Raft store. bs, err := raftboltdb.NewBoltStore(path) if err != nil { t.Fatalf("failed to create bolt store: %s", err) } for i := 4; i > 0; i-- { if err := bs.StoreLog(&raft.Log{ Index: uint64(i), }); err != nil { t.Fatalf("failed to write entry to raft log: %s", err) } } _ = bs.Stats() if err := bs.Close(); err != nil { t.Fatalf("failed to close bolt db: %s", err) } } // mustTempFile returns a path to a temporary file in directory dir. It is up to the // caller to remove the file once it is no longer needed. func mustTempFile() string { tmpfile, err := os.CreateTemp("", "rqlite-db-test") if err != nil { panic(err.Error()) } tmpfile.Close() return tmpfile.Name() }