mirror of
https://github.com/featurebasedb/featurebase.git
synced 2026-08-28 10:54:59 +00:00
198 lines
5.8 KiB
Go
198 lines
5.8 KiB
Go
// Copyright 2021 Molecula Corp. All rights reserved.
|
|
package pilosa_test
|
|
|
|
import (
|
|
"os"
|
|
"strings"
|
|
"testing"
|
|
|
|
pilosa "github.com/featurebasedb/featurebase/v3"
|
|
"github.com/featurebasedb/featurebase/v3/test"
|
|
"github.com/pkg/errors"
|
|
)
|
|
|
|
func TestHolder_Open(t *testing.T) {
|
|
t.Run("ErrIndexPermission", func(t *testing.T) {
|
|
if os.Geteuid() == 0 {
|
|
t.Skip("Skipping permissions test since user is root.")
|
|
}
|
|
// Manual open because MustOpenHolder closes automatically and fails the test on
|
|
// double-close.
|
|
h := test.NewHolder(t)
|
|
err := h.Open()
|
|
if err != nil {
|
|
t.Fatalf("opening holder: %v", err)
|
|
}
|
|
// no automatic close here, because we manually close this, and then
|
|
// *fail* to reopen it.
|
|
|
|
if _, err := h.CreateIndex("test", "", pilosa.IndexOptions{}); err != nil {
|
|
t.Fatal(err)
|
|
} else if err := h.Close(); err != nil {
|
|
t.Fatal(err)
|
|
} else if err := os.Chmod(h.IndexPath("test"), 0000); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer func() {
|
|
_ = os.Chmod(h.IndexPath("test"), 0755)
|
|
}()
|
|
|
|
if err := h.Reopen(); err == nil || !strings.Contains(err.Error(), "permission denied") {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
})
|
|
t.Run("ForeignIndex", func(t *testing.T) {
|
|
t.Run("ErrForeignIndexNotFound", func(t *testing.T) {
|
|
h := test.MustOpenHolder(t)
|
|
|
|
if idx, err := h.CreateIndex("foo", "", pilosa.IndexOptions{}); err != nil {
|
|
t.Fatal(err)
|
|
} else {
|
|
_, err := idx.CreateField("bar", "", pilosa.OptFieldTypeInt(0, 100), pilosa.OptFieldForeignIndex("nonexistent"))
|
|
if err == nil {
|
|
t.Fatalf("expected error: %s", pilosa.ErrForeignIndexNotFound)
|
|
} else if errors.Cause(err) != pilosa.ErrForeignIndexNotFound {
|
|
t.Fatalf("expected error: %s, but got: %s", pilosa.ErrForeignIndexNotFound, err)
|
|
}
|
|
}
|
|
})
|
|
|
|
// Foreign index zzz is opened after foo/bar.
|
|
t.Run("ForeignIndexNotOpenYet", func(t *testing.T) {
|
|
h := test.MustOpenHolder(t)
|
|
|
|
if _, err := h.CreateIndex("zzz", "", pilosa.IndexOptions{}); err != nil {
|
|
t.Fatal(err)
|
|
} else if idx, err := h.CreateIndex("foo", "", pilosa.IndexOptions{}); err != nil {
|
|
t.Fatal(err)
|
|
} else if _, err := idx.CreateField("bar", "", pilosa.OptFieldTypeInt(0, 100), pilosa.OptFieldForeignIndex("zzz")); err != nil {
|
|
t.Fatal(err)
|
|
} else if err := h.Holder.Close(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if err := h.Reopen(); err != nil {
|
|
t.Fatalf("unexpected error: %s", err)
|
|
}
|
|
})
|
|
|
|
// Foreign index aaa is opened before foo/bar.
|
|
t.Run("ForeignIndexIsOpen", func(t *testing.T) {
|
|
h := test.MustOpenHolder(t)
|
|
|
|
if _, err := h.CreateIndex("aaa", "", pilosa.IndexOptions{}); err != nil {
|
|
t.Fatal(err)
|
|
} else if idx, err := h.CreateIndex("foo", "", pilosa.IndexOptions{}); err != nil {
|
|
t.Fatal(err)
|
|
} else if _, err := idx.CreateField("bar", "", pilosa.OptFieldTypeInt(0, 100), pilosa.OptFieldForeignIndex("aaa")); err != nil {
|
|
t.Fatal(err)
|
|
} else if err := h.Holder.Close(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if err := h.Reopen(); err != nil {
|
|
t.Fatalf("unexpected error: %s", err)
|
|
}
|
|
})
|
|
|
|
// Try to re-create existing index
|
|
t.Run("CreateIndexIfNotExists", func(t *testing.T) {
|
|
h := test.MustOpenHolder(t)
|
|
|
|
idx1, err := h.CreateIndexIfNotExists("aaa", "", pilosa.IndexOptions{})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if _, err = h.CreateIndex("aaa", "", pilosa.IndexOptions{}); err == nil {
|
|
t.Fatalf("expected: ConflictError, got: nil")
|
|
} else if _, ok := err.(pilosa.ConflictError); !ok {
|
|
t.Fatalf("expected: ConflictError, got: %s", err)
|
|
}
|
|
|
|
idx2, err := h.CreateIndexIfNotExists("aaa", "", pilosa.IndexOptions{})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if idx1 != idx2 {
|
|
t.Fatalf("expected the same indexes, got: %s and %s", idx1.Name(), idx2.Name())
|
|
}
|
|
})
|
|
})
|
|
}
|
|
|
|
func TestHolder_HasData(t *testing.T) {
|
|
t.Run("IndexDirectory", func(t *testing.T) {
|
|
h := test.MustOpenHolder(t)
|
|
|
|
if ok, err := h.HasData(); ok || err != nil {
|
|
t.Fatal("expected HasData to return false, no err, but", ok, err)
|
|
}
|
|
|
|
if _, err := h.CreateIndex("test", "", pilosa.IndexOptions{}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if ok, err := h.HasData(); !ok || err != nil {
|
|
t.Fatal("expected HasData to return true, but ", ok, err)
|
|
}
|
|
})
|
|
|
|
t.Run("Peek", func(t *testing.T) {
|
|
h := test.MustOpenHolder(t)
|
|
|
|
if ok, err := h.HasData(); ok || err != nil {
|
|
t.Fatal("expected HasData to return false, no err, but", ok, err)
|
|
}
|
|
|
|
// Create an index directory to indicate data exists.
|
|
if err := os.Mkdir(h.IndexPath("test"), 0750); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if ok, err := h.HasData(); !ok || err != nil {
|
|
t.Fatal("expected HasData to return true, no err, but", ok, err)
|
|
}
|
|
})
|
|
|
|
t.Run("Peek at missing directory", func(t *testing.T) {
|
|
// Ensure that hasData is false when dir doesn't exist.
|
|
|
|
// Note that we are intentionally not using test.NewHolder,
|
|
// because we want to create a Holder object with an invalid path,
|
|
// rather than creating a valid holder with a temporary path.
|
|
h := pilosa.NewHolder("bad-path", pilosa.TestHolderConfig())
|
|
|
|
if ok, err := h.HasData(); ok || err != nil {
|
|
t.Fatal("expected HasData to return false, no err, but", ok, err)
|
|
}
|
|
})
|
|
}
|
|
|
|
// Ensure holder can delete an index and its underlying files.
|
|
func TestHolder_DeleteIndex(t *testing.T) {
|
|
|
|
hldr := test.MustOpenHolder(t)
|
|
|
|
// Write bits to separate indexes.
|
|
hldr.SetBit("i0", "f", 100, 200)
|
|
hldr.SetBit("i1", "f", 100, 200)
|
|
|
|
// Ensure i0 exists.
|
|
if _, err := os.Stat(hldr.IndexPath("i0")); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// Delete i0.
|
|
if err := hldr.DeleteIndex("i0"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// Ensure i0 files are removed & i1 still exists.
|
|
if _, err := os.Stat(hldr.IndexPath("i0")); !os.IsNotExist(err) {
|
|
t.Fatal("expected i0 file deletion")
|
|
} else if _, err := os.Stat(hldr.IndexPath("i1")); err != nil {
|
|
t.Fatal("expected i1 files to still exist", err)
|
|
}
|
|
}
|