package tree import ( "context" "testing" "time" "git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/data" cidtest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id/test" oidtest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id/test" usertest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user/test" "github.com/nspcc-dev/neo-go/pkg/crypto/keys" "github.com/stretchr/testify/require" "go.uber.org/zap/zaptest" ) func TestLockConfigurationEncoding(t *testing.T) { for _, tc := range []struct { name string encoded string expectedEncoded string expected data.ObjectLockConfiguration error bool }{ { name: "empty", encoded: "", expectedEncoded: "", expected: data.ObjectLockConfiguration{}, }, { name: "Enabled", encoded: "Enabled", expectedEncoded: "Enabled", expected: data.ObjectLockConfiguration{ ObjectLockEnabled: "Enabled", }, }, { name: "Fully enabled", encoded: "Enabled,10,COMPLIANCE,", expectedEncoded: "Enabled,10,COMPLIANCE,0", expected: data.ObjectLockConfiguration{ ObjectLockEnabled: "Enabled", Rule: &data.ObjectLockRule{ DefaultRetention: &data.DefaultRetention{ Days: 10, Mode: "COMPLIANCE", }, }, }, }, { name: "Missing numbers", encoded: "Enabled,,COMPLIANCE,", expectedEncoded: "Enabled,0,COMPLIANCE,0", expected: data.ObjectLockConfiguration{ ObjectLockEnabled: "Enabled", Rule: &data.ObjectLockRule{ DefaultRetention: &data.DefaultRetention{ Mode: "COMPLIANCE", }, }, }, }, { name: "Missing all", encoded: ",,,", expectedEncoded: ",0,,0", expected: data.ObjectLockConfiguration{Rule: &data.ObjectLockRule{DefaultRetention: &data.DefaultRetention{}}}, }, { name: "Invalid args", encoded: ",,", error: true, }, { name: "Invalid days", encoded: ",a,,", error: true, }, { name: "Invalid years", encoded: ",,,b", error: true, }, } { t.Run(tc.name, func(t *testing.T) { lockConfiguration, err := parseLockConfiguration(tc.encoded) if tc.error { require.Error(t, err) return } require.NoError(t, err) require.Equal(t, tc.expected, *lockConfiguration) encoded := encodeLockConfiguration(lockConfiguration) require.Equal(t, tc.expectedEncoded, encoded) }) } } func TestTreeServiceSettings(t *testing.T) { ctx := context.Background() memCli, err := NewTreeServiceClientMemory() require.NoError(t, err) treeService := NewTree(memCli, zaptest.NewLogger(t)) bktInfo := &data.BucketInfo{ CID: cidtest.ID(), } key, err := keys.NewPrivateKey() require.NoError(t, err) settings := &data.BucketSettings{ Versioning: "Versioning", LockConfiguration: &data.ObjectLockConfiguration{ ObjectLockEnabled: "Enabled", Rule: &data.ObjectLockRule{ DefaultRetention: &data.DefaultRetention{ Days: 1, Mode: "mode", }, }, }, OwnerKey: key.PublicKey(), } err = treeService.PutSettingsNode(ctx, bktInfo, settings) require.NoError(t, err) storedSettings, err := treeService.GetSettingsNode(ctx, bktInfo) require.NoError(t, err) require.Equal(t, settings, storedSettings) } func TestTreeServiceAddVersion(t *testing.T) { ctx := context.Background() memCli, err := NewTreeServiceClientMemory() require.NoError(t, err) treeService := NewTree(memCli, zaptest.NewLogger(t)) bktInfo := &data.BucketInfo{ CID: cidtest.ID(), } userID := usertest.ID() now := time.Now() version := &data.NodeVersion{ BaseNodeVersion: data.BaseNodeVersion{ OID: oidtest.ID(), Size: 10, ETag: "etag", FilePath: "path/to/version", Owner: &userID, Created: &now, }, IsUnversioned: true, } nodeID, err := treeService.AddVersion(ctx, bktInfo, version) require.NoError(t, err) storedNode, err := treeService.GetUnversioned(ctx, bktInfo, "path/to/version") require.NoError(t, err) require.Equal(t, nodeID, storedNode.ID) require.Equal(t, version.BaseNodeVersion.Size, storedNode.Size) require.Equal(t, version.BaseNodeVersion.ETag, storedNode.ETag) require.Equal(t, version.BaseNodeVersion.ETag, storedNode.ETag) require.Equal(t, version.BaseNodeVersion.FilePath, storedNode.FilePath) require.Equal(t, version.BaseNodeVersion.OID, storedNode.OID) versions, err := treeService.GetVersions(ctx, bktInfo, "path/to/version") require.NoError(t, err) require.Len(t, versions, 1) require.Equal(t, storedNode, versions[0]) } func TestGetLatestNode(t *testing.T) { for _, tc := range []struct { name string nodes []NodeResponse expectedNodeID uint64 error bool }{ { name: "empty", nodes: []NodeResponse{}, error: true, }, { name: "one node of the object version", nodes: []NodeResponse{ nodeResponse{ nodeID: 1, parentID: 0, timestamp: 1, meta: []nodeMeta{ { key: oidKV, value: []byte(oidtest.ID().String()), }, }, }, }, expectedNodeID: 1, }, { name: "one node of the object version and one node of the secondary object", nodes: []NodeResponse{ nodeResponse{ nodeID: 2, parentID: 0, timestamp: 3, meta: []nodeMeta{}, }, nodeResponse{ nodeID: 1, parentID: 0, timestamp: 1, meta: []nodeMeta{ { key: oidKV, value: []byte(oidtest.ID().String()), }, }, }, }, expectedNodeID: 1, }, { name: "all nodes represent a secondary object", nodes: []NodeResponse{ nodeResponse{ nodeID: 2, parentID: 0, timestamp: 3, meta: []nodeMeta{}, }, nodeResponse{ nodeID: 4, parentID: 0, timestamp: 5, meta: []nodeMeta{}, }, }, error: true, }, { name: "several nodes of different types and with different timestamp", nodes: []NodeResponse{ nodeResponse{ nodeID: 1, parentID: 0, timestamp: 1, meta: []nodeMeta{ { key: oidKV, value: []byte(oidtest.ID().String()), }, }, }, nodeResponse{ nodeID: 3, parentID: 0, timestamp: 3, meta: []nodeMeta{}, }, nodeResponse{ nodeID: 4, parentID: 0, timestamp: 4, meta: []nodeMeta{ { key: oidKV, value: []byte(oidtest.ID().String()), }, }, }, nodeResponse{ nodeID: 6, parentID: 0, timestamp: 6, meta: []nodeMeta{}, }, }, expectedNodeID: 4, }, } { t.Run(tc.name, func(t *testing.T) { actualNode, err := getLatestNode(tc.nodes) if tc.error { require.Error(t, err) return } require.NoError(t, err) require.Equal(t, tc.expectedNodeID, actualNode.GetNodeID()) }) } }