package meta import ( "crypto/rand" "crypto/sha256" "os" "testing" "github.com/nspcc-dev/neofs-api-go/pkg" "github.com/nspcc-dev/neofs-api-go/pkg/container" objectSDK "github.com/nspcc-dev/neofs-api-go/pkg/object" "github.com/nspcc-dev/neofs-api-go/pkg/owner" v2object "github.com/nspcc-dev/neofs-api-go/v2/object" "github.com/nspcc-dev/neofs-node/pkg/core/object" "github.com/nspcc-dev/neofs-node/pkg/util/test" "github.com/pkg/errors" "github.com/stretchr/testify/require" "go.etcd.io/bbolt" ) func testSelect(t *testing.T, db *DB, fs objectSDK.SearchFilters, exp ...*objectSDK.Address) { res, err := db.Select(fs) require.NoError(t, err) require.Len(t, res, len(exp)) for i := range exp { require.Contains(t, res, exp[i]) } } func testCID() *container.ID { cs := [sha256.Size]byte{} rand.Read(cs[:]) id := container.NewID() id.SetSHA256(cs) return id } func testOID() *objectSDK.ID { cs := [sha256.Size]byte{} rand.Read(cs[:]) id := objectSDK.NewID() id.SetSHA256(cs) return id } func TestDB(t *testing.T) { version := pkg.NewVersion() version.SetMajor(2) version.SetMinor(1) cid := testCID() w, err := owner.NEO3WalletFromPublicKey(&test.DecodeKey(-1).PublicKey) require.NoError(t, err) ownerID := owner.NewID() ownerID.SetNeo3Wallet(w) oid := testOID() obj := object.NewRaw() obj.SetID(oid) obj.SetOwnerID(ownerID) obj.SetContainerID(cid) obj.SetVersion(version) k, v := "key", "value" a := objectSDK.NewAttribute() a.SetKey(k) a.SetValue(v) obj.SetAttributes(a) db := newDB(t) defer releaseDB(db) o := obj.Object() require.NoError(t, db.Put(o)) o2, err := db.Get(o.Address()) require.NoError(t, err) require.Equal(t, o, o2) fs := objectSDK.SearchFilters{} // filter container ID fs.AddObjectContainerIDFilter(objectSDK.MatchStringEqual, cid) testSelect(t, db, fs, o.Address()) // filter owner ID fs.AddObjectOwnerIDFilter(objectSDK.MatchStringEqual, ownerID) testSelect(t, db, fs, o.Address()) // filter attribute fs.AddFilter(k, v, objectSDK.MatchStringEqual) testSelect(t, db, fs, o.Address()) // filter mismatch fs.AddFilter(k, v+"1", objectSDK.MatchStringEqual) testSelect(t, db, fs) } func TestDB_Delete(t *testing.T) { db := newDB(t) defer releaseDB(db) obj := object.NewRaw() obj.SetContainerID(testCID()) obj.SetID(testOID()) o := obj.Object() require.NoError(t, db.Put(o)) addr := o.Address() _, err := db.Get(addr) require.NoError(t, err) fs := objectSDK.SearchFilters{} fs.AddObjectContainerIDFilter(objectSDK.MatchStringEqual, o.GetContainerID()) testSelect(t, db, fs, o.Address()) require.NoError(t, db.Delete(addr)) _, err = db.Get(addr) require.Error(t, err) testSelect(t, db, fs) } func TestDB_SelectProperties(t *testing.T) { db := newDB(t) defer releaseDB(db) parent := object.NewRaw() parent.SetContainerID(testCID()) parent.SetID(testOID()) child := object.NewRaw() child.SetContainerID(testCID()) child.SetID(testOID()) child.SetParent(parent.Object().SDK()) parAddr := parent.Object().Address() childAddr := child.Object().Address() require.NoError(t, db.Put(child.Object())) // root filter fs := objectSDK.SearchFilters{} fs.AddRootFilter() testSelect(t, db, fs, parAddr) // non-root filter fs = fs[:0] fs.AddNonRootFilter() testSelect(t, db, fs, childAddr) // root filter (with random false value) fs = fs[:0] fs.AddFilter(v2object.FilterPropertyRoot, "some false value", objectSDK.MatchStringEqual) testSelect(t, db, fs, childAddr) // leaf filter fs = fs[:0] fs.AddLeafFilter() testSelect(t, db, fs, childAddr) // non-leaf filter fs = fs[:0] fs.AddNonLeafFilter() testSelect(t, db, fs, parAddr) // leaf filter (with random false value) fs = fs[:0] fs.AddFilter(v2object.FilterPropertyLeaf, "some false value", objectSDK.MatchStringEqual) testSelect(t, db, fs, parAddr) lnk := object.NewRaw() lnk.SetContainerID(testCID()) lnk.SetID(testOID()) lnk.SetChildren(testOID()) lnkAddr := lnk.Object().Address() require.NoError(t, db.Put(lnk.Object())) // childfree filter fs = fs[:0] fs.AddChildfreeFilter() testSelect(t, db, fs, childAddr, parAddr) // non-childfree filter fs = fs[:0] fs.AddNonChildfreeFilter() testSelect(t, db, fs, lnkAddr) // childfree filter (with random false value) fs = fs[:0] fs.AddFilter(v2object.FilterPropertyChildfree, "some false value", objectSDK.MatchStringEqual) testSelect(t, db, fs, lnkAddr) } func TestDB_Path(t *testing.T) { path := t.Name() bdb, err := bbolt.Open(path, 0600, nil) require.NoError(t, err) db := NewDB(bdb) defer releaseDB(db) require.Equal(t, path, db.Path()) } func newDB(t testing.TB) *DB { path := t.Name() bdb, err := bbolt.Open(path, 0600, nil) require.NoError(t, err) return NewDB(bdb) } func releaseDB(db *DB) { db.Close() os.Remove(db.Path()) } func TestSelectNonExistentAttributes(t *testing.T) { db := newDB(t) defer releaseDB(db) obj := object.NewRaw() obj.SetID(testOID()) obj.SetContainerID(testCID()) require.NoError(t, db.Put(obj.Object())) fs := objectSDK.SearchFilters{} // add filter by non-existent attribute fs.AddFilter("key", "value", objectSDK.MatchStringEqual) res, err := db.Select(fs) require.NoError(t, err) require.Empty(t, res) } func TestVirtualObject(t *testing.T) { db := newDB(t) defer releaseDB(db) // create object with parent obj := generateObject(t, testPrm{ withParent: true, }) require.NoError(t, db.Put(obj)) childAddr := obj.Address() parAddr := obj.GetParent().Address() // child object must be readable _, err := db.Get(childAddr) require.NoError(t, err) // parent object must not be readable _, err = db.Get(parAddr) require.True(t, errors.Is(err, errNotFound)) fs := objectSDK.SearchFilters{} // both objects should appear in selection testSelect(t, db, fs, childAddr, parAddr) // filter leaves fs.AddLeafFilter() // only child object should appear testSelect(t, db, fs, childAddr) fs = fs[:0] // filter non-leaf objects fs.AddNonLeafFilter() // only parent object should appear testSelect(t, db, fs, parAddr) }