[#XX] Add fallback tests

Signed-off-by: Denis Kirillov <d.kirillov@yadro.com>
This commit is contained in:
Denis Kirillov 2025-04-23 10:52:19 +03:00
parent 626a18f540
commit 99eaae2042
2 changed files with 193 additions and 97 deletions

View file

@ -26,6 +26,7 @@ import (
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/netmap" "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/netmap"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object" "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id" oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
oidtest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id/test"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user" "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys" "github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/panjf2000/ants/v2" "github.com/panjf2000/ants/v2"
@ -64,6 +65,7 @@ func (t *treeServiceMock) GetLatestVersion(context.Context, *cid.ID, string) (*d
type configMock struct { type configMock struct {
additionalFilenameSearch bool additionalFilenameSearch bool
additionalSlashSearch bool additionalSlashSearch bool
indexEnabled bool
cors *data.CORSRule cors *data.CORSRule
} }
@ -76,7 +78,7 @@ func (c *configMock) ArchiveCompression() bool {
} }
func (c *configMock) IndexPageEnabled() bool { func (c *configMock) IndexPageEnabled() bool {
return false return c.indexEnabled
} }
func (c *configMock) IndexPageTemplate() string { func (c *configMock) IndexPageTemplate() string {
@ -259,6 +261,7 @@ func TestBasic(t *testing.T) {
err = json.Unmarshal(r.Response.Body(), &putRes) err = json.Unmarshal(r.Response.Body(), &putRes)
require.NoError(t, err) require.NoError(t, err)
hc.cfg.additionalFilenameSearch = true
obj := hc.frostfs.objects[putRes.ContainerID+"/"+putRes.ObjectID] obj := hc.frostfs.objects[putRes.ContainerID+"/"+putRes.ObjectID]
fileName := prepareObjectAttributes(object.AttributeFileName, objFileName) fileName := prepareObjectAttributes(object.AttributeFileName, objFileName)
filePath := prepareObjectAttributes(object.AttributeFilePath, objFilePath) filePath := prepareObjectAttributes(object.AttributeFilePath, objFilePath)
@ -269,6 +272,14 @@ func TestBasic(t *testing.T) {
r = prepareGetRequest(ctx, cnrID.EncodeToString(), putRes.ObjectID) r = prepareGetRequest(ctx, cnrID.EncodeToString(), putRes.ObjectID)
hc.Handler().DownloadByAddressOrBucketName(r) hc.Handler().DownloadByAddressOrBucketName(r)
require.Equal(t, content, string(r.Response.Body())) require.Equal(t, content, string(r.Response.Body()))
r = prepareGetRequest(ctx, cnrID.EncodeToString(), objFilePath)
hc.Handler().DownloadByAddressOrBucketName(r)
require.Equal(t, content, string(r.Response.Body()))
r = prepareGetRequest(ctx, cnrID.EncodeToString(), objFileName)
hc.Handler().DownloadByAddressOrBucketName(r)
require.Equal(t, content, string(r.Response.Body()))
}) })
t.Run("head", func(t *testing.T) { t.Run("head", func(t *testing.T) {
@ -276,6 +287,16 @@ func TestBasic(t *testing.T) {
hc.Handler().HeadByAddressOrBucketName(r) hc.Handler().HeadByAddressOrBucketName(r)
require.Equal(t, putRes.ObjectID, string(r.Response.Header.Peek(hdrObjectID))) require.Equal(t, putRes.ObjectID, string(r.Response.Header.Peek(hdrObjectID)))
require.Equal(t, putRes.ContainerID, string(r.Response.Header.Peek(hdrContainerID))) require.Equal(t, putRes.ContainerID, string(r.Response.Header.Peek(hdrContainerID)))
r = prepareGetRequest(ctx, cnrID.EncodeToString(), objFilePath)
hc.Handler().HeadByAddressOrBucketName(r)
require.Equal(t, putRes.ObjectID, string(r.Response.Header.Peek(hdrObjectID)))
require.Equal(t, putRes.ContainerID, string(r.Response.Header.Peek(hdrContainerID)))
r = prepareGetRequest(ctx, cnrID.EncodeToString(), objFileName)
hc.Handler().HeadByAddressOrBucketName(r)
require.Equal(t, putRes.ObjectID, string(r.Response.Header.Peek(hdrObjectID)))
require.Equal(t, putRes.ContainerID, string(r.Response.Header.Peek(hdrContainerID)))
}) })
t.Run("get by attribute", func(t *testing.T) { t.Run("get by attribute", func(t *testing.T) {
@ -285,11 +306,11 @@ func TestBasic(t *testing.T) {
r = prepareGetByAttributeRequest(ctx, bktName, attrFileName, objFilePath) r = prepareGetByAttributeRequest(ctx, bktName, attrFileName, objFilePath)
hc.Handler().DownloadByAttribute(r) hc.Handler().DownloadByAttribute(r)
require.Equal(t, content, string(r.Response.Body())) require.Equal(t, fasthttp.StatusNotFound, r.Response.StatusCode())
r = prepareGetByAttributeRequest(ctx, bktName, attrFilePath, objFileName) r = prepareGetByAttributeRequest(ctx, bktName, attrFilePath, objFileName)
hc.Handler().DownloadByAttribute(r) hc.Handler().DownloadByAttribute(r)
require.Equal(t, content, string(r.Response.Body())) require.Equal(t, fasthttp.StatusNotFound, r.Response.StatusCode())
}) })
t.Run("head by attribute", func(t *testing.T) { t.Run("head by attribute", func(t *testing.T) {
@ -300,13 +321,11 @@ func TestBasic(t *testing.T) {
r = prepareGetByAttributeRequest(ctx, bktName, attrFileName, objFilePath) r = prepareGetByAttributeRequest(ctx, bktName, attrFileName, objFilePath)
hc.Handler().HeadByAttribute(r) hc.Handler().HeadByAttribute(r)
require.Equal(t, putRes.ObjectID, string(r.Response.Header.Peek(hdrObjectID))) require.Equal(t, fasthttp.StatusNotFound, r.Response.StatusCode())
require.Equal(t, putRes.ContainerID, string(r.Response.Header.Peek(hdrContainerID)))
r = prepareGetByAttributeRequest(ctx, bktName, attrFilePath, objFileName) r = prepareGetByAttributeRequest(ctx, bktName, attrFilePath, objFileName)
hc.Handler().HeadByAttribute(r) hc.Handler().HeadByAttribute(r)
require.Equal(t, putRes.ObjectID, string(r.Response.Header.Peek(hdrObjectID))) require.Equal(t, fasthttp.StatusNotFound, r.Response.StatusCode())
require.Equal(t, putRes.ContainerID, string(r.Response.Header.Peek(hdrContainerID)))
}) })
t.Run("zip", func(t *testing.T) { t.Run("zip", func(t *testing.T) {
@ -330,101 +349,187 @@ func TestBasic(t *testing.T) {
}) })
} }
func TestFindObjectByAttribute(t *testing.T) { func prepareHandlerAndBucket(t *testing.T) (*handlerContext, cid.ID) {
hc := prepareHandlerContext(t) hc := prepareHandlerContext(t)
hc.cfg.additionalFilenameSearch = true
bktName := "bucket" bktName := "bucket"
cnrID, cnr, err := hc.prepareContainer(bktName, acl.PublicRWExtended) cnrID, cnr, err := hc.prepareContainer(bktName, acl.PublicRWExtended)
require.NoError(t, err) require.NoError(t, err)
hc.frostfs.SetContainer(cnrID, cnr) hc.frostfs.SetContainer(cnrID, cnr)
ctx := context.Background() return hc, cnrID
ctx = middleware.SetNamespace(ctx, "") }
content := "hello" func TestGetObjectWithFallback(t *testing.T) {
r, err := prepareUploadRequest(ctx, cnrID.EncodeToString(), content) ctx := middleware.SetNamespace(context.Background(), "")
require.NoError(t, err)
hc.Handler().Upload(r) t.Run("by oid", func(t *testing.T) {
require.Equal(t, r.Response.StatusCode(), http.StatusOK) hc, cnrID := prepareHandlerAndBucket(t)
var putRes putResponse obj1ID := oidtest.ID()
err = json.Unmarshal(r.Response.Body(), &putRes) obj1 := object.New()
require.NoError(t, err) obj1.SetID(obj1ID)
obj1.SetPayload([]byte("obj1"))
hc.frostfs.objects[cnrID.String()+"/"+obj1ID.String()] = obj1
testAttrVal1 := "/folder/cat.jpg" r := prepareGetRequest(ctx, cnrID.EncodeToString(), obj1ID.String())
testAttrVal2 := "cat.jpg" hc.Handler().DownloadByAddressOrBucketName(r)
testAttrVal3 := "test-attr-val3" require.Equal(t, string(obj1.Payload()), string(r.Response.Body()))
})
for _, tc := range []struct {
name string t.Run("by filepath as it is", func(t *testing.T) {
firstAttr object.Attribute hc, cnrID := prepareHandlerAndBucket(t)
secondAttr object.Attribute
reqAttrKey string obj1ID := oidtest.ID()
reqAttrValue string obj1 := object.New()
err string obj1.SetID(obj1ID)
additionalSearch bool obj1.SetPayload([]byte("obj1"))
}{ obj1.SetAttributes(prepareObjectAttributes(object.AttributeFilePath, "filepath/obj1"))
{ hc.frostfs.objects[cnrID.String()+"/"+obj1ID.String()] = obj1
name: "success search by FileName",
firstAttr: prepareObjectAttributes(attrFilePath, testAttrVal1), obj2ID := oidtest.ID()
secondAttr: prepareObjectAttributes(attrFileName, testAttrVal2), obj2 := object.New()
reqAttrKey: attrFileName, obj2.SetID(obj2ID)
reqAttrValue: testAttrVal2, obj2.SetPayload([]byte("obj2"))
additionalSearch: false, obj2.SetAttributes(prepareObjectAttributes(object.AttributeFilePath, "/filepath/obj2"))
}, hc.frostfs.objects[cnrID.String()+"/"+obj2ID.String()] = obj2
{
name: "failed search by FileName", r := prepareGetRequest(ctx, cnrID.EncodeToString(), "filepath/obj1")
firstAttr: prepareObjectAttributes(attrFilePath, testAttrVal1), hc.Handler().DownloadByAddressOrBucketName(r)
secondAttr: prepareObjectAttributes(attrFileName, testAttrVal2), require.Equal(t, string(obj1.Payload()), string(r.Response.Body()))
reqAttrKey: attrFileName,
reqAttrValue: testAttrVal3, r = prepareGetRequest(ctx, cnrID.EncodeToString(), "/filepath/obj2")
err: "not found", hc.Handler().DownloadByAddressOrBucketName(r)
additionalSearch: false, require.Equal(t, string(obj2.Payload()), string(r.Response.Body()))
}, })
{
name: "success search by FilePath (with additional search)", t.Run("by filepath slash fallback", func(t *testing.T) {
firstAttr: prepareObjectAttributes(attrFilePath, testAttrVal1), hc, cnrID := prepareHandlerAndBucket(t)
secondAttr: prepareObjectAttributes(attrFileName, testAttrVal2),
reqAttrKey: attrFilePath, obj1ID := oidtest.ID()
reqAttrValue: testAttrVal2, obj1 := object.New()
additionalSearch: true, obj1.SetID(obj1ID)
}, obj1.SetPayload([]byte("obj1"))
{ obj1.SetAttributes(prepareObjectAttributes(object.AttributeFilePath, "filepath/obj1"))
name: "failed by FilePath (with additional search)", hc.frostfs.objects[cnrID.String()+"/"+obj1ID.String()] = obj1
firstAttr: prepareObjectAttributes(attrFilePath, testAttrVal1),
secondAttr: prepareObjectAttributes(attrFileName, testAttrVal2), r := prepareGetRequest(ctx, cnrID.EncodeToString(), "/filepath/obj1")
reqAttrKey: attrFilePath, hc.Handler().DownloadByAddressOrBucketName(r)
reqAttrValue: testAttrVal3, require.Equal(t, fasthttp.StatusNotFound, r.Response.StatusCode())
err: "not found",
additionalSearch: true, hc.cfg.additionalSlashSearch = true
},
{ r = prepareGetRequest(ctx, cnrID.EncodeToString(), "/filepath/obj1")
name: "success search by FilePath with leading slash (with additional search)", hc.Handler().DownloadByAddressOrBucketName(r)
firstAttr: prepareObjectAttributes(attrFilePath, testAttrVal1), require.Equal(t, string(obj1.Payload()), string(r.Response.Body()))
secondAttr: prepareObjectAttributes(attrFileName, testAttrVal2), })
reqAttrKey: attrFilePath,
reqAttrValue: "/cat.jpg", t.Run("by filename fallback", func(t *testing.T) {
additionalSearch: true, hc, cnrID := prepareHandlerAndBucket(t)
},
} { obj1ID := oidtest.ID()
t.Run(tc.name, func(t *testing.T) { obj1 := object.New()
obj := hc.frostfs.objects[putRes.ContainerID+"/"+putRes.ObjectID] obj1.SetID(obj1ID)
obj.SetAttributes(tc.firstAttr, tc.secondAttr) obj1.SetPayload([]byte("obj1"))
hc.cfg.additionalFilenameSearch = tc.additionalSearch obj1.SetAttributes(prepareObjectAttributes(object.AttributeFileName, "filename/obj1"))
hc.frostfs.objects[cnrID.String()+"/"+obj1ID.String()] = obj1
objID, err := hc.Handler().findObjectByAttribute(ctx, cnrID, tc.reqAttrKey, tc.reqAttrValue)
if tc.err != "" { r := prepareGetRequest(ctx, cnrID.EncodeToString(), "filename/obj1")
require.Error(t, err) hc.Handler().DownloadByAddressOrBucketName(r)
require.Contains(t, err.Error(), tc.err) require.Equal(t, fasthttp.StatusNotFound, r.Response.StatusCode())
return
} hc.cfg.additionalFilenameSearch = true
require.NoError(t, err) r = prepareGetRequest(ctx, cnrID.EncodeToString(), "filename/obj1")
require.Equal(t, putRes.ObjectID, objID.EncodeToString()) hc.Handler().DownloadByAddressOrBucketName(r)
require.Equal(t, string(obj1.Payload()), string(r.Response.Body()))
})
t.Run("by filename and slash fallback", func(t *testing.T) {
hc, cnrID := prepareHandlerAndBucket(t)
obj1ID := oidtest.ID()
obj1 := object.New()
obj1.SetID(obj1ID)
obj1.SetPayload([]byte("obj1"))
obj1.SetAttributes(prepareObjectAttributes(object.AttributeFileName, "filename/obj1"))
hc.frostfs.objects[cnrID.String()+"/"+obj1ID.String()] = obj1
r := prepareGetRequest(ctx, cnrID.EncodeToString(), "/filename/obj1")
hc.Handler().DownloadByAddressOrBucketName(r)
require.Equal(t, fasthttp.StatusNotFound, r.Response.StatusCode())
hc.cfg.additionalFilenameSearch = true
r = prepareGetRequest(ctx, cnrID.EncodeToString(), "/filename/obj1")
hc.Handler().DownloadByAddressOrBucketName(r)
require.Equal(t, fasthttp.StatusNotFound, r.Response.StatusCode())
hc.cfg.additionalSlashSearch = true
r = prepareGetRequest(ctx, cnrID.EncodeToString(), "/filename/obj1")
hc.Handler().DownloadByAddressOrBucketName(r)
require.Equal(t, string(obj1.Payload()), string(r.Response.Body()))
})
t.Run("index fallback", func(t *testing.T) {
hc, cnrID := prepareHandlerAndBucket(t)
obj1ID := oidtest.ID()
obj1 := object.New()
obj1.SetID(obj1ID)
obj1.SetPayload([]byte("obj1"))
obj1.SetAttributes(prepareObjectAttributes(object.AttributeFilePath, "filepath/index.html"))
hc.frostfs.objects[cnrID.String()+"/"+obj1ID.String()] = obj1
r := prepareGetRequest(ctx, cnrID.EncodeToString(), "filepath/")
hc.Handler().DownloadByAddressOrBucketName(r)
require.Equal(t, fasthttp.StatusNotFound, r.Response.StatusCode())
r = prepareGetRequest(ctx, cnrID.EncodeToString(), "filepath")
hc.Handler().DownloadByAddressOrBucketName(r)
require.Equal(t, fasthttp.StatusNotFound, r.Response.StatusCode())
hc.cfg.indexEnabled = true
r = prepareGetRequest(ctx, cnrID.EncodeToString(), "filepath")
hc.Handler().DownloadByAddressOrBucketName(r)
require.Equal(t, string(obj1.Payload()), string(r.Response.Body()))
r = prepareGetRequest(ctx, cnrID.EncodeToString(), "filepath/")
hc.Handler().DownloadByAddressOrBucketName(r)
require.Equal(t, string(obj1.Payload()), string(r.Response.Body()))
})
t.Run("index filename fallback", func(t *testing.T) {
hc, cnrID := prepareHandlerAndBucket(t)
obj1ID := oidtest.ID()
obj1 := object.New()
obj1.SetID(obj1ID)
obj1.SetPayload([]byte("obj1"))
obj1.SetAttributes(prepareObjectAttributes(object.AttributeFileName, "filename/index.html"))
hc.frostfs.objects[cnrID.String()+"/"+obj1ID.String()] = obj1
r := prepareGetRequest(ctx, cnrID.EncodeToString(), "filename/")
hc.Handler().DownloadByAddressOrBucketName(r)
require.Equal(t, fasthttp.StatusNotFound, r.Response.StatusCode())
r = prepareGetRequest(ctx, cnrID.EncodeToString(), "filename")
hc.Handler().DownloadByAddressOrBucketName(r)
require.Equal(t, fasthttp.StatusNotFound, r.Response.StatusCode())
hc.cfg.indexEnabled = true
hc.cfg.additionalFilenameSearch = true
r = prepareGetRequest(ctx, cnrID.EncodeToString(), "filename")
hc.Handler().DownloadByAddressOrBucketName(r)
require.Equal(t, string(obj1.Payload()), string(r.Response.Body()))
r = prepareGetRequest(ctx, cnrID.EncodeToString(), "filename/")
hc.Handler().DownloadByAddressOrBucketName(r)
require.Equal(t, string(obj1.Payload()), string(r.Response.Body()))
}) })
}
} }
func prepareUploadRequest(ctx context.Context, bucket, content string) (*fasthttp.RequestCtx, error) { func prepareUploadRequest(ctx context.Context, bucket, content string) (*fasthttp.RequestCtx, error) {

View file

@ -175,15 +175,6 @@ func (h *Handler) HeadByAddressOrBucketName(req *fasthttp.RequestCtx) {
Middleware{Func: h.byAttributeSearchMiddleware(h.headObject, object.AttributeFileName, indexFormer), Enabled: fileNameFallbackEnabled && indexPageEnabled}, Middleware{Func: h.byAttributeSearchMiddleware(h.headObject, object.AttributeFileName, indexFormer), Enabled: fileNameFallbackEnabled && indexPageEnabled},
) )
} }
var objID oid.ID
if checkS3Err == nil {
h.byS3Path(ctx, req, bktInfo, oidParam, h.headObject)
} else if err = objID.DecodeString(oidParam); err == nil {
h.byNativeAddress(ctx, req, bktInfo.CID, objID, h.headObject)
} else {
h.logAndSendError(ctx, req, logs.InvalidOIDParam, err)
}
} }
// HeadByAttribute handles attribute-based head requests. // HeadByAttribute handles attribute-based head requests.